Page MenuHomeFreeBSD

No OneTemporary

This file is larger than 256 KB, so syntax highlighting was skipped.
This document is not UTF8. It was detected as ISO-8859-1 (Latin 1) and converted to UTF8 for display.
diff --git a/contrib/wpa/.gitignore b/contrib/wpa/.gitignore
new file mode 100644
index 000000000000..b064303ced30
--- /dev/null
+++ b/contrib/wpa/.gitignore
@@ -0,0 +1,8 @@
+*.pyc
+*~
+tests/hwsim/logs
+tests/remote/logs
+wpaspy/build
+**/parallel-vm.log
+tags
+build/
diff --git a/contrib/wpa/Android.mk b/contrib/wpa/Android.mk
new file mode 100644
index 000000000000..bd7a4097444b
--- /dev/null
+++ b/contrib/wpa/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+
+ifneq ($(filter VER_0_8_X VER_2_1_DEVEL,$(WPA_SUPPLICANT_VERSION)),)
+# The order of the 2 Android.mks does matter!
+# TODO: Clean up the Android.mks, reset all the temporary variables at the
+# end of each Android.mk, so that one Android.mk doesn't depend on variables
+# set up in the other Android.mk.
+include $(LOCAL_PATH)/hostapd/Android.mk \
+ $(LOCAL_PATH)/wpa_supplicant/Android.mk
+endif
diff --git a/contrib/wpa/build_release b/contrib/wpa/build_release
new file mode 100755
index 000000000000..3aa9bf31963a
--- /dev/null
+++ b/contrib/wpa/build_release
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+set -e
+
+if [ -z "$1" ]; then
+ echo "build_release <version>"
+ exit 1
+fi
+
+TMP=tmp.build_release
+RELDIR=`pwd`/Release
+VER=$1
+NOW=`date +%Y-%m-%d`
+
+echo "Version: $VER - $NOW"
+
+DATEw=`head -n 3 wpa_supplicant/ChangeLog | tail -n 1 | sed "s/ .*//"`
+DATEh=`head -n 3 hostapd/ChangeLog | tail -n 1 | sed "s/ .*//"`
+
+if [ "$DATEw" != "$NOW" -o "$DATEh" != "$NOW" ]; then
+ echo "NOTE! Date mismatch in ChangeLog: wpa_supplicant $DATEw hostapd $DATEh != $NOW"
+fi
+
+if [ -r $TMP ]; then
+ echo "Temporary directory '$TMP' exists. Remove it before running this."
+ exit 1
+fi
+
+mkdir $TMP
+mkdir -p $RELDIR
+
+git archive --format=tar --prefix=wpa-$VER/ HEAD \
+ README COPYING CONTRIBUTIONS src wpa_supplicant hostapd hs20 |
+ gzip > $RELDIR/wpa-$VER.tar.gz
+git archive --format=tar --prefix=hostapd-$VER/ HEAD \
+ README COPYING CONTRIBUTIONS src hostapd |
+ gzip > $RELDIR/hostapd-$VER.tar.gz
+git archive --format=tar --prefix=wpa_supplicant-$VER/ HEAD \
+ README COPYING CONTRIBUTIONS src wpa_supplicant hs20/client |
+ tar --directory=$TMP -xf -
+
+cd $TMP
+make -C wpa_supplicant-$VER/wpa_supplicant/doc/docbook man
+rm -f wpa_supplicant-$VER/wpa_supplicant/doc/docbook/manpage.{links,refs}
+tar czf $RELDIR/wpa_supplicant-$VER.tar.gz wpa_supplicant-$VER
+cd ..
+rm -r $TMP
diff --git a/contrib/wpa/doc/.gitignore b/contrib/wpa/doc/.gitignore
new file mode 100644
index 000000000000..28c3fe4e99c3
--- /dev/null
+++ b/contrib/wpa/doc/.gitignore
@@ -0,0 +1,14 @@
+doxygen.warnings
+hostapd.eps
+hostapd.png
+html
+latex
+p2p_arch.eps
+p2p_arch.png
+p2p_arch2.eps
+p2p_arch2.png
+p2p_sm.eps
+p2p_sm.png
+wpa_supplicant.eps
+wpa_supplicant.png
+wpa_supplicant-devel.pdf
diff --git a/contrib/wpa/doc/Makefile b/contrib/wpa/doc/Makefile
new file mode 100644
index 000000000000..62af04a74f1f
--- /dev/null
+++ b/contrib/wpa/doc/Makefile
@@ -0,0 +1,42 @@
+all: docs
+
+%.eps: %.fig
+ fig2dev -L eps $*.fig $*.eps
+
+%.png: %.fig
+ fig2dev -L png -m 3 $*.fig | pngtopnm | pnmscale 0.4 | pnmtopng \
+ > $*.png
+
+%.png: %.dot
+ dot $*.dot -Tpng -o $*.png
+
+%.eps: %.dot
+ dot $*.dot -Tps -o $*.eps
+
+_wpa_supplicant.png: wpa_supplicant.png
+ cp $< $@
+
+_wpa_supplicant.eps: wpa_supplicant.eps
+ cp $< $@
+
+docs-pics: wpa_supplicant.png wpa_supplicant.eps hostapd.png hostapd.eps p2p_sm.png p2p_sm.eps p2p_arch.png p2p_arch.eps p2p_arch2.png p2p_arch2.eps _wpa_supplicant.png _wpa_supplicant.eps
+
+docs: docs-pics
+ (cd ..; doxygen doc/doxygen.conf; cd doc)
+ $(MAKE) -C latex
+ cp latex/refman.pdf wpa_supplicant-devel.pdf
+
+html: docs-pics
+ (cd ..; doxygen doc/doxygen.conf; cd doc)
+
+clean:
+ rm -f *~
+ rm -f wpa_supplicant.eps wpa_supplicant.png
+ rm -f _wpa_supplicant.png _wpa_supplicant.eps
+ rm -f hostapd.eps hostapd.png
+ rm -f p2p_sm.eps p2p_sm.png
+ rm -f p2p_arch.eps p2p_arch.png
+ rm -f p2p_arch2.eps p2p_arch2.png
+ rm -f doxygen.warnings
+ rm -rf html latex
+ rm -f wpa_supplicant-devel.pdf
diff --git a/contrib/wpa/doc/code_structure.doxygen b/contrib/wpa/doc/code_structure.doxygen
new file mode 100644
index 000000000000..454f179753d3
--- /dev/null
+++ b/contrib/wpa/doc/code_structure.doxygen
@@ -0,0 +1,315 @@
+/**
+\page code_structure Structure of the source code
+
+[ \ref _wpa_supplicant_core "wpa_supplicant core functionality" |
+\ref generic_helper_func "Generic helper functions" |
+\ref crypto_func "Cryptographic functions" |
+\ref tls_func "TLS library" |
+\ref configuration "Configuration" |
+\ref ctrl_iface "Control interface" |
+\ref wpa_code "WPA supplicant" |
+\ref eap_peer "EAP peer" |
+\ref eapol_supp "EAPOL supplicant" |
+\ref win_port "Windows port" |
+\ref test_programs "Test programs" ]
+
+wpa_supplicant implementation is divided into number of independent
+modules. Core code includes functionality for controlling the network
+selection, association, and configuration. Independent modules include
+WPA code (key handshake, PMKSA caching, pre-authentication), EAPOL
+state machine, and EAP state machine and methods. In addition, there
+are number of separate files for generic helper functions.
+
+Both WPA and EAPOL/EAP state machines can be used separately in other
+programs than wpa_supplicant. As an example, the included test
+programs eapol_test and preauth_test are using these modules.
+
+\ref driver_wrapper "Driver interface API" is defined in \ref driver.h and
+all hardware/driver dependent functionality is implemented in
+driver_*.c.
+
+
+\section _wpa_supplicant_core wpa_supplicant core functionality
+
+\ref wpa_supplicant.c
+ Program initialization, main control loop
+
+\ref wpa_supplicant/main.c
+ main() for UNIX-like operating systems and MinGW (Windows); this
+ uses command line arguments to configure wpa_supplicant
+
+\ref events.c
+ Driver event processing; \ref wpa_supplicant_event() and related functions
+
+\ref wpa_supplicant_i.h
+ Internal definitions for wpa_supplicant core; should not be
+ included into independent modules
+
+
+\section generic_helper_func Generic helper functions
+
+wpa_supplicant uses generic helper functions some of which are shared
+with with hostapd. The following C files are currently used:
+
+\ref eloop.c and \ref eloop.h
+ Event loop (select() loop with registerable timeouts, socket read
+ callbacks, and signal callbacks)
+
+\ref common.c and \ref common.h
+ Common helper functions
+
+\ref defs.h
+ Definitions shared by multiple files
+
+\ref l2_packet.h, \ref l2_packet_linux.c, and \ref l2_packet_pcap.c
+ Layer 2 (link) access wrapper (includes native Linux implementation
+ and wrappers for libdnet/libpcap). A new l2_packet implementation
+ may need to be added when porting to new operating systems that are
+ not supported by libdnet/libpcap. Makefile can be used to select which
+ l2_packet implementation is included. \ref l2_packet_linux.c uses Linux
+ packet sockets and \ref l2_packet_pcap.c has a more portable version using
+ libpcap and libdnet.
+
+\ref pcsc_funcs.c and \ref pcsc_funcs.h
+ Wrapper for PC/SC lite SIM and smart card readers
+
+\ref priv_netlink.h
+ Private version of netlink definitions from Linux kernel header files;
+ this could be replaced with C library header file once suitable
+ version becomes commonly available
+
+\ref version.h
+ Version number definitions
+
+
+\section crypto_func Cryptographic functions
+
+\ref md5.c and \ref md5.h
+ MD5 (replaced with a crypto library if TLS support is included)
+ HMAC-MD5 (keyed checksum for message authenticity validation)
+
+\ref rc4.c and \ref rc4.h
+ RC4 (broadcast/default key encryption)
+
+\ref sha1.c and \ref sha1.h
+ SHA-1 (replaced with a crypto library if TLS support is included)
+ HMAC-SHA-1 (keyed checksum for message authenticity validation)
+ PRF-SHA-1 (pseudorandom (key/nonce generation) function)
+ PBKDF2-SHA-1 (ASCII passphrase to shared secret)
+ T-PRF (for EAP-FAST)
+ TLS-PRF (RFC 2246)
+
+\ref sha256.c and \ref sha256.h
+ SHA-256 (replaced with a crypto library if TLS support is included)
+
+\ref aes-wrap.c, \ref aes_wrap.h, \ref aes.c
+ AES (replaced with a crypto library if TLS support is included),
+ AES Key Wrap Algorithm with 128-bit KEK, RFC3394 (broadcast/default
+ key encryption),
+ One-Key CBC MAC (OMAC1) hash with AES-128,
+ AES-128 CTR mode encryption,
+ AES-128 EAX mode encryption/decryption,
+ AES-128 CBC
+
+\ref crypto.h
+ Definition of crypto library wrapper
+
+\ref crypto_openssl.c
+ Wrapper functions for libcrypto (OpenSSL)
+
+\ref crypto_internal.c
+ Wrapper functions for internal crypto implementation
+
+\ref crypto_gnutls.c
+ Wrapper functions for libgcrypt (used by GnuTLS)
+
+\ref ms_funcs.c and \ref ms_funcs.h
+ Helper functions for MSCHAPV2 and LEAP
+
+\ref tls.h
+ Definition of TLS library wrapper
+
+\ref tls_none.c
+ Dummy implementation of TLS library wrapper for cases where TLS
+ functionality is not included.
+
+\ref tls_openssl.c
+ TLS library wrapper for openssl
+
+\ref tls_internal.c
+ TLS library for internal TLS implementation
+
+\ref tls_gnutls.c
+ TLS library wrapper for GnuTLS
+
+
+\section tls_func TLS library
+
+\ref asn1.c and \ref asn1.h
+ ASN.1 DER parsing
+
+\ref bignum.c and \ref bignum.h
+ Big number math
+
+\ref rsa.c and \ref rsa.h
+ RSA
+
+\ref x509v3.c and \ref x509v3.h
+ X.509v3 certificate parsing and processing
+
+\ref tlsv1_client.c, \ref tlsv1_client.h
+ TLSv1 client (RFC 2246)
+
+\ref tlsv1_client_i.h
+ Internal structures for TLSv1 client
+
+\ref tlsv1_client_read.c
+ TLSv1 client: read handshake messages
+
+\ref tlsv1_client_write.c
+ TLSv1 client: write handshake messages
+
+\ref tlsv1_common.c and \ref tlsv1_common.h
+ Common TLSv1 routines and definitions
+
+\ref tlsv1_cred.c and \ref tlsv1_cred.h
+ TLSv1 credentials
+
+\ref tlsv1_record.c and \ref tlsv1_record.h
+ TLSv1 record protocol
+
+
+\section configuration Configuration
+
+\ref config_ssid.h
+ Definition of per network configuration items
+
+\ref config.h
+ Definition of the wpa_supplicant configuration
+
+\ref config.c
+ Configuration parser and common functions
+
+\ref wpa_supplicant/config_file.c
+ Configuration backend for text files (e.g., wpa_supplicant.conf)
+
+\ref config_winreg.c
+ Configuration backend for Windows registry
+
+
+\section ctrl_iface Control interface
+
+wpa_supplicant has a \ref ctrl_iface_page "control interface"
+that can be used to get status
+information and manage operations from external programs. An example
+command line interface (wpa_cli) and GUI (wpa_gui) for this interface
+are included in the wpa_supplicant distribution.
+
+\ref wpa_supplicant/ctrl_iface.c and \ref wpa_supplicant/ctrl_iface.h
+ wpa_supplicant-side of the control interface
+
+\ref ctrl_iface_unix.c
+ UNIX domain sockets -based control interface backend
+
+\ref ctrl_iface_udp.c
+ UDP sockets -based control interface backend
+
+\ref ctrl_iface_named_pipe.c
+ Windows named pipes -based control interface backend
+
+\ref wpa_ctrl.c and \ref wpa_ctrl.h
+ Library functions for external programs to provide access to the
+ wpa_supplicant control interface
+
+\ref wpa_cli.c
+ Example program for using wpa_supplicant control interface
+
+
+\section wpa_code WPA supplicant
+
+\ref wpa.c and \ref wpa.h
+ WPA state machine and 4-Way/Group Key Handshake processing
+
+\ref preauth.c and \ref preauth.h
+ PMKSA caching and pre-authentication (RSN/WPA2)
+
+\ref wpa_i.h
+ Internal definitions for WPA code; not to be included to other modules.
+
+\section eap_peer EAP peer
+
+\ref eap_peer_module "EAP peer implementation" is a separate module that
+can be used by other programs than just wpa_supplicant.
+
+\ref eap.c and \ref eap.h
+ EAP state machine and method interface
+
+\ref eap_defs.h
+ Common EAP definitions
+
+\ref eap_i.h
+ Internal definitions for EAP state machine and EAP methods; not to be
+ included in other modules
+
+\ref eap_sim_common.c and \ref eap_sim_common.h
+ Common code for EAP-SIM and EAP-AKA
+
+\ref eap_tls_common.c and \ref eap_tls_common.h
+ Common code for EAP-PEAP, EAP-TTLS, and EAP-FAST
+
+\ref eap_ttls.c and \ref eap_ttls.h
+ EAP-TTLS
+
+\ref eap_pax.c, \ref eap_pax_common.h, \ref eap_pax_common.c
+ EAP-PAX
+
+\ref eap_psk.c, \ref eap_psk_common.h, \ref eap_psk_common.c
+ EAP-PSK (note: this is not needed for WPA-PSK)
+
+\ref eap_sake.c, \ref eap_sake_common.h, \ref eap_sake_common.c
+ EAP-SAKE
+
+\ref eap_gpsk.c, \ref eap_gpsk_common.h, \ref eap_gpsk_common.c
+ EAP-GPSK
+
+\ref eap_aka.c, \ref eap_fast.c, \ref eap_gtc.c, \ref eap_leap.c,
+\ref eap_md5.c, \ref eap_mschapv2.c, \ref eap_otp.c, \ref eap_peap.c,
+\ref eap_sim.c, \ref eap_tls.c
+ Other EAP method implementations
+
+
+\section eapol_supp EAPOL supplicant
+
+\ref eapol_supp_sm.c and \ref eapol_supp_sm.h
+ EAPOL supplicant state machine and IEEE 802.1X processing
+
+
+\section win_port Windows port
+
+\ref ndis_events.c
+ Code for receiving NdisMIndicateStatus() events and delivering them to
+ wpa_supplicant \ref driver_ndis.c in more easier to use form
+
+\ref win_if_list.c
+ External program for listing current network interface
+
+
+\section test_programs Test programs
+
+\ref radius_client.c and \ref radius_client.h
+ RADIUS authentication client implementation for eapol_test
+
+\ref radius.c and \ref radius.h
+ RADIUS message processing for eapol_test
+
+\ref eapol_test.c
+ Standalone EAP testing tool with integrated RADIUS authentication
+ client
+
+\ref preauth_test.c
+ Standalone RSN pre-authentication tool
+
+\ref wpa_passphrase.c
+ WPA ASCII passphrase to PSK conversion
+
+*/
diff --git a/contrib/wpa/doc/ctrl_iface.doxygen b/contrib/wpa/doc/ctrl_iface.doxygen
new file mode 100644
index 000000000000..7dccdc797ef3
--- /dev/null
+++ b/contrib/wpa/doc/ctrl_iface.doxygen
@@ -0,0 +1,1054 @@
+/**
+\page ctrl_iface_page wpa_supplicant control interface
+
+wpa_supplicant implements a control interface that can be used by
+external programs to control the operations of the wpa_supplicant
+daemon and to get status information and event notifications. There is
+a small C library, in a form of a single C file, \ref wpa_ctrl.c, that
+provides helper functions to facilitate the use of the control
+interface. External programs can link this file into them and then use
+the library functions documented in \ref wpa_ctrl.h to interact with
+wpa_supplicant. This library can also be used with C++. \ref wpa_cli.c and
+wpa_gui are example programs using this library.
+
+There are multiple mechanisms for inter-process communication. For
+example, Linux version of wpa_supplicant is using UNIX domain sockets
+for the control interface and Windows version UDP sockets. The use of
+the functions defined in \ref wpa_ctrl.h can be used to hide the details of
+the used IPC from external programs.
+
+
+\section using_ctrl_iface Using the control interface
+
+External programs, e.g., a GUI or a configuration utility, that need to
+communicate with wpa_supplicant should link in \ref wpa_ctrl.c. This
+allows them to use helper functions to open connection to the control
+interface with \ref wpa_ctrl_open() and to send commands with
+\ref wpa_ctrl_request().
+
+wpa_supplicant uses the control interface for two types of communication:
+commands and unsolicited event messages. Commands are a pair of
+messages, a request from the external program and a response from
+wpa_supplicant. These can be executed using \ref wpa_ctrl_request().
+Unsolicited event messages are sent by wpa_supplicant to the control
+interface connection without specific request from the external program
+for receiving each message. However, the external program needs to
+attach to the control interface with \ref wpa_ctrl_attach() to receive these
+unsolicited messages.
+
+If the control interface connection is used both for commands and
+unsolicited event messages, there is potential for receiving an
+unsolicited message between the command request and response.
+\ref wpa_ctrl_request() caller will need to supply a callback, msg_cb,
+for processing these messages. Often it is easier to open two
+control interface connections by calling \ref wpa_ctrl_open() twice and
+then use one of the connections for commands and the other one for
+unsolicited messages. This way command request/response pairs will
+not be broken by unsolicited messages. wpa_cli is an example of how
+to use only one connection for both purposes and wpa_gui demonstrates
+how to use two separate connections.
+
+Once the control interface connection is not needed anymore, it should
+be closed by calling \ref wpa_ctrl_close(). If the connection was used for
+unsolicited event messages, it should be first detached by calling
+\ref wpa_ctrl_detach().
+
+
+\section ctrl_iface_cmds Control interface commands
+
+Following commands can be used with \ref wpa_ctrl_request():
+
+\subsection ctrl_iface_PING PING
+
+This command can be used to test whether wpa_supplicant is replying
+to the control interface commands. The expected reply is \c PONG if the
+connection is open and wpa_supplicant is processing commands.
+
+
+\subsection ctrl_iface_MIB MIB
+
+Request a list of MIB variables (dot1x, dot11). The output is a text
+block with each line in \c variable=value format. For example:
+
+\verbatim
+dot11RSNAOptionImplemented=TRUE
+dot11RSNAPreauthenticationImplemented=TRUE
+dot11RSNAEnabled=FALSE
+dot11RSNAPreauthenticationEnabled=FALSE
+dot11RSNAConfigVersion=1
+dot11RSNAConfigPairwiseKeysSupported=5
+dot11RSNAConfigGroupCipherSize=128
+dot11RSNAConfigPMKLifetime=43200
+dot11RSNAConfigPMKReauthThreshold=70
+dot11RSNAConfigNumberOfPTKSAReplayCounters=1
+dot11RSNAConfigSATimeout=60
+dot11RSNAAuthenticationSuiteSelected=00-50-f2-2
+dot11RSNAPairwiseCipherSelected=00-50-f2-4
+dot11RSNAGroupCipherSelected=00-50-f2-4
+dot11RSNAPMKIDUsed=
+dot11RSNAAuthenticationSuiteRequested=00-50-f2-2
+dot11RSNAPairwiseCipherRequested=00-50-f2-4
+dot11RSNAGroupCipherRequested=00-50-f2-4
+dot11RSNAConfigNumberOfGTKSAReplayCounters=0
+dot11RSNA4WayHandshakeFailures=0
+dot1xSuppPaeState=5
+dot1xSuppHeldPeriod=60
+dot1xSuppAuthPeriod=30
+dot1xSuppStartPeriod=30
+dot1xSuppMaxStart=3
+dot1xSuppSuppControlledPortStatus=Authorized
+dot1xSuppBackendPaeState=2
+dot1xSuppEapolFramesRx=0
+dot1xSuppEapolFramesTx=440
+dot1xSuppEapolStartFramesTx=2
+dot1xSuppEapolLogoffFramesTx=0
+dot1xSuppEapolRespFramesTx=0
+dot1xSuppEapolReqIdFramesRx=0
+dot1xSuppEapolReqFramesRx=0
+dot1xSuppInvalidEapolFramesRx=0
+dot1xSuppEapLengthErrorFramesRx=0
+dot1xSuppLastEapolFrameVersion=0
+dot1xSuppLastEapolFrameSource=00:00:00:00:00:00
+\endverbatim
+
+
+\subsection ctrl_iface_STATUS STATUS
+
+Request current WPA/EAPOL/EAP status information. The output is a text
+block with each line in \c variable=value format. For example:
+
+\verbatim
+bssid=02:00:01:02:03:04
+ssid=test network
+pairwise_cipher=CCMP
+group_cipher=CCMP
+key_mgmt=WPA-PSK
+wpa_state=COMPLETED
+ip_address=192.168.1.21
+Supplicant PAE state=AUTHENTICATED
+suppPortStatus=Authorized
+EAP state=SUCCESS
+\endverbatim
+
+
+\subsection ctrl_iface_STATUS-VERBOSE STATUS-VERBOSE
+
+Same as STATUS, but with more verbosity (i.e., more \c variable=value pairs).
+
+\verbatim
+bssid=02:00:01:02:03:04
+ssid=test network
+id=0
+pairwise_cipher=CCMP
+group_cipher=CCMP
+key_mgmt=WPA-PSK
+wpa_state=COMPLETED
+ip_address=192.168.1.21
+Supplicant PAE state=AUTHENTICATED
+suppPortStatus=Authorized
+heldPeriod=60
+authPeriod=30
+startPeriod=30
+maxStart=3
+portControl=Auto
+Supplicant Backend state=IDLE
+EAP state=SUCCESS
+reqMethod=0
+methodState=NONE
+decision=COND_SUCC
+ClientTimeout=60
+\endverbatim
+
+
+\subsection ctrl_iface_PMKSA PMKSA
+
+Show PMKSA cache
+
+\verbatim
+Index / AA / PMKID / expiration (in seconds) / opportunistic
+1 / 02:00:01:02:03:04 / 000102030405060708090a0b0c0d0e0f / 41362 / 0
+2 / 02:00:01:33:55:77 / 928389281928383b34afb34ba4212345 / 362 / 1
+\endverbatim
+
+
+\subsection ctrl_iface_SET SET <variable> <value>
+
+Set variables:
+- EAPOL::heldPeriod
+- EAPOL::authPeriod
+- EAPOL::startPeriod
+- EAPOL::maxStart
+- dot11RSNAConfigPMKLifetime
+- dot11RSNAConfigPMKReauthThreshold
+- dot11RSNAConfigSATimeout
+
+Example command:
+\verbatim
+SET EAPOL::heldPeriod 45
+\endverbatim
+
+
+\subsection ctrl_iface_LOGON LOGON
+
+IEEE 802.1X EAPOL state machine logon.
+
+
+\subsection ctrl_iface_LOGOFF LOGOFF
+
+IEEE 802.1X EAPOL state machine logoff.
+
+
+\subsection ctrl_iface_REASSOCIATE REASSOCIATE
+
+Force reassociation.
+
+
+\subsection ctrl_iface_RECONNECT RECONNECT
+
+Connect if disconnected (i.e., like \c REASSOCIATE, but only connect
+if in disconnected state).
+
+
+\subsection ctrl_iface_PREAUTH PREAUTH <BSSID>
+
+Start pre-authentication with the given BSSID.
+
+
+\subsection ctrl_iface_ATTACH ATTACH
+
+Attach the connection as a monitor for unsolicited events. This can
+be done with \ref wpa_ctrl_attach().
+
+
+\subsection ctrl_iface_DETACH DETACH
+
+Detach the connection as a monitor for unsolicited events. This can
+be done with \ref wpa_ctrl_detach().
+
+
+\subsection ctrl_iface_LEVEL LEVEL <debug level>
+
+Change debug level.
+
+
+\subsection ctrl_iface_RECONFIGURE RECONFIGURE
+
+Force wpa_supplicant to re-read its configuration data.
+
+
+\subsection ctrl_iface_TERMINATE TERMINATE
+
+Terminate wpa_supplicant process.
+
+
+\subsection ctrl_iface_BSSID BSSID <network id> <BSSID>
+
+Set preferred BSSID for a network. Network id can be received from the
+\c LIST_NETWORKS command output.
+
+
+\subsection ctrl_iface_LIST_NETWORKS LIST_NETWORKS
+
+List configured networks.
+
+\verbatim
+network id / ssid / bssid / flags
+0 example network any [CURRENT]
+\endverbatim
+
+(note: fields are separated with tabs)
+
+
+\subsection ctrl_iface_DISCONNECT DISCONNECT
+
+Disconnect and wait for \c REASSOCIATE or \c RECONNECT command before
+connecting.
+
+
+\subsection ctrl_iface_SCAN SCAN
+
+Request a new BSS scan.
+
+
+\subsection ctrl_iface_SCAN_RESULTS SCAN_RESULTS
+
+Get the latest scan results.
+
+\verbatim
+bssid / frequency / signal level / flags / ssid
+00:09:5b:95:e0:4e 2412 208 [WPA-PSK-CCMP] jkm private
+02:55:24:33:77:a3 2462 187 [WPA-PSK-TKIP] testing
+00:09:5b:95:e0:4f 2412 209 jkm guest
+\endverbatim
+
+(note: fields are separated with tabs)
+
+
+\subsection ctrl_iface_BSS BSS
+
+Get detailed per-BSS scan results. \c BSS command can be used to
+iterate through scan results one BSS at a time and to fetch all
+information from the found BSSes. This provides access to the same
+data that is available through \c SCAN_RESULTS but in a way that
+avoids problems with large number of scan results not fitting in the
+ctrl_iface messages.
+
+There are two options for selecting the BSS with the \c BSS command:
+"BSS <idx>" requests information for the BSS identified by the index
+(0 .. size-1) in the scan results table and "BSS <BSSID>" requests
+information for the given BSS (based on BSSID in 00:01:02:03:04:05
+format).
+
+BSS information is presented in following format. Please note that new
+fields may be added to this field=value data, so the ctrl_iface user
+should be prepared to ignore values it does not understand.
+
+\verbatim
+bssid=00:09:5b:95:e0:4e
+freq=2412
+beacon_int=0
+capabilities=0x0011
+qual=51
+noise=161
+level=212
+tsf=0000000000000000
+ie=000b6a6b6d2070726976617465010180dd180050f20101000050f20401000050f20401000050f2020000
+ssid=jkm private
+\endverbatim
+
+
+
+\subsection ctrl_iface_SELECT_NETWORK SELECT_NETWORK <network id>
+
+Select a network (disable others). Network id can be received from the
+\c LIST_NETWORKS command output.
+
+
+\subsection ctrl_iface_ENABLE_NETWORK ENABLE_NETWORK <network id>
+
+Enable a network. Network id can be received from the
+\c LIST_NETWORKS command output. Special network id \c all can be
+used to enable all network.
+
+
+\subsection ctrl_iface_DISABLE_NETWORK DISABLE_NETWORK <network id>
+
+Disable a network. Network id can be received from the
+\c LIST_NETWORKS command output. Special network id \c all can be
+used to disable all network.
+
+
+\subsection ctrl_iface_ADD_NETWORK ADD_NETWORK
+
+Add a new network. This command creates a new network with empty
+configuration. The new network is disabled and once it has been
+configured it can be enabled with \c ENABLE_NETWORK command. \c ADD_NETWORK
+returns the network id of the new network or FAIL on failure.
+
+
+\subsection ctrl_iface_REMOVE_NETWORK REMOVE_NETWORK <network id>
+
+Remove a network. Network id can be received from the
+\c LIST_NETWORKS command output. Special network id \c all can be
+used to remove all network.
+
+
+\subsection ctrl_iface_SET_NETWORK SET_NETWORK <network id> <variable> <value>
+
+Set network variables. Network id can be received from the
+\c LIST_NETWORKS command output.
+
+This command uses the same variables and data formats as the
+configuration file. See example wpa_supplicant.conf for more details.
+
+- ssid (network name, SSID)
+- psk (WPA passphrase or pre-shared key)
+- key_mgmt (key management protocol)
+- identity (EAP identity)
+- password (EAP password)
+- ...
+
+
+\subsection ctrl_iface_GET_NETWORK GET_NETWORK <network id> <variable>
+
+Get network variables. Network id can be received from the
+\c LIST_NETWORKS command output.
+
+
+\subsection ctrl_iface_SAVE_CONFIG SAVE_CONFIG
+
+Save the current configuration.
+
+
+\subsection ctrl_iface_P2P_FIND P2P_FIND
+
+Start P2P device discovery. Optional parameter can be used to specify
+the duration for the discovery in seconds (e.g., "P2P_FIND 5"). If the
+duration is not specified, discovery will be started for indefinite
+time, i.e., until it is terminated by P2P_STOP_FIND or P2P_CONNECT (to
+start group formation with a discovered peer).
+
+The default search type is to first run a full scan of all channels
+and then continue scanning only social channels (1, 6, 11). This
+behavior can be changed by specifying a different search type: social
+(e.g., "P2P_FIND 5 type=social") will skip the initial full scan and
+only search social channels; progressive (e.g., "P2P_FIND
+type=progressive") starts with a full scan and then searches
+progressively through all channels one channel at the time with the
+social channel scans. Progressive device discovery can be used to find
+new groups (and groups that were not found during the initial scan,
+e.g., due to the GO being asleep) over time without adding
+considerable extra delay for every Search state round.
+
+
+\subsection ctrl_iface_P2P_STOP_FIND P2P_STOP_FIND
+
+Stop ongoing P2P device discovery or other operation (connect, listen
+mode).
+
+
+\subsection ctrl_iface_P2P_CONNECT P2P_CONNECT
+
+Start P2P group formation with a discovered P2P peer. This includes
+group owner negotiation, group interface setup, provisioning, and
+establishing data connection.
+
+P2P_CONNECT <peer device address> <pbc|pin|PIN#>
+[label|display|keypad] [persistent] [join|auth] [go_intent=<0..15>]
+
+Start P2P group formation with a discovered P2P peer. This includes
+optional group owner negotiation, group interface setup, provisioning,
+and establishing data connection.
+
+The <pbc|pin|PIN#> parameter specifies the WPS provisioning
+method. "pbc" string starts pushbutton method, "pin" string start PIN
+method using an automatically generated PIN (which will be returned as
+the command return code), PIN# means that a pre-selected PIN can be
+used (e.g., 12345670). [label|display|keypad] is used with PIN method
+to specify which PIN is used (label=PIN from local label,
+display=dynamically generated random PIN from local display,
+keypad=PIN entered from peer device label or display). "persistent"
+parameter can be used to request a persistent group to be formed.
+
+"join" indicates that this is a command to join an existing group as a
+client. It skips the GO Negotiation part.
+
+"auth" indicates that the WPS parameters are authorized for the peer
+device without actually starting GO Negotiation (i.e., the peer is
+expected to initiate GO Negotiation). This is mainly for testing
+purposes.
+
+The optional "go_intent" parameter can be used to override the default
+GO Intent value.
+
+
+\subsection ctrl_iface_P2P_LISTEN P2P_LISTEN
+
+Start Listen-only state. Optional parameter can be used to specify the
+duration for the Listen operation in seconds. This command may not
+be of that much use during normal operations and is mainly designed
+for testing. It can also be used to keep the device discoverable
+without having to maintain a group.
+
+
+\subsection ctrl_iface_P2P_GROUP_REMOVE P2P_GROUP_REMOVE
+
+Terminate a P2P group. If a new virtual network interface was used for
+the group, it will also be removed. The network interface name of the
+group interface is used as a parameter for this command.
+
+
+\subsection ctrl_iface_P2P_GROUP_ADD P2P_GROUP_ADD
+
+Set up a P2P group owner manually (i.e., without group owner
+negotiation with a specific peer). This is also known as autonomous
+GO. Optional persistent=<network id> can be used to specify restart of
+a persistent group.
+
+
+\subsection ctrl_iface_P2P_PROV_DISC P2P_PROV_DISC
+
+Send P2P provision discovery request to the specified peer. The
+parameters for this command are the P2P device address of the peer and
+the desired configuration method. For example, "P2P_PROV_DISC
+02:01:02:03:04:05 display" would request the peer to display a PIN for
+us and "P2P_PROV_DISC 02:01:02:03:04:05 keypad" would request the peer
+to enter a PIN that we display.
+
+
+\subsection ctrl_iface_P2P_GET_PASSPHRASE P2P_GET_PASSPHRASE
+
+Get the passphrase for a group (only available when acting as a GO).
+
+
+\subsection ctrl_iface_P2P_SERV_DISC_REQ P2P_SERV_DISC_REQ
+
+Schedule a P2P service discovery request. The parameters for this
+command are the device address of the peer device (or 00:00:00:00:00:00
+for wildcard query that is sent to every discovered P2P peer that
+supports service discovery) and P2P Service Query TLV(s) as hexdump.
+For example, "P2P_SERV_DISC_REQ 00:00:00:00:00:00 02000001" schedules
+a request for listing all supported service discovery protocols and
+requests this to be sent to all discovered peers. The pending requests
+are sent during device discovery (see \ref ctrl_iface_P2P_FIND).
+
+This command returns an identifier for the pending query (e.g.,
+"1f77628") that can be used to cancel the request. Directed requests
+will be automatically removed when the specified peer has replied to
+it.
+
+
+\subsection ctrl_iface_P2P_SERV_DISC_CANCEL_REQ P2P_SERV_DISC_CANCEL_REQ
+
+Cancel a pending P2P service discovery request. This command takes a
+single parameter: identifier for the pending query (the value returned
+by \ref ctrl_iface_P2P_SERV_DISC_REQ), e.g.,
+"P2P_SERV_DISC_CANCEL_REQ 1f77628".
+
+
+\subsection ctrl_iface_P2P_SERV_DISC_RESP P2P_SERV_DISC_RESP
+
+Reply to a service discovery query. This command takes following
+parameters: frequency in MHz, destination address, dialog token,
+response TLV(s). The first three parameters are copied from the
+request event. For example,
+"P2P_SERV_DISC_RESP 2437 02:40:61:c2:f3:b7 1 0300000101".
+
+
+\subsection ctrl_iface_P2P_SERVICE_UPDATE P2P_SERVICE_UPDATE
+
+Indicate that local services have changed. This is used to increment
+the P2P service indicator value so that peers know when previously
+cached information may have changed.
+
+
+\subsection ctrl_iface_P2P_SERV_DISC_EXTERNAL P2P_SERV_DISC_EXTERNAL
+
+Configure external processing of P2P service requests: 0 (default) =
+no external processing of requests (i.e., internal code will reject
+each request), 1 = external processing of requests (external program
+is responsible for replying to service discovery requests with
+\ref ctrl_iface_P2P_SERV_DISC_RESP).
+
+
+\subsection ctrl_iface_P2P_REJECT P2P_REJECT
+
+Reject connection attempt from a peer (specified with a device
+address). This is a mechanism to reject a pending GO Negotiation with
+a peer and request to automatically block any further connection or
+discovery of the peer.
+
+
+\subsection ctrl_iface_P2P_INVITE P2P_INVITE
+
+Invite a peer to join a group or to (re)start a persistent group.
+
+
+\subsection ctrl_iface_P2P_PEER P2P_PEER
+
+Fetch information about a discovered peer. This command takes in an
+argument specifying which peer to select: P2P Device Address of the
+peer, "FIRST" to indicate the first peer in the list, or "NEXT-<P2P
+Device Address>" to indicate the entry following the specified peer
+(to allow for iterating through the list).
+
+
+\subsection ctrl_iface_P2P_EXT_LISTEN P2P_EXT_LISTEN
+
+Enable/disable extended listen timing. Without parameters, this
+command disables extended listen timing. When enabling the feature,
+two parameters are used: availability period and availability interval
+(both in milliseconds and with range of 1-65535).
+
+
+\section ctrl_iface_interactive Interactive requests
+
+If wpa_supplicant needs additional information during authentication
+(e.g., password), it will use a specific prefix, \c CTRL-REQ-
+(\a WPA_CTRL_REQ macro) in an unsolicited event message. An external
+program, e.g., a GUI, can provide such information by using
+\c CTRL-RSP- (\a WPA_CTRL_RSP macro) prefix in a command with matching
+field name.
+
+The following fields can be requested in this way from the user:
+- IDENTITY (EAP identity/user name)
+- PASSWORD (EAP password)
+- NEW_PASSWORD (New password if the server is requesting password change)
+- PIN (PIN code for accessing a SIM or smartcard)
+- OTP (one-time password; like password, but the value is used only once)
+- PASSPHRASE (passphrase for a private key file)
+
+\verbatim
+CTRL-REQ-<field name>-<network id>-<human readable text>
+CTRL-RSP-<field name>-<network id>-<value>
+\endverbatim
+
+For example, request from wpa_supplicant:
+\verbatim
+CTRL-REQ-PASSWORD-1-Password needed for SSID test-network
+\endverbatim
+
+And a matching reply from the GUI:
+\verbatim
+CTRL-RSP-PASSWORD-1-secret
+\endverbatim
+
+
+\subsection ctrl_iface_GET_CAPABILITY GET_CAPABILITY <option> [strict]
+
+Get list of supported functionality (eap, pairwise, group,
+proto). Supported functionality is shown as space separate lists of
+values used in the same format as in wpa_supplicant configuration.
+If optional argument, 'strict', is added, only the values that the
+driver claims to explicitly support are included. Without this, all
+available capabilities are included if the driver does not provide
+a mechanism for querying capabilities.
+
+Example request/reply pairs:
+
+\verbatim
+GET_CAPABILITY eap
+AKA FAST GTC LEAP MD5 MSCHAPV2 OTP PAX PEAP PSK SIM TLS TTLS
+\endverbatim
+
+\verbatim
+GET_CAPABILITY pairwise
+CCMP TKIP NONE
+\endverbatim
+
+\verbatim
+GET_CAPABILITY pairwise strict
+\endverbatim
+
+\verbatim
+GET_CAPABILITY group
+CCMP TKIP WEP104 WEP40
+\endverbatim
+
+\verbatim
+GET_CAPABILITY key_mgmt
+WPA-PSK WPA-EAP IEEE8021X NONE
+\endverbatim
+
+\verbatim
+GET_CAPABILITY proto
+RSN WPA
+\endverbatim
+
+\verbatim
+GET_CAPABILITY auth_alg
+OPEN SHARED LEAP
+\endverbatim
+
+
+\subsection ctrl_iface_AP_SCAN AP_SCAN <ap_scan value>
+
+Change ap_scan value:
+0 = no scanning,
+1 = wpa_supplicant requests scans and uses scan results to select the AP,
+2 = wpa_supplicant does not use scanning and just requests driver to
+associate and take care of AP selection
+
+
+\subsection ctrl_iface_INTERFACES INTERFACES
+
+List configured interfaces.
+
+\verbatim
+wlan0
+eth0
+\endverbatim
+
+
+\section ctrl_iface_events Control interface events
+
+wpa_supplicant generates number messages based on events like
+connection or a completion of a task. These are available to external
+programs that attach to receive unsolicited messages over the control
+interface with \ref wpa_ctrl_attach().
+
+The event messages will be delivered over the attach control interface
+as text strings that start with the priority level of the message and
+a fixed prefix text as defined in \ref wpa_ctrl.h. After this, optional
+additional information may be included depending on the event
+message. For example, following event message is delivered when new
+scan results are available:
+
+\verbatim
+<2>CTRL-EVENT-SCAN-RESULTS
+\endverbatim
+
+Following priority levels are used:
+- 0 = MSGDUMP
+- 1 = DEBUG
+- 2 = INFO
+- 3 = WARNING
+- 4 = ERROR
+
+By default, any priority level greater than equal to 2 (INFO) are
+delivered over the attached control interface. LEVEL command can be
+used to set the level of messages which will be delivered. It should
+be noted that there are many debug messages that do not include any
+particulat prefix and are subject to change. They may be used for
+debug information, but can usually be ignored by external programs.
+
+In most cases, the external program can skip over the priority field
+in the beginning of the event message and then compare the following
+text to the event strings from \ref wpa_ctrl.h that the program is
+interested in processing.
+
+Following subsections describe the most common event notifications
+generated by wpa_supplicant.
+
+\subsection ctrl_iface_event_CTRL_REQ CTRL-REQ-
+
+WPA_CTRL_REQ: Request information from a user. See
+\ref ctrl_iface_interactive "Interactive requests" sections for more
+details.
+
+\subsection ctrl_iface_event_CONNECTED CTRL-EVENT-CONNECTED
+
+WPA_EVENT_CONNECTED: Indicate successfully completed authentication
+and that the data connection is now enabled.
+
+\subsection ctrl_iface_event_DISCONNECTED CTRL-EVENT-DISCONNECTED
+
+WPA_EVENT_DISCONNECTED: Disconnected, data connection is not available
+
+\subsection ctrl_iface_event_TERMINATING CTRL-EVENT-TERMINATING
+
+WPA_EVENT_TERMINATING: wpa_supplicant is exiting
+
+\subsection ctrl_iface_event_PASSWORD_CHANGED CTRL-EVENT-PASSWORD-CHANGED
+
+WPA_EVENT_PASSWORD_CHANGED: Password change was completed successfully
+
+\subsection ctrl_iface_event_EAP_NOTIFICATION CTRL-EVENT-EAP-NOTIFICATION
+
+WPA_EVENT_EAP_NOTIFICATION: EAP-Request/Notification received
+
+\subsection ctrl_iface_event_EAP_STARTED CTRL-EVENT-EAP-STARTED
+
+WPA_EVENT_EAP_STARTED: EAP authentication started (EAP-Request/Identity
+received)
+
+\subsection ctrl_iface_event_EAP_METHOD CTRL-EVENT-EAP-METHOD
+
+WPA_EVENT_EAP_METHOD: EAP method selected
+
+\subsection ctrl_iface_event_EAP_SUCCESS CTRL-EVENT-EAP-SUCCESS
+
+WPA_EVENT_EAP_SUCCESS: EAP authentication completed successfully
+
+\subsection ctrl_iface_event_EAP_FAILURE CTRL-EVENT-EAP-FAILURE
+
+WPA_EVENT_EAP_FAILURE: EAP authentication failed (EAP-Failure received)
+
+\subsection ctrl_iface_event_SCAN_RESULTS CTRL-EVENT-SCAN-RESULTS
+
+WPA_EVENT_SCAN_RESULTS: New scan results available
+
+\subsection ctrl_iface_event_BSS_ADDED CTRL-EVENT-BSS-ADDED
+
+WPA_EVENT_BSS_ADDED: A new BSS entry was added. The event prefix is
+followed by the BSS entry id and BSSID.
+
+\verbatim
+CTRL-EVENT-BSS-ADDED 34 00:11:22:33:44:55
+\endverbatim
+
+\subsection ctrl_iface_event_BSS_REMOVED CTRL-EVENT-BSS-REMOVED
+
+WPA_EVENT_BSS_REMOVED: A BSS entry was removed. The event prefix is
+followed by BSS entry id and BSSID.
+
+\verbatim
+CTRL-EVENT-BSS-REMOVED 34 00:11:22:33:44:55
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_OVERLAP_DETECTED WPS-OVERLAP-DETECTED
+
+WPS_EVENT_OVERLAP: WPS overlap detected in PBC mode
+
+\subsection ctrl_iface_event_WPS_AP_AVAILABLE_PBC WPS-AP-AVAILABLE-PBC
+
+WPS_EVENT_AP_AVAILABLE_PBC: Available WPS AP with active PBC found in
+scan results.
+
+\subsection ctrl_iface_event_WPS_AP_AVAILABLE_PIN WPS-AP-AVAILABLE-PIN
+
+WPS_EVENT_AP_AVAILABLE_PIN: Available WPS AP with recently selected PIN
+registrar found in scan results.
+
+\subsection ctrl_iface_event_WPS_AP_AVAILABLE WPS-AP-AVAILABLE
+
+WPS_EVENT_AP_AVAILABLE: Available WPS AP found in scan results
+
+\subsection ctrl_iface_event_WPS_CRED_RECEIVED WPS-CRED-RECEIVED
+
+WPS_EVENT_CRED_RECEIVED: A new credential received
+
+\subsection ctrl_iface_event_WPS_M2D WPS-M2D
+
+WPS_EVENT_M2D: M2D received
+
+\subsection ctrl_iface_event_WPS_FAIL
+
+WPS_EVENT_FAIL: WPS registration failed after M2/M2D
+
+\subsection ctrl_iface_event_WPS_SUCCESS WPS-SUCCESS
+
+WPS_EVENT_SUCCESS: WPS registration completed successfully
+
+\subsection ctrl_iface_event_WPS_TIMEOUT WPS-TIMEOUT
+
+WPS_EVENT_TIMEOUT: WPS enrollment attempt timed out and was terminated
+
+\subsection ctrl_iface_event_WPS_ENROLLEE_SEEN WPS-ENROLLEE-SEEN
+
+WPS_EVENT_ENROLLEE_SEEN: WPS Enrollee was detected (used in AP mode).
+The event prefix is followed by MAC addr, UUID-E, pri dev type,
+config methods, dev passwd id, request type, [dev name].
+
+\verbatim
+WPS-ENROLLEE-SEEN 02:00:00:00:01:00
+572cf82f-c957-5653-9b16-b5cfb298abf1 1-0050F204-1 0x80 4 1
+[Wireless Client]
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_ER_AP_ADD WPS-ER-AP-ADD
+
+WPS_EVENT_ER_AP_ADD: WPS ER discovered an AP
+
+\verbatim
+WPS-ER-AP-ADD 87654321-9abc-def0-1234-56789abc0002 02:11:22:33:44:55
+pri_dev_type=6-0050F204-1 wps_state=1 |Very friendly name|Company|
+Long description of the model|WAP|http://w1.fi/|http://w1.fi/hostapd/
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_ER_AP_REMOVE WPS-ER-AP-REMOVE
+
+WPS_EVENT_ER_AP_REMOVE: WPS ER removed an AP entry
+
+\verbatim
+WPS-ER-AP-REMOVE 87654321-9abc-def0-1234-56789abc0002
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_ER_ENROLLEE_ADD WPS-ER-ENROLLEE-ADD
+
+WPS_EVENT_ER_ENROLLEE_ADD: WPS ER discovered a new Enrollee
+
+\verbatim
+WPS-ER-ENROLLEE-ADD 2b7093f1-d6fb-5108-adbb-bea66bb87333
+02:66:a0:ee:17:27 M1=1 config_methods=0x14d dev_passwd_id=0
+pri_dev_type=1-0050F204-1
+|Wireless Client|Company|cmodel|123|12345|
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_ER_ENROLLEE_REMOVE WPS-ER-ENROLLEE-REMOVE
+
+WPS_EVENT_ER_ENROLLEE_REMOVE: WPS ER removed an Enrollee entry
+
+\verbatim
+WPS-ER-ENROLLEE-REMOVE 2b7093f1-d6fb-5108-adbb-bea66bb87333
+02:66:a0:ee:17:27
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_PIN_NEEDED WPS-PIN-NEEDED
+
+WPS_EVENT_PIN_NEEDED: PIN is needed to complete provisioning with an
+Enrollee. This is followed by information about the Enrollee (UUID,
+MAC address, device name, manufacturer, model name, model number,
+serial number, primary device type).
+\verbatim
+WPS-PIN-NEEDED 5a02a5fa-9199-5e7c-bc46-e183d3cb32f7 02:2a:c4:18:5b:f3
+[Wireless Client|Company|cmodel|123|12345|1-0050F204-1]
+\endverbatim
+
+\subsection ctrl_iface_event_WPS_NEW_AP_SETTINGS WPS-NEW-AP-SETTINGS
+
+WPS_EVENT_NEW_AP_SETTINGS: New AP settings were received
+
+\subsection ctrl_iface_event_WPS_REG_SUCCESS WPS-REG-SUCCESS
+
+WPS_EVENT_REG_SUCCESS: WPS provisioning was completed successfully
+(AP/Registrar)
+
+\subsection ctrl_iface_event_WPS_AP_SETUP_LOCKED WPS-AP-SETUP-LOCKED
+
+WPS_EVENT_AP_SETUP_LOCKED: AP changed into setup locked state due to
+multiple failed configuration attempts using the AP PIN.
+
+\subsection ctrl_iface_event_AP_STA_CONNECTED AP-STA-CONNECTED
+
+AP_STA_CONNECTED: A station associated with us (AP mode event). The
+event prefix is followed by the MAC address of the station.
+
+\verbatim
+AP-STA-CONNECTED 02:2a:c4:18:5b:f3
+\endverbatim
+
+\subsection ctrl_iface_event_AP_STA_DISCONNECTED AP-STA-DISCONNECTED
+
+AP_STA_DISCONNECTED: A station disassociated (AP mode event)
+
+\verbatim
+AP-STA-DISCONNECTED 02:2a:c4:18:5b:f3
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_DEVICE_FOUND P2P-DEVICE-FOUND
+
+P2P_EVENT_DEVICE_FOUND: Indication of a discovered P2P device with
+information about that device.
+
+\verbatim
+P2P-DEVICE-FOUND 02:b5:64:63:30:63 p2p_dev_addr=02:b5:64:63:30:63
+pri_dev_type=1-0050f204-1 name='Wireless Client' config_methods=0x84
+dev_capab=0x21 group_capab=0x0
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_GO_NEG_REQUEST P2P-GO-NEG-REQUEST
+
+P2P_EVENT_GO_NEG_REQUEST: A P2P device requested GO negotiation, but we
+were not ready to start the negotiation.
+
+\verbatim
+P2P-GO-NEG-REQUEST 02:40:61:c2:f3:b7 dev_passwd_id=4
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_GO_NEG_SUCCESS P2P-GO-NEG-SUCCESS
+
+P2P_EVENT_GO_NEG_SUCCESS: Indication of successfully complete group
+owner negotiation.
+
+\subsection ctrl_iface_event_P2P_EVENT_GO_NEG_FAILURE P2P-GO-NEG-FAILURE
+
+P2P_EVENT_GO_NEG_FAILURE: Indication of failed group owner negotiation.
+
+\subsection ctrl_iface_event_P2P_EVENT_GROUP_FORMATION_SUCCESS P2P-GROUP-FORMATION-SUCCESS
+
+P2P_EVENT_GROUP_FORMATION_SUCCESS: Indication that P2P group formation
+has been completed successfully.
+
+\subsection ctrl_iface_event_P2P_EVENT_GROUP_FORMATION_FAILURE P2P-GROUP-FORMATION-FAILURE
+
+P2P_EVENT_GROUP_FORMATION_FAILURE: Indication that P2P group formation
+failed (e.g., due to provisioning failure or timeout).
+
+\subsection ctrl_iface_event_P2P_EVENT_GROUP_STARTED P2P-GROUP-STARTED
+
+P2P_EVENT_GROUP_STARTED: Indication of a new P2P group having been
+started. Additional parameters: network interface name for the group,
+role (GO/client), SSID. The passphrase used in the group is also
+indicated here if known (on GO) or PSK (on client). If the group is a
+persistent one, a flag indicating that is included.
+
+\verbatim
+P2P-GROUP-STARTED wlan0-p2p-0 GO ssid="DIRECT-3F Testing"
+passphrase="12345678" go_dev_addr=02:40:61:c2:f3:b7 [PERSISTENT]
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_GROUP_REMOVED P2P-GROUP-REMOVED
+
+P2P_EVENT_GROUP_REMOVED: Indication of a P2P group having been removed.
+Additional parameters: network interface name for the group, role
+(GO/client).
+
+\verbatim
+P2P-GROUP-REMOVED wlan0-p2p-0 GO
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_PROV_DISC_SHOW_PIN P2P-PROV-DISC-SHOW-PIN
+
+P2P_EVENT_PROV_DISC_SHOW_PIN: Request from the peer for us to display
+a PIN that will be entered on the peer. The following parameters are
+included after the event prefix: peer_address PIN. The PIN is a
+random PIN generated for this connection. P2P_CONNECT command can be
+used to accept the request with the same PIN configured for the
+connection.
+
+\verbatim
+P2P-PROV-DISC-SHOW-PIN 02:40:61:c2:f3:b7 12345670
+p2p_dev_addr=02:40:61:c2:f3:b7 pri_dev_type=1-0050F204-1 name='Test'
+config_methods=0x188 dev_capab=0x21 group_capab=0x0
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_PROV_DISC_ENTER_PIN P2P-PROV-DISC-ENTER-PIN
+
+P2P_EVENT_PROV_DISC_ENTER_PIN: Request from the peer for us to enter a
+PIN displayed on the peer. The following parameter is included after
+the event prefix: peer address.
+
+\verbatim
+P2P-PROV-DISC-ENTER-PIN 02:40:61:c2:f3:b7 p2p_dev_addr=02:40:61:c2:f3:b7
+pri_dev_type=1-0050F204-1 name='Test' config_methods=0x188
+dev_capab=0x21 group_capab=0x0
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_PROV_DISC_PBC_REQ P2P-PROV-DISC-PBC-REQ
+
+P2P_EVENT_PROV_DISC_PBC_REQ: Request from the peer for us to connect
+using PBC. The following parameters are included after the event prefix:
+peer_address. P2P_CONNECT command can be used to accept the request.
+
+\verbatim
+P2P-PROV-DISC-PBC-REQ 02:40:61:c2:f3:b7 p2p_dev_addr=02:40:61:c2:f3:b7
+pri_dev_type=1-0050F204-1 name='Test' config_methods=0x188
+dev_capab=0x21 group_capab=0x0
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_PROV_DISC_PBC_RESP P2P-PROV-DISC-PBC-RESP
+
+P2P_EVENT_PROV_DISC_PBC_RESP: The peer accepted our provision discovery
+request to connect using PBC. The following parameters are included
+after the event prefix: peer_address. P2P_CONNECT command can be used to
+start GO Negotiation after this.
+
+\verbatim
+P2P-PROV-DISC-PBC-RESP 02:40:61:c2:f3:b7
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_SERV_DISC_REQ P2P-SERV-DISC-REQ
+
+P2P-SERV-DISC-REQ: Indicate reception of a P2P service discovery
+request. The following parameters are included after the event prefix:
+frequency in MHz, source address, dialog token, Service Update
+Indicator, Service Query TLV(s) as hexdump.
+
+\verbatim
+P2P-SERV-DISC-REQ 2412 02:40:61:c2:f3:b7 0 0 02000001
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_SERV_DISC_RESP P2P-SERV-DISC-RESP
+
+P2P-SERV-DISC-RESP: Indicate reception of a P2P service discovery
+response. The following parameters are included after the event prefix:
+source address, Service Update Indicator, Service Response TLV(s) as
+hexdump.
+
+\verbatim
+P2P-SERV-DISC-RESP 02:40:61:c2:f3:b7 0 0300000101
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_INVITATION_RECEIVED P2P-INVITATION-RECEIVED
+
+P2P-INVITATION-RECEIVED: Indicate reception of a P2P Invitation
+Request. For persistent groups, the parameter after the event prefix
+indicates which network block includes the persistent group data.
+
+\verbatim
+P2P-INVITATION-RECEIVED sa=02:40:61:c2:f3:b7 persistent=0
+\endverbatim
+
+\subsection ctrl_iface_event_P2P_EVENT_INVITATION_RESULT P2P-INVITATION-RESULT
+
+P2P-INVITATION-RESULT: Indicate result of a P2P invitation that was
+requested with \ref ctrl_iface_P2P_INVITE. The parameter
+status=<value> shows the status code returned by the peer (or -1 on
+local failure or timeout).
+
+\verbatim
+P2P-INVITATION-RESULT status=1
+\endverbatim
+
+*/
diff --git a/contrib/wpa/doc/dbus.doxygen b/contrib/wpa/doc/dbus.doxygen
new file mode 100644
index 000000000000..8231aac41805
--- /dev/null
+++ b/contrib/wpa/doc/dbus.doxygen
@@ -0,0 +1,2394 @@
+/**
+\page dbus wpa_supplicant D-Bus API
+
+This section documents the wpa_supplicant D-Bus API. Every D-Bus
+interface implemented by wpa_supplicant is described here including
+their methods, signals, and properties with arguments, returned
+values, and possible errors.
+
+Interfaces:
+- \ref dbus_main
+- \ref dbus_interface
+- \ref dbus_wps
+- \ref dbus_p2pdevice
+- \ref dbus_bss
+- \ref dbus_network
+- \ref dbus_peer
+- \ref dbus_group
+- \ref dbus_persistent_group
+- \ref dbus_mesh
+
+
+\section dbus_main fi.w1.wpa_supplicant1
+
+Interface implemented by the main wpa_supplicant D-Bus object
+registered in the bus with fi.w1.wpa_supplicant1 name.
+
+\subsection dbus_main_methods Methods
+
+<ul>
+ <li>
+ <h3>CreateInterface ( a{sv} : args ) --> o : interface</h3>
+ <p>Registers a wireless interface in wpa_supplicant.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with arguments used to add the interface to wpa_supplicant. The dictionary may contain the following entries:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+ <tr><td>Ifname</td><td>s</td><td>Name of the network interface to control, e.g., wlan0</td><td>Yes</td>
+ <tr><td>BridgeIfname</td><td>s</td><td>Name of the bridge interface to control, e.g., br0</td><td>No</td>
+ <tr><td>Driver</td><td>s</td><td>Driver name which the interface uses, e.g., nl80211</td><td>No</td>
+ <tr><td>ConfigFile</td><td>s</td><td>Configuration file path</td><td>No</td>
+ </table>
+ </dd>
+ </dl>
+ <h4>Returns</h4>
+ <dl>
+ <dt>o : interface</dt>
+ <dd>A D-Bus path to object representing created interface</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InterfaceExists</dt>
+ <dd>wpa_supplicant already controls this interface.</dd>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>Creating interface failed for an unknown reason.</dd>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>Invalid entries were found in the passed argument.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>RemoveInterface ( o : interface ) --> nothing</h3>
+ <p>Deregisters a wireless interface from wpa_supplicant.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : interface</dt>
+ <dd>A D-Bus path to an object representing an interface to remove returned by CreateInterface</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InterfaceUnknown</dt>
+ <dd>Object pointed by the path doesn't exist or doesn't represent an interface.</dd>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>Removing interface failed for an unknown reason.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>GetInterface ( s : ifname ) --> o : interface</h3>
+ <p>Returns a D-Bus path to an object related to an interface which wpa_supplicant already controls.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : ifname</dt>
+ <dd>Name of the network interface, e.g., wlan0</dd>
+ </dl>
+ <h4>Returns</h4>
+ <dl>
+ <dt>o : interface</dt>
+ <dd>A D-Bus path to an object representing an interface</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InterfaceUnknown</dt>
+ <dd>An interface with the passed name in not controlled by wpa_supplicant.</dd>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>Getting an interface object path failed for an unknown reason.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>ExpectDisconnect ( ) --> nothing</h3>
+ <p>Notify wpa_supplicant of an externally triggered disconnection, e.g., due to system suspend.</p>
+ </li>
+ </ul>
+
+\subsection dbus_main_properties Properties
+
+<ul>
+ <li>
+ <h3>DebugLevel - s - (read/write)</h3>
+ <p>Global wpa_supplicant debugging level. Possible values are
+ "msgdump" (verbose debugging), "debug" (debugging),
+ "info" (informative), "warning" (warnings), and "error" (errors).</p>
+ </li>
+
+ <li>
+ <h3>DebugTimestamp - b - (read/write)</h3>
+ <p>Global wpa_supplicant debugging parameter. Determines if timestamps are shown in debug logs.</p>
+ </li>
+
+ <li>
+ <h3>DebugShowKeys - b - (read/write)</h3>
+ <p>Global wpa_supplicant debugging parameter. Determines if secrets are shown in debug logs.</p>
+ </li>
+
+ <li>
+ <h3>Interfaces - ao - (read)</h3>
+ <p>An array with paths to D-Bus objects representing controlled interfaces each.</p>
+ </li>
+
+ <li>
+ <h3>EapMethods - as - (read)</h3>
+ <p>An array with supported EAP methods names.</p>
+ </li>
+
+ <li>
+ <h3>Capabilities - as - (read)</h3>
+ <p>An array with supported capabilities (e.g., "ap", "ibss-rsn", "p2p", "interworking").</p>
+ </li>
+
+ <li>
+ <h3>WFDIEs - ay - (read/write)</h3>
+ <p>Wi-Fi Display subelements.</p>
+ </li>
+ </ul>
+
+\subsection dbus_main_signals Signals
+
+<ul>
+ <li>
+ <h3>InterfaceAdded ( o : interface, a{sv} : properties )</h3>
+ <p>A new interface was added to wpa_supplicant.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : interface</dt>
+ <dd>A D-Bus path to an object representing the added interface</dd>
+ </dl>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary containing properties of added interface.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>InterfaceRemoved ( o : interface )</h3>
+ <p>An interface was removed from wpa_supplicant.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : interface</dt>
+ <dd>A D-Bus path to an object representing the removed interface</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>PropertiesChanged ( a{sv} : properties )</h3>
+ <p>Some properties have changed.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with pairs of properties names which have changed and theirs new values. Possible dictionary keys are: "DebugParams"</dd>
+ </dl>
+ </li>
+ </ul>
+
+
+\section dbus_interface fi.w1.wpa_supplicant1.Interface
+
+Interface implemented by objects related to network interface added to
+wpa_supplicant, i.e., returned by
+fi.w1.wpa_supplicant1.CreateInterface.
+
+\subsection dbus_interface_methods Methods
+
+<ul>
+ <li>
+ <h3>Scan ( a{sv} : args ) --> nothing</h3>
+ <p>Triggers a scan.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with arguments describing scan type:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+ <tr><td>Type</td><td>s</td><td>Type of the scan. Possible values: "active", "passive"</td><td>Yes</td>
+ <tr><td>SSIDs</td><td>aay</td><td>Array of SSIDs to scan for (applies only if scan type is active)</td><td>No</td>
+ <tr><td>IEs</td><td>aay</td><td>Information elements to used in active scan (applies only if scan type is active)</td><td>No</td>
+ <tr><td>Channels</td><td>a(uu)</td><td>Array of frequencies to scan in form of (center, width) in MHz.</td><td>No</td>
+ <tr><td>AllowRoam</td><td>b</td><td>TRUE (or absent) to allow a roaming decision based on the results of this scan, FALSE to prevent a roaming decision.</td><td>No</td>
+ </table>
+ </dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>Invalid entries were found in the passed argument.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>Disconnect ( ) --> nothing</h3>
+ <p>Disassociates the interface from current network.</p>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.NotConnected</dt>
+ <dd>Interface is not connected to any network.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>AddNetwork ( a{sv} : args ) --> o : network</h3>
+ <p>Adds a new network to the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>A dictionary with network configuration. Dictionary entries are equivalent to entries in the "network" block in wpa_supplicant configuration file. Entry values should be appropriate type to the entry, e.g., an entry with key "frequency" should have value type int.</dd>
+ </dl>
+ <h4>Returns</h4>
+ <dl>
+ <dt>o : network</dt>
+ <dd>A D-Bus path to an object representing a configured network</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>Invalid entries were found in the passed argument.</dd>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>Adding network failed for an unknown reason.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>RemoveNetwork ( o : network ) --> nothing</h3>
+ <p>Removes a configured network from the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : network</dt>
+ <dd>A D-Bus path to an object representing a configured network returned by fi.w1.wpa_supplicant1.Interface.AddNetwork</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.NetworkUnknown</dt>
+ <dd>A passed path doesn't point to any network object.</dd>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>A passed path doesn't point to any network object.</dd>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>Removing network failed for an unknown reason.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>RemoveAllNetworks ( ) --> nothing</h3>
+ <p>Remove all configured networks from the interface.</p>
+ </li>
+
+ <li>
+ <h3>SelectNetwork ( o : network ) --> nothing</h3>
+ <p>Attempt association with a configured network.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : network</dt>
+ <dd>A D-Bus path to an object representing a configured network returned by fi.w1.wpa_supplicant1.Interface.AddNetwork</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.NetworkUnknown</dt>
+ <dd>A passed path doesn't point to any network object.</dd>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>A passed path doesn't point to any network object.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>Reassociate ( ) --> nothing</h3>
+ <p>Attempt reassociation.</p>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InterfaceDisabled</dt>
+ <dd>The interface is disabled.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>Reattach ( ) --> nothing</h3>
+ <p>Attempt reassociation back to the current BSS.</p>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.NotConnected</dt>
+ <dd>Interface is not connected to any network.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>Reconnect ( ) --> nothing</h3>
+ <p>Attempt reconnection and connect if in disconnected state.</p>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InterfaceDisabled</dt>
+ <dd>The interface is disabled.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>Roam ( s : addr ) --> nothing</h3>
+ <p>Initiate a roam to another BSS within the current ESS.</p>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>Missing address argument.</dd>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>Invalid hardware address format.</dd>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>Target BSS not found.</dd>
+ <dt>fi.w1.wpa_supplicant1.NotConnected</dt>
+ <dd>Interface is not connected to any network.</dd>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>Scan processing was not included in the build.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>AddBlob ( s : name, ay : data ) --> nothing</h3>
+ <p>Adds a blob to the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : name</dt>
+ <dd>A name of a blob</dd>
+ <dt>ay : data</dt>
+ <dd>A blob data</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.BlobExists</dt>
+ <dd>A blob with the specified name already exists.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>RemoveBlob ( s : name ) --> nothing</h3>
+ <p>Removes the blob from the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : name</dt>
+ <dd>A name of the blob to remove</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.BlobUnknown</dt>
+ <dd>A blob with the specified name doesn't exist.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>GetBlob ( s : name ) --> ay : data</h3>
+ <p>Returns the blob data of a previously added blob.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : name</dt>
+ <dd>A name of the blob</dd>
+ </dl>
+ <h4>Returns</h4>
+ <dl>
+ <dt>ay : data</dt>
+ <dd>A blob data</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.BlobUnknown</dt>
+ <dd>A blob with the specified name doesn't exist.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>AutoScan ( s : arg ) --> nothing</h3>
+ <p>Set autoscan parameters for the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : arg</dt>
+ <dd>Autoscan parameter line or empty to unset autoscan.</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.NoMemory</dt>
+ <dd>Needed memory was not possible to get allocated.</dd>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>Invalid entries were found in the passed argument.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>TDLSDiscover ( s : peer_address ) --> nothing</h3>
+ <p>Initiate a TDLS discovery for a peer.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : peer_address</dt>
+ <dd>MAC address for the peer to perform TDLS discovery.</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>Initiating the TDLS operation failed for an unknown reason.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>TDLSSetup ( s : peer_address ) --> nothing</h3>
+ <p>Setup a TDLS session for a peer.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : peer_address</dt>
+ <dd>MAC address for the peer to perform TDLS setup.</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>Initiating the TDLS operation failed for an unknown reason.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>TDLSStatus ( s : peer_address ) --> s</h3>
+ <p>Return TDLS status with respect to a peer.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : peer_address</dt>
+ <dd>MAC address for the peer for which status is requested.</dd>
+ </dl>
+ <h4>Returns</h4>
+ <dl>
+ <dt>s : status</dt>
+ <dd>Current status of the TDLS link with the selected peer.</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>TDLSTeardown ( s : peer_address ) --> nothing</h3>
+ <p>Tear down a TDLS session with a peer.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : peer_address</dt>
+ <dd>MAC address for the peer to tear down TDLS connectivity with.</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>Initiating the TDLS operation failed for an unknown reason.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>TDLSChannelSwitch ( a{sv} : args ) --> nothing</h3>
+ <p>Configure TDLS channel switching behavior with a peer.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>A dictionary with arguments identifying the peer and channel switching behavior.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>TDLSCancelChannelSwitch ( s : peer_address ) --> nothing</h3>
+ <p>Disable channel switching for a TDLS session with a peer.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : peer_address</dt>
+ <dd>MAC address for the peer.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>VendorElemAdd ( i: frame_id, ay: ielems ) --> nothing</h3>
+ <p>Add Vendor Elements to corresponding frame ID.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>i : frame_id</dt>
+ <dd>Frame ID for which Vendor specific IE is to be added.</dd>
+ <dt>ay : ielems</dt>
+ <dd>Information Element(s).</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>The "ielems" argument is not a properly formatted or size mismatch.</dd>
+ <dt>fi.w1.wpa_supplicant1.NoMemory</dt>
+ <dd>Needed memory was not possible to get allocated.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>VendorElemGet ( i: frame_id ) --> ay: ielems</h3>
+ <p>Get Vendor Elements of corresponding frame ID.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>i : frame_id</dt>
+ <dd>Frame ID for which Vendor specific IE is being queried.</dd>
+ <dt>ay : ielems</dt>
+ <dd>Information Element(s).</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>The "frame_id" argument is not valid.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>VendorElemRem ( i: frame_id, ay: ielems ) --> nothing</h3>
+ <p>Remove Vendor Elements of corresponding frame ID.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>i : frame_id</dt>
+ <dd>Frame ID for which Vendor specific IE is to be removed.</dd>
+ <dt>ay : ielems</dt>
+ <dd>Information Element(s) OR * to remove all.</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>The "ielems" argument is not a properly formatted or size mismatch.</dd>
+ <dt>fi.w1.wpa_supplicant1.NoMemory</dt>
+ <dd>Needed memory was not possible to get allocated.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>SaveConfig ( ) --> nothing</h3>
+ <p>Save configuration to the configuration file.</p>
+ </li>
+ <li>
+ <h3>AbortScan ( ) --> nothing</h3>
+ <p>Abort ongoing scan operation.</p>
+ </li>
+ <li>
+ <h3>EAPLogoff ( ) --> nothing</h3>
+ <p>IEEE 802.1X EAPOL state machine logoff.</p>
+ </li>
+ <li>
+ <h3>EAPLogon ( ) --> nothing</h3>
+ <p>IEEE 802.1X EAPOL state machine logon.</p>
+ </li>
+
+ <li>
+ <h3>NetworkReply ( o : network, s : field, s : value ) --> nothing</h3>
+ <p>Provide parameter requested by NetworkRequest().</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : network</dt>
+ <dd>A D-Bus path to an object representing the network (copied from NetworkRequest()).</dd>
+ <dt>s : field</dt>
+ <dd>Requested information (copied from NetworkRequest()).</dd>
+ <dt>s : value</dt>
+ <dd>The requested information (e.g., password for EAP authentication).</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.NetworkUnknown</dt>
+ <dd>A passed path doesn't point to any network object.</dd>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>A passed path doesn't point to any network object.</dd>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>IEEE 802.1X support was not included in the build.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>SetPKCS11EngineAndModulePath ( s : pkcs11_engine_path, s : pkcs11_module_path ) --> nothing</h3>
+ <p>Set PKCS #11 engine and module path.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : pkcs11_engine_path</dt>
+ <dd>PKCS #11 engine path.</dd>
+ <dt>s : pkcs11_module_path</dt>
+ <dd>PKCS #11 module path.</dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>org.freedesktop.DBus.Error.Failed.InvalidArgs</dt>
+ <dd>Invalid PKCS #11 engine or module path.</dd>
+ <dt>org.freedesktop.DBus.Error.Failed</dt>
+ <dd>Reinit of the EAPOL state machine with the new PKCS #11 engine and module path failed.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>SignalPoll ( ) --> a{sv} : properties</h3>
+ <p>Fetch signal properties for the current connection.</p>
+ <h4>Returns</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+ <tr><td>linkspeed</td><td>i</td><td>Link speed (Mbps)</td><td>No</td>
+ <tr><td>noise</td><td>i</td><td>Noise (dBm)</td><td>No</td>
+ <tr><td>width</td><td>s</td><td>Channel width</td><td>No</td>
+ <tr><td>frequency</td><td>u</td><td>Frequency (MHz)</td><td>No</td>
+ <tr><td>rssi</td><td>i</td><td>RSSI (dBm)</td><td>No</td>
+ <tr><td>avg-rssi</td><td>i</td><td>Average RSSI (dBm)</td><td>No</td>
+ <tr><td>center-frq1</td><td>i</td><td>VHT segment 1 frequency (MHz)</td><td>No</td>
+ <tr><td>center-frq2</td><td>i</td><td>VHT segment 2 frequency (MHz)</td><td>No</td>
+ </table>
+ </dd>
+ </dl>
+ </li>
+ <li>
+ <h3>FlushBSS ( u : age ) --> nothing</h3>
+ <p>Flush BSS entries from the cache.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>u : age</dt>
+ <dd>Maximum age in seconds for BSS entries to keep in cache (0 = remove all entries).</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>SubscribeProbeReq ( ) --> nothing</h3>
+ <p>Subscribe to receive Probe Request events. This is needed in addition to registering a signal handler for the ProbeRequest signal to avoid flooding D-Bus with all Probe Request indications when no application is interested in them.</p>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.SubscriptionInUse</dt>
+ <dd>Another application is already subscribed.</dd>
+ <dt>fi.w1.wpa_supplicant1.NoMemory</dt>
+ <dd>Needed memory was not possible to get allocated.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>UnsubscribeProbeReq ( ) --> nothing</h3>
+ <p>Unsubscribe from receiving Probe Request events.</p>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.NoSubscription</dt>
+ <dd>No subscription in place.</dd>
+ <dt>fi.w1.wpa_supplicant1.SubscriptionNotYou</dt>
+ <dd>Subscription in place, but for another process.</dd>
+ </dl>
+ </li>
+ </ul>
+
+\subsection dbus_interface_properties Properties
+
+<ul>
+ <li>
+ <h3>Capabilities - a{sv} - (read)</h3>
+ <p>Capabilities of the interface. Dictionary contains following entries:</p>
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th>
+ <tr><td>Pairwise</td><td>as</td><td>Possible array elements: "ccmp-256", "gcmp-256", "ccmp", "gcmp", "tkip", "none"</td>
+ <tr><td>Group</td><td>as</td><td>Possible array elements: "ccmp-256", "gcmp-256", "ccmp", "gcmp", "tkip", "wep104", "wep40"</td>
+ <tr><td>GroupMgmt</td><td>as</td><td>Possible array elements: "aes-128-cmac", "bip-gmac-128", "bip-gmac-256", "bip-cmac-256"</td>
+ <tr><td>KeyMgmt</td><td>as</td><td>Possible array elements: "wpa-psk", "wpa-ft-psk", "wpa-psk-sha256", "wpa-eap", "wpa-ft-eap", "wpa-eap-sha256", "sae", "owe", "ieee8021x", "wpa-none", "wps", "none"</td>
+ <tr><td>Protocol</td><td>as</td><td>Possible array elements: "rsn", "wpa"</td>
+ <tr><td>AuthAlg</td><td>as</td><td>Possible array elements: "open", "shared", "leap"</td>
+ <tr><td>Scan</td><td>as</td><td>Possible array elements: "active", "passive", "ssid"</td>
+ <tr><td>Modes</td><td>as</td><td>Possible array elements: "infrastructure", "ad-hoc", "ap"</td>
+ </table>
+ </li>
+
+ <li>
+ <h3>State - s - (read)</h3>
+ <p>A state of the interface. Possible values are: return "disconnected", "inactive", "scanning", "authenticating", "associating", "associated", "4way_handshake", "group_handshake", "completed","unknown".</p>
+ </li>
+
+ <li>
+ <h3>Scanning - b - (read)</h3>
+ <p>Determines if the interface is already scanning or not</p>
+ </li>
+
+ <li>
+ <h3>ApScan - u - (read/write)</h3>
+ <p>Identical to ap_scan entry in wpa_supplicant configuration file. Possible values are 0, 1 or 2.</p>
+ </li>
+
+ <li>
+ <h3>BSSExpireAge - u - (read/write)</h3>
+ <p>Identical to bss_expiration_age entry in wpa_supplicant configuration file.</p>
+ </li>
+
+ <li>
+ <h3>BSSExpireCount - u - (read/write)</h3>
+ <p>Identical to bss_expiration_scan_count entry in wpa_supplicant configuration file.</p>
+ </li>
+
+ <li>
+ <h3>Country - s - (read/write)</h3>
+ <p>Identical to country entry in wpa_supplicant configuration file.</p>
+ </li>
+
+ <li>
+ <h3>Ifname - s - (read)</h3>
+ <p>Name of network interface controlled by the interface, e.g., wlan0.</p>
+ </li>
+
+ <li>
+ <h3>BridgeIfname - s - (read)</h3>
+ <p>Name of bridge network interface controlled by the interface, e.g., br0.</p>
+ </li>
+
+ <li>
+ <h3>Driver - s - (read)</h3>
+ <p>Name of driver used by the interface, e.g., nl80211.</p>
+ </li>
+
+ <li>
+ <h3>ConfigFile - s - (read)</h3>
+ <p>Configuration file path. Returns an empty string if no configuration file is in use.</p>
+ </li>
+
+ <li>
+ <h3>CurrentBSS - o - (read)</h3>
+ <p>Path to D-Bus object representing BSS which wpa_supplicant is associated with, or "/" if is not associated at all.</p>
+ </li>
+
+ <li>
+ <h3>CurrentNetwork - o - (read)</h3>
+ <p>Path to D-Bus object representing configured network which wpa_supplicant uses at the moment, or "/" if doesn't use any.</p>
+ </li>
+
+ <li>
+ <h3>CurrentAuthMode - s - (read)</h3>
+ <p>Current authentication type.</p>
+ </li>
+
+ <li>
+ <h3>Blobs - as - (read)</h3>
+ <p>List of blobs names added to the Interface.</p>
+ </li>
+
+ <li>
+ <h3>BSSs - ao - (read)</h3>
+ <p>List of D-Bus objects paths representing BSSs known to the interface, i.e., scan results.</p>
+ </li>
+
+ <li>
+ <h3>Stations - ao - (read)</h3>
+ <p>List of D-Bus objects paths representing connected stations in AP mode.</p>
+ </li>
+
+ <li>
+ <h3>Networks - ao - (read)</h3>
+ <p>List of D-Bus objects paths representing configured networks.</p>
+ </li>
+
+ <li>
+ <h3>FastReauth - b - (read/write)</h3>
+ <p>Identical to fast_reauth entry in wpa_supplicant configuration file.</p>
+ </li>
+
+ <li>
+ <h3>ScanInterval - i - (read/write)</h3>
+ <p>Time (in seconds) between scans for a suitable AP. Must be >= 0.</p>
+ </li>
+
+ <li>
+ <h3>PKCS11EnginePath - s - (read)</h3>
+ <p>PKCS #11 engine path.</p>
+ </li>
+
+ <li>
+ <h3>PKCS11ModulePath - s - (read)</h3>
+ <p>PKCS #11 module path.</p>
+ </li>
+
+ <li>
+ <h3>DisconnectReason - i - (read)</h3>
+ <p>The most recent IEEE 802.11 reason code for disconnect. Negative value indicates locally generated disconnection.</p>
+ </li>
+
+ <li>
+ <h3>AuthStatusCode - i - (read)</h3>
+ <p>The most recent IEEE 802.11 status code for authentication.</p>
+ </li>
+
+ <li>
+ <h3>AssocStatusCode - i - (read)</h3>
+ <p>The most recent IEEE 802.11 status code for association rejection.</p>
+ </li>
+
+ <li>
+ <h3>RoamTime - u - (read)</h3>
+ <p>The most recent roam time in milliseconds.</p>
+ </li>
+
+ <li>
+ <h3>RoamComplete - b - (read)</h3>
+ <p>The most recent roam success or failure.</p>
+ </li>
+
+ <li>
+ <h3>SessionLength - u - (read)</h3>
+ <p>The most recent BSS session length in milliseconds.</p>
+ </li>
+
+ <li>
+ <h3>BSSTMStatus - u - (read)</h3>
+ <p>The most recent BSS Transition Management status code.</p>
+ </li>
+
+ <li>
+ <h3>EapolVersion - s - (read/write)</h3>
+ <p>IEEE 802.1X/EAPOL version number</p>
+ </li>
+
+ <li>
+ <h3>Bgscan - s - (read/write)</h3>
+ <p>Background scan and roaming parameters or an empty string if none</p>
+ </li>
+
+ <li>
+ <h3>DisableScanOffload - s - (read/write)</h3>
+ <p>Disable automatic offloading of scan requests</p>
+ </li>
+
+ <li>
+ <h3>OpenscEnginePath - s - (read/write)</h3>
+ <p>Path to the OpenSSL engine for opensc</p>
+ </li>
+
+ <li>
+ <h3>OpensslCiphers - s - (read/write)</h3>
+ <p>OpenSSL cipher string</p>
+ </li>
+
+ <li>
+ <h3>PcscReader - s - (read/write)</h3>
+ <p>PC/SC reader name prefix</p>
+ </li>
+
+ <li>
+ <h3>PcscPin - s - (read/write)</h3>
+ <p>PIN for USIM, GSM SIM, and smartcards</p>
+ </li>
+
+ <li>
+ <h3>ExternalSim - s - (read/write)</h3>
+ <p>Use external processing for SIM/USIM operations</p>
+ </li>
+
+ <li>
+ <h3>DriverParam - s - (read/write)</h3>
+ <p>Driver interface parameters</p>
+ </li>
+
+ <li>
+ <h3>Dot11RSNAConfigPMKLifetime - s - (read/write)</h3>
+ <p>Maximum lifetime of a PMK</p>
+ </li>
+
+ <li>
+ <h3>Dot11RSNAConfigPMKReauthThreshold - s - (read/write)</h3>
+ <p>PMK re-authentication threshold</p>
+ </li>
+
+ <li>
+ <h3>Dot11RSNAConfigSATimeout - s - (read/write)</h3>
+ <p>Security association timeout</p>
+ </li>
+
+ <li>
+ <h3>BssMaxCount - s - (read/write)</h3>
+ <p>Maximum number of BSS entries to keep in memory</p>
+ </li>
+
+ <li>
+ <h3>FilterSsids - s - (read/write)</h3>
+ <p>SSID-based scan result filtering</p>
+ </li>
+
+ <li>
+ <h3>FilterRssi - s - (read/write)</h3>
+ <p>RSSI-based scan result filtering</p>
+ </li>
+
+ <li>
+ <h3>MaxNumSta - s - (read/write)</h3>
+ <p>Maximum number of STAs in an AP/P2P GO</p>
+ </li>
+
+ <li>
+ <h3>DisassocLowAck - s - (read/write)</h3>
+ <p>Disassocicate stations with massive packet loss</p>
+ </li>
+
+ <li>
+ <h3>Interworking - s - (read/write)</h3>
+ <p>Whether Interworking (IEEE 802.11u) is enabled</p>
+ </li>
+
+ <li>
+ <h3>Hessid - s - (read/write)</h3>
+ <p>Homogeneous ESS identifier</p>
+ </li>
+
+ <li>
+ <h3>AccessNetworkType - s - (read/write)</h3>
+ <p>Access Network Type</p>
+ </li>
+
+ <li>
+ <h3>PbcInM1 - s - (read/write)</h3>
+ <p>AP mode WPS probing workaround for PBC with Windows 7</p>
+ </li>
+
+ <li>
+ <h3>Autoscan - s - (read/write)</h3>
+ <p>Automatic scan parameters or an empty string if none</p>
+ </li>
+
+ <li>
+ <h3>WpsNfcDevPwId - s - (read/write)</h3>
+ <p>NFC Device Password ID for password token</p>
+ </li>
+
+ <li>
+ <h3>WpsNfcDhPubkey - s - (read/write)</h3>
+ <p>NFC DH Public Key for password token</p>
+ </li>
+
+ <li>
+ <h3>WpsNfcDhPrivkey - s - (read/write)</h3>
+ <p>NFC DH Private Key for password token</p>
+ </li>
+
+ <li>
+ <h3>WpsNfcDevPw - s - (read/write)</h3>
+ <p>NFC Device Password for password token</p>
+ </li>
+
+ <li>
+ <h3>ExtPasswordBackend - s - (read/write)</h3>
+ <p>External password backend or an empty string if none</p>
+ </li>
+
+ <li>
+ <h3>P2pGoMaxInactivity - s - (read/write)</h3>
+ <p>Timeout in seconds to detect STA inactivity</p>
+ </li>
+
+ <li>
+ <h3>AutoInterworking - s - (read/write)</h3>
+ <p>Whether to use network selection automatically</p>
+ </li>
+
+ <li>
+ <h3>Okc - s - (read/write)</h3>
+ <p>Whether to enable opportunistic key caching by default</p>
+ </li>
+
+ <li>
+ <h3>Pmf - s - (read/write)</h3>
+ <p>Whether to enable/require PMF by default</p>
+ </li>
+
+ <li>
+ <h3>SaeGroups - s - (read/write)</h3>
+ <p>Preference list of enabled groups for SAE</p>
+ </li>
+
+ <li>
+ <h3>DtimPeriod - s - (read/write)</h3>
+ <p>Default DTIM period in Beacon intervals</p>
+ </li>
+
+ <li>
+ <h3>BeaconInt - s - (read/write)</h3>
+ <p>Default Beacon interval in TU</p>
+ </li>
+
+ <li>
+ <h3>IgnoreOldScanRes - s - (read/write)</h3>
+ <p>Ignore scan results older than request</p>
+ </li>
+
+ <li>
+ <h3>FreqList - s - (read/write)</h3>
+ <p>Array of allowed scan frequencies or an empty string for all</p>
+ </li>
+
+ <li>
+ <h3>ScanCurFreq - s - (read/write)</h3>
+ <p>Whether to scan only the current channel</p>
+ </li>
+
+ <li>
+ <h3>SchedScanInterval - s - (read/write)</h3>
+ <p>schedule scan interval</p>
+ </li>
+
+ <li>
+ <h3>TdlsExternalControl - s - (read/write)</h3>
+ <p>External control for TDLS setup requests</p>
+ </li>
+
+ <li>
+ <h3>OsuDir - s - (read/write)</h3>
+ <p>OSU provider information directory</p>
+ </li>
+
+ <li>
+ <h3>WowlanTriggers - s - (read/write)</h3>
+ <p>Wake-on-WLAN triggers</p>
+ </li>
+
+ <li>
+ <h3>P2pSearchDelay - s - (read/write)</h3>
+ <p>Extra delay between concurrent search iterations</p>
+ </li>
+
+ <li>
+ <h3>MacAddr - s - (read/write)</h3>
+ <p>MAC address policy default</p>
+ </li>
+
+ <li>
+ <h3>RandAddrLifetime - s - (read/write)</h3>
+ <p>Lifetime of random MAC address in seconds</p>
+ </li>
+
+ <li>
+ <h3>PreassocMacAddr - s - (read/write)</h3>
+ <p>Pre-association MAC address policy</p>
+ </li>
+
+ <li>
+ <h3>KeyMgmtOffload - s - (read/write)</h3>
+ <p>Use key management offload</p>
+ </li>
+
+ <li>
+ <h3>PassiveScan - s - (read/write)</h3>
+ <p>Whether to force passive scan for network connection</p>
+ </li>
+
+ <li>
+ <h3>ReassocSameBssOptim - s - (read/write)</h3>
+ <p>Whether to optimize reassoc-to-same-BSS</p>
+ </li>
+
+ <li>
+ <h3>WpsPriority - s - (read/write)</h3>
+ <p>Priority for the networks added through WPS</p>
+ </li>
+
+ <li>
+ <h3>MACAddressRandomizationMask - a{say} - (read/write)</h3>
+ <p>Masks to show which bits not to randomize with MAC address randomization. Possible keys are "scan", "sched_scan", and "pno". Values must be an array of 6 bytes.</p>
+ <p>When this property is set, the new dictionary replaces the old value, rather than merging them together. Leaving a key out of the dictionary will turn off MAC address randomization for that scan type.</p>
+ </li>
+ </ul>
+
+\subsection dbus_interface_signals Signals
+
+<ul>
+ <li>
+ <h3>ScanDone ( b : success )</h3>
+ <p>Scanning finished. </p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : success</dt>
+ <dd>Determines if scanning was successful. If so, results are available.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>BSSAdded ( o : BSS, a{sv} : properties )</h3>
+ <p>Interface became aware of a new BSS.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : BSS</dt>
+ <dd>A D-Bus path to an object representing the new BSS.</dd>
+ </dl>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary containing properties of added BSS.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>BSSRemoved ( o : BSS )</h3>
+ <p>BSS disappeared.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : BSS</dt>
+ <dd>A D-Bus path to an object representing the BSS.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>BlobAdded ( s : blobName )</h3>
+ <p>A new blob has been added to the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : blobName</dt>
+ <dd>A name of the added blob.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>BlobRemoved ( s : blobName )</h3>
+ <p>A blob has been removed from the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : blobName</dt>
+ <dd>A name of the removed blob.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>NetworkAdded ( o : network, a{sv} : properties )</h3>
+ <p>A new network has been added to the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : network</dt>
+ <dd>A D-Bus path to an object representing the added network.</dd>
+ </dl>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary containing properties of added network.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>NetworkRemoved ( o : network )</h3>
+ <p>The network has been removed from the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : network</dt>
+ <dd>A D-Bus path to an object representing the removed network.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>NetworkSelected ( o : network )</h3>
+ <p>The network has been selected.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : network</dt>
+ <dd>A D-Bus path to an object representing the selected network.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>StaAuthorized ( s : mac )</h3>
+ <p>A new station has been authorized to the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : mac</dt>
+ <dd>A mac address which has been authorized.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>StaDeauthorized ( s : mac )</h3>
+ <p>A station has been deauthorized to the interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : mac</dt>
+ <dd>A mac address which has been deauthorized.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>StationAdded ( o : Station, a{sv} : properties )</h3>
+ <p>A new station has been added to the interface.</p>
+ <p>This signal complements StaAuthorized, passing the Station object and its properties.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : Station</dt>
+ <dd>A D-Bus path to an object representing the new Station.</dd>
+ </dl>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary containing properties of added Station.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>StationRemoved ( o : Station )</h3>
+ <p>The station has been removed from the interface.</p>
+ <p>This signal complements StaDeauthorized, passing the Station object.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : Station</dt>
+ <dd>A D-Bus path to an object representing the Station.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>PropertiesChanged ( a{sv} : properties )</h3>
+ <p>Some properties have changed.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with pairs of properties names which have changed and theirs new values. Possible dictionary keys are: "ApScan", "Scanning", "State", "CurrentBSS", "CurrentNetwork"</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>Certification ( a{sv} : parameters )</h3>
+ <p>Information about server TLS certificates.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : parameters</dt>
+ <dd>A dictionary with pairs of field names and their values. Possible dictionary keys are: "depth", "subject", "altsubject", "cert_hash", "cert".</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>EAP ( s : status, s : parameter )</h3>
+ <p>Information about EAP peer status.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : status</dt>
+ <dd>Operation, e.g., "started", "accept proposed method", "remote certificate verification", "eap parameter needed", "completion".</dd>
+ <dt>s : parameter</dt>
+ <dd>Information about the operation, e.g., EAP method name, "success".</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>NetworkRequest ( o : network, s : field, s : txt )</h3>
+ <p>Request for network parameter. NetworkResponse() is used to provide the requested parameter.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : network</dt>
+ <dd>D-Bus path to an object representing the network.</dd>
+ <dt>s : field</dt>
+ <dd>Requested information, e.g., "PASSWORD".</dd>
+ <dt>txt : field</dt>
+ <dd>Human readable information about the requested information.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>ProbeRequest ( a{sv} : args )</h3>
+ <p>Information about a received Probe Request frame. This signal is delivered only to a single application that has subscribed to received the events with SubscribeProbeReq().</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>A dictionary with pairs of field names and their values. Possible dictionary keys are: "addr", "dst", "bssid", "ies", "signal".</dd>
+ </dl>
+ </li>
+ </ul>
+
+
+\section dbus_wps fi.w1.wpa_supplicant1.Interface.WPS
+
+Interface for performing WPS (Wi-Fi Simple Config) operations.
+
+\subsection dbus_wps_methods Methods
+
+<ul>
+ <li>
+ <h3>Start ( a{sv} : args ) --> a{sv} : output</h3>
+ <p>Starts WPS configuration. Note: When used with P2P groups, this needs to be issued on the GO group interface.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with arguments used to start WPS configuration. The dictionary may contain the following entries:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+ <tr><td>Role</td><td>s</td><td>The device's role. Possible values are "enrollee" and "registrar".</td><td>Yes</td>
+ <tr><td>Type</td><td>s</td><td>WPS authentication type. Applies only for enrollee role. Possible values are "pin" and "pbc".</td><td>Yes, for enrollee role; otherwise no</td>
+ <tr><td>Pin</td><td>s</td><td>WPS Pin.</td><td>Yes, for registrar role; otherwise optional</td>
+ <tr><td>Bssid</td><td>ay</td><td>Note: This is used to specify the peer MAC address when authorizing WPS connection in AP or P2P GO role.</td><td>No</td>
+ <tr><td>P2PDeviceAddress</td><td>ay</td><td>P2P Device Address of a peer to authorize for PBC connection. Used only in P2P GO role.</td><td>No</td>
+ </table>
+ </dd>
+ </dl>
+ <h4>Returns</h4>
+ <dl>
+ <dt>a{sv} : output</dt>
+ <dd>
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+ <tr><td>Pin</td><td>s</td><td>Newly generated PIN, if not specified for enrollee role and pin authentication type.</td><td>No</td>
+ </table>
+ </dd>
+ </dl>
+ <h4>Possible errors</h4>
+ <dl>
+ <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+ <dd>Starting WPS configuration failed for an unknown reason.</dd>
+ <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+ <dd>Invalid entries were found in the passed argument.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>Cancel ( nothing ) --> nothing</h3>
+ <p>Cancel ongoing WPS operation.</p>
+ </li>
+ </ul>
+
+\subsection dbus_wps_properties Properties
+
+<ul>
+ <li>
+ <h3>ProcessCredentials - b - (read/write)</h3>
+ <p>Determines if the interface will process the credentials (credentials_processed configuration file parameter).</p>
+ </li>
+ <li>
+ <h3>ConfigMethods - s - (read/write)</h3>
+ <p>The currently advertised WPS configuration methods. Available methods: usba ethernet label display ext_nfc_token int_nfc_token nfc_interface push_button keypad virtual_display physical_display virtual_push_button physical_push_button.</p>
+ </li>
+ <li>
+ <h3>DeviceName - s - (read/write)</h3>
+ <p>User-friendly description of device; up to 32 octets encoded in UTF-8.</p>
+ </li>
+ <li>
+ <h3>Manufacturer - s - (read/write)</h3>
+ <p>The manufacturer of the device (up to 64 ASCII characters).</p>
+ </li>
+ <li>
+ <h3>ModelName - s - (read/write)</h3>
+ <p>Model of the device (up to 32 ASCII characters).</p>
+ </li>
+ <li>
+ <h3>ModelNumber - s - (read/write)</h3>
+ <p>Additional device description (up to 32 ASCII characters).</p>
+ </li>
+ <li>
+ <h3>SerialNumber - s - (read/write)</h3>
+ <p>Serial number of the device (up to 32 characters).</p>
+ </li>
+ <li>
+ <h3>DeviceType - ay - (read/write)</h3>
+ <p>Device Type (8 octet value with 2 octet category, 4 octet OUI, 2 octet subcategory.</p>
+ </li>
+ </ul>
+
+\subsection dbus_wps_signals Signals
+
+<ul>
+ <li>
+ <h3>Event ( s : name, a{sv} : args )</h3>
+ <p>WPS event occurred.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : event</dt>
+ <dd>Event type. Possible values are: "success, "fail", "m2d", and
+ "pbc-overlap".</dd>
+ <dt>a{sv} : args</dt>
+ <dd>
+ Event arguments. Empty for success and pbc-overlap events, error information ( "msg" : i, "config_error" : i, "error_indication" : i ) for fail event and following entries for m2d event:
+ <table>
+ <tr><th>config_methods</th><th>Value type</th>
+ <tr><td>manufacturer</td><td>q</td>
+ <tr><td>model_name</td><td>ay</td>
+ <tr><td>model_number</td><td>ay</td>
+ <tr><td>serial_number</td><td>ay</td>
+ <tr><td>dev_name</td><td>ay</td>
+ <tr><td>primary_dev_type</td><td>ay</td>
+ <tr><td>config_error</td><td>q</td>
+ <tr><td>dev_password_id</td><td>q</td>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>Credentials ( a{sv} : credentials )</h3>
+ <p>WPS credentials. Dictionary contains:</p>
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th>
+ <tr><td>BSSID</td><td>ay</td><td></td>
+ <tr><td>SSID</td><td>s</td><td></td>
+ <tr><td>AuthType</td><td>as</td><td>Possible array elements: "open", "shared", "wpa-psk", "wpa-eap", "wpa2-eap", "wpa2-psk"</td>
+ <tr><td>EncrType</td><td>as</td><td>Possible array elements: "none", "wep", "tkip", "aes"</td>
+ <tr><td>Key</td><td>ay</td><td>Key data</td>
+ <tr><td>KeyIndex</td><td>u</td><td>Key index</td>
+ </table>
+ </li>
+
+ <li>
+ <h3>PropertiesChanged ( a{sv} : properties )</h3>
+ <p>Some properties have changed.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with pairs of properties names which have changed and theirs new values. Possible dictionary keys are: "ProcessCredentials"</dd>
+ </dl>
+ </li>
+ </ul>
+
+
+\section dbus_p2pdevice fi.w1.wpa_supplicant1.Interface.P2PDevice
+
+Interface for performing P2P (Wi-Fi Peer-to-Peer) P2P Device operations.
+
+\subsection dbus_p2pdevice_methods Methods
+
+<ul>
+ <li>
+ <h3>Find ( a{sv} : args ) --> nothing</h3>
+ <p>Start P2P find operation (i.e., alternating P2P Search and Listen states to discover peers and be discoverable).</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with parameters for the P2P find operation:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>Timeout</td><td>i</td><td>Timeout for operating in seconds</td><td>no</td></tr>
+ <tr><td>RequestedDeviceTypes</td><td>aay</td><td>WPS Device Types to search for</td><td>no</td></tr>
+ <tr><td>DiscoveryType</td><td>s</td><td>"start_with_full" (default, if not specified), "social", "progressive"</td><td>no</td></tr>
+ <tr><td>freq</td><td>i</td><td>Initial scan channel (frequency in MHz) for the start_with_full case to limit the initial scan to the specified channel</td><td>no</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>StopFind ( nothing ) --> nothing</h3>
+ <p>Stop P2P find operation.</p>
+ </li>
+
+ <li>
+ <h3>Listen ( i : timeout ) --> nothing</h3>
+ <p>Start P2P listen operation (i.e., be discoverable).</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>i : timeout</dt>
+ <dd>Timeout in seconds for stopping the listen operation.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>ExtendedListen ( a{sv} : args ) --> nothing</h3>
+ <p>Configure Extended Listen Timing. If the parameters are omitted, this feature is disabled. If the parameters are included, Listen State will be entered every interval msec for at least period msec. Both values have acceptable range of 1-65535 (with interval obviously having to be larger than or equal to duration). If the P2P module is not idle at the time the Extended Listen Timing timeout occurs, the Listen State operation will be skipped.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with parameters for extended listen. Leave out all items to disable extended listen.
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>period</td><td>i</td><td>Extended listen period in milliseconds; 1-65535.</td><td>no</td></tr>
+ <tr><td>interval</td><td>i</td><td>Extended listen interval in milliseconds; 1-65535.</td><td>no</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>PresenceRequest ( a{sv} : args ) --> nothing</h3>
+ <p>Request a specific GO presence in a P2P group where the local device is a P2P Client. Send a P2P Presence Request to the GO (this is only available when acting as a P2P client). If no duration/interval pairs are given, the request indicates that this client has no special needs for GO presence. The first parameter pair gives the preferred duration and interval values in microseconds. If the second pair is included, that indicates which value would be acceptable.
+ \note This needs to be issued on a P2P group interface if separate group interfaces are used.
+ \bug It would be cleaner to not require .P2PDevice methods to be issued on a group interface. In other words, args['group_object'] could be used to specify the group or this method could be moved to be a .Group PresenceRequest() method.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with parameters for the presence request.
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>duration1</td><td>i</td><td>Duration in microseconds.</td><td>no</td></tr>
+ <tr><td>interval1</td><td>i</td><td>Interval in microseconds.</td><td>no</td></tr>
+ <tr><td>duration2</td><td>i</td><td>Duration in microseconds.</td><td>no</td></tr>
+ <tr><td>interval2</td><td>i</td><td>Interval in microseconds.</td><td>no</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>ProvisionDiscoveryRequest ( o : peer, s : config_method ) --> nothing</h3>
+ </li>
+
+ <li>
+ <h3>Connect ( a{sv} : args ) --> s : generated_pin</h3>
+ <p>Request a P2P group to be started through GO Negotiation or by joining an already operating group.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with parameters for the requested connection:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>peer</td><td>o</td><td></td><td>yes</td></tr>
+ <tr><td>persistent</td><td>b</td><td>Whether to form a persistent group.</td><td>no</td></tr>
+ <tr><td>join</td><td>b</td><td>Whether to join an already operating group instead of forming a new group.</td><td>no</td></tr>
+ <tr><td>authorize_only</td><td>b</td><td>Whether to authorize a peer to initiate GO Negotiation instead of initiating immediately.</td><td>no</td></tr>
+ <tr><td>frequency</td><td>i</td><td>Operating frequency in MHz</td><td>no</td></tr>
+ <tr><td>go_intent</td><td>i</td><td>GO intent 0-15</td><td>no</td></tr>
+ <tr><td>wps_method</td><td>s</td><td>"pbc", "display", "keypad", "pin" (alias for "display")</td><td>yes</td></tr>
+ <tr><td>pin</td><td>s</td><td></td><td>no</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>GroupAdd ( a{sv} : args ) --> nothing</h3>
+ <p>Request a P2P group to be started without GO Negotiation.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with parameters for the requested group:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>persistent</td><td>b</td><td>Whether to form a persistent group.</td><td>no</td></tr>
+ <tr><td>persistent_group_object</td><td>o</td><td></td><td>no</td></tr>
+ <tr><td>frequency</td><td>i</td><td>Operating frequency in MHz</td><td>no</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>Cancel ( nothing ) --> nothing</h3>
+ <p>Stop ongoing P2P group formation operation.</p>
+ </li>
+
+ <li>
+ <h3>Invite ( a{sv} : args ) --> nothing</h3>
+ <p>Invite a peer to join an already operating group or to re-invoke a persistent group.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with parameters for the invitation:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>peer</td><td>o</td><td></td><td>yes</td></tr>
+ <tr><td>persistent_group_object</td><td>o</td><td></td><td>no</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>Disconnect ( nothing ) --> nothing</h3>
+ <p>Terminate a P2P group.
+ \note This needs to be issued on a P2P group interface if separate group interfaces are used.
+ \bug It would be cleaner to not require .P2PDevice methods to be issued on a group interface. In other words, this would either need to be Disconnect(group_object) or moved to be a .Group Disconnect() method.</p>
+ </li>
+
+ <li>
+ <h3>RejectPeer ( o : peer ) --> nothing</h3>
+ <p>Reject connection attempt from a peer (specified with a device address). This is a mechanism to reject a pending GO Negotiation with a peer and request to automatically block any further connection or discovery of the peer.</p>
+ </li>
+
+ <li>
+ <h3>RemoveClient ( a{sv} : args ) --> nothing</h3>
+ <p>Remove the client from all groups (operating and persistent) from the local GO.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with parameters for removing a client:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>peer</td><td>o</td><td>Object path for peer's P2P Device Address</td><td>yes</td></tr>
+ <tr><td>iface</td><td>s</td><td>Interface address[MAC Address format] of the peer to be disconnected. Required if object path is not provided.</td><td>no</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>Flush ( nothing ) --> nothing</h3>
+ <p>Flush P2P peer table and state.</p>
+ </li>
+
+ <li>
+ <h3>AddService ( a{sv} : args ) --> nothing</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with parameters for the service:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>service_type</td><td>s</td><td>"upnp", "bonjour"</td><td>yes</td></tr>
+ <tr><td>version</td><td>u</td><td>Required for UPnP services.</td><td>no</td></tr>
+ <tr><td>service</td><td>s</td><td></td><td></td></tr>
+ <tr><td>query</td><td>ay</td><td></td><td></td></tr>
+ <tr><td>response</td><td>ay</td><td></td><td></td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>DeleteService ( a{sv} : args ) --> nothing</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with parameters for the service:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>service_type</td><td>s</td><td>"upnp", "bonjour"</td><td>yes</td></tr>
+ <tr><td>version</td><td>u</td><td>Required for UPnP services.</td><td>no</td></tr>
+ <tr><td>service</td><td>s</td><td></td><td></td></tr>
+ <tr><td>query</td><td>ay</td><td></td><td></td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>FlushService ( nothing ) --> nothing</h3>
+ </li>
+
+ <li>
+ <h3>ServiceDiscoveryRequest ( a{sv} : args ) --> t : ref</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with following parameters:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>peer_object</td><td>o</td><td></td><td>no</td></tr>
+ <tr><td>service_type</td><td>s</td><td>"upnp"</td><td>no</td></tr>
+ <tr><td>version</td><td>u</td><td>Required for UPnP services.</td><td>no</td></tr>
+ <tr><td>service</td><td>s</td><td></td><td></td></tr>
+ <tr><td>tlv</td><td>ay</td><td></td><td></td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>ServiceDiscoveryResponse ( a{sv} : args ) --> nothing : ref</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with following parameters:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>peer_object</td><td>o</td><td></td><td>yes</td></tr>
+ <tr><td>frequency</td><td>i</td><td></td><td>yes</td></tr>
+ <tr><td>dialog_token</td><td>i</td><td></td><td>yes</td></tr>
+ <tr><td>tlvs</td><td>ay</td><td></td><td>yes</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>ServiceDiscoveryCancelRequest ( t : args ) --> nothing : ref</h3>
+ </li>
+
+ <li>
+ <h3>ServiceUpdate ( nothing ) --> nothing</h3>
+ </li>
+
+ <li>
+ <h3>ServiceDiscoveryExternal ( i : arg ) --> nothing</h3>
+ </li>
+
+ <li>
+ <h3>AddPersistentGroup ( a{sv} : args ) --> o : path</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>
+ A dictionary with following parameters:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+ <tr><td>bssid</td><td>s</td><td>P2P Device Address of the GO in the persistent group.</td><td>yes</td></tr>
+ <tr><td>ssid</td><td>s</td><td>SSID of the group</td><td>yes</td></tr>
+ <tr><td>psk</td><td>s</td><td>Passphrase (on the GO and optionally on P2P Client) or PSK (on P2P Client if passphrase ise not known)</td><td>yes</td></tr>
+ <tr><td>mode</td><td>s</td><td>"3" on GO or "0" on P2P Client</td><td>yes</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>RemovePersistentGroup ( o : path ) --> nothing</h3>
+ </li>
+
+ <li>
+ <h3>RemoveAllPersistentGroups ( nothing ) --> nothing</h3>
+ </li>
+</ul>
+
+\subsection dbus_p2pdevice_properties Properties
+
+<ul>
+ <li>
+ <h3>P2PDeviceConfig - a{sv} - (read/write)</h3>
+ <p>Dictionary with following entries. On write, only the included values are changed.</p>
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+ <tr><td>DeviceName</td><td>s</td><td></td></tr>
+ <tr><td>PrimaryDeviceType</td><td>ay</td><td></td></tr>
+ <tr><td>SecondaryDeviceTypes</td><td>aay</td><td></td></tr>
+ <tr><td>VendorExtension</td><td>aay</td><td></td></tr>
+ <tr><td>GOIntent</td><td>u</td><td></td></tr>
+ <tr><td>PersistentReconnect</td><td>b</td><td></td></tr>
+ <tr><td>ListenRegClass</td><td>u</td><td></td></tr>
+ <tr><td>ListenChannel</td><td>u</td><td></td></tr>
+ <tr><td>OperRegClass</td><td>u</td><td></td></tr>
+ <tr><td>OperChannel</td><td>u</td><td></td></tr>
+ <tr><td>SsidPostfix</td><td>s</td><td></td></tr>
+ <tr><td>IntraBss</td><td>b</td><td></td></tr>
+ <tr><td>GroupIdle</td><td>u</td><td></td></tr>
+ <tr><td>disassoc_low_ack</td><td>u</td><td></td></tr>
+ <tr><td>NoGroupIface</td><td>b</td><td></td></tr>
+ <tr><td>p2p_search_delay</td><td>u</td><td></td></tr>
+ </table>
+ </li>
+
+ <li>
+ <h3>Peers - ao - (read)</h3>
+ </li>
+
+ <li>
+ <h3>Role - s - (read)</h3>
+ <p>\bug What is this trying to indicate? It does not make much sense to have a P2PDevice property role since there can be multiple concurrent groups and the P2P Device role is always active anyway.</p>
+ </li>
+
+ <li>
+ <h3>Group - o - (read)</h3>
+ <p>\bug What is this trying to indicate? It does not make much sense to have a P2PDevice property Group since there can be multiple concurrent groups.</p>
+ </li>
+
+ <li>
+ <h3>PeerGO - o - (read)</h3>
+ <p>\bug What is this trying to indicate? It does not make much sense to have a P2PDevice property PeerGO since there can be multiple concurrent groups.</p>
+ </li>
+
+ <li>
+ <h3>PersistentGroups - ao - (read)</h3>
+ </li>
+</ul>
+
+\subsection dbus_p2pdevice_signals Signals
+
+<ul>
+ <li>
+ <h3>DeviceFound ( o : path )</h3>
+ </li>
+
+ <li>
+ <h3>DeviceFoundProperties ( o : path, a{sv} : properties )</h3>
+ <p>A new peer device has been found.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : path</dt>
+ <dd>A D-Bus path to an object representing the found peer device.</dd>
+ </dl>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary containing properties of the found peer device.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>DeviceLost ( o : path )</h3>
+ </li>
+
+ <li>
+ <h3>FindStopped ( )</h3>
+ </li>
+
+ <li>
+ <h3>ProvisionDiscoveryRequestDisplayPin ( o : peer_object, s : pin )</h3>
+ </li>
+
+ <li>
+ <h3>ProvisionDiscoveryResponseDisplayPin ( o : peer_object, s : pin )</h3>
+ </li>
+
+ <li>
+ <h3>ProvisionDiscoveryRequestEnterPin ( o : peer_object )</h3>
+ </li>
+
+ <li>
+ <h3>ProvisionDiscoveryResponseEnterPin ( o : peer_object )</h3>
+ </li>
+
+ <li>
+ <h3>ProvisionDiscoveryPBCRequest ( o : peer_object )</h3>
+ </li>
+
+ <li>
+ <h3>ProvisionDiscoveryPBCResponse ( o : peer_object )</h3>
+ </li>
+
+ <li>
+ <h3>ProvisionDiscoveryFailure ( o : peer_object, i : status )</h3>
+ </li>
+
+ <li>
+ <h3>GroupStarted ( a{sv} : properties )</h3>
+ <p>A new P2P group was started or joined.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with following information on the added group:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+ <tr><td>interface_object</td><td>o</td><td>D-Bus path of the interface on which this group is operating on. See \ref dbus_interface.</td></tr>
+ <tr><td>role</td><td>s</td><td>The role of the local device in the group: "GO" or "client".</td></tr>
+ <tr><td>group_object</td><td>o</td><td>D-Bus path of the group. See \ref dbus_group.</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>GONegotiationSuccess ( a{sv} : properties )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with following information:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+ <tr><td>peer_object</td><td>o</td><td>D-Bus path of the peer. See \ref dbus_peer.</td></tr>
+ <tr><td>status</td><td>i</td><td></td></tr>
+ <tr><td>passphrase</td><td>s</td><td>Passphrase for the group. Included only if this device becomes the GO of the group.</td></tr>
+ <tr><td>role_go</td><td>s</td><td>The role of the local device in the group: "GO" or "client".</td></tr>
+ <tr><td>ssid</td><td>ay</td><td></td></tr>
+ <tr><td>peer_device_addr</td><td>ay</td><td></td></tr>
+ <tr><td>peer_interface_addr</td><td>ay</td><td></td></tr>
+ <tr><td>wps_method</td><td>s</td><td></td></tr>
+ <tr><td>frequency_list</td><td>ai</td><td></td></tr>
+ <tr><td>persistent_group</td><td>i</td><td></td></tr>
+ <tr><td>peer_config_timeout</td><td>u</td><td></td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>GONegotiationFailure ( a{sv} : properties )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with following information:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+ <tr><td>peer_object</td><td>o</td><td>D-Bus path of the peer. See \ref dbus_peer.</td></tr>
+ <tr><td>status</td><td>i</td><td></td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>GONegotiationRequest ( o : path, q : dev_passwd_id, y : device_go_intent )</h3>
+ </li>
+
+ <li>
+ <h3>InvitationResult ( a{sv} : invite_result )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : invite_result</dt>
+ <dd>A dictionary with following information:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+ <tr><td>status</td><td>i</td><td></td></tr>
+ <tr><td>BSSID</td><td>ay</td><td>Optionally present</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>GroupFinished ( a{sv} : properties )</h3>
+ <p>A P2P group was removed.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with following information of the removed group:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+ <tr><td>interface_object</td><td>o</td><td>D-Bus path of the interface on which this group is operating on. See \ref dbus_interface.</td></tr>
+ <tr><td>role</td><td>s</td><td>The role of the local device in the group: "GO" or "client".</td></tr>
+ <tr><td>group_object</td><td>o</td><td>D-Bus path of the group. See \ref dbus_group.</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>ServiceDiscoveryRequest ( a{sv} : sd_request )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : sd_request</dt>
+ <dd>A dictionary with following information:
+ <table>
+ <tr><td>peer_object</td><td>o</td><td></td></tr>
+ <tr><td>frequency</td><td>i</td><td></td></tr>
+ <tr><td>dialog_token</td><td>i</td><td></td></tr>
+ <tr><td>update_indicator</td><td>q</td><td></td></tr>
+ <tr><td>tlvs</td><td>ay</td><td></td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>ServiceDiscoveryResponse ( a{sv} : sd_response )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : sd_response</dt>
+ <dd>A dictionary with following information:
+ <table>
+ <tr><td>peer_object</td><td>o</td><td></td></tr>
+ <tr><td>update_indicator</td><td>q</td><td></td></tr>
+ <tr><td>tlvs</td><td>ay</td><td></td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>PersistentGroupAdded ( o : path, a{sv} : properties )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : path</dt>
+ <dd>D-Bus object path for the persistent group. See \ref dbus_persistent_group.</dd>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with following information:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+ <tr><td>bssid</td><td>s</td><td>P2P Device Address of the GO in the persistent group.</td></tr>
+ <tr><td>ssid</td><td>s</td><td>SSID of the group</td></tr>
+ <tr><td>psk</td><td>s</td><td>Passphrase (on the GO and optionally on P2P Client) or PSK (on P2P Client if passphrase ise not known)</td></tr>
+ <tr><td>disabled</td><td>s</td><td>Set to "2" to indicate special network block use as a P2P persistent group information</td></tr>
+ <tr><td>mode</td><td>s</td><td>"3" on GO or "0" on P2P Client</td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>PersistentGroupRemoved ( o : path )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : path</dt>
+ <dd>D-Bus object path for the persistent group. See \ref dbus_persistent_group.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>WpsFailed ( s : name, a{sv} : args )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : name</dt>
+ <dd>"fail"</dd>
+ <dt>a{sv} : args</dt>
+ <dd>A dictionary with following information:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+ <tr><td>msg</td><td>i</td><td></td></tr>
+ <tr><td>config_error</td><td>n</td><td></td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>InvitationReceived ( a{sv} : properties )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with following information:
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+ <tr><td>sa</td><td>ay</td><td>Optionally present</td></tr>
+ <tr><td>go_dev_addr</td><td>ay</td><td>Optionally present</td></tr>
+ <tr><td>bssid</td><td>ay</td><td>Optionally present</td></tr>
+ <tr><td>persistent_id</td><td>i</td><td>Optionally present</td></tr>
+ <tr><td>op_freq</td><td>i</td><td></td></tr>
+ </table>
+ </dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>GroupFormationFailure ( s : reason )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>s : reason</dt>
+ <dd>Reason for failure or empty string if not known.</dd>
+ </dl>
+ </li>
+</ul>
+
+\section dbus_bss fi.w1.wpa_supplicant1.BSS
+
+Interface implemented by objects representing a scanned BSSs, i.e.,
+scan results.
+
+\subsection dbus_bss_properties Properties
+
+<ul>
+ <li>
+ <h3>BSSID - ay - (read)</h3>
+ <p>BSSID of the BSS.</p>
+ </li>
+ <li>
+ <h3>SSID - ay - (read)</h3>
+ <p>SSID of the BSS.</p>
+ </li>
+ <li>
+ <h3>WPA - a{sv} - (read)</h3>
+ <p>WPA information of the BSS. Empty dictionary indicates no WPA support. Dictionary entries are:</p>
+ <table>
+ <tr><td>KeyMgmt</td><td>as</td><td>Key management suite. Possible array elements: "wpa-psk", "wpa-eap", "wpa-none"</td>
+ <tr><td>Pairwise</td><td>as</td><td>Pairwise cipher suites. Possible array elements: "ccmp", "tkip"</td>
+ <tr><td>Group</td><td>s</td><td>Group cipher suite. Possible values are: "ccmp", "tkip", "wep104", "wep40"</td>
+ </table>
+ </li>
+ <li>
+ <h3>RSN - a{sv} - (read)</h3>
+ <p>RSN information of the BSS. Empty dictionary indicates no RSN support. Dictionary entries are:</p>
+ <table>
+ <tr><td>KeyMgmt</td><td>as</td><td>Key management suite. Possible array elements: "wpa-psk", "wpa-ft-psk", "wpa-psk-sha256", "wpa-eap", "wpa-ft-eap", "wpa-eap-sha256", "wpa-eap-suite-b", "wpa-eap-suite-b-192", "wpa-fils-sha256", "wpa-fils-sha384", "wpa-ft-fils-sha256", "wpa-ft-fils-sha384", "sae", "ft-sae", "wpa-none"</td>
+ <tr><td>Pairwise</td><td>as</td><td>Pairwise cipher suites. Possible array elements: "ccmp", "tkip"</td>
+ <tr><td>Group</td><td>s</td><td>Group cipher suite. Possible values are: "ccmp", "tkip", "wep104", "wep40"</td>
+ <tr><td>MgmtGroup</td><td>s</td><td>Management frames cipher suite. Possible values are: "aes128cmac"</td>
+ </table>
+ </li>
+ <li>
+ <h3>WPS - a{sv} - (read)</h3>
+ <p>WPS information of the BSS. Empty dictionary indicates no WPS support. Dictionary entries are:</p>
+ <table>
+ <tr><td>Type</td><td>s</td><td>"pbc", "pin", ""</td>
+ </table>
+ </li>
+ <li>
+ <h3>IEs - ay - (read)</h3>
+ <p>All IEs of the BSS as a chain of TLVs</p>
+ </li>
+ <li>
+ <h3>Privacy - b - (read)</h3>
+ <p>Indicates if BSS supports privacy.</p>
+ </li>
+ <li>
+ <h3>Mode - s - (read)</h3>
+ <p>Describes mode of the BSS. Possible values are: "ad-hoc" and "infrastructure".</p>
+ </li>
+ <li>
+ <h3>Frequency - q - (read)</h3>
+ <p>Frequency of the BSS in MHz.</p>
+ </li>
+ <li>
+ <h3>Rates - au - (read)</h3>
+ <p>Descending ordered array of rates supported by the BSS in bits per second.</p>
+ </li>
+ <li>
+ <h3>Signal - n - (read)</h3>
+ <p>Signal strength of the BSS.</p>
+ </li>
+ <li>
+ <h3>Age - u - (read)</h3>
+ <p>Number of seconds since the BSS was last seen.</p>
+ </li>
+ </ul>
+
+\subsection dbus_bss_signals Signals
+
+<ul>
+ <li>
+ <h3>PropertiesChanged ( a{sv} : properties )</h3>
+ <p>Some properties have changed.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with pairs of properties names which have changed and theirs new values.</dd>
+ </dl>
+ </li>
+ </ul>
+
+
+\section dbus_network fi.w1.wpa_supplicant1.Network
+
+Interface implemented by objects representing configured networks,
+i.e., returned by fi.w1.wpa_supplicant1.Interface.AddNetwork.
+
+\subsection dbus_network_properties Properties
+
+<ul>
+ <li>
+ <h3>Enabled - b - (read/write)</h3>
+ <p>Determines if the configured network is enabled or not.</p>
+ </li>
+
+ <li>
+ <h3>Properties - a{sv} - (read/write)</h3>
+ <p>Properties of the configured network. Dictionary contains entries from "network" block of wpa_supplicant configuration file. All values are string type, e.g., frequency is "2437", not 2437. When setting the properties, use the same format as for the AddNetwork() function.</p>
+ </li>
+ </ul>
+
+\subsection dbus_network_signals Signals
+
+<ul>
+ <li>
+ <h3>PropertiesChanged ( a{sv} : properties )</h3>
+ <p>Some properties have changed.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with pairs of properties names which have changed and theirs new values. Possible dictionary keys are: "Enabled"</dd>
+ </dl>
+ </li>
+ </ul>
+
+\section dbus_peer fi.w1.wpa_supplicant1.Peer
+
+Interface implemented by objects representing P2P peer devices.
+
+\subsection dbus_peer_properties Properties
+
+<ul>
+ <li>
+ <h3>DeviceName - s - (read)</h3>
+ </li>
+
+ <li>
+ <h3>Manufacturer - s - (read)</h3>
+ </li>
+
+ <li>
+ <h3>ModelName - s - (read)</h3>
+ </li>
+
+ <li>
+ <h3>ModelNumber - s - (read)</h3>
+ </li>
+
+ <li>
+ <h3>SerialNumber - s - (read)</h3>
+ </li>
+
+ <li>
+ <h3>PrimaryDeviceType - ay - (read)</h3>
+ </li>
+
+ <li>
+ <h3>config_method - q - (read)</h3>
+ </li>
+
+ <li>
+ <h3>level - i - (read)</h3>
+ </li>
+
+ <li>
+ <h3>devicecapability - y - (read)</h3>
+ </li>
+
+ <li>
+ <h3>groupcapability - y - (read)</h3>
+ <p>Group Capability field from the last frame from which this peer information was updated.
+ \note This field is only for debugging purposes and must not be used to determine whether the peer happens to be operating a group as a GO at the moment.
+ </p>
+ </li>
+
+ <li>
+ <h3>SecondaryDeviceTypes - aay - (read)</h3>
+ </li>
+
+ <li>
+ <h3>VendorExtension - aay - (read)</h3>
+ </li>
+
+ <li>
+ <h3>IEs - ay - (read)</h3>
+ <p>This is a confusingly named property that includes Wi-Fi Display subelements from the peer.
+ \bug This should really be renamed since "IEs" means something completely different..
+ </p>
+ </li>
+
+ <li>
+ <h3>DeviceAddress - ay - (read)</h3>
+ <p>The P2P Device Address of the peer.</p>
+ </li>
+
+ <li>
+ <h3>Groups - ao - (read)</h3>
+ <p>The current groups in which this peer is connected.</p>
+ </li>
+</ul>
+
+\subsection dbus_peer_signals Signals
+
+<ul>
+ <li>
+ <h3>PropertiesChanged ( a{sv} : properties )</h3>
+ <p>Some properties have changed.
+ \deprecated Use org.freedesktop.DBus.Properties.PropertiesChanged instead.</p>
+ \todo Explain how ProertiesChanged signals are supposed to be of any real use with Peer objects (i.e., one signal for multiple peers).
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : properties</dt>
+ <dd>A dictionary with pairs of properties names which have changed and their new values.</dd>
+ </dl>
+ </li>
+ </ul>
+
+\section dbus_group fi.w1.wpa_supplicant1.Group
+
+Interface implemented by objects representing active P2P groups.
+
+\subsection dbus_group_properties Properties
+
+<ul>
+ <li>
+ <h3>Members - ao - (read)</h3>
+ <p>Array of D-Bus object paths for the peer devices that are currently connected to the group. This is valid only on the GO device. An empty array is returned in P2P Client role.
+ </li>
+
+ <li>
+ <h3>Group - o - (read)</h3>
+ <p>\todo Why is this here? This D-Bus object path is to this specific group and one needs to know it to fetching this information in the first place..
+ </p>
+ </li>
+
+ <li>
+ <h3>Role - s - (read)</h3>
+ <p>The role of this device in the group: "GO", "client".</p>
+ </li>
+
+ <li>
+ <h3>SSID - ay - (read)</h3>
+ <p>P2P Group SSID.</p>
+ </li>
+
+ <li>
+ <h3>BSSID - ay - (read)</h3>
+ <p>P2P Group BSSID (the P2P Interface Address of the GO).</p>
+ </li>
+
+ <li>
+ <h3>Frequency - q - (read)</h3>
+ <p>The frequency (in MHz) of the group operating channel.</p>
+ </li>
+
+ <li>
+ <h3>Passphrase - s - (read)</h3>
+ <p>Passphrase used in the group. This is always available on the GO. For P2P Client role, this may be available depending on whether the peer GO provided the passphrase during the WPS provisioning step. If not available, an empty string is returned.</p>
+ </li>
+
+ <li>
+ <h3>PSK - ay - (read)</h3>
+ <p>PSK used in the group.</p>
+ </li>
+
+ <li>
+ <h3>WPSVendorExtensions - aay - (read/write)</h3>
+ <p>WPS vendor extension attributes used on the GO. This is valid only the in the GO role. An empty array is returned in P2P Client role. At maximum, 10 separate vendor extension byte arrays can be configured. The GO device will include the configured attributes in WPS exchanges.</p>
+ </li>
+</ul>
+
+\subsection dbus_group_signals Signals
+
+<ul>
+ <li>
+ <h3>PeerJoined ( o : peer )</h3>
+ <p>A peer device has joined the group. This is indicated only on the GO device.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : peer</dt>
+ <dd>A D-Bus path to the object representing the peer. See \ref dbus_peer.</dd>
+ </dl>
+ </li>
+
+ <li>
+ <h3>PeerDisconnected ( o : peer )</h3>
+ <p>A peer device has left the group. This is indicated only on the GO device.</p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>o : peer</dt>
+ <dd>A D-Bus path to the object representing the peer. See \ref dbus_peer.</dd>
+ </dl>
+ </li>
+</ul>
+
+\section dbus_persistent_group fi.w1.wpa_supplicant1.PersistentGroup
+
+Interface implemented by objects representing persistent P2P groups.
+
+\subsection dbus_persistent_group_properties Properties
+
+<ul>
+ <li>
+ <h3>Properties - a{sv} - (read/write)</h3>
+ <p>Properties of the persistent group. These are same properties as in the \ref dbus_network. When writing this, only the entries to be modified are included, i.e., any item that is not included will be left at its existing value. The following entries are used for persistent groups:</p>
+ <table>
+ <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+ <tr><td>bssid</td><td>s</td><td>P2P Device Address of the GO in the persistent group.</td></tr>
+ <tr><td>ssid</td><td>s</td><td>SSID of the group</td></tr>
+ <tr><td>psk</td><td>s</td><td>Passphrase (on the GO and optionally on P2P Client) or PSK (on P2P Client if passphrase ise not known)</td></tr>
+ <tr><td>disabled</td><td>s</td><td>Set to "2" to indicate special network block use as a P2P persistent group information</td></tr>
+ <tr><td>mode</td><td>s</td><td>"3" on GO or "0" on P2P Client</td></tr>
+ </table>
+ </li>
+</ul>
+
+\section dbus_mesh fi.w1.wpa_supplicant1.Interface.Mesh
+
+Interface for performing mesh operations.
+
+\subsection dbus_mesh_properties Properties
+
+<ul>
+ <li>
+ <h3>MeshPeers - aay - (read)</h3>
+ </li>
+
+ <li>
+ <h3>MeshGroup - ay - (read)</h3>
+ </li>
+</ul>
+
+\subsection dbus_mesh_signals Signals
+
+<ul>
+ <li>
+ <h3>MeshGroupStarted ( a{sv} : args )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>A dictionary containing information of the started mesh group.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>MeshGroupRemoved ( a{sv} : args )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>A dictionary containing information of the removed mesh group.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>MeshPeerConnected ( a{sv} : args )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>A dictionary containing information of the connected mesh peer.</dd>
+ </dl>
+ </li>
+ <li>
+ <h3>MeshPeerDisconnected ( a{sv} : args )</h3>
+ <p></p>
+ <h4>Arguments</h4>
+ <dl>
+ <dt>a{sv} : args</dt>
+ <dd>A dictionary containing information of the disconnected mesh peer.</dd>
+ </dl>
+ </li>
+</ul>
+
+*/
diff --git a/contrib/wpa/doc/directories.doxygen b/contrib/wpa/doc/directories.doxygen
new file mode 100644
index 000000000000..15e5bdae463c
--- /dev/null
+++ b/contrib/wpa/doc/directories.doxygen
@@ -0,0 +1,90 @@
+/**
+
+\dir hostapd hostapd
+
+hostapd-specific code for configuration, control interface, and AP
+management.
+
+
+\dir src/common Common functionality
+
+This module includes IEEE 802.11, IEEE 802.1X, and WPA related
+functionality that is shared between AP and station modes.
+
+
+\dir src/crypto Cryptographical functionality and wrappers
+
+This module defines crypto and tls interfaces to provide portability
+layer for different crypto/TLS libraries. Wrappers for number of
+libraries are also included here. In addition, internal implementation
+of various crypto functions are provided as an alternative for an
+external library and to extend some algorithms.
+
+
+\dir src/drivers Driver wrappers
+
+This directory includes the driver interface definition and all the
+driver wrappers that can be used to interact with different drivers
+without making rest of the software dependent on which particular
+driver is used.
+
+
+\dir src/eap_common Common EAP functionality for server and peer
+
+
+\dir src/eap_peer EAP peer
+
+
+\dir src/eap_server EAP server
+
+
+\dir src/eapol_auth EAPOL authenticator
+
+
+\dir src/eapol_supp EAPOL supplicant
+
+
+\dir src/l2_packet Layer 2 packet interface
+
+This module defines an interface for layer 2 (link layer) packet
+sendinf and receiving. All the wrappers for supported mechanisms are
+also included here. This is used to port packet access for new
+operating systems without having to make rest of the source code
+depend on which OS network stack is used.
+
+
+\dir src/radius RADIUS
+
+RADIUS module includes RADIUS message building and parsing
+functionality and separate RADIUS client and server functions.
+
+
+\dir src/rsn_supp IEEE 802.11 RSN and WPA supplicant
+
+
+\dir src/tls Internal TLS server and client implementation
+
+This module can be used as an alternative to using an external TLS
+library.
+
+
+\dir src/utils Utility functions
+
+Independent set of helper functions that most other components
+use. This includes portability wrappers and helpers for common tasks.
+
+
+\dir src/wps Wi-Fi Protected Setup
+
+This directory includes Wi-Fi Protected Setup functions for Registrar
+(both internal in an AP and an External Registrar and
+Enrollee. Minimal UPnP and HTTP functionality is also provided for the
+functionality needed to implement Wi-Fi Protected Setup.
+
+
+\dir wpa_supplicant wpa_supplicant
+
+wpa_supplicant-specific code for configuration, control interface, and
+client management.
+
+*/
diff --git a/contrib/wpa/doc/doxygen.conf b/contrib/wpa/doc/doxygen.conf
new file mode 100644
index 000000000000..3f01173930c4
--- /dev/null
+++ b/contrib/wpa/doc/doxygen.conf
@@ -0,0 +1,1547 @@
+# Doxyfile 1.6.1
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project
+#
+# All text after a hash (#) is considered a comment and will be ignored
+# The format is:
+# TAG = value [value, ...]
+# For lists items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (" ")
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the config file
+# that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# http://www.gnu.org/software/libiconv for the list of possible encodings.
+
+DOXYFILE_ENCODING = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
+# by quotes) that should identify the project.
+
+PROJECT_NAME = "wpa_supplicant / hostapd"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number.
+# This could be handy for archiving the generated documentation or
+# if some version control system is used.
+
+PROJECT_NUMBER = 2.9
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
+# base path where the generated documentation will be put.
+# If a relative path is entered, it will be relative to the location
+# where doxygen was started. If left blank the current directory will be used.
+
+OUTPUT_DIRECTORY = doc
+
+# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create
+# 4096 sub-directories (in 2 levels) under the output directory of each output
+# format and will distribute the generated files over these directories.
+# Enabling this option can be useful when feeding doxygen a huge amount of
+# source files, where putting all generated files in the same directory would
+# otherwise cause performance problems for the file system.
+
+CREATE_SUBDIRS = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# The default language is English, other supported languages are:
+# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional,
+# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German,
+# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English
+# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian,
+# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak,
+# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese.
+
+OUTPUT_LANGUAGE = English
+
+# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will
+# include brief member descriptions after the members that are listed in
+# the file and class documentation (similar to JavaDoc).
+# Set to NO to disable this.
+
+BRIEF_MEMBER_DESC = YES
+
+# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend
+# the brief description of a member or function before the detailed description.
+# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+
+REPEAT_BRIEF = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator
+# that is used to form the text in various listings. Each string
+# in this list, if found as the leading text of the brief description, will be
+# stripped from the text and the result after processing the whole list, is
+# used as the annotated text. Otherwise, the brief description is used as-is.
+# If left blank, the following values are used ("$name" is automatically
+# replaced with the name of the entity): "The $name class" "The $name widget"
+# "The $name file" "is" "provides" "specifies" "contains"
+# "represents" "a" "an" "the"
+
+ABBREVIATE_BRIEF =
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# Doxygen will generate a detailed section even if there is only a brief
+# description.
+
+ALWAYS_DETAILED_SEC = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+
+INLINE_INHERITED_MEMB = NO
+
+# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full
+# path before files name in the file list and in the header files. If set
+# to NO the shortest path that makes the file name unique will be used.
+
+FULL_PATH_NAMES = YES
+
+# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag
+# can be used to strip a user-defined part of the path. Stripping is
+# only done if one of the specified strings matches the left-hand part of
+# the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the
+# path to strip.
+
+STRIP_FROM_PATH =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of
+# the path mentioned in the documentation of a class, which tells
+# the reader which header file to include in order to use a class.
+# If left blank only the name of the header file containing the class
+# definition is used. Otherwise one should specify the include paths that
+# are normally passed to the compiler using the -I flag.
+
+STRIP_FROM_INC_PATH =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter
+# (but less readable) file names. This can be useful is your file systems
+# doesn't support long names like on DOS, Mac, or CD-ROM.
+
+SHORT_NAMES = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen
+# will interpret the first line (until the first dot) of a JavaDoc-style
+# comment as the brief description. If set to NO, the JavaDoc
+# comments will behave just like regular Qt-style comments
+# (thus requiring an explicit @brief command for a brief description.)
+
+JAVADOC_AUTOBRIEF = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then Doxygen will
+# interpret the first line (until the first dot) of a Qt-style
+# comment as the brief description. If set to NO, the comments
+# will behave just like regular Qt-style comments (thus requiring
+# an explicit \brief command for a brief description.)
+
+QT_AUTOBRIEF = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen
+# treat a multi-line C++ special comment block (i.e. a block of //! or ///
+# comments) as a brief description. This used to be the default behaviour.
+# The new default is to treat a multi-line C++ comment block as a detailed
+# description. Set this tag to YES if you prefer the old behaviour instead.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented
+# member inherits the documentation from any documented member that it
+# re-implements.
+
+INHERIT_DOCS = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce
+# a new page for each member. If set to NO, the documentation of a member will
+# be part of the file/class/namespace that contains it.
+
+SEPARATE_MEMBER_PAGES = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab.
+# Doxygen uses this value to replace tabs by spaces in code fragments.
+
+TAB_SIZE = 8
+
+# This tag can be used to specify a number of aliases that acts
+# as commands in the documentation. An alias has the form "name=value".
+# For example adding "sideeffect=\par Side Effects:\n" will allow you to
+# put the command \sideeffect (or @sideeffect) in the documentation, which
+# will result in a user-defined paragraph with heading "Side Effects:".
+# You can put \n's in the value part of an alias to insert newlines.
+
+ALIASES =
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C
+# sources only. Doxygen will then generate output that is more tailored for C.
+# For instance, some of the names that are used will be different. The list
+# of all members will be omitted, etc.
+
+OPTIMIZE_OUTPUT_FOR_C = YES
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java
+# sources only. Doxygen will then generate output that is more tailored for
+# Java. For instance, namespaces will be presented as packages, qualified
+# scopes will look different, etc.
+
+OPTIMIZE_OUTPUT_JAVA = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources only. Doxygen will then generate output that is more tailored for
+# Fortran.
+
+OPTIMIZE_FOR_FORTRAN = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for
+# VHDL.
+
+OPTIMIZE_OUTPUT_VHDL = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it parses.
+# With this tag you can assign which parser to use for a given extension.
+# Doxygen has a built-in mapping, but you can override or extend it using this tag.
+# The format is ext=language, where ext is a file extension, and language is one of
+# the parsers supported by doxygen: IDL, Java, Javascript, C#, C, C++, D, PHP,
+# Objective-C, Python, Fortran, VHDL, C, C++. For instance to make doxygen treat
+# .inc files as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C. Note that for custom extensions you also need to set FILE_PATTERNS otherwise the files are not read by doxygen.
+
+EXTENSION_MAPPING =
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should
+# set this tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string); v.s.
+# func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+
+BUILTIN_STL_SUPPORT = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+
+CPP_CLI_SUPPORT = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only.
+# Doxygen will parse them like normal C++ but will assume all classes use public
+# instead of private inheritance when no explicit protection keyword is present.
+
+SIP_SUPPORT = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate getter
+# and setter methods for a property. Setting this option to YES (the default)
+# will make doxygen to replace the get and set methods by a property in the
+# documentation. This will only work if the methods are indeed getting or
+# setting a simple type. If this is not the case, or you want to show the
+# methods anyway, you should set this option to NO.
+
+IDL_PROPERTY_SUPPORT = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES, then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+
+DISTRIBUTE_GROUP_DOC = NO
+
+# Set the SUBGROUPING tag to YES (the default) to allow class member groups of
+# the same type (for instance a group of public functions) to be put as a
+# subgroup of that type (e.g. under the Public Functions section). Set it to
+# NO to prevent subgrouping. Alternatively, this can be done per class using
+# the \nosubgrouping command.
+
+SUBGROUPING = YES
+
+# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum
+# is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically
+# be useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+
+TYPEDEF_HIDES_STRUCT = NO
+
+# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to
+# determine which symbols to keep in memory and which to flush to disk.
+# When the cache is full, less often used symbols will be written to disk.
+# For small to medium size projects (<1000 input files) the default value is
+# probably good enough. For larger projects a too small cache size can cause
+# doxygen to be busy swapping symbols to and from disk most of the time
+# causing a significant performance penalty.
+# If the system has enough physical memory increasing the cache will improve the
+# performance by keeping more symbols in memory. Note that the value works on
+# a logarithmic scale so increasing the size by one will roughly double the
+# memory usage. The cache size is given by this formula:
+# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0,
+# corresponding to a cache size of 2^16 = 65536 symbols
+
+SYMBOL_CACHE_SIZE = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in
+# documentation are documented, even if no documentation was available.
+# Private class members and static file members will be hidden unless
+# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES
+
+EXTRACT_ALL = NO
+
+# If the EXTRACT_PRIVATE tag is set to YES all private members of a class
+# will be included in the documentation.
+
+EXTRACT_PRIVATE = NO
+
+# If the EXTRACT_STATIC tag is set to YES all static members of a file
+# will be included in the documentation.
+
+EXTRACT_STATIC = NO
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs)
+# defined locally in source files will be included in the documentation.
+# If set to NO only classes defined in header files are included.
+
+EXTRACT_LOCAL_CLASSES = YES
+
+# This flag is only useful for Objective-C code. When set to YES local
+# methods, which are defined in the implementation section but not in
+# the interface are included in the documentation.
+# If set to NO (the default) only methods in the interface are included.
+
+EXTRACT_LOCAL_METHODS = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base
+# name of the file that contains the anonymous namespace. By default
+# anonymous namespace are hidden.
+
+EXTRACT_ANON_NSPACES = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all
+# undocumented members of documented classes, files or namespaces.
+# If set to NO (the default) these members will be included in the
+# various overviews, but no documentation section is generated.
+# This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_MEMBERS = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy.
+# If set to NO (the default) these classes will be included in the various
+# overviews. This option has no effect if EXTRACT_ALL is enabled.
+
+HIDE_UNDOC_CLASSES = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all
+# friend (class|struct|union) declarations.
+# If set to NO (the default) these declarations will be included in the
+# documentation.
+
+HIDE_FRIEND_COMPOUNDS = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any
+# documentation blocks found inside the body of a function.
+# If set to NO (the default) these blocks will be appended to the
+# function's detailed documentation block.
+
+HIDE_IN_BODY_DOCS = NO
+
+# The INTERNAL_DOCS tag determines if documentation
+# that is typed after a \internal command is included. If the tag is set
+# to NO (the default) then the documentation will be excluded.
+# Set it to YES to include the internal documentation.
+
+INTERNAL_DOCS = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate
+# file names in lower-case letters. If set to YES upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# and Mac users are advised to set this option to NO.
+
+CASE_SENSE_NAMES = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen
+# will show members with their full class and namespace scopes in the
+# documentation. If set to YES the scope will be hidden.
+
+HIDE_SCOPE_NAMES = NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen
+# will put a list of the files that are included by a file in the documentation
+# of that file.
+
+SHOW_INCLUDE_FILES = YES
+
+# If the INLINE_INFO tag is set to YES (the default) then a tag [inline]
+# is inserted in the documentation for inline members.
+
+INLINE_INFO = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen
+# will sort the (detailed) documentation of file and class members
+# alphabetically by member name. If set to NO the members will appear in
+# declaration order.
+
+SORT_MEMBER_DOCS = YES
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the
+# brief documentation of file, namespace and class members alphabetically
+# by member name. If set to NO (the default) the members will appear in
+# declaration order.
+
+SORT_BRIEF_DOCS = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the (brief and detailed) documentation of class members so that constructors and destructors are listed first. If set to NO (the default) the constructors will appear in the respective orders defined by SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the
+# hierarchy of group names into alphabetical order. If set to NO (the default)
+# the group names will appear in their defined order.
+
+SORT_GROUP_NAMES = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be
+# sorted by fully-qualified names, including namespaces. If set to
+# NO (the default), the class list will be sorted only by class name,
+# not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the
+# alphabetical list.
+
+SORT_BY_SCOPE_NAME = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or
+# disable (NO) the todo list. This list is created by putting \todo
+# commands in the documentation.
+
+GENERATE_TODOLIST = YES
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or
+# disable (NO) the test list. This list is created by putting \test
+# commands in the documentation.
+
+GENERATE_TESTLIST = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or
+# disable (NO) the bug list. This list is created by putting \bug
+# commands in the documentation.
+
+GENERATE_BUGLIST = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or
+# disable (NO) the deprecated list. This list is created by putting
+# \deprecated commands in the documentation.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional
+# documentation sections, marked by \if sectionname ... \endif.
+
+ENABLED_SECTIONS =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines
+# the initial value of a variable or define consists of for it to appear in
+# the documentation. If the initializer consists of more lines than specified
+# here it will be hidden. Use a value of 0 to hide initializers completely.
+# The appearance of the initializer of individual variables and defines in the
+# documentation can be controlled using \showinitializer or \hideinitializer
+# command in the documentation regardless of this setting.
+
+MAX_INITIALIZER_LINES = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated
+# at the bottom of the documentation of classes and structs. If set to YES the
+# list will mention the files that were used to generate the documentation.
+
+SHOW_USED_FILES = YES
+
+# If the sources in your project are distributed over multiple directories
+# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy
+# in the documentation. The default is NO.
+
+SHOW_DIRECTORIES = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page.
+# This will remove the Files entry from the Quick Index and from the
+# Folder Tree View (if specified). The default is YES.
+
+SHOW_FILES = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the
+# Namespaces page.
+# This will remove the Namespaces entry from the Quick Index
+# and from the Folder Tree View (if specified). The default is YES.
+
+SHOW_NAMESPACES = YES
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command <command> <input-file>, where <command> is the value of
+# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file
+# provided by doxygen. Whatever the program writes to standard output
+# is used as the file version. See the manual for examples.
+
+FILE_VERSION_FILTER =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed by
+# doxygen. The layout file controls the global structure of the generated output files
+# in an output format independent way. The create the layout file that represents
+# doxygen's defaults, run doxygen with the -l option. You can optionally specify a
+# file name after the option, if omitted DoxygenLayout.xml will be used as the name
+# of the layout file.
+
+LAYOUT_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated
+# by doxygen. Possible values are YES and NO. If left blank NO is used.
+
+QUIET = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated by doxygen. Possible values are YES and NO. If left blank
+# NO is used.
+
+WARNINGS = YES
+
+# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings
+# for undocumented members. If EXTRACT_ALL is set to YES then this flag will
+# automatically be disabled.
+
+WARN_IF_UNDOCUMENTED = YES
+
+# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some
+# parameters in a documented function, or documenting parameters that
+# don't exist or using markup commands wrongly.
+
+WARN_IF_DOC_ERROR = YES
+
+# This WARN_NO_PARAMDOC option can be abled to get warnings for
+# functions that are documented, but have no documentation for their parameters
+# or return value. If set to NO (the default) doxygen will only warn about
+# wrong or incomplete parameter documentation, but not about the absence of
+# documentation.
+
+WARN_NO_PARAMDOC = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that
+# doxygen can produce. The string should contain the $file, $line, and $text
+# tags, which will be replaced by the file and line number from which the
+# warning originated and the warning text. Optionally the format may contain
+# $version, which will be replaced by the version of the file (if it could
+# be obtained via FILE_VERSION_FILTER)
+
+WARN_FORMAT = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning
+# and error messages should be written. If left blank the output is written
+# to stderr.
+
+WARN_LOGFILE = doc/doxygen.warnings
+
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag can be used to specify the files and/or directories that contain
+# documented source files. You may enter file names like "myfile.cpp" or
+# directories like "/usr/src/myproject". Separate the files or directories
+# with spaces.
+
+INPUT = \
+ doc \
+ hostapd \
+ wpa_supplicant \
+ wpa_supplicant/dbus \
+ eap_example \
+ src/ap \
+ src/common \
+ src/crypto \
+ src/drivers \
+ src/eap_common \
+ src/eapol_auth \
+ src/eapol_supp \
+ src/eap_peer \
+ src/eap_server \
+ src/l2_packet \
+ src/p2p \
+ src/pae \
+ src/radius \
+ src/rsn_supp \
+ src/tls \
+ src/utils \
+ src/wps
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
+# also the default input encoding. Doxygen uses libiconv (or the iconv built
+# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for
+# the list of possible encodings.
+
+INPUT_ENCODING = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank the following patterns are tested:
+# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx
+# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90
+
+FILE_PATTERNS = *.c *.h *.cpp *.m *.doxygen
+
+# The RECURSIVE tag can be used to turn specify whether or not subdirectories
+# should be searched for input files as well. Possible values are YES and NO.
+# If left blank NO is used.
+
+RECURSIVE = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+
+EXCLUDE =
+
+# The EXCLUDE_SYMLINKS tag can be used select whether or not files or
+# directories that are symbolic links (a Unix filesystem feature) are excluded
+# from the input.
+
+EXCLUDE_SYMLINKS = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories. Note that the wildcards are matched
+# against the file with absolute path, so to exclude all test directories
+# for example use the pattern */test/*
+
+EXCLUDE_PATTERNS =
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+
+EXCLUDE_SYMBOLS =
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or
+# directories that contain example code fragments that are included (see
+# the \include command).
+
+EXAMPLE_PATH =
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp
+# and *.h) to filter out the source-files in the directories. If left
+# blank all files are included.
+
+EXAMPLE_PATTERNS =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude
+# commands irrespective of the value of the RECURSIVE tag.
+# Possible values are YES and NO. If left blank NO is used.
+
+EXAMPLE_RECURSIVE = NO
+
+# The IMAGE_PATH tag can be used to specify one or more files or
+# directories that contain image that are included in the documentation (see
+# the \image command).
+
+IMAGE_PATH = doc
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command <filter> <input-file>, where <filter>
+# is the value of the INPUT_FILTER tag, and <input-file> is the name of an
+# input file. Doxygen will then use the output that the filter program writes
+# to standard output.
+# If FILTER_PATTERNS is specified, this tag will be
+# ignored.
+
+# You can download the filter tool from
+# http://w1.fi/tools/kerneldoc2doxygen-hostap.pl
+INPUT_FILTER = kerneldoc2doxygen-hostap.pl
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis.
+# Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match.
+# The filters are a list of the form:
+# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further
+# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER
+# is applied to all files.
+
+FILTER_PATTERNS =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will be used to filter the input files when producing source
+# files to browse (i.e. when SOURCE_BROWSER is set to YES).
+
+FILTER_SOURCE_FILES = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will
+# be generated. Documented entities will be cross-referenced with these sources.
+# Note: To get rid of all source code in the generated output, make sure also
+# VERBATIM_HEADERS is set to NO.
+
+SOURCE_BROWSER = NO
+
+# Setting the INLINE_SOURCES tag to YES will include the body
+# of functions and classes directly in the documentation.
+
+INLINE_SOURCES = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct
+# doxygen to hide any special comment blocks from generated source code
+# fragments. Normal C and C++ comments will always remain visible.
+
+STRIP_CODE_COMMENTS = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES
+# then for each documented function all documented
+# functions referencing it will be listed.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES
+# then for each documented function all documented entities
+# called/used by that function will be listed.
+
+REFERENCES_RELATION = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES (the default)
+# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from
+# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will
+# link to the source code.
+# Otherwise they will link to the documentation.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code
+# will point to the HTML generated by the htags(1) tool instead of doxygen
+# built-in source browser. The htags tool is part of GNU's global source
+# tagging system (see http://www.gnu.org/software/global/global.html). You
+# will need version 4.8.6 or higher.
+
+USE_HTAGS = NO
+
+# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen
+# will generate a verbatim copy of the header file for each class for
+# which an include is specified. Set to NO to disable this.
+
+VERBATIM_HEADERS = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index
+# of all compounds will be generated. Enable this if the project
+# contains a lot of classes, structs, unions or interfaces.
+
+ALPHABETICAL_INDEX = YES
+
+# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then
+# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns
+# in which this list will be split (can be a number in the range [1..20])
+
+COLS_IN_ALPHA_INDEX = 3
+
+# In case all classes in a project start with a common prefix, all
+# classes will be put under the same header in the alphabetical index.
+# The IGNORE_PREFIX tag can be used to specify one or more prefixes that
+# should be ignored while generating the index headers.
+
+IGNORE_PREFIX =
+
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES (the default) Doxygen will
+# generate HTML output.
+
+GENERATE_HTML = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `html' will be used as the default path.
+
+HTML_OUTPUT = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for
+# each generated HTML page (for example: .htm,.php,.asp). If it is left blank
+# doxygen will generate files with .html extension.
+
+HTML_FILE_EXTENSION = .html
+
+# The HTML_HEADER tag can be used to specify a personal HTML header for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard header.
+
+HTML_HEADER =
+
+# The HTML_FOOTER tag can be used to specify a personal HTML footer for
+# each generated HTML page. If it is left blank doxygen will generate a
+# standard footer.
+
+HTML_FOOTER =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading
+# style sheet that is used by each HTML page. It can be used to
+# fine-tune the look of the HTML output. If the tag is left blank doxygen
+# will generate a default style sheet. Note that doxygen will try to copy
+# the style sheet file to the HTML output directory, so don't put your own
+# stylesheet in the HTML output directory as well, or it will be erased!
+
+HTML_STYLESHEET =
+
+# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes,
+# files or namespaces will be aligned in HTML using tables. If set to
+# NO a bullet list will be used.
+
+HTML_ALIGN_MEMBERS = YES
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded. For this to work a browser that supports
+# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox
+# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari).
+
+HTML_DYNAMIC_SECTIONS = NO
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files
+# will be generated that can be used as input for Apple's Xcode 3
+# integrated development environment, introduced with OSX 10.5 (Leopard).
+# To create a documentation set, doxygen will generate a Makefile in the
+# HTML output directory. Running make will produce the docset in that
+# directory and running "make install" will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find
+# it at startup.
+# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html for more information.
+
+GENERATE_DOCSET = NO
+
+# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the
+# feed. A documentation feed provides an umbrella under which multiple
+# documentation sets from a single provider (such as a company or product suite)
+# can be grouped.
+
+DOCSET_FEEDNAME = "Doxygen generated docs"
+
+# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that
+# should uniquely identify the documentation set bundle. This should be a
+# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen
+# will append .docset to the name.
+
+DOCSET_BUNDLE_ID = org.doxygen.Project
+
+# If the GENERATE_HTMLHELP tag is set to YES, additional index files
+# will be generated that can be used as input for tools like the
+# Microsoft HTML help workshop to generate a compiled HTML help file (.chm)
+# of the generated HTML documentation.
+
+GENERATE_HTMLHELP = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can
+# be used to specify the file name of the resulting .chm file. You
+# can add a path in front of the file if the result should not be
+# written to the html output directory.
+
+CHM_FILE =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can
+# be used to specify the location (absolute path including file name) of
+# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run
+# the HTML help compiler on the generated index.hhp.
+
+HHC_LOCATION =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag
+# controls if a separate .chi index file is generated (YES) or that
+# it should be included in the master .chm file (NO).
+
+GENERATE_CHI = NO
+
+# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING
+# is used to encode HtmlHelp index (hhk), content (hhc) and project file
+# content.
+
+CHM_INDEX_ENCODING =
+
+# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag
+# controls whether a binary table of contents is generated (YES) or a
+# normal table of contents (NO) in the .chm file.
+
+BINARY_TOC = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members
+# to the contents of the HTML help documentation and to the tree view.
+
+TOC_EXPAND = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and QHP_VIRTUAL_FOLDER
+# are set, an additional index file will be generated that can be used as input for
+# Qt's qhelpgenerator to generate a Qt Compressed Help (.qch) of the generated
+# HTML documentation.
+
+GENERATE_QHP = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can
+# be used to specify the file name of the resulting .qch file.
+# The path specified is relative to the HTML output folder.
+
+QCH_FILE =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#namespace
+
+QHP_NAMESPACE =
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating
+# Qt Help Project output. For more information please see
+# http://doc.trolltech.com/qthelpproject.html#virtual-folders
+
+QHP_VIRTUAL_FOLDER = doc
+
+# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to add.
+# For more information please see
+# http://doc.trolltech.com/qthelpproject.html#custom-filters
+
+QHP_CUST_FILTER_NAME =
+
+# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the custom filter to add.For more information please see
+# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters">Qt Help Project / Custom Filters</a>.
+
+QHP_CUST_FILTER_ATTRS =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this project's
+# filter section matches.
+# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes">Qt Help Project / Filter Attributes</a>.
+
+QHP_SECT_FILTER_ATTRS =
+
+# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can
+# be used to specify the location of Qt's qhelpgenerator.
+# If non-empty doxygen will try to run qhelpgenerator on the generated
+# .qhp file.
+
+QHG_LOCATION =
+
+# The DISABLE_INDEX tag can be used to turn on/off the condensed index at
+# top of each HTML page. The value NO (the default) enables the index and
+# the value YES disables it.
+
+DISABLE_INDEX = NO
+
+# This tag can be used to set the number of enum values (range [1..20])
+# that doxygen will group on one line in the generated HTML documentation.
+
+ENUM_VALUES_PER_LINE = 4
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information.
+# If the tag value is set to YES, a side panel will be generated
+# containing a tree-like index structure (just like the one that
+# is generated for HTML Help). For this to work a browser that supports
+# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser).
+# Windows users are probably better off using the HTML help feature.
+
+GENERATE_TREEVIEW = NO
+
+# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories,
+# and Class Hierarchy pages using a tree view instead of an ordered list.
+
+USE_INLINE_TREES = NO
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
+# used to set the initial width (in pixels) of the frame in which the tree
+# is shown.
+
+TREEVIEW_WIDTH = 250
+
+# Use this tag to change the font size of Latex formulas included
+# as images in the HTML documentation. The default is 10. Note that
+# when you change the font size after a successful doxygen run you need
+# to manually remove any form_*.png images from the HTML output directory
+# to force them to be regenerated.
+
+FORMULA_FONTSIZE = 10
+
+# When the SEARCHENGINE tag is enable doxygen will generate a search box for the HTML output. The underlying search engine uses javascript
+# and DHTML and should work on any modern browser. Note that when using HTML help (GENERATE_HTMLHELP) or Qt help (GENERATE_QHP)
+# there is already a search function so this one should typically
+# be disabled.
+
+SEARCHENGINE = YES
+
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will
+# generate Latex output.
+
+GENERATE_LATEX = YES
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `latex' will be used as the default path.
+
+LATEX_OUTPUT = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked. If left blank `latex' will be used as the default command name.
+
+LATEX_CMD_NAME = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to
+# generate index for LaTeX. If left blank `makeindex' will be used as the
+# default command name.
+
+MAKEINDEX_CMD_NAME = makeindex
+
+# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact
+# LaTeX documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_LATEX = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used
+# by the printer. Possible values are: a4, a4wide, letter, legal and
+# executive. If left blank a4wide will be used.
+
+PAPER_TYPE = a4wide
+
+# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX
+# packages that should be included in the LaTeX output.
+
+EXTRA_PACKAGES =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for
+# the generated latex document. The header should contain everything until
+# the first chapter. If it is left blank doxygen will generate a
+# standard header. Notice: only use this tag if you know what you are doing!
+
+LATEX_HEADER =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated
+# is prepared for conversion to pdf (using ps2pdf). The pdf file will
+# contain links (just like the HTML output) instead of page references
+# This makes the output suitable for online browsing using a pdf viewer.
+
+PDF_HYPERLINKS = YES
+
+# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of
+# plain latex in the generated Makefile. Set this option to YES to get a
+# higher quality PDF documentation.
+
+USE_PDFLATEX = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode.
+# command to the generated LaTeX files. This will instruct LaTeX to keep
+# running if errors occur, instead of asking the user for help.
+# This option is also used when generating formulas in HTML.
+
+LATEX_BATCHMODE = NO
+
+# If LATEX_HIDE_INDICES is set to YES then doxygen will not
+# include the index chapters (such as File Index, Compound Index, etc.)
+# in the output.
+
+LATEX_HIDE_INDICES = NO
+
+# If LATEX_SOURCE_CODE is set to YES then doxygen will include source code with syntax highlighting in the LaTeX output. Note that which sources are shown also depends on other settings such as SOURCE_BROWSER.
+
+LATEX_SOURCE_CODE = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output
+# The RTF output is optimized for Word 97 and may not look very pretty with
+# other RTF readers or editors.
+
+GENERATE_RTF = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `rtf' will be used as the default path.
+
+RTF_OUTPUT = rtf
+
+# If the COMPACT_RTF tag is set to YES Doxygen generates more compact
+# RTF documents. This may be useful for small projects and may help to
+# save some trees in general.
+
+COMPACT_RTF = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated
+# will contain hyperlink fields. The RTF file will
+# contain links (just like the HTML output) instead of page references.
+# This makes the output suitable for online browsing using WORD or other
+# programs which support those fields.
+# Note: wordpad (write) and others do not support links.
+
+RTF_HYPERLINKS = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# config file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+
+RTF_STYLESHEET_FILE =
+
+# Set optional variables used in the generation of an rtf document.
+# Syntax is similar to doxygen's config file.
+
+RTF_EXTENSIONS_FILE =
+
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES (the default) Doxygen will
+# generate man pages
+
+GENERATE_MAN = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `man' will be used as the default path.
+
+MAN_OUTPUT = man
+
+# The MAN_EXTENSION tag determines the extension that is added to
+# the generated man pages (default is the subroutine's section .3)
+
+MAN_EXTENSION = .3
+
+# If the MAN_LINKS tag is set to YES and Doxygen generates man output,
+# then it will generate one additional man file for each entity
+# documented in the real man page(s). These additional files
+# only source the real man page, but without them the man command
+# would be unable to find the correct page. The default is NO.
+
+MAN_LINKS = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES Doxygen will
+# generate an XML file that captures the structure of
+# the code including all documentation.
+
+GENERATE_XML = NO
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be
+# put in front of it. If left blank `xml' will be used as the default path.
+
+XML_OUTPUT = xml
+
+# The XML_SCHEMA tag can be used to specify an XML schema,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_SCHEMA =
+
+# The XML_DTD tag can be used to specify an XML DTD,
+# which can be used by a validating XML parser to check the
+# syntax of the XML files.
+
+XML_DTD =
+
+# If the XML_PROGRAMLISTING tag is set to YES Doxygen will
+# dump the program listings (including syntax highlighting
+# and cross-referencing information) to the XML output. Note that
+# enabling this will significantly increase the size of the XML output.
+
+XML_PROGRAMLISTING = YES
+
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will
+# generate an AutoGen Definitions (see autogen.sf.net) file
+# that captures the structure of the code including all
+# documentation. Note that this feature is still experimental
+# and incomplete at the moment.
+
+GENERATE_AUTOGEN_DEF = NO
+
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES Doxygen will
+# generate a Perl module file that captures the structure of
+# the code including all documentation. Note that this
+# feature is still experimental and incomplete at the
+# moment.
+
+GENERATE_PERLMOD = NO
+
+# If the PERLMOD_LATEX tag is set to YES Doxygen will generate
+# the necessary Makefile rules, Perl scripts and LaTeX code to be able
+# to generate PDF and DVI output from the Perl module output.
+
+PERLMOD_LATEX = NO
+
+# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be
+# nicely formatted so it can be parsed by a human reader.
+# This is useful
+# if you want to understand what is going on.
+# On the other hand, if this
+# tag is set to NO the size of the Perl module output will be much smaller
+# and Perl will parse it just the same.
+
+PERLMOD_PRETTY = YES
+
+# The names of the make variables in the generated doxyrules.make file
+# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX.
+# This is useful so different doxyrules.make files included by the same
+# Makefile don't overwrite each other's variables.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will
+# evaluate all C-preprocessor directives found in the sources and include
+# files.
+
+ENABLE_PREPROCESSING = YES
+
+# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro
+# names in the source code. If set to NO (the default) only conditional
+# compilation will be performed. Macro expansion can be done in a controlled
+# way by setting EXPAND_ONLY_PREDEF to YES.
+
+MACRO_EXPANSION = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES
+# then the macro expansion is limited to the macros specified with the
+# PREDEFINED and EXPAND_AS_DEFINED tags.
+
+EXPAND_ONLY_PREDEF = NO
+
+# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files
+# in the INCLUDE_PATH (see below) will be search if a #include is found.
+
+SEARCH_INCLUDES = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by
+# the preprocessor.
+
+INCLUDE_PATH =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will
+# be used.
+
+INCLUDE_FILE_PATTERNS =
+
+# The PREDEFINED tag can be used to specify one or more macro names that
+# are defined before the preprocessor is started (similar to the -D option of
+# gcc). The argument of the tag is a list of macros of the form: name
+# or name=definition (no spaces). If the definition and the = are
+# omitted =1 is assumed. To prevent a macro definition from being
+# undefined via #undef or recursively expanded use the := operator
+# instead of the = operator.
+
+PREDEFINED = IEEE8021X_EAPOL CONFIG_CTRL_IFACE CONFIG_P2P
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
+# this tag can be used to specify a list of macro names that should be expanded.
+# The macro definition that is found in the sources will be used.
+# Use the PREDEFINED tag if you want to use a different macro definition.
+
+EXPAND_AS_DEFINED =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then
+# doxygen's preprocessor will remove all function-like macros that are alone
+# on a line, have an all uppercase name, and do not end with a semicolon. Such
+# function macros are typically used for boiler-plate code, and will confuse
+# the parser if not removed.
+
+SKIP_FUNCTION_MACROS = YES
+
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES option can be used to specify one or more tagfiles.
+# Optionally an initial location of the external documentation
+# can be added for each tagfile. The format of a tag file without
+# this location is as follows:
+#
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+#
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where "loc1" and "loc2" can be relative or absolute paths or
+# URLs. If a location is present for each tag, the installdox tool
+# does not have to be run to correct the links.
+# Note that each tag file must have a unique name
+# (where the name does NOT include the path)
+# If a tag file is not located in the directory in which doxygen
+# is run, you must also specify the path to the tagfile here.
+
+TAGFILES =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create
+# a tag file that is based on the input files it reads.
+
+GENERATE_TAGFILE =
+
+# If the ALLEXTERNALS tag is set to YES all external classes will be listed
+# in the class index. If set to NO only the inherited external classes
+# will be listed.
+
+ALLEXTERNALS = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will
+# be listed.
+
+EXTERNAL_GROUPS = YES
+
+# The PERL_PATH should be the absolute path and name of the perl script
+# interpreter (i.e. the result of `which perl').
+
+PERL_PATH = /usr/bin/perl
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will
+# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base
+# or super classes. Setting the tag to NO turns the diagrams off. Note that
+# this option is superseded by the HAVE_DOT option below. This is only a
+# fallback. It is recommended to install and use dot, since it yields more
+# powerful graphs.
+
+CLASS_DIAGRAMS = NO
+
+# You can define message sequence charts within doxygen comments using the \msc
+# command. Doxygen will then run the mscgen tool (see
+# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the
+# documentation. The MSCGEN_PATH tag allows you to specify the directory where
+# the mscgen tool resides. If left empty the tool is assumed to be found in the
+# default search path.
+
+MSCGEN_PATH =
+
+# If set to YES, the inheritance and collaboration graphs will hide
+# inheritance and usage relations if the target is undocumented
+# or is not a class.
+
+HIDE_UNDOC_RELATIONS = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz, a graph visualization
+# toolkit from AT&T and Lucent Bell Labs. The other options in this section
+# have no effect if this option is set to NO (the default)
+
+HAVE_DOT = YES
+
+# By default doxygen will write a font called FreeSans.ttf to the output
+# directory and reference it in all dot files that doxygen generates. This
+# font does not include all possible unicode characters however, so when you need
+# these (or just want a differently looking font) you can specify the font name
+# using DOT_FONTNAME. You need need to make sure dot is able to find the font,
+# which can be done by putting it in a standard location or by setting the
+# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory
+# containing the font.
+
+DOT_FONTNAME = FreeSans
+
+# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs.
+# The default size is 10pt.
+
+DOT_FONTSIZE = 10
+
+# By default doxygen will tell dot to use the output directory to look for the
+# FreeSans.ttf font (which doxygen will put there itself). If you specify a
+# different font using DOT_FONTNAME you can set the path where dot
+# can find it using this tag.
+
+DOT_FONTPATH =
+
+# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect inheritance relations. Setting this tag to YES will force the
+# the CLASS_DIAGRAMS tag to NO.
+
+CLASS_GRAPH = NO
+
+# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for each documented class showing the direct and
+# indirect implementation dependencies (inheritance, containment, and
+# class references variables) of the class with other documented classes.
+
+COLLABORATION_GRAPH = NO
+
+# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen
+# will generate a graph for groups, showing the direct groups dependencies
+
+GROUP_GRAPHS = NO
+
+# If the UML_LOOK tag is set to YES doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+
+UML_LOOK = NO
+
+# If set to YES, the inheritance and collaboration graphs will show the
+# relations between templates and their instances.
+
+TEMPLATE_RELATIONS = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT
+# tags are set to YES then doxygen will generate a graph for each documented
+# file showing the direct and indirect include dependencies of the file with
+# other documented files.
+
+INCLUDE_GRAPH = NO
+
+# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and
+# HAVE_DOT tags are set to YES then doxygen will generate a graph for each
+# documented header file showing the documented files that directly or
+# indirectly include this file.
+
+INCLUDED_BY_GRAPH = NO
+
+# If the CALL_GRAPH and HAVE_DOT options are set to YES then
+# doxygen will generate a call dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable call graphs
+# for selected functions only using the \callgraph command.
+
+CALL_GRAPH = NO
+
+# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then
+# doxygen will generate a caller dependency graph for every global function
+# or class method. Note that enabling this option will significantly increase
+# the time of a run. So in most cases it will be better to enable caller
+# graphs for selected functions only using the \callergraph command.
+
+CALLER_GRAPH = NO
+
+# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen
+# will graphical hierarchy of all classes instead of a textual one.
+
+GRAPHICAL_HIERARCHY = NO
+
+# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES
+# then doxygen will show the dependencies a directory has on other directories
+# in a graphical way. The dependency relations are determined by the #include
+# relations between the files in the directories.
+
+DIRECTORY_GRAPH = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. Possible values are png, jpg, or gif
+# If left blank png will be used.
+
+DOT_IMAGE_FORMAT = png
+
+# The tag DOT_PATH can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+
+DOT_PATH =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the
+# \dotfile command).
+
+DOTFILE_DIRS =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of
+# nodes that will be shown in the graph. If the number of nodes in a graph
+# becomes larger than this value, doxygen will truncate the graph, which is
+# visualized by representing a node as a red box. Note that doxygen if the
+# number of direct children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note
+# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+
+DOT_GRAPH_MAX_NODES = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the
+# graphs generated by dot. A depth value of 3 means that only nodes reachable
+# from the root by following a path via at most 3 edges will be shown. Nodes
+# that lay further from the root node will be omitted. Note that setting this
+# option to 1 or 2 may greatly reduce the computation time needed for large
+# code bases. Also note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+
+MAX_DOT_GRAPH_DEPTH = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not
+# seem to support this out of the box. Warning: Depending on the platform used,
+# enabling this option may lead to badly anti-aliased labels on the edges of
+# a graph (i.e. they become hard to read).
+
+DOT_TRANSPARENT = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10)
+# support this, this feature is disabled by default.
+
+DOT_MULTI_TARGETS = YES
+
+# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will
+# generate a legend page explaining the meaning of the various boxes and
+# arrows in the dot generated graphs.
+
+GENERATE_LEGEND = YES
+
+# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will
+# remove the intermediate dot files that are used to generate
+# the various graphs.
+
+DOT_CLEANUP = YES
+
+
+#---------------------------------------------------------------------------
+# Project additions
+#---------------------------------------------------------------------------
+
+# Disable autolink support due to wpa_supplicant getting unfortunately
+# auto-linked to struct wpa_supplicant due to having an underscore in the name.
+AUTOLINK_SUPPORT = FALSE
diff --git a/contrib/wpa/doc/driver_wrapper.doxygen b/contrib/wpa/doc/driver_wrapper.doxygen
new file mode 100644
index 000000000000..66211b3abdfb
--- /dev/null
+++ b/contrib/wpa/doc/driver_wrapper.doxygen
@@ -0,0 +1,180 @@
+/**
+\page driver_wrapper Driver wrapper implementation (driver.h, drivers.c)
+
+All hardware and driver dependent functionality is in separate C files
+that implement defined wrapper functions. Other parts
+of the wpa_supplicant are designed to be hardware, driver, and operating
+system independent.
+
+Driver wrappers need to implement whatever calls are used in the
+target operating system/driver for controlling wireless LAN
+devices. As an example, in case of Linux, these are mostly some glue
+code and ioctl() calls and netlink message parsing for Linux Wireless
+Extensions (WE). Since features required for WPA were added only recently to
+Linux Wireless Extensions (in version 18), some driver specific code is used
+in number of driver interface implementations. These driver dependent parts
+can be replaced with generic code in \ref driver_wext.c once the target driver
+includes full support for WE-18. After that, all Linux drivers, at
+least in theory, could use the same driver wrapper code.
+
+A driver wrapper needs to implement some or all of the functions
+defined in \ref driver.h. These functions are registered by filling struct
+\ref wpa_driver_ops with function pointers. Hardware independent parts of
+wpa_supplicant will call these functions to control the driver/wlan
+card. In addition, support for driver events is required. The event
+callback function, \ref wpa_supplicant_event(), and its parameters are
+documented in \ref driver.h. In addition, a pointer to the 'struct
+\ref wpa_driver_ops' needs to be registered in \ref drivers.c file.
+
+When porting to other operating systems, the driver wrapper should be
+modified to use the native interface of the target OS. It is possible
+that some extra requirements for the interface between the driver
+wrapper and generic wpa_supplicant code are discovered during porting
+to a new operating system. These will be addressed on case by case
+basis by modifying the interface and updating the other driver
+wrappers for this. The goal is to avoid changing this interface
+without very good reasons in order to limit the number of changes
+needed to other wrappers and hardware independent parts of
+wpa_supplicant. When changes are required, recommended way is to
+make them in backwards compatible way that allows existing driver
+interface implementations to be compiled without any modification.
+
+Generic Linux Wireless Extensions functions are implemented in
+\ref driver_wext.c. All Linux driver wrappers can use these when the kernel
+driver supports the generic ioctl()s and wireless events. Driver
+specific functions are implemented in separate C files, e.g.,
+\ref driver_hostap.c. These files need to define struct \ref wpa_driver_ops
+entry that will be used in \ref wpa_supplicant.c when calling driver
+functions. struct \ref wpa_driver_ops entries are registered in \ref drivers.c.
+
+In general, it is likely to be useful to first take a look at couple
+of driver interface examples before starting on implementing a new
+one. \ref driver_hostap.c and \ref driver_wext.c include a complete
+implementation for Linux drivers that use wpa_supplicant-based control
+of WPA IE and roaming. \ref driver_ndis.c (with help from \ref driver_ndis_.c)
+is an example of a complete interface for Windows NDIS interface for
+drivers that generate WPA IE themselves and decide when to roam. These
+example implementations include full support for all security modes.
+
+
+\section driver_req Driver requirements for WPA
+
+WPA introduces new requirements for the device driver. At least some
+of these need to be implemented in order to provide enough support for
+wpa_supplicant.
+
+\subsection driver_tkip_ccmp TKIP/CCMP
+
+WPA requires that the pairwise cipher suite (encryption algorithm for
+unicast data packets) is TKIP or CCMP. These are new encryption
+protocols and thus, the driver will need to be modified to support
+them. Depending on the used wlan hardware, some parts of these may be
+implemented by the hardware/firmware.
+
+Specification for both TKIP and CCMP is available from IEEE (IEEE
+802.11i amendment). Fully functional, hardware independent
+implementation of both encryption protocols is also available in Host
+AP driver (driver/modules/hostap_{tkip,ccmp}.c). In addition, Linux 2.6
+kernel tree has generic implementations for WEP, TKIP, and CCMP that can
+be used in Linux drivers.
+
+The driver will also need to provide configuration mechanism to allow
+user space programs to configure TKIP and CCMP. Linux Wireless Extensions
+v18 added support for configuring these algorithms and
+individual/non-default keys. If the target kernel does not include WE-18,
+private ioctls can be used to provide similar functionality.
+
+\subsection driver_roaming Roaming control and scanning support
+
+wpa_supplicant can optionally control AP selection based on the
+information received from Beacon and/or Probe Response frames
+(ap_scan=1 mode in configuration). This means that the driver should
+support external control for scan process. In case of Linux, use of
+new Wireless Extensions scan support (i.e., 'iwlist wlan0 scan') is
+recommended. The current driver wrapper (\ref driver_wext.c) uses this for
+scan results.
+
+Scan results must also include the WPA information element. Support for
+this was added in WE-18. With older versions, a custom event can be used
+to provide the full WPA IE (including element id and length) as a hex
+string that is included in the scan results.
+
+wpa_supplicant needs to also be able to request the driver to
+associate with a specific BSS. Current Host AP driver and matching
+\ref driver_hostap.c wrapper uses following sequence for this
+request. Similar/identical mechanism should be usable also with other
+drivers.
+
+- set WPA IE for AssocReq with private ioctl
+- set SSID with SIOCSIWESSID
+- set channel/frequency with SIOCSIWFREQ
+- set BSSID with SIOCSIWAP
+ (this last ioctl will trigger the driver to request association)
+
+\subsection driver_wpa_ie WPA IE generation
+
+wpa_supplicant selects which cipher suites and key management suites
+are used. Based on this information, it generates a WPA IE. This is
+provided to the driver interface in the associate call. This does not
+match with Windows NDIS drivers which generate the WPA IE
+themselves.
+
+wpa_supplicant allows Windows NDIS-like behavior by providing the
+selected cipher and key management suites in the associate call. If
+the driver generates its own WPA IE and that differs from the one
+generated by wpa_supplicant, the driver has to inform wpa_supplicant
+about the used WPA IE (i.e., the one it used in (Re)Associate
+Request). This notification is done using EVENT_ASSOCINFO event (see
+\ref driver.h). wpa_supplicant is normally configured to use
+ap_scan=2 mode with drivers that control WPA IE generation and roaming.
+
+\subsection driver_events Driver events
+
+wpa_supplicant needs to receive event callbacks when certain events
+occur (association, disassociation, Michael MIC failure, scan results
+available, PMKSA caching candidate). These events and the callback
+details are defined in \ref driver.h (\ref wpa_supplicant_event() function
+and enum \ref wpa_event_type).
+
+On Linux, association and disassociation can use existing Wireless
+Extensions event that is reporting new AP with SIOCGIWAP
+event. Similarly, completion of a scan can be reported with SIOCGIWSCAN
+event.
+
+Michael MIC failure event was added in WE-18. Older versions of Wireless
+Extensions will need to use a custom event. Host AP driver used a custom
+event with following contents: MLME-MICHAELMICFAILURE.indication(keyid=#
+broadcast/unicast addr=addr2). This is the recommended format until
+the driver can be moved to use WE-18 mechanism.
+
+\subsection driver_wext_summary Summary of Linux Wireless Extensions use
+
+AP selection depends on ap_scan configuration:
+
+ap_scan=1:
+
+- wpa_supplicant requests scan with SIOCSIWSCAN
+- driver reports scan complete with wireless event SIOCGIWSCAN
+- wpa_supplicant reads scan results with SIOCGIWSCAN (multiple call if
+ a larger buffer is needed)
+- wpa_supplicant decides which AP to use based on scan results
+- wpa_supplicant configures driver to associate with the selected BSS
+ (SIOCSIWMODE, SIOCSIWGENIE, SIOCSIWAUTH, SIOCSIWFREQ,
+ SIOCSIWESSID, SIOCSIWAP)
+
+ap_scan=2:
+
+- wpa_supplicant configures driver to associate with an SSID
+ (SIOCSIWMODE, SIOCSIWGENIE, SIOCSIWAUTH, SIOCSIWESSID)
+
+
+After this, both modes use similar steps:
+
+- optionally (or required for drivers that generate WPA/RSN IE for
+ (Re)AssocReq), driver reports association parameters (AssocReq IEs)
+ with wireless event IWEVASSOCREQIE (and optionally IWEVASSOCRESPIE)
+- driver reports association with wireless event SIOCGIWAP
+- wpa_supplicant takes care of EAPOL frame handling (validating
+ information from associnfo and if needed, from scan results if WPA/RSN
+ IE from the Beacon frame is not reported through associnfo)
+*/
diff --git a/contrib/wpa/doc/eap.doxygen b/contrib/wpa/doc/eap.doxygen
new file mode 100644
index 000000000000..472e882004d3
--- /dev/null
+++ b/contrib/wpa/doc/eap.doxygen
@@ -0,0 +1,87 @@
+/**
+\page eap_peer_module EAP peer implementation
+
+Extensible Authentication Protocol (EAP) is an authentication framework
+defined in RFC 3748. wpa_supplicant uses a separate code module for EAP
+peer implementation. This module was designed to use only a minimal set
+of direct function calls (mainly, to debug/event functions) in order for
+it to be usable in other programs. The design of the EAP
+implementation is based loosely on RFC 4137. The state machine is
+defined in this RFC and so is the interface between the peer state
+machine and methods. As such, this RFC provides useful information for
+understanding the EAP peer implementation in wpa_supplicant.
+
+Some of the terminology used in EAP state machine is referring to
+EAPOL (IEEE 802.1X), but there is no strict requirement on the lower
+layer being IEEE 802.1X if EAP module is built for other programs than
+wpa_supplicant. These terms should be understood to refer to the
+lower layer as defined in RFC 4137.
+
+
+\section adding_eap_methods Adding EAP methods
+
+Each EAP method is implemented as a separate module, usually as one C
+file named eap_<name of the method>.c, e.g., \ref eap_md5.c. All EAP
+methods use the same interface between the peer state machine and
+method specific functions. This allows new EAP methods to be added
+without modifying the core EAP state machine implementation.
+
+New EAP methods need to be registered by adding them into the build
+(Makefile) and the EAP method registration list in the
+\ref eap_peer_register_methods() function of \ref eap_methods.c. Each EAP
+method should use a build-time configuration option, e.g., EAP_TLS, in
+order to make it possible to select which of the methods are included
+in the build.
+
+EAP methods must implement the interface defined in \ref eap_i.h. struct
+\ref eap_method defines the needed function pointers that each EAP method
+must provide. In addition, the EAP type and name are registered using
+this structure. This interface is based on section 4.4 of RFC 4137.
+
+It is recommended that the EAP methods would use generic helper
+functions, \ref eap_msg_alloc() and \ref eap_hdr_validate() when processing
+messages. This allows code sharing and can avoid missing some of the
+needed validation steps for received packets. In addition, these
+functions make it easier to change between expanded and legacy EAP
+header, if needed.
+
+When adding an EAP method that uses a vendor specific EAP type
+(Expanded Type as defined in RFC 3748, Chapter 5.7), the new method
+must be registered by passing vendor id instead of EAP_VENDOR_IETF to
+\ref eap_peer_method_alloc(). These methods must not try to emulate
+expanded types by registering a legacy EAP method for type 254. See
+\ref eap_vendor_test.c for an example of an EAP method implementation that
+is implemented as an expanded type.
+
+
+\section used_eap_library Using EAP implementation as a library
+
+The Git repository has an eap_example directory that contains an
+example showing how EAP peer and server code from wpa_supplicant and
+hostapd can be used as a library. The example program initializes both
+an EAP server and an EAP peer entities and then runs through an
+EAP-PEAP/MSCHAPv2 authentication.
+
+\ref eap_example_peer.c shows the initialization and glue code needed to
+control the EAP peer implementation. \ref eap_example_server.c does the
+same for EAP server. \ref eap_example.c is an example that ties in both the
+EAP server and client parts to allow an EAP authentication to be
+shown.
+
+In this example, the EAP messages are passed between the server and
+the peer are passed by direct function calls within the same process.
+In practice, server and peer functionalities would likely reside in
+separate devices and the EAP messages would be transmitted between the
+devices based on an external protocol. For example, in IEEE 802.11
+uses IEEE 802.1X EAPOL state machines to control the transmission of
+EAP messages and WiMax supports optional PMK EAP authentication
+mechanism that transmits EAP messages as defined in IEEE 802.16e.
+
+The EAP library links in number of helper functions from \ref src/utils and
+\ref src/crypto directories. Most of these are suitable as-is, but it may
+be desirable to replace the debug output code in \ref src/utils/wpa_debug.c
+by dropping this file from the library and re-implementing the
+functions there in a way that better fits in with the main
+application.
+
+*/
diff --git a/contrib/wpa/doc/eap_server.doxygen b/contrib/wpa/doc/eap_server.doxygen
new file mode 100644
index 000000000000..f60ac79b1e01
--- /dev/null
+++ b/contrib/wpa/doc/eap_server.doxygen
@@ -0,0 +1,56 @@
+/**
+\page eap_server_module EAP server implementation
+
+Extensible Authentication Protocol (EAP) is an authentication framework
+defined in RFC 3748. hostapd uses a separate code module for EAP server
+implementation. This module was designed to use only a minimal set of
+direct function calls (mainly, to debug/event functions) in order for
+it to be usable in other programs. The design of the EAP
+implementation is based loosely on RFC 4137. The state machine is
+defined in this RFC and so is the interface between the server state
+machine and methods. As such, this RFC provides useful information for
+understanding the EAP server implementation in hostapd.
+
+Some of the terminology used in EAP state machine is referring to
+EAPOL (IEEE 802.1X), but there is no strict requirement on the lower
+layer being IEEE 802.1X if EAP module is built for other programs than
+wpa_supplicant. These terms should be understood to refer to the
+lower layer as defined in RFC 4137.
+
+
+\section adding_eap_methods Adding EAP methods
+
+Each EAP method is implemented as a separate module, usually as one C
+file named eap_server_<name of the method>.c, e.g., \ref eap_server_md5.c. All EAP
+methods use the same interface between the server state machine and
+method specific functions. This allows new EAP methods to be added
+without modifying the core EAP state machine implementation.
+
+New EAP methods need to be registered by adding them into the build
+(Makefile) and the EAP method registration list in the
+\ref eap_server_register_methods() function of \ref eap_server_methods.c. Each EAP
+method should use a build-time configuration option, e.g., EAP_TLS, in
+order to make it possible to select which of the methods are included
+in the build.
+
+EAP methods must implement the interface defined in \ref eap_i.h. struct
+\ref eap_method defines the needed function pointers that each EAP method
+must provide. In addition, the EAP type and name are registered using
+this structure. This interface is based on section 4.4 of RFC 4137.
+
+It is recommended that the EAP methods would use generic helper
+functions, \ref eap_msg_alloc() and \ref eap_hdr_validate() when processing
+messages. This allows code sharing and can avoid missing some of the
+needed validation steps for received packets. In addition, these
+functions make it easier to change between expanded and legacy EAP
+header, if needed.
+
+When adding an EAP method that uses a vendor specific EAP type
+(Expanded Type as defined in RFC 3748, Chapter 5.7), the new method
+must be registered by passing vendor id instead of EAP_VENDOR_IETF to
+\ref eap_server_method_alloc(). These methods must not try to emulate
+expanded types by registering a legacy EAP method for type 254. See
+\ref eap_server_vendor_test.c for an example of an EAP method implementation that
+is implemented as an expanded type.
+
+*/
diff --git a/contrib/wpa/doc/hostapd.fig b/contrib/wpa/doc/hostapd.fig
new file mode 100644
index 000000000000..ea4ab3a2894e
--- /dev/null
+++ b/contrib/wpa/doc/hostapd.fig
@@ -0,0 +1,264 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 1875 4050 2925 4350
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1875 4050 2925 4050 2925 4350 1875 4350 1875 4050
+4 0 0 50 -1 0 12 0.0000 4 180 735 2025 4275 l2_packet\001
+-6
+6 4725 1200 5925 1500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4725 1200 5925 1200 5925 1500 4725 1500 4725 1200
+4 0 0 50 -1 0 12 0.0000 4 135 1005 4800 1425 GUI frontend\001
+-6
+6 6000 2700 7200 3225
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6000 2700 7200 2700 7200 3225 6000 3225 6000 2700
+4 0 0 50 -1 0 12 0.0000 4 135 975 6075 2925 WPA/WPA2\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 3150 state machine\001
+-6
+6 6000 4950 7200 5475
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6000 4950 7200 4950 7200 5475 6000 5475 6000 4950
+4 0 0 50 -1 0 12 0.0000 4 135 360 6075 5175 EAP\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 5400 state machine\001
+-6
+6 4350 3900 5025 4425
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4350 3900 5025 3900 5025 4425 4350 4425 4350 3900
+4 0 0 50 -1 0 12 0.0000 4 105 420 4500 4125 event\001
+4 0 0 50 -1 0 12 0.0000 4 180 315 4500 4350 loop\001
+-6
+6 4275 2550 5100 2850
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4275 2550 5100 2550 5100 2850 4275 2850 4275 2550
+4 0 0 50 -1 0 12 0.0000 4 135 450 4425 2775 ctrl i/f\001
+-6
+6 6000 3900 7200 4425
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6000 3900 7200 3900 7200 4425 6000 4425 6000 3900
+4 0 0 50 -1 0 12 0.0000 4 135 600 6075 4125 EAPOL\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 4350 state machine\001
+-6
+6 2775 3150 4050 3450
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 2775 3150 4050 3150 4050 3450 2775 3450 2775 3150
+4 0 0 50 -1 0 12 0.0000 4 180 990 2925 3375 configuration\001
+-6
+6 3450 1200 4575 1500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3450 1200 4575 1200 4575 1500 3450 1500 3450 1200
+4 0 0 50 -1 0 12 0.0000 4 180 870 3600 1425 hostapd_cli\001
+-6
+6 3525 7800 5775 8100
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3525 7800 5775 7800 5775 8100 3525 8100 3525 7800
+4 0 0 50 -1 0 12 0.0000 4 135 2145 3600 8025 kernel network device driver\001
+-6
+6 4275 6000 5100 6300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4275 6000 5100 6000 5100 6300 4275 6300 4275 6000
+4 0 0 50 -1 0 12 0.0000 4 135 630 4350 6225 driver i/f\001
+-6
+6 8175 4725 9225 5025
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 4725 9225 4725 9225 5025 8175 5025 8175 4725
+4 0 0 50 -1 0 12 0.0000 4 135 735 8250 4950 EAP-TLS\001
+-6
+6 9300 4725 10350 5025
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 4725 10350 4725 10350 5025 9300 5025 9300 4725
+4 0 0 50 -1 0 12 0.0000 4 135 810 9375 4950 EAP-MD5\001
+-6
+6 8175 5100 9225 5400
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 5100 9225 5100 9225 5400 8175 5400 8175 5100
+4 0 0 50 -1 0 12 0.0000 4 135 885 8250 5325 EAP-PEAP\001
+-6
+6 9300 5100 10350 5400
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 5100 10350 5100 10350 5400 9300 5400 9300 5100
+4 0 0 50 -1 0 12 0.0000 4 135 840 9375 5325 EAP-TTLS\001
+-6
+6 8175 5475 9225 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 5475 9225 5475 9225 5775 8175 5775 8175 5475
+4 0 0 50 -1 0 12 0.0000 4 135 780 8250 5700 EAP-GTC\001
+-6
+6 8175 5850 9225 6150
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 5850 9225 5850 9225 6150 8175 6150 8175 5850
+4 0 0 50 -1 0 12 0.0000 4 135 750 8250 6075 EAP-SIM\001
+-6
+6 8175 6225 9225 6525
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 6225 9225 6225 9225 6525 8175 6525 8175 6225
+4 0 0 50 -1 0 12 0.0000 4 135 765 8250 6450 EAP-PSK\001
+-6
+6 9300 5850 10350 6150
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 5850 10350 5850 10350 6150 9300 6150 9300 5850
+4 0 0 50 -1 0 12 0.0000 4 135 825 9375 6075 EAP-AKA\001
+-6
+6 9300 5475 10350 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 5475 10350 5475 10350 5775 9300 5775 9300 5475
+4 0 0 50 -1 0 12 0.0000 4 135 795 9375 5700 EAP-PAX\001
+-6
+6 8175 6600 9675 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 6600 9675 6600 9675 6900 8175 6900 8175 6600
+4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 6825 EAP-MSCHAPv2\001
+-6
+6 8700 3450 9375 3750
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8700 3450 9375 3450 9375 3750 8700 3750 8700 3450
+4 0 0 50 -1 0 12 0.0000 4 150 480 8775 3675 crypto\001
+-6
+6 9600 3450 10275 3750
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9600 3450 10275 3450 10275 3750 9600 3750 9600 3450
+4 0 0 50 -1 0 12 0.0000 4 135 315 9750 3675 TLS\001
+-6
+6 6000 5775 7200 6300
+6 6000 5775 7200 6300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6000 5775 7200 5775 7200 6300 6000 6300 6000 5775
+4 0 0 50 -1 0 12 0.0000 4 135 690 6075 6000 RADIUS\001
+-6
+4 0 0 50 -1 0 12 0.0000 4 90 480 6075 6225 server\001
+-6
+6 8100 2250 8925 2775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8100 2250 8925 2250 8925 2775 8100 2775 8100 2250
+4 0 0 50 -1 0 12 0.0000 4 135 690 8175 2475 RADIUS\001
+4 0 0 50 -1 0 12 0.0000 4 135 420 8175 2700 client\001
+-6
+6 3150 5475 4425 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3150 5475 4425 5475 4425 5775 3150 5775 3150 5475
+4 0 0 50 -1 0 12 0.0000 4 135 990 3300 5700 driver events\001
+-6
+6 1950 5550 2625 6075
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1950 5550 2625 5550 2625 6075 1950 6075 1950 5550
+4 0 0 50 -1 0 12 0.0000 4 135 540 2025 5775 Station\001
+4 0 0 50 -1 0 12 0.0000 4 135 375 2025 6000 table\001
+-6
+6 1875 4725 2925 5250
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1875 4725 2925 4725 2925 5250 1875 5250 1875 4725
+4 0 0 50 -1 0 12 0.0000 4 135 960 1950 4950 IEEE 802.11\001
+4 0 0 50 -1 0 12 0.0000 4 135 555 1950 5175 MLME\001
+-6
+2 1 1 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2
+ 1275 4200 1875 4200
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 4500 2550 3900 1500
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 4800 2550 5400 1500
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 2925 4200 4350 4200
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 5025 3900 6000 3000
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 5025 4200 6000 4200
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4650 6000 4650 4425
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6600 4425 6600 4950
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6600 3225 6600 3900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 7200 5250 8100 5250
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 9075 4425 9075 3750
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 7200 3000 8700 3525
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4650 3900 4650 2850
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 7200 4125 8700 3675
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6000 4350 5025 6000
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6000 3150 4875 6000
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1500 2100 10800 2100 10800 7500 1500 7500 1500 2100
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 9900 4425 9900 3750
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 1
+ 4350 3900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4350 3900 4050 3450
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4350 4425 4050 5475
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 2250 7200 4200 7800
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 7200 7200 5100 7800
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 2775 6900 3675 6900 3675 7200 2775 7200 2775 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3750 6900 4650 6900 4650 7200 3750 7200 3750 6900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+ 2250 6900 2250 6600 7200 6600 7200 6900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 3225 6900 3225 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4200 6900 4200 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 5175 6900 5175 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6150 6900 6150 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4650 6600 4650 6300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1800 6900 2700 6900 2700 7200 1800 7200 1800 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4725 6900 5625 6900 5625 7200 4725 7200 4725 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 5700 6900 6600 6900 6600 7200 5700 7200 5700 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6675 6900 7800 6900 7800 7200 6675 7200 6675 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8100 6975 10425 6975 10425 4425 8100 4425 8100 6975
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6600 5475 6600 5775
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 5025 4425 6000 5775
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+ 4800 3900 5925 2550 8100 2550
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 7200 3900 8475 2775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9450 2250 10425 2250 10425 2775 9450 2775 9450 2250
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 8925 2475 9450 2475
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 2325 5550 2325 5250
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 2925 4950 4350 4275
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3
+ 2850 4725 5775 2400 8100 2400
+4 0 0 50 -1 0 12 0.0000 4 135 915 375 3975 EAPOL and\001
+4 0 0 50 -1 0 12 0.0000 4 180 630 375 4200 pre-auth\001
+4 0 0 50 -1 0 12 0.0000 4 180 810 375 4425 ethertypes\001
+4 0 0 50 -1 0 12 0.0000 4 135 1050 375 4650 from/to kernel\001
+4 0 0 50 -1 0 12 0.0000 4 135 1920 3675 1875 frontend control interface\001
+4 0 0 50 -1 2 14 0.0000 4 195 720 1637 2371 hostapd\001
+4 0 0 50 -1 0 12 0.0000 4 180 600 3825 7125 prism54\001
+4 0 0 50 -1 0 12 0.0000 4 180 510 1875 7125 hostap\001
+4 0 0 50 -1 0 12 0.0000 4 135 600 2850 7125 nl80211\001
+4 0 0 50 -1 0 12 0.0000 4 135 270 4800 7125 bsd\001
+4 0 0 50 -1 0 12 0.0000 4 105 300 6750 7125 test\001
+4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 wired\001
+4 0 0 50 -1 0 12 0.0000 4 135 1050 8700 4650 EAP methods\001
+4 0 0 50 -1 0 12 0.0000 4 135 690 9525 2475 RADIUS\001
+4 0 0 50 -1 0 12 0.0000 4 180 825 9525 2700 accounting\001
diff --git a/contrib/wpa/doc/hostapd_ctrl_iface.doxygen b/contrib/wpa/doc/hostapd_ctrl_iface.doxygen
new file mode 100644
index 000000000000..4d2bac82c43f
--- /dev/null
+++ b/contrib/wpa/doc/hostapd_ctrl_iface.doxygen
@@ -0,0 +1,66 @@
+/**
+\page hostapd_ctrl_iface_page hostapd control interface
+
+hostapd implements a control interface that can be used by
+external programs to control the operations of the hostapd
+daemon and to get status information and event notifications. There is
+a small C library, in a form of a single C file, \ref wpa_ctrl.c, that
+provides helper functions to facilitate the use of the control
+interface. External programs can link this file into them and then use
+the library functions documented in \ref wpa_ctrl.h to interact with
+wpa_supplicant. This library can also be used with C++. \ref hostapd_cli.c
+is an example program using this library.
+
+There are multiple mechanisms for inter-process communication. For
+example, Linux version of hostapd is using UNIX domain sockets for the
+control interface. The use of the functions defined in \ref wpa_ctrl.h can
+be used to hide the details of the used IPC from external programs.
+
+
+\section using_ctrl_iface Using the control interface
+
+External programs, e.g., a GUI or a configuration utility, that need to
+communicate with hostapd should link in \ref wpa_ctrl.c. This
+allows them to use helper functions to open connection to the control
+interface with \ref wpa_ctrl_open() and to send commands with
+\ref wpa_ctrl_request().
+
+hostapd uses the control interface for two types of communication:
+commands and unsolicited event messages. Commands are a pair of
+messages, a request from the external program and a response from
+hostapd. These can be executed using \ref wpa_ctrl_request().
+Unsolicited event messages are sent by hostapd to the control
+interface connection without specific request from the external program
+for receiving each message. However, the external program needs to
+attach to the control interface with \ref wpa_ctrl_attach() to receive these
+unsolicited messages.
+
+If the control interface connection is used both for commands and
+unsolicited event messages, there is potential for receiving an
+unsolicited message between the command request and response.
+\ref wpa_ctrl_request() caller will need to supply a callback, msg_cb,
+for processing these messages. Often it is easier to open two
+control interface connections by calling \ref wpa_ctrl_open() twice and
+then use one of the connections for commands and the other one for
+unsolicited messages. This way command request/response pairs will
+not be broken by unsolicited messages. \ref wpa_cli.c is an example of how
+to use only one connection for both purposes and wpa_gui demonstrates
+how to use two separate connections.
+
+Once the control interface connection is not needed anymore, it should
+be closed by calling \ref wpa_ctrl_close(). If the connection was used for
+unsolicited event messages, it should be first detached by calling
+\ref wpa_ctrl_detach().
+
+
+\section ctrl_iface_cmds Control interface commands
+
+Following commands can be used with \ref wpa_ctrl_request():
+
+\subsection ctrl_iface_PING PING
+
+This command can be used to test whether hostapd is replying
+to the control interface commands. The expected reply is \c PONG if the
+connection is open and hostapd is processing commands.
+
+*/
diff --git a/contrib/wpa/doc/mainpage.doxygen b/contrib/wpa/doc/mainpage.doxygen
new file mode 100644
index 000000000000..329afea3fa6a
--- /dev/null
+++ b/contrib/wpa/doc/mainpage.doxygen
@@ -0,0 +1,95 @@
+/**
+\mainpage Developers' documentation for wpa_supplicant and hostapd
+
+The goal of this documentation and comments in the source code is to
+give enough information for other developers to understand how
+wpa_supplicant and hostapd have been implemented, how they can be
+modified, how new drivers can be supported, and how the source code
+can be ported to other operating systems. If any information is
+missing, feel free to contact Jouni Malinen <j@w1.fi> for more
+information. Contributions as patch files are also very welcome at the
+same address. Please note that this software is licensed under the
+BSD license (the one with advertisement clause removed). All
+contributions to wpa_supplicant and hostapd are expected to use
+compatible licensing terms.
+
+The source code and read-only access to the combined wpa_supplicant
+and hostapd Git repository is available from the project home page at
+http://w1.fi/wpa_supplicant/. This developers' documentation is also
+available as a PDF file from
+http://w1.fi/wpa_supplicant/wpa_supplicant-devel.pdf .
+
+
+\section _wpa_supplicant wpa_supplicant
+
+wpa_supplicant is a WPA Supplicant for Linux, BSD and Windows with
+support for WPA and WPA2 (IEEE 802.11i / RSN). Supplicant is the IEEE
+802.1X/WPA component that is used in the client stations. It
+implements key negotiation with a WPA Authenticator and it can optionally
+control roaming and IEEE 802.11 authentication/association of the wlan
+driver.
+
+The design goal for wpa_supplicant was to use hardware, driver, and
+OS independent, portable C code for all WPA functionality. The source
+code is divided into separate C files as shown on the \ref
+code_structure "code structure page". All hardware/driver specific
+functionality is in separate files that implement a \ref
+driver_wrapper "well-defined driver API". Information about porting
+to different target boards and operating systems is available on
+the \ref porting "porting page".
+
+EAPOL (IEEE 802.1X) state machines are implemented as a separate
+module that interacts with \ref eap_peer_module "EAP peer implementation".
+In addition to programs aimed at normal production use,
+wpa_supplicant source tree includes number of \ref testing_tools
+"testing and development tools" that make it easier to test the
+programs without having to setup a full test setup with wireless
+cards. These tools can also be used to implement automatic test
+suites.
+
+wpa_supplicant implements a
+\ref ctrl_iface_page "control interface" that can be used by
+external programs to control the operations of the wpa_supplicant
+daemon and to get status information and event notifications. There is
+a small C library that provides helper functions to facilitate the use of the
+control interface. This library can also be used with C++.
+
+\image html _wpa_supplicant.png "wpa_supplicant modules"
+\image latex _wpa_supplicant.eps "wpa_supplicant modules" width=15cm
+
+
+\section _hostapd hostapd
+
+hostapd includes IEEE 802.11 access point management (authentication /
+association), IEEE 802.1X/WPA/WPA2 Authenticator, EAP server, and
+RADIUS authentication server functionality. It can be build with
+various configuration option, e.g., a standalone AP management
+solution or a RADIUS authentication server with support for number of
+EAP methods.
+
+The design goal for hostapd was to use hardware, driver, and
+OS independent, portable C code for all WPA functionality. The source
+code is divided into separate C files as shown on the \ref
+code_structure "code structure page". All hardware/driver specific
+functionality is in separate files that implement a \ref
+driver_wrapper "well-defined driver API". Information about porting
+to different target boards and operating systems is available on
+the \ref porting "porting page".
+
+EAPOL (IEEE 802.1X) state machines are implemented as a separate
+module that interacts with \ref eap_server_module "EAP server implementation".
+Similarly, RADIUS authentication server is in its own separate module.
+Both IEEE 802.1X and RADIUS authentication server can use EAP server
+functionality.
+
+hostapd implements a \ref hostapd_ctrl_iface_page "control interface"
+that can be used by external programs to control the operations of the
+hostapdt daemon and to get status information and event notifications.
+There is a small C library that provides helper functions to facilitate
+the use of the control interface. This library can also be used with
+C++.
+
+\image html hostapd.png "hostapd modules"
+\image latex hostapd.eps "hostapd modules" width=15cm
+
+*/
diff --git a/contrib/wpa/doc/p2p.doxygen b/contrib/wpa/doc/p2p.doxygen
new file mode 100644
index 000000000000..d4d86e3edfa2
--- /dev/null
+++ b/contrib/wpa/doc/p2p.doxygen
@@ -0,0 +1,471 @@
+/**
+\page p2p Wi-Fi Direct - P2P module
+
+Wi-Fi Direct functionality is implemented any many levels in the WLAN
+stack from low-level driver operations to high-level GUI design. This
+document covers the parts that can be user by wpa_supplicant. However,
+it should be noted that alternative designs are also possible, so some
+of the functionality may reside in other components in the system.
+
+The driver (or WLAN firmware/hardware) is expected to handle low-level
+operations related to P2P Power Management and channel scheduling. In
+addition, support for virtual network interface and data frame
+processing is done inside the driver. Configuration for these
+low-level operations is defined in the driver interface:
+src/drivers/driver.h. This defines both the commands and events used to
+interact with the driver.
+
+P2P module implements higher layer functionality for management P2P
+groups. It takes care of Device Discovery, Service Discovery, Group
+Owner Negotiation, P2P Invitation. In addition, it maintains
+information about neighboring P2P Devices. This module could be used
+in designs that do not use wpa_supplicant and it could also reside
+inside the driver/firmware component. P2P module API is defined in
+\ref src/p2p/p2p.h.
+
+Provisioning step of Group Formation is implemented using WPS
+(\ref src/wps/wps.h).
+
+wpa_supplicant includes code in interact with both the P2P module
+(\ref wpa_supplicant/p2p_supplicant.c) and WPS
+(\ref wpa_supplicant/wps_supplicant.c). The driver operations are passed
+through these files, i.e., core P2P or WPS code does not interact
+directly with the driver interface.
+
+
+\section p2p_arch P2P architecture
+
+P2P functionality affects many areas of the system architecture. This
+section shows couple of examples on the location of main P2P
+components. In the diagrams below, green arrows are used to show
+communication paths from the P2P module to upper layer management
+functionality and all the way to a GUI that user could use to manage
+P2P connections. Blue arrows show the path taken for lower layer
+operations. Glue code is used to bind the P2P module API to the rest
+of the system to provide access both towards upper and lower layer
+functionality.
+
+\subsection p2p_arch_mac80211 P2P architecture with Linux/mac80211/ath9k
+
+An architecture where the P2P module resides inside the
+wpa_supplicant process is used with Linux mac80211-based drivers,
+e.g., ath9k. The following diagram shows the main components related
+to P2P functionality in such an architecture.
+
+\image html p2p_arch.png "P2P module within wpa_supplicant"
+\image latex p2p_arch.eps "P2P module within wpa_supplicant" width=15cm
+
+\subsection p2p_arch_umac P2P architecture with UMAC
+
+The following diagram shows the main components related to P2P
+functionality in an architecture where the P2P module resides inside
+the kernel IEEE 802.11 stack (UMAC in the figure).
+
+\image html p2p_arch2.png "P2P module in kernel
+\image latex p2p_arch2.eps "P2P module in kernel" width=15cm
+
+
+\section p2p_module P2P module
+
+P2P module manages discovery and group formation with a single state
+machine, i.e., only a single operation per device can be in progress
+at any given time. The following diagram describes the P2P state
+machine. For clarity, it does not include state transitions on
+operation timeouts to the IDLE state. The states that are marked with
+dotted ellipse are listed for clarity to describe the protocol
+functionality for Device Discovery phase, but are not used in the
+implementation (the SEARCH state is used to manage the initial Scan
+and the alternating Listen and Search states within Find).
+
+\image html p2p_sm.png "P2P module state machine"
+\image latex p2p_sm.eps "P2P module state machine" width=15cm
+
+\subsection p2p_module_api P2P module API
+
+P2P module API is defined in \ref src/p2p/p2p.h. The API consists of
+functions for requesting operations and for providing event
+notifications. Similar set of callback functions are configured with
+struct p2p_config to provide callback functions that P2P module can
+use to request operations and to provide event notifications. In
+addition, there are number of generic helper functions that can be
+used for P2P related operations.
+
+These are the main functions for an upper layer management entity to
+request P2P operations:
+- \ref p2p_find()
+- \ref p2p_stop_find()
+- \ref p2p_listen()
+- \ref p2p_connect()
+- \ref p2p_reject()
+- \ref p2p_prov_disc_req()
+- \ref p2p_sd_request()
+- \ref p2p_sd_cancel_request()
+- \ref p2p_sd_response()
+- \ref p2p_sd_service_update()
+- \ref p2p_invite()
+
+These are the main callback functions for P2P module to provide event
+notifications to the upper layer management entity:
+
+- \ref p2p_config::dev_found()
+- \ref p2p_config::go_neg_req_rx()
+- \ref p2p_config::go_neg_completed()
+- \ref p2p_config::sd_request()
+- \ref p2p_config::sd_response()
+- \ref p2p_config::prov_disc_req()
+- \ref p2p_config::prov_disc_resp()
+- \ref p2p_config::invitation_process()
+- \ref p2p_config::invitation_received()
+- \ref p2p_config::invitation_result()
+
+The P2P module uses following functions to request lower layer driver
+operations:
+
+- \ref p2p_config::p2p_scan()
+- \ref p2p_config::send_probe_resp()
+- \ref p2p_config::send_action()
+- \ref p2p_config::send_action_done()
+- \ref p2p_config::start_listen()
+- \ref p2p_config::stop_listen()
+
+Events from lower layer driver operations are delivered to the P2P
+module with following functions:
+
+- \ref p2p_probe_req_rx()
+- \ref p2p_rx_action()
+- \ref p2p_scan_res_handler()
+- \ref p2p_scan_res_handled()
+- \ref p2p_send_action_cb()
+- \ref p2p_listen_cb()
+
+In addition to the per-device state, the P2P module maintains
+per-group state for group owners. This is initialized with a call to
+p2p_group_init() when a group is created and deinitialized with
+p2p_group_deinit(). The upper layer GO management entity uses
+following functions to interact with the P2P per-group state:
+
+- \ref p2p_group_notif_assoc()
+- \ref p2p_group_notif_disassoc()
+- \ref p2p_group_notif_formation_done()
+- \ref p2p_group_match_dev_type()
+
+The P2P module will use following callback function to update P2P IE
+for GO Beacon and Probe Response frames:
+
+- \ref p2p_group_config::ie_update()
+
+
+\section p2p_driver P2P driver operations (low-level interface)
+
+The following driver wrapper functions are needed for P2P in addition
+to the standard station/AP mode operations when the P2P module resides
+within wpa_supplicant:
+- \ref wpa_driver_ops::if_add()
+- \ref wpa_driver_ops::if_remove()
+- \ref wpa_driver_ops::remain_on_channel()
+- \ref wpa_driver_ops::cancel_remain_on_channel()
+- \ref wpa_driver_ops::send_action()
+- \ref wpa_driver_ops::probe_req_report()
+
+The following driver wrapper events are needed for P2P in addition to
+the standard station/AP mode events when the P2P module resides within
+wpa_supplicant:
+- \ref wpa_event_type::EVENT_RX_MGMT
+- \ref wpa_event_type::EVENT_REMAIN_ON_CHANNEL
+- \ref wpa_event_type::EVENT_CANCEL_REMAIN_ON_CHANNEL
+- \ref wpa_event_type::EVENT_RX_PROBE_REQ
+
+
+\section p2p_go_neg P2P device discovery and group formation
+
+This section shows an example sequence of operations that can be used
+to implement P2P device discovery and group formation. The function
+calls are described based on the P2P module API. The exact design for
+the glue code outside the P2P module depends on the architecture used
+in the system.
+
+An upper layer management entity starts P2P device discovery by
+calling \ref p2p_find(). The P2P module start the discovery by requesting a
+full scan to be completed by calling \ref p2p_config::p2p_scan(). Results
+from the scan will be reported by calling \ref p2p_scan_res_handler() and
+after last result, the scan result processing is terminated with a
+call to \ref p2p_scan_res_handled(). The P2P peers that are found during
+the full scan are reported with the \ref p2p_config::dev_found() callback.
+
+After the full scan, P2P module start alternating between Listen and
+Search states until the device discovery operation times out or
+terminated, e.g., with a call to \ref p2p_stop_find().
+
+When going into the Listen state, the P2P module requests the driver
+to be configured to be awake on the listen channel with a call to
+\ref p2p_config::start_listen(). The glue code using the P2P module may
+implement this, e.g., by using remain-on-channel low-level driver
+functionality for off-channel operation. Once the driver is available
+on the requested channel, notification of this is delivered by calling
+\ref p2p_listen_cb(). The Probe Request frames that are received during the
+Listen period are delivered to the P2P module by calling
+\ref p2p_config::p2p_probe_req_rx() and P2P module request a response to
+these to be sent by using \ref p2p_config::send_probe_resp() callback
+function. If a group owner negotiation from another P2P device is
+received during the device discovery phase, that is indicated to the
+upper layer code with the \ref p2p_config::go_neg_req_tx() callback.
+
+The Search state is implemented by using the normal scan interface,
+i.e., the P2P module will call \ref p2p_config::p2p_scan() just like in the
+full scan phase described. Similarly, scan results from the search
+operation will be delivered to the P2P module using the
+\ref p2p_scan_res_handler() and \ref p2p_scan_res_handled() functions.
+
+Once the upper layer management entity has found a peer with which it
+wants to connect by forming a new group, it initiates group owner
+negotiation by calling \ref p2p_connect(). Before doing this, the upper
+layer code is responsible for asking the user to provide the PIN to be
+used during the provisioning step with the peer or the push button
+press for PBC mode. The glue code will need to figure out the intended
+interface address for the group before group owner negotiation can be
+started.
+
+Optional Provision Discovery mechanism can be used to request the peer
+to display a PIN for the local device to enter (and vice versa). Upper
+layer management entity can request the specific mechanism by calling
+\ref p2p_prov_disc_req(). The response to this will be reported with the
+\ref p2p_config::prov_disc_resp() callback. If the peer device started
+Provision Discovery, an accepted request will be reported with the
+\ref p2p_config::prov_disc_req() callback. The P2P module will
+automatically accept the Provision Discovery for display and keypad
+methods, but it is up to the upper layer manegement entity to actually
+generate the PIN and to configure it with following \ref p2p_connect() call
+to actually authorize the connection.
+
+The P2P module will use \ref p2p_config::send_action() callback to request
+lower layer code to transmit an Action frame during group owner
+negotiation. \ref p2p_send_action_cb() is used to report the result of
+transmission. If the peer is not reachable, the P2P module will try to
+find it by alternating between Action frame send and Listen
+states. The Listen state for this phase will be used similarly to the
+Listen state during device discovery as described above.
+
+Once the group owner negotiation has been completed, its results will
+be reported with the \ref p2p_config::go_neg_completed() callback. The
+upper layer management code or the glue code using the P2P module API
+is responsible for creating a new group interface and starting
+provisioning step at this point by configuring WPS Registrar or
+Enrollee functionality based on the reported group owner negotiation
+results. The upper layer code is also responsible for timing out WPS
+provisioning if it cannot be completed in 15 seconds.
+
+Successful completion of the WPS provisioning is reported with a call
+to \ref p2p_wps_success_cb(). The P2P module will clear its group formation
+state at this point and allows new group formation attempts to be
+started. The upper layer management code is responsible for configuring
+the GO to accept associations from devices and the client to connect to
+the GO with the provisioned credentials. GO is also responsible for
+calling \ref p2p_group_notif_formation_done() as described below.
+
+If the WPS provisioning step fails or times out, this is reported with
+a call to \ref p2p_group_formation_failed(). The P2P module will clear its
+group formation state at this point and allows new group formation
+attempts to be started. The upper layer management code is responsible
+for removing the group interface for the failed group.
+
+
+\section p2p_sd P2P service discovery
+
+P2P protocol includes service discovery functionality that can be used
+to discover which services are provided by the peers before forming a
+group. This leverages the Generic Advertisement Service (GAS) protocol
+from IEEE 802.11u and P2P vendor-specific contents inside the Native
+GAS messages.
+
+The P2P module takes care of GAS encapsulation, fragmentation, and
+actual transmission and reception of the Action frames needed for
+service discovery. The user of the P2P module is responsible for
+providing P2P specific Service Request TLV(s) for queries and Service
+Response TLV(s) for responses.
+
+\subsection p2p_sd_query Querying services of peers
+
+Service discovery is implemented by processing pending queries as a
+part of the device discovery phase. \ref p2p_sd_request() function is used
+to schedule service discovery queries to a specific peer or to all
+discovered peers. \ref p2p_sd_cancel_request() can be used to cancel a
+scheduled query. Queries that are specific to a single peer will be
+removed automatically after the response has been received.
+
+After the service discovery queries have been queued, device discovery
+is started with a call to \ref p2p_find(). The pending service discovery
+queries are then sent whenever a peer is discovered during the find
+operation. Responses to the queries will be reported with the
+\ref p2p_config::sd_response() callback.
+
+\subsection p2p_sd_response Replying to service discovery queries from peers
+
+The received service discovery requests will be indicated with the
+\ref p2p_config::sd_request() callback. The response to the query is sent
+by calling \ref p2p_sd_response().
+
+\subsection p2p_sd_indicator Service update indicator
+
+P2P service discovery provides a mechanism to notify peers about
+changes in available services. This works by incrementing Service
+Update Indicator value whenever there is a change in the
+services. This value is included in all SD request and response
+frames. The value received from the peers will be included in the
+\ref p2p_config::sd_request() and \ref p2p_config::sd_response() callbacks. The
+value to be sent to the peers is incremented with a call to
+\ref p2p_sd_service_update() whenever availability of the local services
+changes.
+
+
+\section p2p_go P2P group owner
+
+This section describes how P2P module can be used for managing
+per-group information in a group owner. The function calls are
+described based on the P2P module API. The exact design for the glue
+code outside the P2P module depends on the architecture used in the
+system.
+
+When a P2P group interface is created in group owner role, per-group
+data is initialized with \ref p2p_group_init(). This call provides a
+pointer to the per-device P2P module context and configures the
+per-group operation. The configured \ref p2p_group_config::ie_update()
+callback is used to set the initial P2P IE for Beacon and Probe
+Response frames in the group owner. The AP mode implementation may use
+this information to add IEs into the frames.
+
+Once the group formation has been completed (or if it is skipped in
+case of manual group setup), \ref p2p_group_notif_formation_done() is
+called. This will allow the P2P module to update the P2P IE for
+Beacon and Probe Response frames.
+
+The SME/MLME code that managements IEEE 802.11 association processing
+needs to inform P2P module whenever a P2P client associates or
+disassociates with the group. This is done by calling
+\ref p2p_group_notif_assoc() and \ref p2p_group_notif_disassoc(). The P2P module
+manages a list of group members and updates the P2P Group Information
+subelement in the P2P IE based on the information from the P2P
+clients. The \ref p2p_group_config::ie_update() callback is used whenever
+the P2P IE in Probe Response frames needs to be changed.
+
+The SME/MLME code that takes care of replying to Probe Request frames
+can use \ref p2p_group_match_dev_type() to check whether the Probe Request
+frame request a reply only from groups that include a specific device
+type in one of the clients or GO. A match will be reported if the
+Probe Request does not request a specific device type, so this
+function can be used to filter or received Probe Request frames and
+only the ones that result in non-zero return value need to be replied.
+
+When the P2P group interface for GO role is removed,
+\ref p2p_group_deinit() is used to deinitialize the per-group P2P module
+state.
+
+
+\section p2p_ctrl_iface P2P control interface
+
+wpa_supplicant \ref ctrl_iface_page "control interface" can be used
+to manage P2P functionality from an external program (e.g., a GUI or a
+system configuration manager). This interface can be used directly
+through the control interface backend mechanism (e.g., local domain
+sockets on Linux) or with help of wpa_cli (e.g., from a script).
+
+The following P2P-related commands are available:
+- \ref ctrl_iface_P2P_FIND P2P_FIND
+- \ref ctrl_iface_P2P_STOP_FIND P2P_STOP_FIND
+- \ref ctrl_iface_P2P_CONNECT P2P_CONNECT
+- \ref ctrl_iface_P2P_LISTEN P2P_LISTEN
+- \ref ctrl_iface_P2P_GROUP_REMOVE P2P_GROUP_REMOVE
+- \ref ctrl_iface_P2P_GROUP_ADD P2P_GROUP_ADD
+- \ref ctrl_iface_P2P_PROV_DISC P2P_PROV_DISC
+- \ref ctrl_iface_P2P_SERV_DISC_REQ P2P_SERV_DISC_REQ
+- \ref ctrl_iface_P2P_SERV_DISC_CANCEL_REQ P2P_SERV_DISC_CANCEL_REQ
+- \ref ctrl_iface_P2P_SERV_DISC_RESP P2P_SERV_DISC_RESP
+- \ref ctrl_iface_P2P_SERVICE_UPDATE P2P_SERVICE_UPDATE
+- \ref ctrl_iface_P2P_SERV_DISC_EXTERNAL P2P_SERV_DISC_EXTERNAL
+- \ref ctrl_iface_P2P_REJECT P2P_REJECT
+- \ref ctrl_iface_P2P_INVITE P2P_INVITE
+
+The following P2P-related events are used:
+- \ref ctrl_iface_event_P2P_EVENT_DEVICE_FOUND P2P-DEVICE-FOUND
+- \ref ctrl_iface_event_P2P_EVENT_GO_NEG_REQUEST P2P-GO-NEG-REQUEST
+- \ref ctrl_iface_event_P2P_EVENT_GO_NEG_SUCCESS P2P-GO-NEG-SUCCESS
+- \ref ctrl_iface_event_P2P_EVENT_GO_NEG_FAILURE P2P-GO-NEG-FAILURE
+- \ref ctrl_iface_event_P2P_EVENT_GROUP_FORMATION_SUCCESS P2P-GROUP-FORMATION-SUCCESS
+- \ref ctrl_iface_event_P2P_EVENT_GROUP_FORMATION_FAILURE P2P-GROUP-FORMATION-FAILURE
+- \ref ctrl_iface_event_P2P_EVENT_GROUP_STARTED P2P-GROUP-STARTED
+- \ref ctrl_iface_event_P2P_EVENT_GROUP_REMOVED P2P-GROUP-REMOVED
+- \ref ctrl_iface_event_P2P_EVENT_PROV_DISC_SHOW_PIN P2P-PROV-DISC-SHOW-PIN
+- \ref ctrl_iface_event_P2P_EVENT_PROV_DISC_ENTER_PIN P2P-PROV-DISC-ENTER-PIN
+- \ref ctrl_iface_event_P2P_EVENT_SERV_DISC_REQ P2P-SERV-DISC-REQ
+- \ref ctrl_iface_event_P2P_EVENT_SERV_DISC_RESP P2P-SERV-DISC-RESP
+- \ref ctrl_iface_event_P2P_EVENT_INVITATION_RECEIVED P2P-INVITATION-RECEIVED
+- \ref ctrl_iface_event_P2P_EVENT_INVITATION_RESULT P2P-INVITATION-RESULT
+
+
+\subsection p2p_wpa_gui GUI example (wpa_gui)
+
+wpa_gui has an example implementation of a GUI that could be used to
+manage P2P operations. The P2P related functionality is contained
+mostly in wpa_supplicant/wpa_gui-qt4/peers.cpp and it shows how the
+control interface commands and events can be used.
+
+
+\subsection p2p_wpa_cli wpa_cli example
+
+wpa_cli can be used to control wpa_supplicant in interactive
+mode. The following sessions show examples of commands used for
+device discovery and group formation. The lines starting with "> " are
+commands from the user (followed by command result indication) and
+lines starting with "<2>" are event messages from wpa_supplicant.
+
+P2P device "Wireless Client":
+
+\verbatim
+> p2p_find
+OK
+> <2>P2P-DEVICE-FOUND 02:40:61:c2:f3:b7 p2p_dev_addr=02:40:61:c2:f3:b7
+pri_dev_type=1-0050F204-1 name='Wireless Client 2' config_methods=0x18c
+dev_capab=0x1 group_capab=0x0
+<2>P2P-GO-NEG-REQUEST 02:40:61:c2:f3:b7
+<2>P2P-GO-NEG-REQUEST 02:40:61:c2:f3:b7
+> p2p_connect 02:40:61:c2:f3:b7 pbc
+OK
+<2>P2P-GO-NEG-SUCCESS
+<2>P2P-GROUP-FORMATION-SUCCESS
+<2>P2P-GROUP-STARTED sta0-p2p-0 client DIRECT-vM
+> interface
+Available interfaces:
+sta0-p2p-0
+sta0
+> p2p_group_remove sta0-p2p-0
+<2>P2P-GROUP-REMOVED sta0-p2p-0 client
+OK
+> term
+OK
+\endverbatim
+
+
+P2P device "Wireless Client2" (which ended up operating in GO role):
+
+\verbatim
+> p2p_find
+OK
+<2>P2P-DEVICE-FOUND 02:f0:bc:44:87:62 p2p_dev_addr=02:f0:bc:44:87:62
+pri_dev_type=1-0050F204-1 name='Wireless Client' config_methods=0x18c
+dev_capab=0x1 group_capab=0x0
+> p2p_connect 02:f0:bc:44:87:62 pbc
+OK
+<2>P2P-GO-NEG-SUCCESS
+<2>P2P-GROUP-FORMATION-SUCCESS
+<2>P2P-GROUP-STARTED sta1-p2p-0 GO DIRECT-vM
+> interface
+Available interfaces:
+sta1-p2p-0
+sta1
+> p2p_group_remove sta1-p2p-0
+<2>P2P-GROUP-REMOVED sta1-p2p-0 GO
+OK
+> term
+OK
+\endverbatim
+
+*/
diff --git a/contrib/wpa/doc/p2p_arch.dot b/contrib/wpa/doc/p2p_arch.dot
new file mode 100644
index 000000000000..27ae0e2509d4
--- /dev/null
+++ b/contrib/wpa/doc/p2p_arch.dot
@@ -0,0 +1,85 @@
+digraph p2p_arch {
+ ranksep=.75;
+ size = "7.5,7.5";
+
+ edge [dir=none];
+
+ subgraph cluster_wpa_gui {
+ label = "wpa_gui";
+
+ status -> Qt;
+ scan -> Qt;
+ network -> Qt;
+ Qt -> peers;
+ Qt -> WPS;
+ Qt -> gui_ctrl;
+
+ gui_ctrl [label="ctrl i/f"];
+ }
+
+ subgraph cluster_wpa_supplicant {
+ label = "wpa_supplicant"
+
+ ctrl_iface [label="ctrl i/f"];
+ authenticator [label="Authenticator"];
+ supplicant [label="Supplicant"];
+ driver_iface [label="driver i/f"];
+ p2p_module [label="P2P\nmodule"];
+ wps_registrar [label="WPS\nRegistrar"];
+ wps_enrollee [label="WPS\nEnrollee"];
+ mgmt_entity [label="Management\nentity"];
+
+ ctrl_iface -> mgmt_entity;
+ p2p_module -> mgmt_entity;
+ wps_registrar -> mgmt_entity;
+ wps_enrollee -> mgmt_entity;
+ mgmt_entity -> authenticator;
+ mgmt_entity -> supplicant;
+ mgmt_entity -> driver_iface;
+
+ { rank = same; mgmt_entity; p2p_module; }
+ }
+
+ subgraph cluster_wpa_cli {
+ label = "wpa_cli -a"
+
+ wpa_cli_action;
+ }
+
+ subgraph cluster_dnsmasq {
+ label = "dnsmasq"
+
+ dnsmasq;
+ }
+
+ subgraph cluster_dhclient {
+ label = "dhclient"
+
+ dhclient;
+ }
+
+ subgraph cluster_kernel {
+ label = "Linux kernel"
+
+ cfg80211 -> mac80211;
+ netdev -> mac80211;
+ mac80211 -> ath9k;
+ }
+
+ gui_ctrl -> ctrl_iface;
+ wpa_cli_action -> ctrl_iface;
+
+ driver_iface -> cfg80211;
+
+ wpa_cli_action -> dnsmasq;
+ wpa_cli_action -> dhclient;
+
+ dnsmasq -> netdev;
+ dhclient -> netdev;
+
+ edge [color=blue,dir=both];
+ p2p_module -> mgmt_entity -> driver_iface -> cfg80211 -> mac80211 -> ath9k;
+
+ edge [color=green,dir=both];
+ peers -> Qt -> gui_ctrl -> ctrl_iface -> mgmt_entity -> p2p_module;
+}
diff --git a/contrib/wpa/doc/p2p_arch2.dot b/contrib/wpa/doc/p2p_arch2.dot
new file mode 100644
index 000000000000..9c7b4b57a17f
--- /dev/null
+++ b/contrib/wpa/doc/p2p_arch2.dot
@@ -0,0 +1,85 @@
+digraph p2p_arch2 {
+ ranksep=.75;
+ size = "7.5,7.5";
+
+ edge [dir=none];
+
+ subgraph cluster_wpa_gui {
+ label = "wpa_gui";
+
+ status -> Qt;
+ scan -> Qt;
+ network -> Qt;
+ Qt -> peers;
+ Qt -> WPS;
+ Qt -> gui_ctrl;
+
+ gui_ctrl [label="ctrl i/f"];
+ }
+
+ subgraph cluster_wpa_supplicant {
+ label = "wpa_supplicant"
+
+ ctrl_iface [label="ctrl i/f"];
+ authenticator [label="Authenticator"];
+ supplicant [label="Supplicant"];
+ driver_iface [label="driver i/f"];
+ wps_registrar [label="WPS\nRegistrar"];
+ wps_enrollee [label="WPS\nEnrollee"];
+ mgmt_entity [label="Management\nentity"];
+
+ ctrl_iface -> mgmt_entity;
+ wps_registrar -> mgmt_entity;
+ wps_enrollee -> mgmt_entity;
+ mgmt_entity -> authenticator;
+ mgmt_entity -> supplicant;
+ mgmt_entity -> driver_iface;
+ }
+
+ subgraph cluster_wpa_cli {
+ label = "wpa_cli -a"
+
+ wpa_cli_action;
+ }
+
+ subgraph cluster_dnsmasq {
+ label = "dnsmasq"
+
+ dnsmasq;
+ }
+
+ subgraph cluster_dhclient {
+ label = "dhclient"
+
+ dhclient;
+ }
+
+ subgraph cluster_kernel {
+ label = "Kernel"
+
+ ioctl -> umac;
+ netdev -> umac;
+ umac -> p2p_module;
+ p2p_module [label="P2P\nmodule"];
+ umac -> driver;
+
+ { rank = same; umac; p2p_module; }
+ }
+
+ gui_ctrl -> ctrl_iface;
+ wpa_cli_action -> ctrl_iface;
+
+ driver_iface -> ioctl;
+
+ wpa_cli_action -> dnsmasq;
+ wpa_cli_action -> dhclient;
+
+ dnsmasq -> netdev;
+ dhclient -> netdev;
+
+ edge [color=blue,dir=both];
+ p2p_module -> umac -> driver;
+
+ edge [color=green,dir=both];
+ peers -> Qt -> gui_ctrl -> ctrl_iface -> mgmt_entity -> driver_iface -> ioctl -> umac -> p2p_module;
+}
diff --git a/contrib/wpa/doc/p2p_sm.dot b/contrib/wpa/doc/p2p_sm.dot
new file mode 100644
index 000000000000..640caefcc6ad
--- /dev/null
+++ b/contrib/wpa/doc/p2p_sm.dot
@@ -0,0 +1,62 @@
+digraph p2p {
+ ranksep=.75;
+ size = "8.5,7.5";
+
+ start -> IDLE;
+ start [label="Init",shape=none];
+
+ /* Discovery: Scan followed by Find(SEARCH,LISTEN) */
+ subgraph cluster_0 {
+ label="Discovery";
+ color=lightgrey;
+ node [color=blue];
+ /* SCAN and LISTEN currently not used in the implementation */
+ SCAN [style=dotted];
+ LISTEN [style=dotted];
+
+ SCAN -> LISTEN;
+ LISTEN -> SEARCH -> LISTEN [style=dotted];
+ SEARCH -> SD_DURING_FIND [label="Peer SD capab\nand no info", weight=100];
+ SD_DURING_FIND -> SEARCH [label="RX SD Resp\nor timeout", weight=100];
+ SEARCH -> PROV_DISC_DURING_FIND [label="Prov Disc cmd\nand no Resp", weight=100];
+ PROV_DISC_DURING_FIND -> SEARCH [label="RX Prov Disc Resp\nor timeout", weight=100];
+ }
+
+ /* Group Formation */
+ subgraph cluster_1 {
+ label="Group Formation";
+ color=lightgrey;
+ node [color=green];
+
+ CONNECT -> CONNECT_LISTEN [style=dotted,weight=100];
+ CONNECT_LISTEN -> CONNECT [style=dotted,weight=100];
+ CONNECT -> WAIT_PEER_IDLE [label="RX GO Neg Resp\n(info unavail)"];
+ WAIT_PEER_IDLE -> WAIT_PEER_CONNECT [style=dotted,weight=100];
+ WAIT_PEER_CONNECT -> WAIT_PEER_IDLE [style=dotted,weight=100];
+
+ CONNECT -> GO_NEG [label="RX GO Neg Resp\n(success)", weight=10];
+ CONNECT_LISTEN -> GO_NEG [label="RX GO Neg Req or\nTX GO Neg Resp"];
+ WAIT_PEER_CONNECT -> GO_NEG [label="RX GO Neg Req"];
+ GO_NEG -> PROVISIONING [label="TX/RX GO Neg Conf"];
+ }
+
+ PROVISIONING -> IDLE [label="WPS\nsuccess"];
+
+ /* External triggers */
+ IDLE -> SCAN [label="Find cmd",weight=20];
+ IDLE -> CONNECT [label="Connect cmd",weight=20];
+ IDLE -> LISTEN_ONLY [label="Listen cmd"];
+
+ /* Timeouts */
+/*
+ edge [color=red];
+ WAIT_PEER_IDLE -> IDLE [label="timeout", weight=0];
+ WAIT_PEER_CONNECT -> IDLE [label="timeout", weight=0];
+ CONNECT -> IDLE [label="timeout", weight=0];
+ CONNECT_LISTEN -> IDLE [label="timeout", weight=0];
+ GO_NEG -> IDLE [label="timeout", weight=0];
+ PROVISIONING -> IDLE [label="timeout", weight=0];
+ LISTEN_ONLY -> IDLE [label="timeout", weight=0];
+ SEARCH -> IDLE [label="timeout", weight=0];
+*/
+}
diff --git a/contrib/wpa/doc/porting.doxygen b/contrib/wpa/doc/porting.doxygen
new file mode 100644
index 000000000000..b4b78ef0504a
--- /dev/null
+++ b/contrib/wpa/doc/porting.doxygen
@@ -0,0 +1,209 @@
+/**
+\page porting Porting to different target boards and operating systems
+
+wpa_supplicant was designed to be easily portable to different
+hardware (board, CPU) and software (OS, drivers) targets. It is
+already used with number of operating systems and numerous wireless
+card models and drivers. The main wpa_supplicant repository includes
+support for Linux, FreeBSD, and Windows. In addition, the code has been
+ported to number of other operating systems like VxWorks, PalmOS,
+Windows CE, and Windows Mobile. On the hardware
+side, wpa_supplicant is used on various systems: desktops, laptops,
+PDAs, and embedded devices with CPUs including x86, PowerPC,
+arm/xscale, and MIPS. Both big and little endian configurations are
+supported.
+
+
+\section ansi_c_extra Extra functions on top of ANSI C
+
+wpa_supplicant is mostly using ANSI C functions that are available on
+most targets. However, couple of additional functions that are common
+on modern UNIX systems are used. Number of these are listed with
+prototypes in \ref common.h (the \verbatim #ifdef CONFIG_ANSI_C_EXTRA \endverbatim
+block). These functions may need to be implemented or at least defined
+as macros to native functions in the target OS or C library.
+
+Many of the common ANSI C functions are used through a wrapper
+definitions in \ref os.h to allow these to be replaced easily with a
+platform specific version in case standard C libraries are not
+available. In addition, \ref os.h defines couple of common platform
+specific functions that are implemented in \ref os_unix.c for UNIX like
+targets and in \ref os_win32.c for Win32 API. If the target platform does
+not support either of these examples, a new os_*.c file may need to be
+added.
+
+Unless OS_NO_C_LIB_DEFINES is defined, the standard ANSI C and POSIX
+functions are used by defining the os_*() wrappers to use them
+directly in order to avoid extra cost in size and speed. If the target
+platform needs different versions of the functions, \ref os.h can be
+modified to define the suitable macros or alternatively,
+OS_NO_C_LIB_DEFINES may be defined for the build and the wrapper
+functions can then be implemented in a new os_*.c wrapper file.
+
+\ref common.h defines number of helper macros for handling integers of
+different size and byte order. Suitable version of these definitions
+may need to be added for the target platform.
+
+
+\section configuration_backend Configuration backend
+
+wpa_supplicant implements a configuration interface that allows the
+backend to be easily replaced in order to read configuration data from
+a suitable source depending on the target platform. \ref config.c
+implements the generic code that can be shared with all configuration
+backends. Each backend is implemented in its own config_*.c file.
+
+The included \ref config_file.c backend uses a text file for configuration
+and \ref config_winreg.c uses Windows registry. These files can be used as
+an example for a new configuration backend if the target platform uses
+different mechanism for configuration parameters. In addition,
+\ref config_none.c can be used as an empty starting point for building a
+new configuration backend.
+
+
+\section driver_iface_porting Driver interface
+
+Unless the target OS and driver is already supported, most porting
+projects have to implement a driver wrapper. This may be done by
+adding a new driver interface module or modifying an existing module
+(driver_*.c) if the new target is similar to one of them. \ref
+driver_wrapper "Driver wrapper implementation" describes the details
+of the driver interface and discusses the tasks involved in porting
+this part of wpa_supplicant.
+
+
+\section l2_packet_porting l2_packet (link layer access)
+
+wpa_supplicant needs to have access to sending and receiving layer 2
+(link layer) packets with two Ethertypes: EAP-over-LAN (EAPOL) 0x888e
+and RSN pre-authentication 0x88c7. \ref l2_packet.h defines the interfaces
+used for this in the core wpa_supplicant implementation.
+
+If the target operating system supports a generic mechanism for link
+layer access, that is likely the best mechanism for providing the
+needed functionality for wpa_supplicant. Linux packet socket is an
+example of such a generic mechanism. If this is not available, a
+separate interface may need to be implemented to the network stack or
+driver. This is usually an intermediate or protocol driver that is
+operating between the device driver and the OS network stack. If such
+a mechanism is not feasible, the interface can also be implemented
+directly in the device driver.
+
+The main wpa_supplicant repository includes l2_packet implementations
+for Linux using packet sockets (\ref l2_packet_linux.c), more portable
+version using libpcap/libdnet libraries (\ref l2_packet_pcap.c; this
+supports WinPcap, too), and FreeBSD specific version of libpcap
+interface (\ref l2_packet_freebsd.c).
+
+If the target operating system is supported by libpcap (receiving) and
+libdnet (sending), \ref l2_packet_pcap.c can likely be used with minimal or
+no changes. If this is not a case or a proprietary interface for link
+layer is required, a new l2_packet module may need to be
+added. Alternatively, for hostapd,
+struct \ref wpa_driver_ops::hapd_send_eapol() handler can
+be used to override the l2_packet library if the link layer access is
+integrated with the driver interface implementation.
+
+
+\section eloop_porting Event loop
+
+wpa_supplicant uses a single process/thread model and an event loop
+to provide callbacks on events (registered timeout, received packet,
+signal). eloop.h defines the event loop interface. \ref eloop.c is an
+implementation of such an event loop using select() and sockets. This
+is suitable for most UNIX/POSIX systems. When porting to other
+operating systems, it may be necessary to replace that implementation
+with OS specific mechanisms that provide similar functionality.
+
+
+\section ctrl_iface_porting Control interface
+
+wpa_supplicant uses a \ref ctrl_iface_page "control interface"
+to allow external processed
+to get status information and to control the operations. Currently,
+this is implemented with socket based communication; both UNIX domain
+sockets and UDP sockets are supported. If the target OS does not
+support sockets, this interface will likely need to be modified to use
+another mechanism like message queues. The control interface is
+optional component, so it is also possible to run wpa_supplicant
+without porting this part.
+
+The wpa_supplicant side of the control interface is implemented in
+\ref wpa_supplicant/ctrl_iface.c. Matching client side is implemented as a control
+interface library in \ref wpa_ctrl.c.
+
+
+\section entry_point Program entry point
+
+wpa_supplicant defines a set of functions that can be used to
+initialize main supplicant processing. Each operating system has a
+mechanism for starting new processing or threads. This is usually a
+function with a specific set of arguments and calling convention. This
+function is responsible on initializing wpa_supplicant.
+
+\ref wpa_supplicant/main.c includes an entry point for UNIX-like
+operating system, i.e., main() function that uses command line arguments
+for setting parameters for wpa_supplicant. When porting to other
+operating systems, similar OS-specific entry point implementation is
+needed. It can be implemented in a new file that is then linked with
+wpa_supplicant instead of main.o. \ref wpa_supplicant/main.c is also a
+good example on how the initialization process should be done.
+
+The supplicant initialization functions are defined in
+\ref wpa_supplicant_i.h. In most cases, the entry point function should
+start by fetching configuration parameters. After this, a global
+wpa_supplicant context is initialized with a call to
+\ref wpa_supplicant_init(). After this, existing network interfaces can be
+added with \ref wpa_supplicant_add_iface(). \ref wpa_supplicant_run() is then
+used to start the main event loop. Once this returns at program
+termination time, \ref wpa_supplicant_deinit() is used to release global
+context data.
+
+\ref wpa_supplicant_add_iface() and \ref wpa_supplicant_remove_iface() can be
+used dynamically to add and remove interfaces based on when
+wpa_supplicant processing is needed for them. This can be done, e.g.,
+when hotplug network adapters are being inserted and ejected. It is
+also possible to do this when a network interface is being
+enabled/disabled if it is desirable that wpa_supplicant processing
+for the interface is fully enabled/disabled at the same time.
+
+
+\section simple_build Simple build example
+
+One way to start a porting project is to begin with a very simple
+build of wpa_supplicant with WPA-PSK support and once that is
+building correctly, start adding features.
+
+Following command can be used to build very simple version of
+wpa_supplicant:
+
+\verbatim
+cc -o wpa_supplicant config.c eloop.c common.c md5.c rc4.c sha1.c \
+ config_none.c l2_packet_none.c tls_none.c wpa.c preauth.c \
+ aes_wrap.c wpa_supplicant.c events.c main_none.c drivers.c
+\endverbatim
+
+The end result is not really very useful since it uses empty functions
+for configuration parsing and layer 2 packet access and does not
+include a driver interface. However, this is a good starting point
+since the build is complete in the sense that all functions are
+present and this is easy to configure to a build system by just
+including the listed C files.
+
+Once this version can be build successfully, the end result can be
+made functional by adding a proper program entry point (main*.c),
+driver interface (driver_*.c and matching CONFIG_DRIVER_* define for
+registration in \ref drivers.c), configuration parser/writer (config_*.c),
+and layer 2 packet access implementation (l2_packet_*.c). After these
+components have been added, the end result should be a working
+WPA/WPA2-PSK enabled supplicant.
+
+After the basic functionality has been verified to work, more features
+can be added by linking in more files and defining C pre-processor
+defines. Currently, the best source of information for what options
+are available and which files needs to be included is in the Makefile
+used for building the supplicant with make. Similar configuration will
+be needed for build systems that either use different type of make
+tool or a GUI-based project configuration.
+
+*/
diff --git a/contrib/wpa/doc/testing_tools.doxygen b/contrib/wpa/doc/testing_tools.doxygen
new file mode 100644
index 000000000000..d12652415634
--- /dev/null
+++ b/contrib/wpa/doc/testing_tools.doxygen
@@ -0,0 +1,201 @@
+/**
+\page testing_tools Testing and development tools
+
+[ \ref eapol_test "eapol_test" |
+\ref preauth_test "preauth_test" |
+\ref unit_tests "Unit tests" |
+\ref wpa_trace "Tracing code" ]
+
+wpa_supplicant source tree includes number of testing and development
+tools that make it easier to test the programs without having to setup
+a full test setup with wireless cards. In addition, these tools can be
+used to implement automatic tests suites.
+
+\section eapol_test eapol_test - EAP peer and RADIUS client testing
+
+eapol_test is a program that links together the same EAP peer
+implementation that wpa_supplicant is using and the RADIUS
+authentication client code from hostapd. In addition, it has minimal
+glue code to combine these two components in similar ways to IEEE
+802.1X/EAPOL Authenticator state machines. In other words, it
+integrates IEEE 802.1X Authenticator (normally, an access point) and
+IEEE 802.1X Supplicant (normally, a wireless client) together to
+generate a single program that can be used to test EAP methods without
+having to setup an access point and a wireless client.
+
+The main uses for eapol_test are in interoperability testing of EAP
+methods against RADIUS servers and in development testing for new EAP
+methods. It can be easily used to automate EAP testing for
+interoperability and regression since the program can be run from
+shell scripts without require additional test components apart from a
+RADIUS server. For example, the automated EAP tests described in
+eap_testing.txt are implemented with eapol_test. Similarly, eapol_test
+could be used to implement an automated regression test suite for a
+RADIUS authentication server.
+
+eapol_test uses the same build time configuration file, .config, as
+wpa_supplicant. This file is used to select which EAP methods are
+included in eapol_test. This program is not built with the default
+Makefile target, so a separate make command needs to be used to
+compile the tool:
+
+\verbatim
+make eapol_test
+\endverbatim
+
+The resulting eapol_test binary has following command like options:
+
+\verbatim
+usage:
+eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] [-s<AS secret>] \
+ [-r<count>] [-t<timeout>] [-C<Connect-Info>] \
+ [-M<client MAC address>]
+eapol_test scard
+eapol_test sim <PIN> <num triplets> [debug]
+
+options:
+ -c<conf> = configuration file
+ -a<AS IP> = IP address of the authentication server, default 127.0.0.1
+ -p<AS port> = UDP port of the authentication server, default 1812
+ -s<AS secret> = shared secret with the authentication server, default 'radius'
+ -r<count> = number of re-authentications
+ -W = wait for a control interface monitor before starting
+ -S = save configuration after authentiation
+ -n = no MPPE keys expected
+ -t<timeout> = sets timeout in seconds (default: 30 s)
+ -C<Connect-Info> = RADIUS Connect-Info (default: CONNECT 11Mbps 802.11b)
+ -M<client MAC address> = Set own MAC address (Calling-Station-Id,
+ default: 02:00:00:00:00:01)
+\endverbatim
+
+
+As an example,
+\verbatim
+eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1
+\endverbatim
+tries to complete EAP authentication based on the network
+configuration from test.conf against the RADIUS server running on the
+local host. A re-authentication is triggered to test fast
+re-authentication. The configuration file uses the same format for
+network blocks as wpa_supplicant.
+
+
+\section preauth_test preauth_test - WPA2 pre-authentication and EAP peer testing
+
+preauth_test is similar to eapol_test in the sense that in combines
+EAP peer implementation with something else, in this case, with WPA2
+pre-authentication. This tool can be used to test pre-authentication
+based on the code that wpa_supplicant is using. As such, it tests
+both the wpa_supplicant implementation and the functionality of an
+access point.
+
+preauth_test is built with:
+
+\verbatim
+make preauth_test
+\endverbatim
+
+and it uses following command line arguments:
+
+\verbatim
+usage: preauth_test <conf> <target MAC address> <ifname>
+\endverbatim
+
+For example,
+\verbatim
+preauth_test test.conf 02:11:22:33:44:55 eth0
+\endverbatim
+would use network configuration from test.conf to try to complete
+pre-authentication with AP using BSSID 02:11:22:33:44:55. The
+pre-authentication packets would be sent using the eth0 interface.
+
+
+\section unit_tests Unit tests
+
+Number of the components (.c files) used in wpa_supplicant define
+their own unit tests for automated validation of the basic
+functionality. Most of the tests for cryptographic algorithms are
+using standard test vectors to validate functionality. These tests can
+be useful especially when verifying port to a new CPU target.
+
+The test programs are collected in the tests subdirectory. All
+automated unit tests can be run with
+
+\verbatim
+make run-tests
+\endverbatim
+
+This make target builds and runs each test and terminates with zero
+exit code if all tests were completed successfully.
+
+
+\section wpa_trace Tracing code for developer debuggin
+
+wpa_supplicant and hostapd can be built with tracing code that will
+track and analyze memory allocations and other resource registrations
+and certain API uses. If incorrect use is detected, a backtrace of the
+call location (and/or allocation location) is shown. This can also be
+used to detect certain categories of memory leaks and report them
+automatically when the program is terminated. The report will also
+include information about forgotten eloop events.
+
+The trace code can be enabled with CONFIG_WPA_TRACE=y build
+option. More verbose backtrace information can be generated if libbfd
+is available and the binaries are not stripped of symbol
+information. This is enabled with CONFIG_WPA_TRACE_BFD=y.
+
+For example, a memory leak (forgotten os_free() call) would show up
+like this when the program is terminated:
+
+\verbatim
+MEMLEAK[0x82d200]: len 128
+WPA_TRACE: memleak - START
+[0]: ./wpa_supplicant(os_malloc+0x59) [0x41a5e9]
+ os_malloc() ../src/utils/os_unix.c:359
+[1]: ./wpa_supplicant(os_zalloc+0x16) [0x41a676]
+ os_zalloc() ../src/utils/os_unix.c:418
+[2]: ./wpa_supplicant(wpa_supplicant_init+0x38) [0x48b508]
+ wpa_supplicant_init() wpa_supplicant.c:2315
+[3]: ./wpa_supplicant(main+0x2f3) [0x491073]
+ main() main.c:252
+WPA_TRACE: memleak - END
+MEMLEAK: total 128 bytes
+\endverbatim
+
+Another type of error that can be detected is freeing of memory area
+that was registered for some use and is still be referenced:
+
+\verbatim
+WPA_TRACE: Freeing referenced memory - START
+[2]: ./wpa_supplicant(os_free+0x5c) [0x41a53c]
+ os_free() ../src/utils/os_unix.c:411
+[3]: ./wpa_supplicant(wpa_supplicant_remove_iface+0x30) [0x48b380]
+ wpa_supplicant_remove_iface() wpa_supplicant.c:2259
+[4]: ./wpa_supplicant(wpa_supplicant_deinit+0x20) [0x48b3e0]
+ wpa_supplicant_deinit() wpa_supplicant.c:2430
+[5]: ./wpa_supplicant(main+0x357) [0x4910d7]
+ main() main.c:276
+WPA_TRACE: Freeing referenced memory - END
+WPA_TRACE: Reference registration - START
+[1]: ./wpa_supplicant [0x41c040]
+ eloop_trace_sock_add_ref() ../src/utils/eloop.c:94
+[2]: ./wpa_supplicant(wpa_supplicant_ctrl_iface_deinit+0x17) [0x473247]
+ wpa_supplicant_ctrl_iface_deinit() ctrl_iface_unix.c:436
+[3]: ./wpa_supplicant [0x48b21c]
+ wpa_supplicant_cleanup() wpa_supplicant.c:378
+ wpa_supplicant_deinit_iface() wpa_supplicant.c:2155
+[4]: ./wpa_supplicant(wpa_supplicant_remove_iface+0x30) [0x48b380]
+ wpa_supplicant_remove_iface() wpa_supplicant.c:2259
+[5]: ./wpa_supplicant(wpa_supplicant_deinit+0x20) [0x48b3e0]
+ wpa_supplicant_deinit() wpa_supplicant.c:2430
+[6]: ./wpa_supplicant(main+0x357) [0x4910d7]
+ main() main.c:276
+WPA_TRACE: Reference registration - END
+Aborted
+\endverbatim
+
+This type of error results in showing backtraces for both the location
+where the incorrect freeing happened and the location where the memory
+area was marked referenced.
+
+*/
diff --git a/contrib/wpa/doc/wpa_supplicant.fig b/contrib/wpa/doc/wpa_supplicant.fig
new file mode 100644
index 000000000000..d2c4306b8e03
--- /dev/null
+++ b/contrib/wpa/doc/wpa_supplicant.fig
@@ -0,0 +1,247 @@
+#FIG 3.2
+Landscape
+Center
+Inches
+Letter
+100.00
+Single
+-2
+1200 2
+6 1875 4050 2925 4350
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1875 4050 2925 4050 2925 4350 1875 4350 1875 4050
+4 0 0 50 -1 0 12 0.0000 4 180 735 2025 4275 l2_packet\001
+-6
+6 3450 1200 4275 1500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3450 1200 4275 1200 4275 1500 3450 1500 3450 1200
+4 0 0 50 -1 0 12 0.0000 4 180 585 3600 1425 wpa_cli\001
+-6
+6 4725 1200 5925 1500
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4725 1200 5925 1200 5925 1500 4725 1500 4725 1200
+4 0 0 50 -1 0 12 0.0000 4 135 1005 4800 1425 GUI frontend\001
+-6
+6 6000 2700 7200 3225
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6000 2700 7200 2700 7200 3225 6000 3225 6000 2700
+4 0 0 50 -1 0 12 0.0000 4 135 975 6075 2925 WPA/WPA2\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 3150 state machine\001
+-6
+6 6000 4950 7200 5475
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6000 4950 7200 4950 7200 5475 6000 5475 6000 4950
+4 0 0 50 -1 0 12 0.0000 4 135 360 6075 5175 EAP\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 5400 state machine\001
+-6
+6 8700 3000 9375 3300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8700 3000 9375 3000 9375 3300 8700 3300 8700 3000
+4 0 0 50 -1 0 12 0.0000 4 150 480 8775 3225 crypto\001
+-6
+6 4350 3900 5025 4425
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4350 3900 5025 3900 5025 4425 4350 4425 4350 3900
+4 0 0 50 -1 0 12 0.0000 4 105 420 4500 4125 event\001
+4 0 0 50 -1 0 12 0.0000 4 180 315 4500 4350 loop\001
+-6
+6 4275 2550 5100 2850
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4275 2550 5100 2550 5100 2850 4275 2850 4275 2550
+4 0 0 50 -1 0 12 0.0000 4 135 450 4425 2775 ctrl i/f\001
+-6
+6 6000 3900 7200 4425
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6000 3900 7200 3900 7200 4425 6000 4425 6000 3900
+4 0 0 50 -1 0 12 0.0000 4 135 600 6075 4125 EAPOL\001
+4 0 0 50 -1 0 12 0.0000 4 135 1065 6075 4350 state machine\001
+-6
+6 1800 6000 7800 8100
+6 1800 6000 7800 7200
+6 1800 6900 2700 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1800 6900 2700 6900 2700 7200 1800 7200 1800 6900
+4 0 0 50 -1 0 12 0.0000 4 105 375 1875 7125 wext\001
+-6
+6 4725 6900 5625 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4725 6900 5625 6900 5625 7200 4725 7200 4725 6900
+4 0 0 50 -1 0 12 0.0000 4 135 555 4800 7125 hermes\001
+-6
+6 6675 6900 7800 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 6675 6900 7800 6900 7800 7200 6675 7200 6675 6900
+4 0 0 50 -1 0 12 0.0000 4 180 930 6750 7125 ndiswrapper\001
+-6
+6 5700 6900 6600 7200
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 5700 6900 6600 6900 6600 7200 5700 7200 5700 6900
+4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 atmel\001
+-6
+6 4275 6000 5100 6300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 4275 6000 5100 6000 5100 6300 4275 6300 4275 6000
+4 0 0 50 -1 0 12 0.0000 4 135 630 4350 6225 driver i/f\001
+-6
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 2775 6900 3675 6900 3675 7200 2775 7200 2775 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3750 6900 4650 6900 4650 7200 3750 7200 3750 6900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 4
+ 2250 6900 2250 6600 7200 6600 7200 6900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 3225 6900 3225 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4200 6900 4200 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 5175 6900 5175 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6150 6900 6150 6600
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4650 6600 4650 6300
+4 0 0 50 -1 0 12 0.0000 4 180 510 2850 7125 hostap\001
+4 0 0 50 -1 0 12 0.0000 4 135 600 3825 7125 nl80211\001
+-6
+6 3525 7800 5775 8100
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 3525 7800 5775 7800 5775 8100 3525 8100 3525 7800
+4 0 0 50 -1 0 12 0.0000 4 135 2145 3600 8025 kernel network device driver\001
+-6
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 2250 7200 4200 7800
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 7200 7200 5100 7800
+-6
+6 9600 3000 10275 3300
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9600 3000 10275 3000 10275 3300 9600 3300 9600 3000
+4 0 0 50 -1 0 12 0.0000 4 135 315 9750 3225 TLS\001
+-6
+6 8100 4425 10425 7350
+6 8175 4725 9225 5025
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 4725 9225 4725 9225 5025 8175 5025 8175 4725
+4 0 0 50 -1 0 12 0.0000 4 135 735 8250 4950 EAP-TLS\001
+-6
+6 9300 4725 10350 5025
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 4725 10350 4725 10350 5025 9300 5025 9300 4725
+4 0 0 50 -1 0 12 0.0000 4 135 810 9375 4950 EAP-MD5\001
+-6
+6 8175 5100 9225 5400
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 5100 9225 5100 9225 5400 8175 5400 8175 5100
+4 0 0 50 -1 0 12 0.0000 4 135 885 8250 5325 EAP-PEAP\001
+-6
+6 9300 5100 10350 5400
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 5100 10350 5100 10350 5400 9300 5400 9300 5100
+4 0 0 50 -1 0 12 0.0000 4 135 840 9375 5325 EAP-TTLS\001
+-6
+6 8175 5475 9225 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 5475 9225 5475 9225 5775 8175 5775 8175 5475
+4 0 0 50 -1 0 12 0.0000 4 135 780 8250 5700 EAP-GTC\001
+-6
+6 9300 5475 10350 5775
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 5475 10350 5475 10350 5775 9300 5775 9300 5475
+4 0 0 50 -1 0 12 0.0000 4 135 765 9375 5700 EAP-OTP\001
+-6
+6 8175 5850 9225 6150
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 5850 9225 5850 9225 6150 8175 6150 8175 5850
+4 0 0 50 -1 0 12 0.0000 4 135 750 8250 6075 EAP-SIM\001
+-6
+6 9300 6225 10350 6525
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 6225 10350 6225 10350 6525 9300 6525 9300 6225
+4 0 0 50 -1 0 12 0.0000 4 135 465 9375 6450 LEAP\001
+-6
+6 8175 6225 9225 6525
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 6225 9225 6225 9225 6525 8175 6525 8175 6225
+4 0 0 50 -1 0 12 0.0000 4 135 765 8250 6450 EAP-PSK\001
+-6
+6 9300 5850 10350 6150
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 5850 10350 5850 10350 6150 9300 6150 9300 5850
+4 0 0 50 -1 0 12 0.0000 4 135 825 9375 6075 EAP-AKA\001
+-6
+6 8175 6975 9675 7275
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 6975 9675 6975 9675 7275 8175 7275 8175 6975
+4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 7200 EAP-MSCHAPv2\001
+-6
+6 9300 6600 10350 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 9300 6600 10350 6600 10350 6900 9300 6900 9300 6600
+4 0 0 50 -1 0 12 0.0000 4 135 870 9375 6825 EAP-FAST\001
+-6
+6 8175 6600 9225 6900
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8175 6600 9225 6600 9225 6900 8175 6900 8175 6600
+4 0 0 50 -1 0 12 0.0000 4 135 795 8250 6825 EAP-PAX\001
+-6
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 8100 7350 10425 7350 10425 4425 8100 4425 8100 7350
+4 0 0 50 -1 0 12 0.0000 4 135 1050 8700 4650 EAP methods\001
+-6
+6 2775 5025 4050 5325
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 2775 5025 4050 5025 4050 5325 2775 5325 2775 5025
+4 0 0 50 -1 0 12 0.0000 4 135 990 2925 5250 driver events\001
+-6
+6 2775 3150 4050 3450
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 2775 3150 4050 3150 4050 3450 2775 3450 2775 3150
+4 0 0 50 -1 0 12 0.0000 4 180 990 2925 3375 configuration\001
+-6
+2 1 1 1 0 7 50 -1 -1 3.000 0 0 -1 0 0 2
+ 1275 4200 1875 4200
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 4500 2550 3900 1500
+2 1 1 1 0 7 50 -1 -1 4.000 0 0 -1 0 0 2
+ 4800 2550 5400 1500
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 2925 4200 4350 4200
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 5025 3900 6000 3000
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 5025 4200 6000 4200
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4650 6000 4650 4425
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6600 4425 6600 4950
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6600 3225 6600 3900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 7200 5250 8100 5250
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 9075 4425 9075 3300
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 7200 3000 8700 3150
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4650 3900 4650 2850
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 7200 4125 8700 3300
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6000 4350 5025 6000
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 6000 3150 4875 6000
+2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
+ 1500 2100 10800 2100 10800 7500 1500 7500 1500 2100
+2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 9900 4425 9900 3300
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 1
+ 4350 3900
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4350 3900 4050 3450
+2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
+ 4350 4425 4050 5025
+4 0 0 50 -1 0 12 0.0000 4 135 915 375 3975 EAPOL and\001
+4 0 0 50 -1 0 12 0.0000 4 180 630 375 4200 pre-auth\001
+4 0 0 50 -1 0 12 0.0000 4 180 810 375 4425 ethertypes\001
+4 0 0 50 -1 0 12 0.0000 4 135 1050 375 4650 from/to kernel\001
+4 0 0 50 -1 0 12 0.0000 4 135 1920 3675 1875 frontend control interface\001
+4 0 0 50 -1 2 14 0.0000 4 210 1440 1637 2371 wpa_supplicant\001
diff --git a/contrib/wpa/eap_example/.gitignore b/contrib/wpa/eap_example/.gitignore
new file mode 100644
index 000000000000..4d6d2d1301b2
--- /dev/null
+++ b/contrib/wpa/eap_example/.gitignore
@@ -0,0 +1,4 @@
+*.d
+eap_example
+libeap.so
+libeap.a
diff --git a/contrib/wpa/eap_example/Makefile b/contrib/wpa/eap_example/Makefile
new file mode 100644
index 000000000000..691466f03c9a
--- /dev/null
+++ b/contrib/wpa/eap_example/Makefile
@@ -0,0 +1,119 @@
+ALL=eap_example
+
+include ../src/build.rules
+
+CFLAGS += -I.
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+
+EAP_LIBS += ../src/utils/libutils.a
+EAP_LIBS += ../src/crypto/libcrypto.a
+EAP_LIBS += ../src/tls/libtls.a
+
+OBJS_both += ../src/eap_common/eap_peap_common.o
+OBJS_both += ../src/eap_common/eap_psk_common.o
+OBJS_both += ../src/eap_common/eap_pax_common.o
+OBJS_both += ../src/eap_common/eap_sake_common.o
+OBJS_both += ../src/eap_common/eap_gpsk_common.o
+OBJS_both += ../src/eap_common/chap.o
+
+OBJS_peer += ../src/eap_peer/eap_tls.o
+OBJS_peer += ../src/eap_peer/eap_peap.o
+OBJS_peer += ../src/eap_peer/eap_ttls.o
+OBJS_peer += ../src/eap_peer/eap_md5.o
+OBJS_peer += ../src/eap_peer/eap_mschapv2.o
+OBJS_peer += ../src/eap_peer/mschapv2.o
+OBJS_peer += ../src/eap_peer/eap_otp.o
+OBJS_peer += ../src/eap_peer/eap_gtc.o
+OBJS_peer += ../src/eap_peer/eap_leap.o
+OBJS_peer += ../src/eap_peer/eap_psk.o
+OBJS_peer += ../src/eap_peer/eap_pax.o
+OBJS_peer += ../src/eap_peer/eap_sake.o
+OBJS_peer += ../src/eap_peer/eap_gpsk.o
+OBJS_peer += ../src/eap_peer/eap.o
+OBJS_peer += ../src/eap_common/eap_common.o
+OBJS_peer += ../src/eap_peer/eap_methods.o
+OBJS_peer += ../src/eap_peer/eap_tls_common.o
+
+CFLAGS += -DEAP_TLS
+CFLAGS += -DEAP_PEAP
+CFLAGS += -DEAP_TTLS
+CFLAGS += -DEAP_MD5
+CFLAGS += -DEAP_MSCHAPv2
+CFLAGS += -DEAP_GTC
+CFLAGS += -DEAP_OTP
+CFLAGS += -DEAP_LEAP
+CFLAGS += -DEAP_PSK
+CFLAGS += -DEAP_PAX
+CFLAGS += -DEAP_SAKE
+CFLAGS += -DEAP_GPSK -DEAP_GPSK_SHA256
+
+CFLAGS += -DEAP_SERVER_IDENTITY
+CFLAGS += -DEAP_SERVER_TLS
+CFLAGS += -DEAP_SERVER_PEAP
+CFLAGS += -DEAP_SERVER_TTLS
+CFLAGS += -DEAP_SERVER_MD5
+CFLAGS += -DEAP_SERVER_MSCHAPV2
+CFLAGS += -DEAP_SERVER_GTC
+CFLAGS += -DEAP_SERVER_PSK
+CFLAGS += -DEAP_SERVER_PAX
+CFLAGS += -DEAP_SERVER_SAKE
+CFLAGS += -DEAP_SERVER_GPSK -DEAP_SERVER_GPSK_SHA256
+
+CFLAGS += -DIEEE8021X_EAPOL
+
+
+# Optional components to add EAP server support
+OBJS_server += ../src/eap_server/eap_server_tls.o
+OBJS_server += ../src/eap_server/eap_server_peap.o
+OBJS_server += ../src/eap_server/eap_server_ttls.o
+OBJS_server += ../src/eap_server/eap_server_md5.o
+OBJS_server += ../src/eap_server/eap_server_mschapv2.o
+OBJS_server += ../src/eap_server/eap_server_gtc.o
+OBJS_server += ../src/eap_server/eap_server_psk.o
+OBJS_server += ../src/eap_server/eap_server_pax.o
+OBJS_server += ../src/eap_server/eap_server_sake.o
+OBJS_server += ../src/eap_server/eap_server_gpsk.o
+OBJS_server += ../src/eap_server/eap_server.o
+OBJS_server += ../src/eap_server/eap_server_identity.o
+OBJS_server += ../src/eap_server/eap_server_methods.o
+OBJS_server += ../src/eap_server/eap_server_tls_common.o
+CFLAGS += -DEAP_SERVER
+
+
+OBJS_lib=$(OBJS_both) $(OBJS_peer) $(OBJS_server)
+_OBJS_VAR := OBJS_lib
+include ../src/objs.mk
+
+OBJS_ex = eap_example.o eap_example_peer.o eap_example_server.o
+_OBJS_VAR := OBJS_ex
+include ../src/objs.mk
+
+_OBJS_VAR := EAP_LIBS
+include ../src/objs.mk
+
+
+ifneq ($(CONFIG_SOLIB), yes)
+LIBEAP = libeap.a
+libeap.a: $(EAP_LIBS) $(OBJS_lib)
+ $(AR) crT libeap.a $^
+ $(RANLIB) libeap.a
+
+else
+CFLAGS += -fPIC -DPIC
+LDFLAGS += -shared
+
+LIBEAP = libeap.so
+libeap.so: $(EAP_LIBS) $(OBJS_lib)
+ $(LDO) $(LDFLAGS) $^ -o $(LIBEAP)
+
+endif
+
+eap_example: $(OBJS_ex) $(LIBEAP)
+ $(LDO) $(LDFLAGS) -o eap_example $(OBJS_ex) -L. -leap $(LIBS)
+
+clean: common-clean
+ rm -f core *~ *.o *.d libeap.a libeap.so
+
+-include $(OBJS:%.o=%.d)
diff --git a/contrib/wpa/eap_example/README b/contrib/wpa/eap_example/README
new file mode 100644
index 000000000000..0c2921e3be93
--- /dev/null
+++ b/contrib/wpa/eap_example/README
@@ -0,0 +1,42 @@
+EAP peer/server library and example program
+Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+
+This software may be distributed under the terms of the BSD license.
+See the parent directory README for more details.
+
+
+The interfaces of the EAP server/peer implementation are based on RFC
+4137 (EAP State Machines). This RFC is coordinated with the state
+machines defined in IEEE 802.1X-2004. hostapd and wpa_supplicant
+include implementation of the IEEE 802.1X EAPOL state machines and the
+interface between them and EAP. However, the EAP implementation can be
+used with other protocols, too, by providing a compatible interface
+which maps the EAPOL<->EAP variables to another protocol.
+
+This directory contains an example showing how EAP peer and server
+code from wpa_supplicant and hostapd can be used as a library. The
+example program initializes both an EAP server and an EAP peer
+entities and then runs through an EAP-PEAP/MSCHAPv2 authentication.
+
+eap_example_peer.c shows the initialization and glue code needed to
+control the EAP peer implementation. eap_example_server.c does the
+same for EAP server. eap_example.c is an example that ties in both the
+EAP server and client parts to allow an EAP authentication to be
+shown.
+
+In this example, the EAP messages are passed between the server and
+the peer are passed by direct function calls within the same process.
+In practice, server and peer functionalities would likely reside in
+separate devices and the EAP messages would be transmitted between the
+devices based on an external protocol. For example, in IEEE 802.11
+uses IEEE 802.1X EAPOL state machines to control the transmission of
+EAP messages and WiMax supports optional PMK EAP authentication
+mechanism that transmits EAP messages as defined in IEEE 802.16e.
+
+
+The EAP library links in number of helper functions from src/utils and
+src/crypto directories. Most of these are suitable as-is, but it may
+be desirable to replace the debug output code in src/utils/wpa_debug.c
+by dropping this file from the library and re-implementing the
+functions there in a way that better fits in with the main
+application.
diff --git a/contrib/wpa/eap_example/ca.pem b/contrib/wpa/eap_example/ca.pem
new file mode 100644
index 000000000000..b128893a1e7b
--- /dev/null
+++ b/contrib/wpa/eap_example/ca.pem
@@ -0,0 +1,55 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 15624081837803162817 (0xd8d3e3a6cbe3ccc1)
+ Signature Algorithm: sha1WithRSAEncryption
+ Issuer: C=FI, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: Jun 29 16:41:22 2013 GMT
+ Not After : Jun 27 16:41:22 2023 GMT
+ Subject: C=FI, O=w1.fi, CN=Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:be:1e:86:e4:79:03:c1:d1:94:d5:d4:b3:b1:28:
+ 90:76:fb:b8:a6:cd:6d:1c:d1:48:f4:08:9a:67:ff:
+ f9:a6:54:b1:19:29:df:29:1b:cd:f1:6f:66:01:e7:
+ db:79:ce:c0:39:2a:25:13:26:94:0c:2c:7b:5a:2c:
+ 81:0f:94:ee:51:d0:75:e6:46:db:17:46:a7:15:8b:
+ 0e:57:0f:b0:54:76:63:12:ca:86:18:bc:1a:c3:16:
+ c0:70:09:d6:6b:43:39:b8:98:29:46:ac:cb:6a:ad:
+ 38:88:3b:07:dc:81:cd:3a:f6:1d:f6:2f:ef:1d:d7:
+ ae:8a:b6:d1:e7:b3:15:02:b9
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+ X509v3 Authority Key Identifier:
+ keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha1WithRSAEncryption
+ 1a:cf:77:60:44:43:c4:55:0e:99:e0:89:aa:b9:d3:7b:32:b7:
+ 5c:9c:7c:ca:fe:8c:d4:94:c6:5e:f3:83:19:5f:29:59:68:a4:
+ 4f:dc:04:2e:b8:71:c0:6d:3b:ae:01:e4:b9:88:99:cc:ce:82:
+ be:6a:28:c2:ac:6a:94:c6:87:90:ed:85:3c:10:71:c5:ff:3c:
+ 70:64:e2:41:62:31:ea:86:7b:11:8c:93:ea:c6:f3:f3:4e:f9:
+ d4:f2:81:90:d7:f4:fa:a1:91:6e:d4:dd:15:3e:26:3b:ac:1e:
+ c3:c2:1f:ed:bb:34:bf:cb:b2:67:c6:c6:51:e8:51:22:b4:f3:
+ 92:e8
+-----BEGIN CERTIFICATE-----
+MIICLDCCAZWgAwIBAgIJANjT46bL48zBMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xMzA2
+MjkxNjQxMjJaFw0yMzA2MjcxNjQxMjJaMC8xCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAvh6G5HkDwdGU1dSzsSiQdvu4ps1tHNFI9AiaZ//5plSxGSnfKRvN8W9m
+Aefbec7AOSolEyaUDCx7WiyBD5TuUdB15kbbF0anFYsOVw+wVHZjEsqGGLwawxbA
+cAnWa0M5uJgpRqzLaq04iDsH3IHNOvYd9i/vHdeuirbR57MVArkCAwEAAaNQME4w
+HQYDVR0OBBYEFLiS3v2KGLMww59V8zNdtMgpikEUMB8GA1UdIwQYMBaAFLiS3v2K
+GLMww59V8zNdtMgpikEUMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
+Gs93YERDxFUOmeCJqrnTezK3XJx8yv6M1JTGXvODGV8pWWikT9wELrhxwG07rgHk
+uYiZzM6CvmoowqxqlMaHkO2FPBBxxf88cGTiQWIx6oZ7EYyT6sbz80751PKBkNf0
++qGRbtTdFT4mO6wew8If7bs0v8uyZ8bGUehRIrTzkug=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/eap_example/dh.conf b/contrib/wpa/eap_example/dh.conf
new file mode 100644
index 000000000000..7bc83251c592
--- /dev/null
+++ b/contrib/wpa/eap_example/dh.conf
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAP3V8IHq3H2DUlYywsvjYNuS17eCdt0mJo6/os6PHqdhgkMrPxF9u4Gr
+qKXq9e6GqmZYdjta30N3FkXaV924BJ0xOqb2TntiKg4u50/l6hSUneWt6UFBaizd
+XrqjNFIme/5RXMZ7RglXliBpCepAaFLMcKhOS4ulUyYYHSy+oqRjAgEC
+-----END DH PARAMETERS-----
diff --git a/contrib/wpa/eap_example/eap_example.c b/contrib/wpa/eap_example/eap_example.c
new file mode 100644
index 000000000000..8a48cd34b030
--- /dev/null
+++ b/contrib/wpa/eap_example/eap_example.c
@@ -0,0 +1,47 @@
+/*
+ * Example application showing how EAP peer and server code from
+ * wpa_supplicant/hostapd can be used as a library. This example program
+ * initializes both an EAP server and an EAP peer entities and then runs
+ * through an EAP-PEAP/MSCHAPv2 authentication.
+ * Copyright (c) 2007, 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 "common.h"
+
+
+int eap_example_peer_init(void);
+void eap_example_peer_deinit(void);
+int eap_example_peer_step(void);
+
+int eap_example_server_init(void);
+void eap_example_server_deinit(void);
+int eap_example_server_step(void);
+
+
+int main(int argc, char *argv[])
+{
+ int res_s, res_p;
+
+ wpa_debug_level = 0;
+
+ if (eap_example_peer_init() < 0 ||
+ eap_example_server_init() < 0)
+ return -1;
+
+ do {
+ printf("---[ server ]--------------------------------\n");
+ res_s = eap_example_server_step();
+ printf("---[ peer ]----------------------------------\n");
+ res_p = eap_example_peer_step();
+ } while (res_s || res_p);
+
+ eap_example_peer_deinit();
+ eap_example_server_deinit();
+
+ return 0;
+}
diff --git a/contrib/wpa/eap_example/eap_example_peer.c b/contrib/wpa/eap_example/eap_example_peer.c
new file mode 100644
index 000000000000..5fe0cb7fa7f6
--- /dev/null
+++ b/contrib/wpa/eap_example/eap_example_peer.c
@@ -0,0 +1,377 @@
+/*
+ * Example application showing how EAP peer code from wpa_supplicant can be
+ * used as a library.
+ * Copyright (c) 2007, 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 "common.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_config.h"
+#include "wpabuf.h"
+
+void eap_example_server_rx(const u8 *data, size_t data_len);
+
+
+struct eap_peer_ctx {
+ bool eapSuccess;
+ bool eapRestart;
+ bool eapFail;
+ bool eapResp;
+ bool eapNoResp;
+ bool eapReq;
+ bool portEnabled;
+ bool altAccept; /* for EAP */
+ bool altReject; /* for EAP */
+ bool eapTriggerStart;
+
+ struct wpabuf *eapReqData; /* for EAP */
+
+ unsigned int idleWhile; /* for EAP state machine */
+
+ struct eap_peer_config eap_config;
+ struct eap_sm *eap;
+};
+
+
+static struct eap_peer_ctx eap_ctx;
+
+
+static struct eap_peer_config * peer_get_config(void *ctx)
+{
+ struct eap_peer_ctx *peer = ctx;
+ return &peer->eap_config;
+}
+
+
+static bool peer_get_bool(void *ctx, enum eapol_bool_var variable)
+{
+ struct eap_peer_ctx *peer = ctx;
+ if (peer == NULL)
+ return false;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ return peer->eapSuccess;
+ case EAPOL_eapRestart:
+ return peer->eapRestart;
+ case EAPOL_eapFail:
+ return peer->eapFail;
+ case EAPOL_eapResp:
+ return peer->eapResp;
+ case EAPOL_eapNoResp:
+ return peer->eapNoResp;
+ case EAPOL_eapReq:
+ return peer->eapReq;
+ case EAPOL_portEnabled:
+ return peer->portEnabled;
+ case EAPOL_altAccept:
+ return peer->altAccept;
+ case EAPOL_altReject:
+ return peer->altReject;
+ case EAPOL_eapTriggerStart:
+ return peer->eapTriggerStart;
+ }
+ return false;
+}
+
+
+static void peer_set_bool(void *ctx, enum eapol_bool_var variable, bool value)
+{
+ struct eap_peer_ctx *peer = ctx;
+ if (peer == NULL)
+ return;
+ switch (variable) {
+ case EAPOL_eapSuccess:
+ peer->eapSuccess = value;
+ break;
+ case EAPOL_eapRestart:
+ peer->eapRestart = value;
+ break;
+ case EAPOL_eapFail:
+ peer->eapFail = value;
+ break;
+ case EAPOL_eapResp:
+ peer->eapResp = value;
+ break;
+ case EAPOL_eapNoResp:
+ peer->eapNoResp = value;
+ break;
+ case EAPOL_eapReq:
+ peer->eapReq = value;
+ break;
+ case EAPOL_portEnabled:
+ peer->portEnabled = value;
+ break;
+ case EAPOL_altAccept:
+ peer->altAccept = value;
+ break;
+ case EAPOL_altReject:
+ peer->altReject = value;
+ break;
+ case EAPOL_eapTriggerStart:
+ peer->eapTriggerStart = value;
+ break;
+ }
+}
+
+
+static unsigned int peer_get_int(void *ctx, enum eapol_int_var variable)
+{
+ struct eap_peer_ctx *peer = ctx;
+ if (peer == NULL)
+ return 0;
+ switch (variable) {
+ case EAPOL_idleWhile:
+ return peer->idleWhile;
+ }
+ return 0;
+}
+
+
+static void peer_set_int(void *ctx, enum eapol_int_var variable,
+ unsigned int value)
+{
+ struct eap_peer_ctx *peer = ctx;
+ if (peer == NULL)
+ return;
+ switch (variable) {
+ case EAPOL_idleWhile:
+ peer->idleWhile = value;
+ break;
+ }
+}
+
+
+static struct wpabuf * peer_get_eapReqData(void *ctx)
+{
+ struct eap_peer_ctx *peer = ctx;
+ if (peer == NULL || peer->eapReqData == NULL)
+ return NULL;
+
+ return peer->eapReqData;
+}
+
+
+static void peer_set_config_blob(void *ctx, struct wpa_config_blob *blob)
+{
+ printf("TODO: %s\n", __func__);
+}
+
+
+static const struct wpa_config_blob *
+peer_get_config_blob(void *ctx, const char *name)
+{
+ printf("TODO: %s\n", __func__);
+ return NULL;
+}
+
+
+static void peer_notify_pending(void *ctx)
+{
+ printf("TODO: %s\n", __func__);
+}
+
+
+static int eap_peer_register_methods(void)
+{
+ int ret = 0;
+
+#ifdef EAP_MD5
+ if (ret == 0)
+ ret = eap_peer_md5_register();
+#endif /* EAP_MD5 */
+
+#ifdef EAP_TLS
+ if (ret == 0)
+ ret = eap_peer_tls_register();
+#endif /* EAP_TLS */
+
+#ifdef EAP_MSCHAPv2
+ if (ret == 0)
+ ret = eap_peer_mschapv2_register();
+#endif /* EAP_MSCHAPv2 */
+
+#ifdef EAP_PEAP
+ if (ret == 0)
+ ret = eap_peer_peap_register();
+#endif /* EAP_PEAP */
+
+#ifdef EAP_TTLS
+ if (ret == 0)
+ ret = eap_peer_ttls_register();
+#endif /* EAP_TTLS */
+
+#ifdef EAP_GTC
+ if (ret == 0)
+ ret = eap_peer_gtc_register();
+#endif /* EAP_GTC */
+
+#ifdef EAP_OTP
+ if (ret == 0)
+ ret = eap_peer_otp_register();
+#endif /* EAP_OTP */
+
+#ifdef EAP_SIM
+ if (ret == 0)
+ ret = eap_peer_sim_register();
+#endif /* EAP_SIM */
+
+#ifdef EAP_LEAP
+ if (ret == 0)
+ ret = eap_peer_leap_register();
+#endif /* EAP_LEAP */
+
+#ifdef EAP_PSK
+ if (ret == 0)
+ ret = eap_peer_psk_register();
+#endif /* EAP_PSK */
+
+#ifdef EAP_AKA
+ if (ret == 0)
+ ret = eap_peer_aka_register();
+#endif /* EAP_AKA */
+
+#ifdef EAP_AKA_PRIME
+ if (ret == 0)
+ ret = eap_peer_aka_prime_register();
+#endif /* EAP_AKA_PRIME */
+
+#ifdef EAP_FAST
+ if (ret == 0)
+ ret = eap_peer_fast_register();
+#endif /* EAP_FAST */
+
+#ifdef EAP_PAX
+ if (ret == 0)
+ ret = eap_peer_pax_register();
+#endif /* EAP_PAX */
+
+#ifdef EAP_SAKE
+ if (ret == 0)
+ ret = eap_peer_sake_register();
+#endif /* EAP_SAKE */
+
+#ifdef EAP_GPSK
+ if (ret == 0)
+ ret = eap_peer_gpsk_register();
+#endif /* EAP_GPSK */
+
+#ifdef EAP_WSC
+ if (ret == 0)
+ ret = eap_peer_wsc_register();
+#endif /* EAP_WSC */
+
+#ifdef EAP_IKEV2
+ if (ret == 0)
+ ret = eap_peer_ikev2_register();
+#endif /* EAP_IKEV2 */
+
+#ifdef EAP_VENDOR_TEST
+ if (ret == 0)
+ ret = eap_peer_vendor_test_register();
+#endif /* EAP_VENDOR_TEST */
+
+#ifdef EAP_TNC
+ if (ret == 0)
+ ret = eap_peer_tnc_register();
+#endif /* EAP_TNC */
+
+ return ret;
+}
+
+
+static struct eapol_callbacks eap_cb;
+static struct eap_config eap_conf;
+
+int eap_example_peer_init(void)
+{
+ if (eap_peer_register_methods() < 0)
+ return -1;
+
+ os_memset(&eap_ctx, 0, sizeof(eap_ctx));
+
+ eap_ctx.eap_config.identity = (u8 *) os_strdup("user");
+ eap_ctx.eap_config.identity_len = 4;
+ eap_ctx.eap_config.password = (u8 *) os_strdup("password");
+ eap_ctx.eap_config.password_len = 8;
+ eap_ctx.eap_config.cert.ca_cert = os_strdup("ca.pem");
+ eap_ctx.eap_config.fragment_size = 1398;
+
+ os_memset(&eap_cb, 0, sizeof(eap_cb));
+ eap_cb.get_config = peer_get_config;
+ eap_cb.get_bool = peer_get_bool;
+ eap_cb.set_bool = peer_set_bool;
+ eap_cb.get_int = peer_get_int;
+ eap_cb.set_int = peer_set_int;
+ eap_cb.get_eapReqData = peer_get_eapReqData;
+ eap_cb.set_config_blob = peer_set_config_blob;
+ eap_cb.get_config_blob = peer_get_config_blob;
+ eap_cb.notify_pending = peer_notify_pending;
+
+ os_memset(&eap_conf, 0, sizeof(eap_conf));
+ eap_ctx.eap = eap_peer_sm_init(&eap_ctx, &eap_cb, &eap_ctx, &eap_conf);
+ if (eap_ctx.eap == NULL)
+ return -1;
+
+ /* Enable "port" to allow authentication */
+ eap_ctx.portEnabled = true;
+
+ return 0;
+}
+
+
+void eap_example_peer_deinit(void)
+{
+ eap_peer_sm_deinit(eap_ctx.eap);
+ eap_peer_unregister_methods();
+ wpabuf_free(eap_ctx.eapReqData);
+ os_free(eap_ctx.eap_config.identity);
+ os_free(eap_ctx.eap_config.password);
+ os_free(eap_ctx.eap_config.cert.ca_cert);
+}
+
+
+int eap_example_peer_step(void)
+{
+ int res;
+ res = eap_peer_sm_step(eap_ctx.eap);
+
+ if (eap_ctx.eapResp) {
+ struct wpabuf *resp;
+ printf("==> Response\n");
+ eap_ctx.eapResp = false;
+ resp = eap_get_eapRespData(eap_ctx.eap);
+ if (resp) {
+ /* Send EAP response to the server */
+ eap_example_server_rx(wpabuf_head(resp),
+ wpabuf_len(resp));
+ wpabuf_free(resp);
+ }
+ }
+
+ if (eap_ctx.eapSuccess) {
+ res = 0;
+ if (eap_key_available(eap_ctx.eap)) {
+ const u8 *key;
+ size_t key_len;
+ key = eap_get_eapKeyData(eap_ctx.eap, &key_len);
+ wpa_hexdump(MSG_DEBUG, "EAP keying material",
+ key, key_len);
+ }
+ }
+
+ return res;
+}
+
+
+void eap_example_peer_rx(const u8 *data, size_t data_len)
+{
+ /* Make received EAP message available to the EAP library */
+ eap_ctx.eapReq = true;
+ wpabuf_free(eap_ctx.eapReqData);
+ eap_ctx.eapReqData = wpabuf_alloc_copy(data, data_len);
+}
diff --git a/contrib/wpa/eap_example/eap_example_server.c b/contrib/wpa/eap_example/eap_example_server.c
new file mode 100644
index 000000000000..3a132bb3466e
--- /dev/null
+++ b/contrib/wpa/eap_example/eap_example_server.c
@@ -0,0 +1,300 @@
+/*
+ * Example application showing how EAP server code from hostapd can be used as
+ * a library.
+ * Copyright (c) 2007, 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 "common.h"
+#include "crypto/tls.h"
+#include "eap_server/eap.h"
+#include "wpabuf.h"
+
+void eap_example_peer_rx(const u8 *data, size_t data_len);
+
+
+struct eap_server_ctx {
+ struct eap_eapol_interface *eap_if;
+ struct eap_sm *eap;
+ void *tls_ctx;
+};
+
+static struct eap_server_ctx eap_ctx;
+
+
+static int server_get_eap_user(void *ctx, const u8 *identity,
+ size_t identity_len, int phase2,
+ struct eap_user *user)
+{
+ os_memset(user, 0, sizeof(*user));
+
+ if (!phase2) {
+ /* Only allow EAP-PEAP as the Phase 1 method */
+ user->methods[0].vendor = EAP_VENDOR_IETF;
+ user->methods[0].method = EAP_TYPE_PEAP;
+ return 0;
+ }
+
+ if (identity_len != 4 || identity == NULL ||
+ os_memcmp(identity, "user", 4) != 0) {
+ printf("Unknown user\n");
+ return -1;
+ }
+
+ /* Only allow EAP-MSCHAPv2 as the Phase 2 method */
+ user->methods[0].vendor = EAP_VENDOR_IETF;
+ user->methods[0].method = EAP_TYPE_MSCHAPV2;
+ user->password = (u8 *) os_strdup("password");
+ user->password_len = 8;
+
+ return 0;
+}
+
+
+static const char * server_get_eap_req_id_text(void *ctx, size_t *len)
+{
+ *len = 0;
+ return NULL;
+}
+
+
+static struct eapol_callbacks eap_cb;
+static struct eap_config eap_conf;
+
+static int eap_example_server_init_tls(void)
+{
+ struct tls_config tconf;
+ struct tls_connection_params tparams;
+
+ os_memset(&tconf, 0, sizeof(tconf));
+ eap_ctx.tls_ctx = tls_init(&tconf);
+ if (eap_ctx.tls_ctx == NULL)
+ return -1;
+
+ os_memset(&tparams, 0, sizeof(tparams));
+ tparams.ca_cert = "ca.pem";
+ tparams.client_cert = "server.pem";
+ /* tparams.private_key = "server.key"; */
+ tparams.private_key = "server-key.pem";
+ /* tparams.private_key_passwd = "whatever"; */
+ tparams.dh_file = "dh.conf";
+
+ if (tls_global_set_params(eap_ctx.tls_ctx, &tparams)) {
+ printf("Failed to set TLS parameters\n");
+ return -1;
+ }
+
+ if (tls_global_set_verify(eap_ctx.tls_ctx, 0, 1)) {
+ printf("Failed to set check_crl\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int eap_server_register_methods(void)
+{
+ int ret = 0;
+
+#ifdef EAP_SERVER_IDENTITY
+ if (ret == 0)
+ ret = eap_server_identity_register();
+#endif /* EAP_SERVER_IDENTITY */
+
+#ifdef EAP_SERVER_MD5
+ if (ret == 0)
+ ret = eap_server_md5_register();
+#endif /* EAP_SERVER_MD5 */
+
+#ifdef EAP_SERVER_TLS
+ if (ret == 0)
+ ret = eap_server_tls_register();
+#endif /* EAP_SERVER_TLS */
+
+#ifdef EAP_SERVER_MSCHAPV2
+ if (ret == 0)
+ ret = eap_server_mschapv2_register();
+#endif /* EAP_SERVER_MSCHAPV2 */
+
+#ifdef EAP_SERVER_PEAP
+ if (ret == 0)
+ ret = eap_server_peap_register();
+#endif /* EAP_SERVER_PEAP */
+
+#ifdef EAP_SERVER_TLV
+ if (ret == 0)
+ ret = eap_server_tlv_register();
+#endif /* EAP_SERVER_TLV */
+
+#ifdef EAP_SERVER_GTC
+ if (ret == 0)
+ ret = eap_server_gtc_register();
+#endif /* EAP_SERVER_GTC */
+
+#ifdef EAP_SERVER_TTLS
+ if (ret == 0)
+ ret = eap_server_ttls_register();
+#endif /* EAP_SERVER_TTLS */
+
+#ifdef EAP_SERVER_SIM
+ if (ret == 0)
+ ret = eap_server_sim_register();
+#endif /* EAP_SERVER_SIM */
+
+#ifdef EAP_SERVER_AKA
+ if (ret == 0)
+ ret = eap_server_aka_register();
+#endif /* EAP_SERVER_AKA */
+
+#ifdef EAP_SERVER_AKA_PRIME
+ if (ret == 0)
+ ret = eap_server_aka_prime_register();
+#endif /* EAP_SERVER_AKA_PRIME */
+
+#ifdef EAP_SERVER_PAX
+ if (ret == 0)
+ ret = eap_server_pax_register();
+#endif /* EAP_SERVER_PAX */
+
+#ifdef EAP_SERVER_PSK
+ if (ret == 0)
+ ret = eap_server_psk_register();
+#endif /* EAP_SERVER_PSK */
+
+#ifdef EAP_SERVER_SAKE
+ if (ret == 0)
+ ret = eap_server_sake_register();
+#endif /* EAP_SERVER_SAKE */
+
+#ifdef EAP_SERVER_GPSK
+ if (ret == 0)
+ ret = eap_server_gpsk_register();
+#endif /* EAP_SERVER_GPSK */
+
+#ifdef EAP_SERVER_VENDOR_TEST
+ if (ret == 0)
+ ret = eap_server_vendor_test_register();
+#endif /* EAP_SERVER_VENDOR_TEST */
+
+#ifdef EAP_SERVER_FAST
+ if (ret == 0)
+ ret = eap_server_fast_register();
+#endif /* EAP_SERVER_FAST */
+
+#ifdef EAP_SERVER_WSC
+ if (ret == 0)
+ ret = eap_server_wsc_register();
+#endif /* EAP_SERVER_WSC */
+
+#ifdef EAP_SERVER_IKEV2
+ if (ret == 0)
+ ret = eap_server_ikev2_register();
+#endif /* EAP_SERVER_IKEV2 */
+
+#ifdef EAP_SERVER_TNC
+ if (ret == 0)
+ ret = eap_server_tnc_register();
+#endif /* EAP_SERVER_TNC */
+
+ return ret;
+}
+
+
+int eap_example_server_init(void)
+{
+ struct eap_session_data eap_sess;
+
+ if (eap_server_register_methods() < 0)
+ return -1;
+
+ os_memset(&eap_ctx, 0, sizeof(eap_ctx));
+
+ if (eap_example_server_init_tls() < 0)
+ return -1;
+
+ os_memset(&eap_cb, 0, sizeof(eap_cb));
+ eap_cb.get_eap_user = server_get_eap_user;
+ eap_cb.get_eap_req_id_text = server_get_eap_req_id_text;
+
+ os_memset(&eap_conf, 0, sizeof(eap_conf));
+ eap_conf.eap_server = 1;
+ eap_conf.ssl_ctx = eap_ctx.tls_ctx;
+
+ os_memset(&eap_sess, 0, sizeof(eap_sess));
+ eap_ctx.eap = eap_server_sm_init(&eap_ctx, &eap_cb, &eap_conf,
+ &eap_sess);
+ if (eap_ctx.eap == NULL)
+ return -1;
+
+ eap_ctx.eap_if = eap_get_interface(eap_ctx.eap);
+
+ /* Enable "port" and request EAP to start authentication. */
+ eap_ctx.eap_if->portEnabled = true;
+ eap_ctx.eap_if->eapRestart = true;
+
+ return 0;
+}
+
+
+void eap_example_server_deinit(void)
+{
+ eap_server_sm_deinit(eap_ctx.eap);
+ eap_server_unregister_methods();
+ tls_deinit(eap_ctx.tls_ctx);
+}
+
+
+int eap_example_server_step(void)
+{
+ int res, process = 0;
+
+ res = eap_server_sm_step(eap_ctx.eap);
+
+ if (eap_ctx.eap_if->eapReq) {
+ printf("==> Request\n");
+ process = 1;
+ eap_ctx.eap_if->eapReq = 0;
+ }
+
+ if (eap_ctx.eap_if->eapSuccess) {
+ printf("==> Success\n");
+ process = 1;
+ res = 0;
+ eap_ctx.eap_if->eapSuccess = 0;
+
+ if (eap_ctx.eap_if->eapKeyAvailable) {
+ wpa_hexdump(MSG_DEBUG, "EAP keying material",
+ eap_ctx.eap_if->eapKeyData,
+ eap_ctx.eap_if->eapKeyDataLen);
+ }
+ }
+
+ if (eap_ctx.eap_if->eapFail) {
+ printf("==> Fail\n");
+ process = 1;
+ eap_ctx.eap_if->eapFail = 0;
+ }
+
+ if (process && eap_ctx.eap_if->eapReqData) {
+ /* Send EAP request to the peer */
+ eap_example_peer_rx(wpabuf_head(eap_ctx.eap_if->eapReqData),
+ wpabuf_len(eap_ctx.eap_if->eapReqData));
+ }
+
+ return res;
+}
+
+
+void eap_example_server_rx(const u8 *data, size_t data_len)
+{
+ /* Make received EAP message available to the EAP library */
+ wpabuf_free(eap_ctx.eap_if->eapRespData);
+ eap_ctx.eap_if->eapRespData = wpabuf_alloc_copy(data, data_len);
+ if (eap_ctx.eap_if->eapRespData)
+ eap_ctx.eap_if->eapResp = true;
+}
diff --git a/contrib/wpa/eap_example/server-key.pem b/contrib/wpa/eap_example/server-key.pem
new file mode 100644
index 000000000000..0fe2cec47330
--- /dev/null
+++ b/contrib/wpa/eap_example/server-key.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQC6oHdVIhSFVWWbZCyt7ZvdZTHJ2mBQzjjWNNzovBueMOcS41Ns
+ye1IA3mBaZjOirh3RzZFz8bg8XsecYlU9wHMIq2gQrGoNZ5gqjqYUdD/H+6+jQpj
++6I5F/JkYfZlAjJ5dOGf0YllVanDIJ6/aVaz908/qVTC2o88r/J1VPp+gQIDAQAB
+AoGAR/C5b3DOtkMgAtGPw5AXiDWNBsGOZTfJgxEnovN4Nfel64sDyqjgNeVY/kDl
+baDd0OT7j9ezU1zi1+5uQPlikFSvzgpdLgQhKkvWLzzehafb2uVaJ4VsRqS3WXK8
+RE06cYx4VQRkvQvMAXWsuua9pw36OrlpQnm3HlAbrks8Mm0CQQDgMEu2WPMWP2wj
+Q8735zbj7D0AxEFlcegPZr/QZ3qU//G0HL35FG18lsuTbDzesrf7apo3W1BBQLjS
+ZSNtyNsLAkEA1Ru6aEy/Cj2u1GYHu1u/RcshKC+W7rdVT0wDeiSTUzKafZNiwVhY
+1Epk4k5HnHB327ysTI1LiOzUMMmuNYUkIwJAKUkbmFAXLCCv5GqnYcXluOGXdl2u
+AWWRq8xrRJDZ5TihJV8pqQYXB5upj9Od/hEBir5d+hXJ2Mp3ft97P8t+cwJAGeWQ
+tXP+EySDxlPPxLjVeYnBsbx2vvOQbl5yXblsHcQcef4bFhvCT6nqsIWKtjwElLNM
+zNCuySjecD9R6DcRuQJBAJWrpgny77wP29x1WQ/29J8ZJfxe4N5wAj1SePBVNgZ3
+gfm1O+c6niNwe8RnfQimppLrrR+qK33te2SPGXiwi6g=
+-----END RSA PRIVATE KEY-----
diff --git a/contrib/wpa/eap_example/server.key b/contrib/wpa/eap_example/server.key
new file mode 100644
index 000000000000..1416327bef21
--- /dev/null
+++ b/contrib/wpa/eap_example/server.key
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALqgd1UiFIVVZZtk
+LK3tm91lMcnaYFDOONY03Oi8G54w5xLjU2zJ7UgDeYFpmM6KuHdHNkXPxuDxex5x
+iVT3AcwiraBCsag1nmCqOphR0P8f7r6NCmP7ojkX8mRh9mUCMnl04Z/RiWVVqcMg
+nr9pVrP3Tz+pVMLajzyv8nVU+n6BAgMBAAECgYBH8LlvcM62QyAC0Y/DkBeINY0G
+wY5lN8mDESei83g196XriwPKqOA15Vj+QOVtoN3Q5PuP17NTXOLX7m5A+WKQVK/O
+Cl0uBCEqS9YvPN6Fp9va5VonhWxGpLdZcrxETTpxjHhVBGS9C8wBday65r2nDfo6
+uWlCebceUBuuSzwybQJBAOAwS7ZY8xY/bCNDzvfnNuPsPQDEQWVx6A9mv9BnepT/
+8bQcvfkUbXyWy5NsPN6yt/tqmjdbUEFAuNJlI23I2wsCQQDVG7poTL8KPa7UZge7
+W79FyyEoL5but1VPTAN6JJNTMpp9k2LBWFjUSmTiTkeccHfbvKxMjUuI7NQwya41
+hSQjAkApSRuYUBcsIK/kaqdhxeW44Zd2Xa4BZZGrzGtEkNnlOKElXympBhcHm6mP
+053+EQGKvl36FcnYynd+33s/y35zAkAZ5ZC1c/4TJIPGU8/EuNV5icGxvHa+85Bu
+XnJduWwdxBx5/hsWG8JPqeqwhYq2PASUs0zM0K7JKN5wP1HoNxG5AkEAlaumCfLv
+vA/b3HVZD/b0nxkl/F7g3nACPVJ48FU2BneB+bU75zqeI3B7xGd9CKamkuutH6or
+fe17ZI8ZeLCLqA==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/eap_example/server.pem b/contrib/wpa/eap_example/server.pem
new file mode 100644
index 000000000000..93b39b9d5027
--- /dev/null
+++ b/contrib/wpa/eap_example/server.pem
@@ -0,0 +1,64 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 15624081837803162903 (0xd8d3e3a6cbe3cd17)
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: Oct 3 22:13:55 2018 GMT
+ Not After : Oct 3 22:13:55 2019 GMT
+ Subject: C=FI, O=w1.fi, CN=server.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (1024 bit)
+ Modulus:
+ 00:ba:a0:77:55:22:14:85:55:65:9b:64:2c:ad:ed:
+ 9b:dd:65:31:c9:da:60:50:ce:38:d6:34:dc:e8:bc:
+ 1b:9e:30:e7:12:e3:53:6c:c9:ed:48:03:79:81:69:
+ 98:ce:8a:b8:77:47:36:45:cf:c6:e0:f1:7b:1e:71:
+ 89:54:f7:01:cc:22:ad:a0:42:b1:a8:35:9e:60:aa:
+ 3a:98:51:d0:ff:1f:ee:be:8d:0a:63:fb:a2:39:17:
+ f2:64:61:f6:65:02:32:79:74:e1:9f:d1:89:65:55:
+ a9:c3:20:9e:bf:69:56:b3:f7:4f:3f:a9:54:c2:da:
+ 8f:3c:af:f2:75:54:fa:7e:81
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 31:4F:10:5C:67:9F:BE:4E:88:D6:DC:C5:AB:9E:12:88:86:69:02:4F
+ X509v3 Authority Key Identifier:
+ keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+ Authority Information Access:
+ OCSP - URI:http://server.w1.fi:8888/
+
+ X509v3 Subject Alternative Name:
+ DNS:server.w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ Signature Algorithm: sha256WithRSAEncryption
+ b6:98:ae:d9:9b:9a:44:49:b2:06:ee:af:36:83:cb:cd:cb:c9:
+ f3:38:6d:65:cb:e9:81:d2:25:dd:76:12:5c:da:3f:a1:0e:11:
+ a5:04:ed:05:29:2d:66:94:82:a2:80:67:d1:d8:78:71:72:5f:
+ 10:c3:51:a2:7b:f5:0b:5f:ec:70:12:99:cb:65:6f:50:7f:2b:
+ 05:7c:b4:d7:1b:21:77:66:47:33:f3:a7:d6:fb:ce:97:fe:5f:
+ fd:df:1f:1d:6f:ef:22:5a:c6:78:d2:2b:07:1e:55:ec:80:62:
+ 06:7a:be:6a:0d:4d:96:c2:d5:df:76:56:b0:85:6a:f8:a0:27:
+ 62:31
+-----BEGIN CERTIFICATE-----
+MIIClTCCAf6gAwIBAgIJANjT46bL480XMA0GCSqGSIb3DQEBCwUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xODEw
+MDMyMjEzNTVaFw0xOTEwMDMyMjEzNTVaMDQxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEVMBMGA1UEAwwMc2VydmVyLncxLmZpMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQC6oHdVIhSFVWWbZCyt7ZvdZTHJ2mBQzjjWNNzovBueMOcS41Ns
+ye1IA3mBaZjOirh3RzZFz8bg8XsecYlU9wHMIq2gQrGoNZ5gqjqYUdD/H+6+jQpj
++6I5F/JkYfZlAjJ5dOGf0YllVanDIJ6/aVaz908/qVTC2o88r/J1VPp+gQIDAQAB
+o4GzMIGwMAkGA1UdEwQCMAAwHQYDVR0OBBYEFDFPEFxnn75OiNbcxaueEoiGaQJP
+MB8GA1UdIwQYMBaAFLiS3v2KGLMww59V8zNdtMgpikEUMDUGCCsGAQUFBwEBBCkw
+JzAlBggrBgEFBQcwAYYZaHR0cDovL3NlcnZlci53MS5maTo4ODg4LzAXBgNVHREE
+EDAOggxzZXJ2ZXIudzEuZmkwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcN
+AQELBQADgYEAtpiu2ZuaREmyBu6vNoPLzcvJ8zhtZcvpgdIl3XYSXNo/oQ4RpQTt
+BSktZpSCooBn0dh4cXJfEMNRonv1C1/scBKZy2VvUH8rBXy01xshd2ZHM/On1vvO
+l/5f/d8fHW/vIlrGeNIrBx5V7IBiBnq+ag1NlsLV33ZWsIVq+KAnYjE=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/hostapd/.gitignore b/contrib/wpa/hostapd/.gitignore
new file mode 100644
index 000000000000..857f3dc4196c
--- /dev/null
+++ b/contrib/wpa/hostapd/.gitignore
@@ -0,0 +1,5 @@
+.config
+hostapd
+hostapd_cli
+hlr_auc_gw
+nt_password_hash
diff --git a/contrib/wpa/hostapd/sae_pk_gen.c b/contrib/wpa/hostapd/sae_pk_gen.c
new file mode 100644
index 000000000000..c31eff75b538
--- /dev/null
+++ b/contrib/wpa/hostapd/sae_pk_gen.c
@@ -0,0 +1,196 @@
+/*
+ * SAE-PK password/modifier generator
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * 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 "utils/base64.h"
+#include "crypto/crypto.h"
+#include "common/sae.h"
+
+
+int main(int argc, char *argv[])
+{
+ char *der = NULL;
+ size_t der_len;
+ struct crypto_ec_key *key = NULL;
+ struct wpabuf *pub = NULL;
+ u8 *data = NULL, *m;
+ size_t data_len;
+ char *b64 = NULL, *pw = NULL, *pos, *src;
+ int sec, j;
+ int ret = -1;
+ u8 hash[SAE_MAX_HASH_LEN];
+ char hash_hex[2 * SAE_MAX_HASH_LEN + 1];
+ u8 pw_base_bin[SAE_MAX_HASH_LEN];
+ u8 *dst;
+ int group;
+ size_t hash_len;
+ unsigned long long i, expected;
+ char m_hex[2 * SAE_PK_M_LEN + 1];
+ u32 sec_1b, val20;
+
+ wpa_debug_level = MSG_INFO;
+ if (os_program_init() < 0)
+ goto fail;
+
+ if (argc != 4) {
+ fprintf(stderr,
+ "usage: sae_pk_gen <DER ECPrivateKey file> <Sec:3|5> <SSID>\n");
+ goto fail;
+ }
+
+ sec = atoi(argv[2]);
+ if (sec != 3 && sec != 5) {
+ fprintf(stderr,
+ "Invalid Sec value (allowed values: 3 and 5)\n");
+ goto fail;
+ }
+ sec_1b = sec == 3;
+ expected = 1;
+ for (j = 0; j < sec; j++)
+ expected *= 256;
+
+ der = os_readfile(argv[1], &der_len);
+ if (!der) {
+ fprintf(stderr, "Could not read %s: %s\n",
+ argv[1], strerror(errno));
+ goto fail;
+ }
+
+ key = crypto_ec_key_parse_priv((u8 *) der, der_len);
+ if (!key) {
+ fprintf(stderr, "Could not parse ECPrivateKey\n");
+ goto fail;
+ }
+
+ pub = crypto_ec_key_get_subject_public_key(key);
+ if (!pub) {
+ fprintf(stderr, "Failed to build SubjectPublicKey\n");
+ goto fail;
+ }
+
+ group = crypto_ec_key_group(key);
+ switch (group) {
+ case 19:
+ hash_len = 32;
+ break;
+ case 20:
+ hash_len = 48;
+ break;
+ case 21:
+ hash_len = 64;
+ break;
+ default:
+ fprintf(stderr, "Unsupported private key group\n");
+ goto fail;
+ }
+
+ data_len = os_strlen(argv[3]) + SAE_PK_M_LEN + wpabuf_len(pub);
+ data = os_malloc(data_len);
+ if (!data) {
+ fprintf(stderr, "No memory for data buffer\n");
+ goto fail;
+ }
+ os_memcpy(data, argv[3], os_strlen(argv[3]));
+ m = data + os_strlen(argv[3]);
+ if (os_get_random(m, SAE_PK_M_LEN) < 0) {
+ fprintf(stderr, "Could not generate random Modifier M\n");
+ goto fail;
+ }
+ os_memcpy(m + SAE_PK_M_LEN, wpabuf_head(pub), wpabuf_len(pub));
+
+ fprintf(stderr, "Searching for a suitable Modifier M value\n");
+ for (i = 0;; i++) {
+ if (sae_hash(hash_len, data, data_len, hash) < 0) {
+ fprintf(stderr, "Hash failed\n");
+ goto fail;
+ }
+ if (hash[0] == 0 && hash[1] == 0) {
+ if ((hash[2] & 0xf0) == 0)
+ fprintf(stderr, "\r%3.2f%%",
+ 100.0 * (double) i / (double) expected);
+ for (j = 2; j < sec; j++) {
+ if (hash[j])
+ break;
+ }
+ if (j == sec)
+ break;
+ }
+ inc_byte_array(m, SAE_PK_M_LEN);
+ }
+
+ if (wpa_snprintf_hex(m_hex, sizeof(m_hex), m, SAE_PK_M_LEN) < 0 ||
+ wpa_snprintf_hex(hash_hex, sizeof(hash_hex), hash, hash_len) < 0)
+ goto fail;
+ fprintf(stderr, "\nFound a valid hash in %llu iterations: %s\n",
+ i + 1, hash_hex);
+
+ b64 = base64_encode(der, der_len, NULL);
+ if (!b64)
+ goto fail;
+ src = pos = b64;
+ while (*src) {
+ if (*src != '\n')
+ *pos++ = *src;
+ src++;
+ }
+ *pos = '\0';
+
+ /* Skip 8*Sec bits and add Sec_1b as the every 20th bit starting with
+ * one. */
+ os_memset(pw_base_bin, 0, sizeof(pw_base_bin));
+ dst = pw_base_bin;
+ for (j = 0; j < 8 * (int) hash_len / 20; j++) {
+ val20 = sae_pk_get_be19(hash + sec);
+ val20 |= sec_1b << 19;
+ sae_pk_buf_shift_left_19(hash + sec, hash_len - sec);
+
+ if (j & 1) {
+ *dst |= (val20 >> 16) & 0x0f;
+ dst++;
+ *dst++ = (val20 >> 8) & 0xff;
+ *dst++ = val20 & 0xff;
+ } else {
+ *dst++ = (val20 >> 12) & 0xff;
+ *dst++ = (val20 >> 4) & 0xff;
+ *dst = (val20 << 4) & 0xf0;
+ }
+ }
+ if (wpa_snprintf_hex(hash_hex, sizeof(hash_hex),
+ pw_base_bin, hash_len - sec) >= 0)
+ fprintf(stderr, "PasswordBase binary data for base32: %s",
+ hash_hex);
+
+ pw = sae_pk_base32_encode(pw_base_bin, 20 * 3 - 5);
+ if (!pw)
+ goto fail;
+
+ printf("# SAE-PK password/M/private key for Sec=%d.\n", sec);
+ printf("sae_password=%s|pk=%s:%s\n", pw, m_hex, b64);
+ printf("# Longer passwords can be used for improved security at the cost of usability:\n");
+ for (j = 4; j <= ((int) hash_len * 8 + 5 - 8 * sec) / 19; j++) {
+ os_free(pw);
+ pw = sae_pk_base32_encode(pw_base_bin, 20 * j - 5);
+ if (pw)
+ printf("# %s\n", pw);
+ }
+
+ ret = 0;
+fail:
+ os_free(der);
+ wpabuf_free(pub);
+ crypto_ec_key_deinit(key);
+ os_free(data);
+ os_free(b64);
+ os_free(pw);
+
+ os_program_deinit();
+
+ return ret;
+}
diff --git a/contrib/wpa/hs20/server/.gitignore b/contrib/wpa/hs20/server/.gitignore
new file mode 100644
index 000000000000..fecb096c128a
--- /dev/null
+++ b/contrib/wpa/hs20/server/.gitignore
@@ -0,0 +1 @@
+hs20_spp_server
diff --git a/contrib/wpa/hs20/server/Makefile b/contrib/wpa/hs20/server/Makefile
new file mode 100644
index 000000000000..0cab6d6b010a
--- /dev/null
+++ b/contrib/wpa/hs20/server/Makefile
@@ -0,0 +1,42 @@
+ALL=hs20_spp_server
+
+include ../../src/build.rules
+
+CFLAGS += -I../../src
+CFLAGS += -I../../src/utils
+CFLAGS += -I../../src/crypto
+
+LIBS += -lsqlite3
+
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+LIBS += -lrt
+
+ifndef CONFIG_NO_GITVER
+# Add VERSION_STR postfix for builds from a git repository
+ifeq ($(wildcard ../../.git),../../.git)
+GITVER := $(shell git describe --dirty=+)
+ifneq ($(GITVER),)
+CFLAGS += -DGIT_VERSION_STR_POSTFIX=\"-$(GITVER)\"
+endif
+endif
+endif
+
+OBJS=spp_server.o
+OBJS += hs20_spp_server.o
+OBJS += ../../src/utils/xml-utils.o
+OBJS += ../../src/utils/base64.o
+OBJS += ../../src/utils/common.o
+OBJS += ../../src/utils/os_unix.o
+OBJS += ../../src/utils/wpa_debug.o
+OBJS += ../../src/crypto/md5-internal.o
+CFLAGS += $(shell xml2-config --cflags)
+LIBS += $(shell xml2-config --libs)
+OBJS += ../../src/utils/xml_libxml2.o
+
+_OBJS_VAR := OBJS
+include ../../src/objs.mk
+hs20_spp_server: $(OBJS)
+ $(LDO) $(LDFLAGS) -o hs20_spp_server $(OBJS) $(LIBS)
+
+clean: common-clean
+ rm -f core *~
diff --git a/contrib/wpa/hs20/server/ca/clean.sh b/contrib/wpa/hs20/server/ca/clean.sh
new file mode 100755
index 000000000000..c72dcbda45e9
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/clean.sh
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+for i in server-client server server-revoked user ocsp; do
+ rm -f $i.csr $i.key $i.pem
+done
+
+rm -f openssl.cnf.tmp
+if [ -d demoCA ]; then
+ rm -r demoCA
+fi
+rm -f ca.pem logo.asn1 logo.der server.der ocsp-server-cache.der
+rm -f my-openssl.cnf my-openssl-root.cnf
+#rm -r rootCA
diff --git a/contrib/wpa/hs20/server/ca/est-csrattrs.cnf b/contrib/wpa/hs20/server/ca/est-csrattrs.cnf
new file mode 100644
index 000000000000..b50ea00d0b77
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/est-csrattrs.cnf
@@ -0,0 +1,17 @@
+asn1 = SEQUENCE:attrs
+
+[attrs]
+#oid1 = OID:challengePassword
+attr1 = SEQUENCE:extreq
+oid2 = OID:sha256WithRSAEncryption
+
+[extreq]
+oid = OID:extensionRequest
+vals = SET:extreqvals
+
+[extreqvals]
+
+oid1 = OID:macAddress
+#oid2 = OID:imei
+#oid3 = OID:meid
+#oid4 = OID:DevId
diff --git a/contrib/wpa/hs20/server/ca/est-csrattrs.sh b/contrib/wpa/hs20/server/ca/est-csrattrs.sh
new file mode 100755
index 000000000000..0b73a0408284
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/est-csrattrs.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+openssl asn1parse -genconf est-csrattrs.cnf -out est-csrattrs.der -oid hs20.oid
+base64 est-csrattrs.der > est-attrs.b64
diff --git a/contrib/wpa/hs20/server/ca/hs20.oid b/contrib/wpa/hs20/server/ca/hs20.oid
new file mode 100644
index 000000000000..a829ff29bf44
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/hs20.oid
@@ -0,0 +1,7 @@
+1.3.6.1.1.1.1.22 macAddress
+1.2.840.113549.1.9.14 extensionRequest
+1.3.6.1.4.1.40808.1.1.1 id-wfa-hotspot-friendlyName
+1.3.6.1.4.1.40808.1.1.2 id-kp-HS2.0Auth
+1.3.6.1.4.1.40808.1.1.3 imei
+1.3.6.1.4.1.40808.1.1.4 meid
+1.3.6.1.4.1.40808.1.1.5 DevId
diff --git a/contrib/wpa/hs20/server/ca/ocsp-req.sh b/contrib/wpa/hs20/server/ca/ocsp-req.sh
new file mode 100755
index 000000000000..931a20696d02
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-req.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+for i in *.pem; do
+ echo "===[ $i ]==================="
+ openssl ocsp -text -CAfile ca.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+
+# openssl ocsp -text -CAfile rootCA/cacert.pem -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+
+# openssl ocsp -text -CAfile rootCA/cacert.pem -verify_other demoCA/cacert.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+# openssl ocsp -text -CAfile rootCA/cacert.pem -VAfile ca.pem -trust_other -issuer demoCA/cacert.pem -cert $i -url http://localhost:8888/
+done
diff --git a/contrib/wpa/hs20/server/ca/ocsp-responder-ica.sh b/contrib/wpa/hs20/server/ca/ocsp-responder-ica.sh
new file mode 100755
index 000000000000..116c6e1c3d01
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-responder-ica.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner demoCA/cacert.pem -rkey demoCA/private/cakey-plain.pem -CA demoCA/cacert.pem -resp_no_certs -text
diff --git a/contrib/wpa/hs20/server/ca/ocsp-responder.sh b/contrib/wpa/hs20/server/ca/ocsp-responder.sh
new file mode 100755
index 000000000000..620947d01af0
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-responder.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+openssl ocsp -index demoCA/index.txt -port 8888 -nmin 5 -rsigner ocsp.pem -rkey ocsp.key -CA demoCA/cacert.pem -text -ignore_err
diff --git a/contrib/wpa/hs20/server/ca/ocsp-update-cache.sh b/contrib/wpa/hs20/server/ca/ocsp-update-cache.sh
new file mode 100755
index 000000000000..f2b23250cadd
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/ocsp-update-cache.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# NOTE: You may need to replace 'localhost' with your OCSP server hostname.
+openssl ocsp \
+ -no_nonce \
+ -CAfile ca.pem \
+ -verify_other demoCA/cacert.pem \
+ -issuer demoCA/cacert.pem \
+ -cert server.pem \
+ -url http://localhost:8888/ \
+ -respout ocsp-server-cache.der
diff --git a/contrib/wpa/hs20/server/ca/openssl-root.cnf b/contrib/wpa/hs20/server/ca/openssl-root.cnf
new file mode 100644
index 000000000000..5bc50be1dbc9
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/openssl-root.cnf
@@ -0,0 +1,125 @@
+# OpenSSL configuration file for Hotspot 2.0 PKI (Root CA)
+
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+oid_section = new_oids
+
+[ new_oids ]
+
+#logotypeoid=1.3.6.1.5.5.7.1.12
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./rootCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several certificates with same subject
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = usr_cert # The extentions to add to the cert
+
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = default # use public key default MD
+preserve = no # keep passed DN ordering
+
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = match
+stateOrProvinceName = optional
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+input_password = @PASSWORD@
+output_password = @PASSWORD@
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = US
+countryName_min = 2
+countryName_max = 2
+
+localityName = Locality Name (eg, city)
+localityName_default = Tuusula
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = WFA Hotspot 2.0
+
+##organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+#@OU@
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+[ req_attributes ]
+
+[ v3_req ]
+
+# Extensions to add to a certificate request
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName=DNS:example.com,DNS:another.example.com
+
+[ v3_ca ]
+
+# Hotspot 2.0 PKI requirements
+subjectKeyIdentifier=hash
+basicConstraints = critical,CA:true
+keyUsage = critical, cRLSign, keyCertSign
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
diff --git a/contrib/wpa/hs20/server/ca/openssl.cnf b/contrib/wpa/hs20/server/ca/openssl.cnf
new file mode 100644
index 000000000000..61410138340f
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/openssl.cnf
@@ -0,0 +1,200 @@
+# OpenSSL configuration file for Hotspot 2.0 PKI (Intermediate CA)
+
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+oid_section = new_oids
+
+[ new_oids ]
+
+#logotypeoid=1.3.6.1.5.5.7.1.12
+
+####################################################################
+[ ca ]
+default_ca = CA_default # The default ca section
+
+####################################################################
+[ CA_default ]
+
+dir = ./demoCA # Where everything is kept
+certs = $dir/certs # Where the issued certs are kept
+crl_dir = $dir/crl # Where the issued crl are kept
+database = $dir/index.txt # database index file.
+#unique_subject = no # Set to 'no' to allow creation of
+ # several certificates with same subject
+new_certs_dir = $dir/newcerts # default place for new certs.
+
+certificate = $dir/cacert.pem # The CA certificate
+serial = $dir/serial # The current serial number
+crlnumber = $dir/crlnumber # the current crl number
+ # must be commented out to leave a V1 CRL
+crl = $dir/crl.pem # The current CRL
+private_key = $dir/private/cakey.pem# The private key
+RANDFILE = $dir/private/.rand # private random number file
+
+x509_extensions = ext_client # The extentions to add to the cert
+
+name_opt = ca_default # Subject Name options
+cert_opt = ca_default # Certificate field options
+
+# Extension copying option: use with caution.
+copy_extensions = copy
+
+default_days = 365 # how long to certify for
+default_crl_days= 30 # how long before next CRL
+default_md = default # use public key default MD
+preserve = no # keep passed DN ordering
+
+policy = policy_match
+
+# For the CA policy
+[ policy_match ]
+countryName = supplied
+stateOrProvinceName = optional
+organizationName = supplied
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_osu_server ]
+countryName = match
+stateOrProvinceName = optional
+organizationName = match
+organizationalUnitName = supplied
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+####################################################################
+[ req ]
+default_bits = 2048
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca # The extentions to add to the self signed cert
+
+input_password = @PASSWORD@
+output_password = @PASSWORD@
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = FI
+countryName_min = 2
+countryName_max = 2
+
+localityName = Locality Name (eg, city)
+localityName_default = Tuusula
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = @DOMAIN@
+
+##organizationalUnitName = Organizational Unit Name (eg, section)
+#organizationalUnitName_default =
+#@OU@
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+[ req_attributes ]
+
+[ v3_ca ]
+
+# Hotspot 2.0 PKI requirements
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, cRLSign, keyCertSign
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
+# For SP intermediate CA
+#subjectAltName=critical,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:engExample OSU
+#nameConstraints=permitted;DNS:.@DOMAIN@
+#1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
+
+[ v3_osu_server ]
+
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, keyEncipherment
+#@ALTNAME@
+
+#logotypeoid=ASN1:SEQUENCE:LogotypeExtn
+1.3.6.1.5.5.7.1.12=ASN1:SEQUENCE:LogotypeExtn
+[LogotypeExtn]
+communityLogos=EXP:0,SEQUENCE:LogotypeInfo
+[LogotypeInfo]
+# note: implicit tag converted to explicit for CHOICE
+direct=EXP:0,SEQUENCE:LogotypeData
+[LogotypeData]
+image=SEQUENCE:LogotypeImage
+[LogotypeImage]
+imageDetails=SEQUENCE:LogotypeDetails
+imageInfo=SEQUENCE:LogotypeImageInfo
+[LogotypeDetails]
+mediaType=IA5STRING:image/png
+logotypeHash=SEQUENCE:HashAlgAndValues
+logotypeURI=SEQUENCE:URI
+[HashAlgAndValues]
+value1=SEQUENCE:HashAlgAndValueSHA256
+#value2=SEQUENCE:HashAlgAndValueSHA1
+[HashAlgAndValueSHA256]
+hashAlg=SEQUENCE:sha256_alg
+hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH256@
+[HashAlgAndValueSHA1]
+hashAlg=SEQUENCE:sha1_alg
+hashValue=FORMAT:HEX,OCTETSTRING:@LOGO_HASH1@
+[sha256_alg]
+algorithm=OID:sha256
+[sha1_alg]
+algorithm=OID:sha1
+[URI]
+uri=IA5STRING:@LOGO_URI@
+[LogotypeImageInfo]
+# default value color(1), component optional
+#type=IMP:0,INTEGER:1
+fileSize=INTEGER:7549
+xSize=INTEGER:128
+ySize=INTEGER:80
+language=IMP:4,IA5STRING:zxx
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
+
+[ ext_client ]
+
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
+#@ALTNAME@
+extendedKeyUsage = clientAuth
+
+[ ext_server ]
+
+# Hotspot 2.0 PKI requirements
+basicConstraints=critical, CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:@OCSP_URI@
+#@ALTNAME@
+extendedKeyUsage = critical, serverAuth
+keyUsage = critical, keyEncipherment
diff --git a/contrib/wpa/hs20/server/ca/setup.sh b/contrib/wpa/hs20/server/ca/setup.sh
new file mode 100755
index 000000000000..78abcccff455
--- /dev/null
+++ b/contrib/wpa/hs20/server/ca/setup.sh
@@ -0,0 +1,209 @@
+#!/bin/sh
+
+if [ -z "$OPENSSL" ]; then
+ OPENSSL=openssl
+fi
+export OPENSSL_CONF=$PWD/openssl.cnf
+PASS=whatever
+if [ -z "$DOMAIN" ]; then
+ DOMAIN=w1.fi
+fi
+COMPANY=w1.fi
+OPER_ENG="engw1.fi TESTING USE"
+OPER_FI="finw1.fi TESTIKÄYTTÖ"
+CNR="Hotspot 2.0 Trust Root CA - 99"
+CNO="ocsp.$DOMAIN"
+CNV="osu-revoked.$DOMAIN"
+CNOC="osu-client.$DOMAIN"
+OSU_SERVER_HOSTNAME="osu.$DOMAIN"
+DEBUG=0
+OCSP_URI="http://$CNO:8888/"
+LOGO_URI="http://osu.w1.fi/w1fi_logo.png"
+LOGO_HASH256="4532f7ec36424381617c03c6ce87b55a51d6e7177ffafda243cebf280a68954d"
+LOGO_HASH1="5e1d5085676eede6b02da14d31c523ec20ffba0b"
+
+# Command line overrides
+USAGE=$( cat <<EOF
+Usage:\n
+# -c: Company name, used to generate Subject name CN for Intermediate CA\n
+# -C: Subject name CN of the Root CA ($CNR)\n
+# -D: Enable debugging (set -x, etc)\n
+# -g: Logo sha1 hash ($LOGO_HASH1)\n
+# -G: Logo sha256 hash ($LOGO_HASH256)\n
+# -h: Show this help message\n
+# -l: Logo URI ($LOGO_URI)\n
+# -m: Domain ($DOMAIN)\n
+# -o: Subject name CN for OSU-Client Server ($CNOC)\n
+# -O: Subject name CN for OCSP Server ($CNO)\n
+# -p: passphrase for private keys ($PASS)\n
+# -r: Operator-english ($OPER_ENG)\n
+# -R: Operator-finish ($OPER_FI)\n
+# -S: OSU Server name ($OSU_SERVER_HOSTNAME)\n
+# -u: OCSP-URI ($OCSP_URI)\n
+# -V: Subject name CN for OSU-Revoked Server ($CNV)\n
+EOF
+)
+
+while getopts "c:C:Dg:G:l:m:o:O:p:r:R:S:u:V:h" flag
+ do
+ case $flag in
+ c) COMPANY=$OPTARG;;
+ C) CNR=$OPTARG;;
+ D) DEBUG=1;;
+ g) LOGO_HASH1=$OPTARG;;
+ G) LOGO_HASH256=$OPTARG;;
+ h) echo -e $USAGE; exit 0;;
+ l) LOGO_URI=$OPTARG;;
+ m) DOMAIN=$OPTARG;;
+ o) CNOC=$OPTARG;;
+ O) CNO=$OPTARG;;
+ p) PASS=$OPTARG;;
+ r) OPER_ENG=$OPTARG;;
+ R) OPER_FI=$OPTARG;;
+ S) OSU_SERVER_HOSTNAME=$OPTARG;;
+ u) OCSP_URI=$OPTARG;;
+ V) CNV=$OPTARG;;
+ *) echo "Unknown flag: $flag"; echo -e $USAGE; exit 1;;
+ esac
+done
+
+fail()
+{
+ echo "$*"
+ exit 1
+}
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+if [ $DEBUG = 1 ]
+then
+ set -x
+fi
+
+# Set the passphrase and some other common config accordingly.
+cat openssl-root.cnf | sed "s/@PASSWORD@/$PASS/" \
+ > my-openssl-root.cnf
+
+cat openssl.cnf | sed "s/@PASSWORD@/$PASS/" |
+sed "s,@OCSP_URI@,$OCSP_URI," |
+sed "s,@LOGO_URI@,$LOGO_URI," |
+sed "s,@LOGO_HASH1@,$LOGO_HASH1," |
+sed "s,@LOGO_HASH256@,$LOGO_HASH256," |
+sed "s/@DOMAIN@/$DOMAIN/" \
+ > my-openssl.cnf
+
+
+cat my-openssl-root.cnf | sed "s/#@CN@/commonName_default = $CNR/" > openssl.cnf.tmp
+mkdir -p rootCA/certs rootCA/crl rootCA/newcerts rootCA/private
+touch rootCA/index.txt
+if [ -e rootCA/private/cakey.pem ]; then
+ echo " * Use existing Root CA"
+else
+ echo " * Generate Root CA private key"
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:4096 -keyout rootCA/private/cakey.pem -out rootCA/careq.pem || fail "Failed to generate Root CA private key"
+ echo " * Sign Root CA certificate"
+ $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out rootCA/cacert.pem -days 10957 -batch -keyfile rootCA/private/cakey.pem -passin pass:$PASS -selfsign -extensions v3_ca -outdir rootCA/newcerts -infiles rootCA/careq.pem || fail "Failed to sign Root CA certificate"
+ $OPENSSL x509 -in rootCA/cacert.pem -out rootCA/cacert.der -outform DER || fail "Failed to create rootCA DER"
+ sha256sum rootCA/cacert.der > rootCA/cacert.fingerprint || fail "Failed to create rootCA fingerprint"
+fi
+if [ ! -e rootCA/crlnumber ]; then
+ echo 00 > rootCA/crlnumber
+fi
+
+echo
+echo "---[ Intermediate CA ]--------------------------------------------------"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $COMPANY Hotspot 2.0 Intermediate CA/" > openssl.cnf.tmp
+mkdir -p demoCA/certs demoCA/crl demoCA/newcerts demoCA/private
+touch demoCA/index.txt
+if [ -e demoCA/private/cakey.pem ]; then
+ echo " * Use existing Intermediate CA"
+else
+ echo " * Generate Intermediate CA private key"
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -keyout demoCA/private/cakey.pem -out demoCA/careq.pem || fail "Failed to generate Intermediate CA private key"
+ echo " * Sign Intermediate CA certificate"
+ $OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out demoCA/cacert.pem -days 3652 -batch -keyfile rootCA/private/cakey.pem -cert rootCA/cacert.pem -passin pass:$PASS -extensions v3_ca -infiles demoCA/careq.pem || fail "Failed to sign Intermediate CA certificate"
+ # horrible from security view point, but for testing purposes since OCSP responder does not seem to support -passin
+ openssl rsa -in demoCA/private/cakey.pem -out demoCA/private/cakey-plain.pem -passin pass:$PASS
+ $OPENSSL x509 -in demoCA/cacert.pem -out demoCA/cacert.der -outform DER || fail "Failed to create demoCA DER."
+ sha256sum demoCA/cacert.der > demoCA/cacert.fingerprint || fail "Failed to create demoCA fingerprint"
+fi
+if [ ! -e demoCA/crlnumber ]; then
+ echo 00 > demoCA/crlnumber
+fi
+
+echo
+echo "OCSP responder"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNO/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out ocsp.csr -keyout ocsp.key -extensions v3_OCSP
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -keyfile demoCA/private/cakey.pem -passin pass:$PASS -in ocsp.csr -out ocsp.pem -days 730 -extensions v3_OCSP || fail "Could not generate ocsp.pem"
+
+echo
+echo "---[ Server - to be revoked ] ------------------------------------------"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNV/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-revoked.csr -keyout server-revoked.key
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-revoked.csr -out server-revoked.pem -key $PASS -days 730 -extensions ext_server
+$OPENSSL ca -revoke server-revoked.pem -key $PASS
+
+echo
+echo "---[ Server - with client ext key use ] ---------------------------------"
+echo "---[ Only used for negative-testing for OSU-client implementation ] -----"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = $CNOC/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out server-client.csr -keyout server-client.key || fail "Could not create server-client.key"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server-client.csr -out server-client.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create server-client.pem"
+
+echo
+echo "---[ User ]-------------------------------------------------------------"
+echo
+
+cat my-openssl.cnf | sed "s/#@CN@/commonName_default = User/" > openssl.cnf.tmp
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -out user.csr -keyout user.key || fail "Could not create user.key"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in user.csr -out user.pem -key $PASS -days 730 -extensions ext_client || fail "Could not create user.pem"
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+ALT="DNS:$OSU_SERVER_HOSTNAME"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_ENG"
+ALT="$ALT,otherName:1.3.6.1.4.1.40808.1.1.1;UTF8String:$OPER_FI"
+
+cat my-openssl.cnf |
+ sed "s/#@CN@/commonName_default = $OSU_SERVER_HOSTNAME/" |
+ sed "s/^##organizationalUnitName/organizationalUnitName/" |
+ sed "s/#@OU@/organizationalUnitName_default = Hotspot 2.0 Online Sign Up Server/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,$ALT/" \
+ > openssl.cnf.tmp
+echo $OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server
+$OPENSSL req -config $PWD/openssl.cnf.tmp -batch -sha256 -new -newkey rsa:2048 -nodes -out server.csr -keyout server.key -reqexts v3_osu_server || fail "Failed to generate server request"
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -md sha256 -in server.csr -out server.pem -key $PASS -days 730 -extensions ext_server -policy policy_osu_server || fail "Failed to sign server certificate"
+
+#dump logotype details for debugging
+$OPENSSL x509 -in server.pem -out server.der -outform DER
+openssl asn1parse -in server.der -inform DER | grep HEX | tail -1 | sed 's/.*://' | xxd -r -p > logo.der
+openssl asn1parse -in logo.der -inform DER > logo.asn1
+
+
+echo
+echo "---[ CRL ]---------------------------------------------------------------"
+echo
+
+$OPENSSL ca -config $PWD/my-openssl.cnf -gencrl -md sha256 -out demoCA/crl/crl.pem -passin pass:$PASS
+
+echo
+echo "---[ Verify ]------------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile rootCA/cacert.pem demoCA/cacert.pem
+$OPENSSL verify -CAfile rootCA/cacert.pem -untrusted demoCA/cacert.pem *.pem
+
+cat rootCA/cacert.pem demoCA/cacert.pem > ca.pem
diff --git a/contrib/wpa/hs20/server/ca/w1fi_logo.png b/contrib/wpa/hs20/server/ca/w1fi_logo.png
new file mode 100644
index 000000000000..ac7c259fff2e
Binary files /dev/null and b/contrib/wpa/hs20/server/ca/w1fi_logo.png differ
diff --git a/contrib/wpa/hs20/server/hs20-osu-server.txt b/contrib/wpa/hs20/server/hs20-osu-server.txt
new file mode 100644
index 000000000000..22478ad9d2cb
--- /dev/null
+++ b/contrib/wpa/hs20/server/hs20-osu-server.txt
@@ -0,0 +1,262 @@
+Hotspot 2.0 OSU server
+======================
+
+The information in this document is based on the assumption that Ubuntu
+16.04 server (64-bit) distribution is used and the web server is
+Apache2. Neither of these are requirements for the installation, but if
+other combinations are used, the package names and configuration
+parameters may need to be adjusted.
+
+NOTE: This implementation and the example configuration here is meant
+only for testing purposes in a lab environment. This design is not
+secure to be installed in a publicly available Internet server without
+considerable amount of modification and review for security issues.
+
+
+Build dependencies
+------------------
+
+Ubuntu 16.04 server
+- default installation
+- upgraded to latest package versions
+ sudo apt-get update
+ sudo apt-get upgrade
+
+Packages needed for running the service:
+ sudo apt-get install sqlite3
+ sudo apt-get install apache2
+ sudo apt-get install php-sqlite3 php-xml libapache2-mod-php
+
+Additional packages needed for building the components:
+ sudo apt-get install build-essential
+ sudo apt-get install libsqlite3-dev
+ sudo apt-get install libssl-dev
+ sudo apt-get install libxml2-dev
+
+
+Installation location
+---------------------
+
+Select a location for the installation root directory. The example here
+assumes /home/user/hs20-server to be used, but this can be changed by
+editing couple of files as indicated below.
+
+sudo mkdir -p /home/user/hs20-server
+sudo chown $USER /home/user/hs20-server
+mkdir -p /home/user/hs20-server/spp
+mkdir -p /home/user/hs20-server/AS
+
+
+Build
+-----
+
+# hostapd as RADIUS server
+cd hostapd
+
+#example build configuration
+cat > .config <<EOF
+CONFIG_DRIVER_NONE=y
+CONFIG_PKCS12=y
+CONFIG_RADIUS_SERVER=y
+CONFIG_EAP=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_AKA_PRIME=y
+CONFIG_SQLITE=y
+CONFIG_HS20=y
+EOF
+
+make hostapd hlr_auc_gw
+cp hostapd hlr_auc_gw /home/user/hs20-server/AS
+
+# build hs20_spp_server
+cd ../hs20/server
+make clean
+make
+cp hs20_spp_server /home/user/hs20-server/spp
+# prepare database (web server user/group needs to have write access)
+mkdir -p /home/user/hs20-server/AS/DB
+sudo chgrp www-data /home/user/hs20-server/AS/DB
+sudo chmod g+w /home/user/hs20-server/AS/DB
+sqlite3 /home/user/hs20-server/AS/DB/eap_user.db < sql.txt
+sudo chgrp www-data /home/user/hs20-server/AS/DB/eap_user.db
+sudo chmod g+w /home/user/hs20-server/AS/DB/eap_user.db
+# add example configuration (note: need to update URLs to match the system)
+sqlite3 /home/user/hs20-server/AS/DB/eap_user.db < sql-example.txt
+
+# copy PHP scripts
+# Modify config.php if different installation directory is used.
+# Modify PHP scripts to get the desired behavior for user interaction (or use
+# the examples as-is for initial testing).
+cp -r www /home/user/hs20-server
+
+# Create /home/user/hs20-server/terms-and-conditions file (HTML segment to be
+# inserted within the BODY section of the page).
+cat > /home/user/hs20-server/terms-and-conditions <<EOF
+<P>Terms and conditions..</P>
+EOF
+
+# Build local keys and certs
+cd ca
+# Display help options.
+./setup.sh -h
+
+# Remove old keys, fill in appropriate values, and generate your keys.
+# For instance:
+./clean.sh
+rm -fr rootCA"
+old_hostname=myserver.local
+./setup.sh -C "Hotspot 2.0 Trust Root CA - CT" \
+ -o $old_hostname-osu-client \
+ -O $old_hostname-oscp -p lanforge -S $old_hostname \
+ -V $old_hostname-osu-revoked \
+ -m local -u http://$old_hostname:8888/
+
+# Configure subscription policies
+mkdir -p /home/user/hs20-server/spp/policy
+cat > /home/user/hs20-server/spp/policy/default.xml <<EOF
+<Policy>
+ <PolicyUpdate>
+ <UpdateInterval>30</UpdateInterval>
+ <UpdateMethod>ClientInitiated</UpdateMethod>
+ <Restriction>Unrestricted</Restriction>
+ <URI>https://policy-server.osu.example.com/hs20/spp.php</URI>
+ </PolicyUpdate>
+</Policy>
+EOF
+
+
+# Install Hotspot 2.0 SPP and OMA DM XML schema/DTD files
+
+# XML schema for SPP
+# Copy the latest XML schema into /home/user/hs20-server/spp/spp.xsd
+
+# OMA DM Device Description Framework DTD
+# Copy into /home/user/hs20-server/spp/dm_ddf-v1_2.dtd
+# http://www.openmobilealliance.org/tech/DTD/dm_ddf-v1_2.dtd
+
+
+# Configure RADIUS authentication service
+# Note: Change the URL to match the setup
+# Note: Install AAA server key/certificate and root CA in Key directory
+
+cat > /home/user/hs20-server/AS/as-sql.conf <<EOF
+driver=none
+radius_server_clients=as.radius_clients
+eap_server=1
+eap_user_file=sqlite:DB/eap_user.db
+ca_cert=Key/ca.pem
+server_cert=Key/server.pem
+private_key=Key/server.key
+private_key_passwd=passphrase
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=eap_sim.db
+subscr_remediation_url=https://subscription-server.osu.example.com/hs20/spp.php
+EOF
+
+# Set RADIUS passphrase for the APs
+# Note: Modify to match the setup
+cat > /home/user/hs20-server/AS/as.radius_clients <<EOF
+0.0.0.0/0 radius
+EOF
+
+
+Start RADIUS authentication server
+----------------------------------
+
+cd /home/user/hs20-server/AS
+./hostapd -B as-sql.conf
+
+
+OSEN RADIUS server configuration notes
+
+The OSEN RADIUS server config file should have the 'ocsp_stapling_response'
+configuration in it. For example:
+
+# hostapd-radius config for the radius used by the OSEN AP
+interface=eth0#0
+driver=none
+logger_syslog=-1
+logger_syslog_level=2
+logger_stdout=-1
+logger_stdout_level=2
+ctrl_interface=/var/run/hostapd
+ctrl_interface_group=0
+eap_server=1
+eap_user_file=/home/user/hs20-server/AS/hostapd-osen.eap_user
+server_id=ben-ota-2-osen
+radius_server_auth_port=1811
+radius_server_clients=/home/user/hs20-server/AS/hostap.radius_clients
+
+ca_cert=/home/user/hs20-server/ca/ca.pem
+server_cert=/home/user/hs20-server/ca/server.pem
+private_key=/home/user/hs20-server/ca/server.key
+private_key_passwd=whatever
+
+ocsp_stapling_response=/home/user/hs20-server/ca/ocsp-server-cache.der
+
+The /home/user/hs20-server/AS/hostapd-osen.eap_user file should look
+similar to this, and should coorelate with the osu_nai entry in
+the non-OSEN VAP config file. For instance:
+
+# cat hostapd-osen.eap_user
+# For OSEN authentication (Hotspot 2.0 Release 2)
+"osen@w1.fi" WFA-UNAUTH-TLS
+
+
+# Run OCSP server:
+cd /home/user/hs20-server/ca
+./ocsp-responder.sh&
+
+# Update cache (This should be run periodically)
+./ocsp-update-cache.sh
+
+
+Configure web server
+--------------------
+
+Edit /etc/apache2/sites-available/default-ssl
+
+Add following block just before "SSL Engine Switch" line":
+
+ Alias /hs20/ "/home/user/hs20-server/www/"
+ <Directory "/home/user/hs20-server/www/">
+ Options Indexes MultiViews FollowSymLinks
+ AllowOverride None
+ Require all granted
+ SSLOptions +StdEnvVars
+ </Directory>
+
+Update SSL configuration to use the OSU server certificate/key.
+They keys and certs are called 'server.key' and 'server.pem' from
+ca/setup.sh.
+
+To support subscription remediation using client certificates, set
+"SSLVerifyClient optional" and configure the trust root CA(s) for the
+client certificates with SSLCACertificateFile.
+
+Enable default-ssl site and restart Apache2:
+ sudo a2ensite default-ssl
+ sudo a2enmod ssl
+ sudo service apache2 restart
+
+
+Management UI
+-------------
+
+The sample PHP scripts include a management UI for testing
+purposes. That is available at https://<server>/hs20/users.php
+
+
+AP configuration
+----------------
+
+APs can now be configured to use the OSU server as the RADIUS
+authentication server. In addition, the OSU Provider List ANQP element
+should be configured to use the SPP (SOAP+XML) option and with the
+following Server URL:
+https://<server>/hs20/spp.php/signup?realm=example.com
diff --git a/contrib/wpa/hs20/server/hs20_spp_server.c b/contrib/wpa/hs20/server/hs20_spp_server.c
new file mode 100644
index 000000000000..347c40a73d6a
--- /dev/null
+++ b/contrib/wpa/hs20/server/hs20_spp_server.c
@@ -0,0 +1,207 @@
+/*
+ * Hotspot 2.0 SPP server - standalone version
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <sqlite3.h>
+
+#include "common.h"
+#include "common/version.h"
+#include "xml-utils.h"
+#include "spp_server.h"
+
+
+static void write_timestamp(FILE *f)
+{
+ time_t t;
+ struct tm *tm;
+
+ time(&t);
+ tm = localtime(&t);
+
+ fprintf(f, "%04u-%02u-%02u %02u:%02u:%02u ",
+ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+}
+
+
+void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (ctx->debug_log == NULL)
+ return;
+
+ write_timestamp(ctx->debug_log);
+ va_start(ap, fmt);
+ vfprintf(ctx->debug_log, fmt, ap);
+ va_end(ap);
+
+ fprintf(ctx->debug_log, "\n");
+}
+
+
+void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node)
+{
+ char *str;
+
+ if (ctx->debug_log == NULL)
+ return;
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+
+ write_timestamp(ctx->debug_log);
+ fprintf(ctx->debug_log, "%s: '%s'\n", title, str);
+ os_free(str);
+}
+
+
+static int process(struct hs20_svc *ctx)
+{
+ int dmacc = 0;
+ xml_node_t *soap, *spp, *resp;
+ char *user, *realm, *post, *str;
+
+ ctx->addr = getenv("HS20ADDR");
+ if (ctx->addr)
+ debug_print(ctx, 1, "Connection from %s", ctx->addr);
+ ctx->test = getenv("HS20TEST");
+ if (ctx->test)
+ debug_print(ctx, 1, "Requested test functionality: %s",
+ ctx->test);
+
+ user = getenv("HS20USER");
+ if (user && strlen(user) == 0)
+ user = NULL;
+ realm = getenv("HS20REALM");
+ if (realm == NULL) {
+ debug_print(ctx, 1, "HS20REALM not set");
+ return -1;
+ }
+ post = getenv("HS20POST");
+ if (post == NULL) {
+ debug_print(ctx, 1, "HS20POST not set");
+ return -1;
+ }
+
+ ctx->imsi = getenv("HS20IMSI");
+ if (ctx->imsi)
+ debug_print(ctx, 1, "IMSI %s", ctx->imsi);
+
+ ctx->eap_method = getenv("HS20EAPMETHOD");
+ if (ctx->eap_method)
+ debug_print(ctx, 1, "EAP method %s", ctx->eap_method);
+
+ ctx->id_hash = getenv("HS20IDHASH");
+ if (ctx->id_hash)
+ debug_print(ctx, 1, "ID-HASH %s", ctx->id_hash);
+
+ soap = xml_node_from_buf(ctx->xml, post);
+ if (soap == NULL) {
+ debug_print(ctx, 1, "Could not parse SOAP data");
+ return -1;
+ }
+ debug_dump_node(ctx, "Received SOAP message", soap);
+ spp = soap_get_body(ctx->xml, soap);
+ if (spp == NULL) {
+ debug_print(ctx, 1, "Could not get SPP message");
+ xml_node_free(ctx->xml, soap);
+ return -1;
+ }
+ debug_dump_node(ctx, "Received SPP message", spp);
+
+ resp = hs20_spp_server_process(ctx, spp, user, realm, dmacc);
+ xml_node_free(ctx->xml, soap);
+ if (resp == NULL && user == NULL) {
+ debug_print(ctx, 1, "Request HTTP authentication");
+ return 2; /* Request authentication */
+ }
+ if (resp == NULL) {
+ debug_print(ctx, 1, "No response");
+ return -1;
+ }
+
+ soap = soap_build_envelope(ctx->xml, resp);
+ if (soap == NULL) {
+ debug_print(ctx, 1, "SOAP envelope building failed");
+ return -1;
+ }
+ str = xml_node_to_str(ctx->xml, soap);
+ xml_node_free(ctx->xml, soap);
+ if (str == NULL) {
+ debug_print(ctx, 1, "Could not get node string");
+ return -1;
+ }
+ printf("%s", str);
+ free(str);
+
+ return 0;
+}
+
+
+static void usage(void)
+{
+ printf("usage:\n"
+ "hs20_spp_server -r<root directory> [-f<debug log>]\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct hs20_svc ctx;
+ int ret;
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ for (;;) {
+ int c = getopt(argc, argv, "f:r:v");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'f':
+ if (ctx.debug_log)
+ break;
+ ctx.debug_log = fopen(optarg, "a");
+ if (ctx.debug_log == NULL) {
+ printf("Could not write to %s\n", optarg);
+ return -1;
+ }
+ break;
+ case 'r':
+ ctx.root_dir = optarg;
+ break;
+ case 'v':
+ printf("hs20_spp_server v%s\n", VERSION_STR);
+ return 0;
+ default:
+ usage();
+ return -1;
+ }
+ }
+ if (ctx.root_dir == NULL) {
+ usage();
+ return -1;
+ }
+ ctx.xml = xml_node_init_ctx(&ctx, NULL);
+ if (ctx.xml == NULL)
+ return -1;
+ if (hs20_spp_server_init(&ctx) < 0) {
+ xml_node_deinit_ctx(ctx.xml);
+ return -1;
+ }
+
+ ret = process(&ctx);
+ debug_print(&ctx, 1, "process() --> %d", ret);
+
+ xml_node_deinit_ctx(ctx.xml);
+ hs20_spp_server_deinit(&ctx);
+ if (ctx.debug_log)
+ fclose(ctx.debug_log);
+
+ return ret;
+}
diff --git a/contrib/wpa/hs20/server/spp_server.c b/contrib/wpa/hs20/server/spp_server.c
new file mode 100644
index 000000000000..a50e9074f7b4
--- /dev/null
+++ b/contrib/wpa/hs20/server/spp_server.c
@@ -0,0 +1,2933 @@
+/*
+ * Hotspot 2.0 SPP server
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <time.h>
+#include <errno.h>
+#include <sqlite3.h>
+
+#include "common.h"
+#include "base64.h"
+#include "md5_i.h"
+#include "xml-utils.h"
+#include "spp_server.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_OMA_DM_DMACC "urn:oma:mo:oma-dm-dmacc:1.0"
+#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
+
+
+/* TODO: timeout to expire sessions */
+
+enum hs20_session_operation {
+ NO_OPERATION,
+ UPDATE_PASSWORD,
+ CONTINUE_SUBSCRIPTION_REMEDIATION,
+ CONTINUE_POLICY_UPDATE,
+ USER_REMEDIATION,
+ SUBSCRIPTION_REGISTRATION,
+ POLICY_REMEDIATION,
+ POLICY_UPDATE,
+ FREE_REMEDIATION,
+ CLEAR_REMEDIATION,
+ CERT_REENROLL,
+};
+
+
+static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *field);
+static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
+ const char *field);
+static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
+ const char *realm, int use_dmacc);
+static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *user,
+ const char *realm,
+ int add_est_user);
+
+
+static int db_add_session(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *pw,
+ const char *redirect_uri,
+ enum hs20_session_operation operation,
+ const u8 *mac_addr)
+{
+ char *sql;
+ int ret = 0;
+ char addr[20];
+
+ if (mac_addr)
+ snprintf(addr, sizeof(addr), MACSTR, MAC2STR(mac_addr));
+ else
+ addr[0] = '\0';
+ sql = sqlite3_mprintf("INSERT INTO sessions(timestamp,id,user,realm,"
+ "operation,password,redirect_uri,mac_addr,test) "
+ "VALUES "
+ "(strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
+ "%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)",
+ sessionid, user ? user : "", realm ? realm : "",
+ operation, pw ? pw : "",
+ redirect_uri ? redirect_uri : "",
+ addr, ctx->test);
+ if (sql == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session entry into sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ ret = -1;
+ }
+ sqlite3_free(sql);
+ return ret;
+}
+
+
+static void db_update_session_password(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *sessionid,
+ const char *pw)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET password=%Q WHERE id=%Q AND "
+ "user=%Q AND realm=%Q",
+ pw, sessionid, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update session password: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_update_session_machine_managed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *sessionid,
+ const int pw_mm)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET machine_managed=%Q WHERE id=%Q AND user=%Q AND realm=%Q",
+ pw_mm ? "1" : "0", sessionid, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to update session machine_managed: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_pps(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET pps=%Q WHERE id=%Q AND "
+ "user=%Q AND realm=%Q",
+ str, sessionid, user, realm);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session pps: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_devinfo(struct hs20_svc *ctx, const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET devinfo=%Q WHERE id=%Q",
+ str, sessionid);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session devinfo: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_devdetail(struct hs20_svc *ctx,
+ const char *sessionid,
+ xml_node_t *node)
+{
+ char *str;
+ char *sql;
+
+ str = xml_node_to_str(ctx->xml, node);
+ if (str == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE sessions SET devdetail=%Q WHERE id=%Q",
+ str, sessionid);
+ free(str);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session devdetail: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_dmacc(struct hs20_svc *ctx, const char *sessionid,
+ const char *username, const char *password)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET osu_user=%Q, osu_password=%Q WHERE id=%Q",
+ username, password, sessionid);
+ if (!sql)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session DMAcc: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_eap_method(struct hs20_svc *ctx,
+ const char *sessionid,
+ const char *method)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET eap_method=%Q WHERE id=%Q",
+ method, sessionid);
+ if (!sql)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session EAP method: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_add_session_id_hash(struct hs20_svc *ctx, const char *sessionid,
+ const char *id_hash)
+{
+ char *sql;
+
+ sql = sqlite3_mprintf("UPDATE sessions SET mobile_identifier_hash=%Q WHERE id=%Q",
+ id_hash, sessionid);
+ if (!sql)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add session ID hash: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_remove_session(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid)
+{
+ char *sql;
+
+ if (user == NULL || realm == NULL) {
+ sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
+ "id=%Q", sessionid);
+ } else {
+ sql = sqlite3_mprintf("DELETE FROM sessions WHERE "
+ "user=%Q AND realm=%Q AND id=%Q",
+ user, realm, sessionid);
+ }
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to delete session entry from "
+ "sqlite database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void hs20_eventlog(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *notes,
+ const char *dump)
+{
+ char *sql;
+ char *user_buf = NULL, *realm_buf = NULL;
+
+ debug_print(ctx, 1, "eventlog: %s", notes);
+
+ if (user == NULL) {
+ user_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
+ "user");
+ user = user_buf;
+ realm_buf = db_get_session_val(ctx, NULL, NULL, sessionid,
+ "realm");
+ realm = realm_buf;
+ }
+
+ sql = sqlite3_mprintf("INSERT INTO eventlog"
+ "(user,realm,sessionid,timestamp,notes,dump,addr)"
+ " VALUES (%Q,%Q,%Q,"
+ "strftime('%%Y-%%m-%%d %%H:%%M:%%f','now'),"
+ "%Q,%Q,%Q)",
+ user, realm, sessionid, notes,
+ dump ? dump : "", ctx->addr ? ctx->addr : "");
+ free(user_buf);
+ free(realm_buf);
+ if (sql == NULL)
+ return;
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add eventlog entry into sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void hs20_eventlog_node(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *sessionid, const char *notes,
+ xml_node_t *node)
+{
+ char *str;
+
+ if (node)
+ str = xml_node_to_str(ctx->xml, node);
+ else
+ str = NULL;
+ hs20_eventlog(ctx, user, realm, sessionid, notes, str);
+ free(str);
+}
+
+
+static void db_update_mo_str(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *name,
+ const char *str)
+{
+ char *sql;
+ if (user == NULL || realm == NULL || name == NULL)
+ return;
+ sql = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ name, str, user, realm);
+ if (sql == NULL)
+ return;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update user MO entry in sqlite "
+ "database: %s", sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+}
+
+
+static void db_update_mo(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *name, xml_node_t *mo)
+{
+ char *str;
+
+ str = xml_node_to_str(ctx->xml, mo);
+ if (str == NULL)
+ return;
+
+ db_update_mo_str(ctx, user, realm, name, str);
+ free(str);
+}
+
+
+static void add_text_node(struct hs20_svc *ctx, xml_node_t *parent,
+ const char *name, const char *value)
+{
+ xml_node_create_text(ctx->xml, parent, NULL, name, value ? value : "");
+}
+
+
+static void add_text_node_conf(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *parent, const char *name,
+ const char *field)
+{
+ char *val;
+ val = db_get_osu_config_val(ctx, realm, field);
+ xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
+ os_free(val);
+}
+
+
+static void add_text_node_conf_corrupt(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *parent, const char *name,
+ const char *field)
+{
+ char *val;
+
+ val = db_get_osu_config_val(ctx, realm, field);
+ if (val) {
+ size_t len;
+
+ len = os_strlen(val);
+ if (len > 0) {
+ if (val[len - 1] == '0')
+ val[len - 1] = '1';
+ else
+ val[len - 1] = '0';
+ }
+ }
+ xml_node_create_text(ctx->xml, parent, NULL, name, val ? val : "");
+ os_free(val);
+}
+
+
+static int new_password(char *buf, int buflen)
+{
+ int i;
+
+ if (buflen < 1)
+ return -1;
+ buf[buflen - 1] = '\0';
+ if (os_get_random((unsigned char *) buf, buflen - 1) < 0)
+ return -1;
+
+ for (i = 0; i < buflen - 1; i++) {
+ unsigned char val = buf[i];
+ val %= 2 * 26 + 10;
+ if (val < 26)
+ buf[i] = 'a' + val;
+ else if (val < 2 * 26)
+ buf[i] = 'A' + val - 26;
+ else
+ buf[i] = '0' + val - 2 * 26;
+ }
+
+ return 0;
+}
+
+
+struct get_db_field_data {
+ const char *field;
+ char *value;
+};
+
+
+static int get_db_field(void *ctx, int argc, char *argv[], char *col[])
+{
+ struct get_db_field_data *data = ctx;
+ int i;
+
+ for (i = 0; i < argc; i++) {
+ if (os_strcmp(col[i], data->field) == 0 && argv[i]) {
+ os_free(data->value);
+ data->value = os_strdup(argv[i]);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+
+static char * db_get_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *field, int dmacc)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ cmd = sqlite3_mprintf("SELECT %s FROM users WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ field, dmacc ? "osu_user" : "identity",
+ user, realm);
+ if (cmd == NULL)
+ return NULL;
+ memset(&data, 0, sizeof(data));
+ data.field = field;
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "Could not find user '%s'", user);
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: user='%s' realm='%s' field='%s' dmacc=%d --> "
+ "value='%s'", user, realm, field, dmacc, data.value);
+
+ return data.value;
+}
+
+
+static int db_update_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *field,
+ const char *val, int dmacc)
+{
+ char *cmd;
+ int ret;
+
+ cmd = sqlite3_mprintf("UPDATE users SET %s=%Q WHERE %s=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ field, val, dmacc ? "osu_user" : "identity", user,
+ realm);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to update user in sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ ret = -1;
+ } else {
+ debug_print(ctx, 1,
+ "DB: user='%s' realm='%s' field='%s' set to '%s'",
+ user, realm, field, val);
+ ret = 0;
+ }
+ sqlite3_free(cmd);
+
+ return ret;
+}
+
+
+static char * db_get_session_val(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *field)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ if (user == NULL || realm == NULL) {
+ cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
+ "id=%Q", field, session_id);
+ } else {
+ cmd = sqlite3_mprintf("SELECT %s FROM sessions WHERE "
+ "user=%Q AND realm=%Q AND id=%Q",
+ field, user, realm, session_id);
+ }
+ if (cmd == NULL)
+ return NULL;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ memset(&data, 0, sizeof(data));
+ data.field = field;
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "DB: Could not find session %s: %s",
+ session_id, sqlite3_errmsg(ctx->db));
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: return '%s'", data.value);
+ return data.value;
+}
+
+
+static int update_password(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *pw, int dmacc)
+{
+ char *cmd;
+
+ cmd = sqlite3_mprintf("UPDATE users SET password=%Q, "
+ "remediation='' "
+ "WHERE %s=%Q AND phase2=1",
+ pw, dmacc ? "osu_user" : "identity",
+ user);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update database for user '%s'",
+ user);
+ }
+ sqlite3_free(cmd);
+
+ return 0;
+}
+
+
+static int clear_remediation(struct hs20_svc *ctx, const char *user,
+ const char *realm, int dmacc)
+{
+ char *cmd;
+
+ cmd = sqlite3_mprintf("UPDATE users SET remediation='' WHERE %s=%Q",
+ dmacc ? "osu_user" : "identity",
+ user);
+ if (cmd == NULL)
+ return -1;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ if (sqlite3_exec(ctx->db, cmd, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update database for user '%s'",
+ user);
+ }
+ sqlite3_free(cmd);
+
+ return 0;
+}
+
+
+static int add_eap_ttls(struct hs20_svc *ctx, xml_node_t *parent)
+{
+ xml_node_t *node;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "EAPMethod");
+ if (node == NULL)
+ return -1;
+
+ add_text_node(ctx, node, "EAPType", "21");
+ add_text_node(ctx, node, "InnerMethod", "MS-CHAP-V2");
+
+ return 0;
+}
+
+
+static xml_node_t * build_username_password(struct hs20_svc *ctx,
+ xml_node_t *parent,
+ const char *user, const char *pw)
+{
+ xml_node_t *node;
+ char *b64;
+ size_t len;
+
+ node = xml_node_create(ctx->xml, parent, NULL, "UsernamePassword");
+ if (node == NULL)
+ return NULL;
+
+ add_text_node(ctx, node, "Username", user);
+
+ b64 = base64_encode(pw, strlen(pw), NULL);
+ if (b64 == NULL)
+ return NULL;
+ len = os_strlen(b64);
+ if (len > 0 && b64[len - 1] == '\n')
+ b64[len - 1] = '\0';
+ add_text_node(ctx, node, "Password", b64);
+ free(b64);
+
+ return node;
+}
+
+
+static int add_username_password(struct hs20_svc *ctx, xml_node_t *cred,
+ const char *user, const char *pw,
+ int machine_managed)
+{
+ xml_node_t *node;
+
+ node = build_username_password(ctx, cred, user, pw);
+ if (node == NULL)
+ return -1;
+
+ add_text_node(ctx, node, "MachineManaged",
+ machine_managed ? "TRUE" : "FALSE");
+ add_text_node(ctx, node, "SoftTokenApp", "");
+ add_eap_ttls(ctx, node);
+
+ return 0;
+}
+
+
+static void add_creation_date(struct hs20_svc *ctx, xml_node_t *cred)
+{
+ char str[30];
+ time_t now;
+ struct tm tm;
+
+ time(&now);
+ gmtime_r(&now, &tm);
+ snprintf(str, sizeof(str), "%04u-%02u-%02uT%02u:%02u:%02uZ",
+ tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
+ tm.tm_hour, tm.tm_min, tm.tm_sec);
+ xml_node_create_text(ctx->xml, cred, NULL, "CreationDate", str);
+}
+
+
+static xml_node_t * build_credential_pw(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *pw, int machine_managed)
+{
+ xml_node_t *cred;
+
+ cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
+ if (cred == NULL) {
+ debug_print(ctx, 1, "Failed to create Credential node");
+ return NULL;
+ }
+ add_creation_date(ctx, cred);
+ if (add_username_password(ctx, cred, user, pw, machine_managed) < 0) {
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return cred;
+}
+
+
+static xml_node_t * build_credential(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ char *new_pw, size_t new_pw_len)
+{
+ if (new_password(new_pw, new_pw_len) < 0)
+ return NULL;
+ debug_print(ctx, 1, "Update password to '%s'", new_pw);
+ return build_credential_pw(ctx, user, realm, new_pw, 1);
+}
+
+
+static xml_node_t * build_credential_cert(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *cert_fingerprint)
+{
+ xml_node_t *cred, *cert;
+
+ cred = xml_node_create_root(ctx->xml, NULL, NULL, NULL, "Credential");
+ if (cred == NULL) {
+ debug_print(ctx, 1, "Failed to create Credential node");
+ return NULL;
+ }
+ add_creation_date(ctx, cred);
+ cert = xml_node_create(ctx->xml, cred, NULL, "DigitalCertificate");
+ add_text_node(ctx, cert, "CertificateType", "x509v3");
+ add_text_node(ctx, cert, "CertSHA256Fingerprint", cert_fingerprint);
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return cred;
+}
+
+
+static xml_node_t * build_post_dev_data_response(struct hs20_svc *ctx,
+ xml_namespace_t **ret_ns,
+ const char *session_id,
+ const char *status,
+ const char *error_code)
+{
+ xml_node_t *spp_node = NULL;
+ xml_namespace_t *ns;
+
+ spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+ "sppPostDevDataResponse");
+ 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, ns, "sessionID", session_id);
+ xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", status);
+
+ if (error_code) {
+ xml_node_t *node;
+ 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 add_update_node(struct hs20_svc *ctx, xml_node_t *spp_node,
+ xml_namespace_t *ns, const char *uri,
+ xml_node_t *upd_node)
+{
+ xml_node_t *node, *tnds;
+ char *str;
+
+ tnds = mo_to_tnds(ctx->xml, upd_node, 0, NULL, NULL);
+ if (!tnds)
+ return -1;
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (str == NULL)
+ return -1;
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "updateNode", str);
+ free(str);
+
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", uri);
+
+ return 0;
+}
+
+
+static xml_node_t * read_subrem_file(struct hs20_svc *ctx,
+ const char *subrem_id,
+ char *uri, size_t uri_size)
+{
+ char fname[200];
+ char *buf, *buf2, *pos;
+ size_t len;
+ xml_node_t *node;
+
+ os_snprintf(fname, sizeof(fname), "%s/spp/subrem/%s",
+ ctx->root_dir, subrem_id);
+ debug_print(ctx, 1, "Use subrem file %s", fname);
+
+ buf = os_readfile(fname, &len);
+ if (!buf)
+ return NULL;
+ buf2 = os_realloc(buf, len + 1);
+ if (!buf2) {
+ os_free(buf);
+ return NULL;
+ }
+ buf = buf2;
+ buf[len] = '\0';
+
+ pos = os_strchr(buf, '\n');
+ if (!pos) {
+ os_free(buf);
+ return NULL;
+ }
+ *pos++ = '\0';
+ os_strlcpy(uri, buf, uri_size);
+
+ node = xml_node_from_buf(ctx->xml, pos);
+ os_free(buf);
+
+ return node;
+}
+
+
+static xml_node_t * build_sub_rem_resp(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id,
+ int machine_rem, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *cred;
+ char buf[400];
+ char new_pw[33];
+ char *status;
+ char *cert;
+
+ cert = db_get_val(ctx, user, realm, "cert", dmacc);
+ if (cert && cert[0] == '\0') {
+ os_free(cert);
+ cert = NULL;
+ }
+ if (cert) {
+ char *subrem;
+
+ /* No change needed in PPS MO unless specifically asked to */
+ cred = NULL;
+ buf[0] = '\0';
+
+ subrem = db_get_val(ctx, user, realm, "subrem", dmacc);
+ if (subrem && subrem[0]) {
+ cred = read_subrem_file(ctx, subrem, buf, sizeof(buf));
+ if (!cred) {
+ debug_print(ctx, 1,
+ "Could not create updateNode from subrem file");
+ os_free(subrem);
+ os_free(cert);
+ return NULL;
+ }
+ }
+ os_free(subrem);
+ } else {
+ char *real_user = NULL;
+ char *pw;
+
+ if (dmacc) {
+ real_user = db_get_val(ctx, user, realm, "identity",
+ dmacc);
+ if (!real_user) {
+ debug_print(ctx, 1,
+ "Could not find user identity for dmacc user '%s'",
+ user);
+ return NULL;
+ }
+ }
+
+ pw = db_get_session_val(ctx, user, realm, session_id,
+ "password");
+ if (pw && pw[0]) {
+ debug_print(ctx, 1, "New password from the user: '%s'",
+ pw);
+ snprintf(new_pw, sizeof(new_pw), "%s", pw);
+ free(pw);
+ cred = build_credential_pw(ctx,
+ real_user ? real_user : user,
+ realm, new_pw, 0);
+ } else {
+ cred = build_credential(ctx,
+ real_user ? real_user : user,
+ realm, new_pw, sizeof(new_pw));
+ }
+
+ free(real_user);
+ if (!cred) {
+ debug_print(ctx, 1, "Could not build credential");
+ os_free(cert);
+ return NULL;
+ }
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
+ realm);
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL) {
+ debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
+ os_free(cert);
+ return NULL;
+ }
+
+ if ((cred && add_update_node(ctx, spp_node, ns, buf, cred) < 0) ||
+ (!cred && !xml_node_create(ctx->xml, spp_node, ns, "noMOUpdate"))) {
+ debug_print(ctx, 1, "Could not add update node");
+ xml_node_free(ctx->xml, spp_node);
+ os_free(cert);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ machine_rem ? "machine remediation" :
+ "user remediation", cred);
+ xml_node_free(ctx->xml, cred);
+
+ if (cert) {
+ debug_print(ctx, 1, "Request DB remediation clearing on success notification (certificate credential)");
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ CLEAR_REMEDIATION, NULL);
+ } else {
+ debug_print(ctx, 1, "Request DB password update on success "
+ "notification");
+ db_add_session(ctx, user, realm, session_id, new_pw, NULL,
+ UPDATE_PASSWORD, NULL);
+ }
+ os_free(cert);
+
+ return spp_node;
+}
+
+
+static xml_node_t * machine_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id, int dmacc)
+{
+ return build_sub_rem_resp(ctx, user, realm, session_id, 1, dmacc);
+}
+
+
+static xml_node_t * cert_reenroll(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id)
+{
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ CERT_REENROLL, NULL);
+ return spp_exec_get_certificate(ctx, session_id, user, realm, 0);
+}
+
+
+static xml_node_t * policy_remediation(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *policy;
+ char buf[400];
+ const char *status;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires policy remediation", NULL);
+
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ POLICY_REMEDIATION, NULL);
+
+ policy = build_policy(ctx, user, realm, dmacc);
+ if (!policy) {
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "No update available at this time", NULL);
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "policy update (sub rem)", policy);
+ xml_node_free(ctx->xml, policy);
+
+ return spp_node;
+}
+
+
+static xml_node_t * browser_remediation(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *redirect_uri,
+ const char *uri)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *exec_node;
+
+ if (redirect_uri == NULL) {
+ debug_print(ctx, 1, "Missing redirectURI attribute for user "
+ "remediation");
+ return NULL;
+ }
+ debug_print(ctx, 1, "redirectURI %s", redirect_uri);
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+ xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
+ uri);
+ return spp_node;
+}
+
+
+static xml_node_t * user_remediation(struct hs20_svc *ctx, const char *user,
+ const char *realm, const char *session_id,
+ const char *redirect_uri)
+{
+ char uri[300], *val;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires user remediation", NULL);
+ val = db_get_osu_config_val(ctx, realm, "remediation_url");
+ if (val == NULL)
+ return NULL;
+
+ db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
+ USER_REMEDIATION, NULL);
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ return browser_remediation(ctx, session_id, redirect_uri, uri);
+}
+
+
+static xml_node_t * free_remediation(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id,
+ const char *redirect_uri)
+{
+ char uri[300], *val;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "requires free/public account remediation", NULL);
+ val = db_get_osu_config_val(ctx, realm, "free_remediation_url");
+ if (val == NULL)
+ return NULL;
+
+ db_add_session(ctx, user, realm, session_id, NULL, redirect_uri,
+ FREE_REMEDIATION, NULL);
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ return browser_remediation(ctx, session_id, redirect_uri, uri);
+}
+
+
+static xml_node_t * no_sub_rem(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id)
+{
+ const char *status;
+
+ hs20_eventlog(ctx, user, realm, session_id,
+ "no subscription mediation available", NULL);
+
+ status = "No update available at this time";
+ return build_post_dev_data_response(ctx, NULL, session_id, status,
+ NULL);
+}
+
+
+static xml_node_t * hs20_subscription_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc,
+ const char *redirect_uri)
+{
+ char *type, *identity;
+ xml_node_t *ret;
+ char *free_account;
+
+ identity = db_get_val(ctx, user, realm, "identity", dmacc);
+ if (identity == NULL || strlen(identity) == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "user not found in database for remediation",
+ NULL);
+ os_free(identity);
+ return build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred",
+ "Not found");
+ }
+ os_free(identity);
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ if (free_account && strcmp(free_account, user) == 0) {
+ free(free_account);
+ return no_sub_rem(ctx, user, realm, session_id);
+ }
+ free(free_account);
+
+ type = db_get_val(ctx, user, realm, "remediation", dmacc);
+ if (type && strcmp(type, "free") != 0) {
+ char *val;
+ int shared = 0;
+ val = db_get_val(ctx, user, realm, "shared", dmacc);
+ if (val)
+ shared = atoi(val);
+ free(val);
+ if (shared) {
+ free(type);
+ return no_sub_rem(ctx, user, realm, session_id);
+ }
+ }
+ if (type && strcmp(type, "user") == 0)
+ ret = user_remediation(ctx, user, realm, session_id,
+ redirect_uri);
+ else if (type && strcmp(type, "free") == 0)
+ ret = free_remediation(ctx, user, realm, session_id,
+ redirect_uri);
+ else if (type && strcmp(type, "policy") == 0)
+ ret = policy_remediation(ctx, user, realm, session_id, dmacc);
+ else if (type && strcmp(type, "machine") == 0)
+ ret = machine_remediation(ctx, user, realm, session_id, dmacc);
+ else if (type && strcmp(type, "reenroll") == 0)
+ ret = cert_reenroll(ctx, user, realm, session_id);
+ else
+ ret = no_sub_rem(ctx, user, realm, session_id);
+ free(type);
+
+ return ret;
+}
+
+
+static xml_node_t * read_policy_file(struct hs20_svc *ctx,
+ const char *policy_id)
+{
+ char fname[200];
+
+ snprintf(fname, sizeof(fname), "%s/spp/policy/%s.xml",
+ ctx->root_dir, policy_id);
+ debug_print(ctx, 1, "Use policy file %s", fname);
+
+ return node_from_file(ctx->xml, fname);
+}
+
+
+static void update_policy_update_uri(struct hs20_svc *ctx, const char *realm,
+ xml_node_t *policy)
+{
+ xml_node_t *node;
+ char *url;
+
+ node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate/URI");
+ if (!node)
+ return;
+
+ url = db_get_osu_config_val(ctx, realm, "policy_url");
+ if (!url)
+ return;
+ xml_node_set_text(ctx->xml, node, url);
+ free(url);
+}
+
+
+static xml_node_t * build_policy(struct hs20_svc *ctx, const char *user,
+ const char *realm, int use_dmacc)
+{
+ char *policy_id;
+ xml_node_t *policy, *node;
+
+ policy_id = db_get_val(ctx, user, realm, "policy", use_dmacc);
+ if (policy_id == NULL || strlen(policy_id) == 0) {
+ free(policy_id);
+ policy_id = strdup("default");
+ if (policy_id == NULL)
+ return NULL;
+ }
+ policy = read_policy_file(ctx, policy_id);
+ free(policy_id);
+ if (policy == NULL)
+ return NULL;
+
+ update_policy_update_uri(ctx, realm, policy);
+
+ node = get_node_uri(ctx->xml, policy, "Policy/PolicyUpdate");
+ if (node && use_dmacc) {
+ char *pw;
+ pw = db_get_val(ctx, user, realm, "osu_password", use_dmacc);
+ if (pw == NULL ||
+ build_username_password(ctx, node, user, pw) == NULL) {
+ debug_print(ctx, 1, "Failed to add Policy/PolicyUpdate/"
+ "UsernamePassword");
+ free(pw);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+ free(pw);
+ }
+
+ return policy;
+}
+
+
+static xml_node_t * hs20_policy_update(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *session_id, int dmacc)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node;
+ xml_node_t *policy;
+ char buf[400];
+ const char *status;
+ char *identity;
+
+ identity = db_get_val(ctx, user, realm, "identity", dmacc);
+ if (identity == NULL || strlen(identity) == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "user not found in database for policy update",
+ NULL);
+ os_free(identity);
+ return build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred",
+ "Not found");
+ }
+ os_free(identity);
+
+ policy = build_policy(ctx, user, realm, dmacc);
+ if (!policy) {
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "No update available at this time", NULL);
+ }
+
+ db_add_session(ctx, user, realm, session_id, NULL, NULL, POLICY_UPDATE,
+ NULL);
+
+ status = "Update complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Policy",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, policy) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ xml_node_free(ctx->xml, policy);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id, "policy update",
+ policy);
+ xml_node_free(ctx->xml, policy);
+
+ return spp_node;
+}
+
+
+static xml_node_t * spp_get_mo(struct hs20_svc *ctx, xml_node_t *node,
+ const char *urn, int *valid, char **ret_err)
+{
+ xml_node_t *child, *tnds, *mo;
+ const char *name;
+ char *mo_urn;
+ char *str;
+ char fname[200];
+
+ *valid = -1;
+ if (ret_err)
+ *ret_err = NULL;
+
+ xml_node_for_each_child(ctx->xml, child, node) {
+ xml_node_for_each_check(ctx->xml, child);
+ name = xml_node_get_localname(ctx->xml, child);
+ if (strcmp(name, "moContainer") != 0)
+ continue;
+ mo_urn = xml_node_get_attr_value_ns(ctx->xml, child, SPP_NS_URI,
+ "moURN");
+ if (strcasecmp(urn, mo_urn) == 0) {
+ xml_node_get_attr_value_free(ctx->xml, mo_urn);
+ break;
+ }
+ xml_node_get_attr_value_free(ctx->xml, mo_urn);
+ }
+
+ if (child == NULL)
+ return NULL;
+
+ debug_print(ctx, 1, "moContainer text for %s", urn);
+ debug_dump_node(ctx, "moContainer", child);
+
+ str = xml_node_get_text(ctx->xml, child);
+ debug_print(ctx, 1, "moContainer payload: '%s'", str);
+ tnds = xml_node_from_buf(ctx->xml, str);
+ xml_node_get_text_free(ctx->xml, str);
+ if (tnds == NULL) {
+ debug_print(ctx, 1, "could not parse moContainer text");
+ return NULL;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/spp/dm_ddf-v1_2.dtd", ctx->root_dir);
+ if (xml_validate_dtd(ctx->xml, tnds, fname, ret_err) == 0)
+ *valid = 1;
+ else if (ret_err && *ret_err &&
+ os_strcmp(*ret_err, "No declaration for attribute xmlns of element MgmtTree\n") == 0) {
+ free(*ret_err);
+ debug_print(ctx, 1, "Ignore OMA-DM DDF DTD validation error for MgmtTree namespace declaration with xmlns attribute");
+ *ret_err = NULL;
+ *valid = 1;
+ } else
+ *valid = 0;
+
+ mo = tnds_to_mo(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (mo == NULL) {
+ debug_print(ctx, 1, "invalid moContainer for %s", urn);
+ }
+
+ return mo;
+}
+
+
+static xml_node_t * spp_exec_upload_mo(struct hs20_svc *ctx,
+ const char *session_id, const char *urn)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node, *exec_node;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ node = xml_node_create(ctx->xml, exec_node, ns, "uploadMO");
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", urn);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_subscription_registration(struct hs20_svc *ctx,
+ const char *realm,
+ const char *session_id,
+ const char *redirect_uri,
+ const u8 *mac_addr)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *exec_node;
+ char uri[300], *val;
+
+ if (db_add_session(ctx, NULL, realm, session_id, NULL, redirect_uri,
+ SUBSCRIPTION_REGISTRATION, mac_addr) < 0)
+ return NULL;
+ val = db_get_osu_config_val(ctx, realm, "signup_url");
+ if (val == NULL)
+ return NULL;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ snprintf(uri, sizeof(uri), "%s%s", val, session_id);
+ os_free(val);
+ xml_node_create_text(ctx->xml, exec_node, ns, "launchBrowserToURI",
+ uri);
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ return build_sub_rem_resp(ctx, user, realm, session_id, 0, dmacc);
+}
+
+
+static char * db_get_osu_config_val(struct hs20_svc *ctx, const char *realm,
+ const char *field)
+{
+ char *cmd;
+ struct get_db_field_data data;
+
+ cmd = sqlite3_mprintf("SELECT value FROM osu_config WHERE realm=%Q AND "
+ "field=%Q", realm, field);
+ if (cmd == NULL)
+ return NULL;
+ debug_print(ctx, 1, "DB: %s", cmd);
+ memset(&data, 0, sizeof(data));
+ data.field = "value";
+ if (sqlite3_exec(ctx->db, cmd, get_db_field, &data, NULL) != SQLITE_OK)
+ {
+ debug_print(ctx, 1, "DB: Could not find osu_config %s: %s",
+ realm, sqlite3_errmsg(ctx->db));
+ sqlite3_free(cmd);
+ return NULL;
+ }
+ sqlite3_free(cmd);
+
+ debug_print(ctx, 1, "DB: return '%s'", data.value);
+ return data.value;
+}
+
+
+static xml_node_t * build_pps(struct hs20_svc *ctx,
+ const char *user, const char *realm,
+ const char *pw, const char *cert,
+ int machine_managed, const char *test,
+ const char *imsi, const char *dmacc_username,
+ const char *dmacc_password,
+ xml_node_t *policy_node)
+{
+ xml_node_t *pps, *c, *trust, *aaa, *aaa1, *upd, *homesp, *p;
+ xml_node_t *cred, *eap, *userpw;
+
+ pps = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "PerProviderSubscription");
+ if (!pps) {
+ xml_node_free(ctx->xml, policy_node);
+ return NULL;
+ }
+
+ add_text_node(ctx, pps, "UpdateIdentifier", "1");
+
+ c = xml_node_create(ctx->xml, pps, NULL, "Cred01");
+
+ add_text_node(ctx, c, "CredentialPriority", "1");
+
+ if (imsi)
+ goto skip_aaa_trust_root;
+ aaa = xml_node_create(ctx->xml, c, NULL, "AAAServerTrustRoot");
+ aaa1 = xml_node_create(ctx->xml, aaa, NULL, "AAA1");
+ add_text_node_conf(ctx, realm, aaa1, "CertURL",
+ "aaa_trust_root_cert_url");
+ if (test && os_strcmp(test, "corrupt_aaa_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/AAAServerTrustRoot/Root*/CertSHA256FingerPrint");
+ add_text_node_conf_corrupt(ctx, realm, aaa1,
+ "CertSHA256Fingerprint",
+ "aaa_trust_root_cert_fingerprint");
+ } else {
+ add_text_node_conf(ctx, realm, aaa1, "CertSHA256Fingerprint",
+ "aaa_trust_root_cert_fingerprint");
+ }
+
+ if (test && os_strcmp(test, "corrupt_polupd_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/Policy/PolicyUpdate/Trustroot/CertSHA256FingerPrint");
+ p = xml_node_create(ctx->xml, c, NULL, "Policy");
+ upd = xml_node_create(ctx->xml, p, NULL, "PolicyUpdate");
+ add_text_node(ctx, upd, "UpdateInterval", "30");
+ add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
+ add_text_node(ctx, upd, "Restriction", "Unrestricted");
+ add_text_node_conf(ctx, realm, upd, "URI", "policy_url");
+ trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
+ add_text_node_conf(ctx, realm, trust, "CertURL",
+ "policy_trust_root_cert_url");
+ add_text_node_conf_corrupt(ctx, realm, trust,
+ "CertSHA256Fingerprint",
+ "policy_trust_root_cert_fingerprint");
+ }
+skip_aaa_trust_root:
+
+ upd = xml_node_create(ctx->xml, c, NULL, "SubscriptionUpdate");
+ add_text_node(ctx, upd, "UpdateInterval", "4294967295");
+ add_text_node(ctx, upd, "UpdateMethod", "SPP-ClientInitiated");
+ add_text_node(ctx, upd, "Restriction", "HomeSP");
+ add_text_node_conf(ctx, realm, upd, "URI", "spp_http_auth_url");
+ trust = xml_node_create(ctx->xml, upd, NULL, "TrustRoot");
+ add_text_node_conf(ctx, realm, trust, "CertURL", "trust_root_cert_url");
+ if (test && os_strcmp(test, "corrupt_subrem_hash") == 0) {
+ debug_print(ctx, 1,
+ "TEST: Corrupt PPS/Cred*/SubscriptionUpdate/Trustroot/CertSHA256FingerPrint");
+ add_text_node_conf_corrupt(ctx, realm, trust,
+ "CertSHA256Fingerprint",
+ "trust_root_cert_fingerprint");
+ } else {
+ add_text_node_conf(ctx, realm, trust, "CertSHA256Fingerprint",
+ "trust_root_cert_fingerprint");
+ }
+
+ if (dmacc_username &&
+ !build_username_password(ctx, upd, dmacc_username,
+ dmacc_password)) {
+ xml_node_free(ctx->xml, pps);
+ xml_node_free(ctx->xml, policy_node);
+ return NULL;
+ }
+
+ if (policy_node)
+ xml_node_add_child(ctx->xml, c, policy_node);
+
+ homesp = xml_node_create(ctx->xml, c, NULL, "HomeSP");
+ add_text_node_conf(ctx, realm, homesp, "FriendlyName", "friendly_name");
+ add_text_node_conf(ctx, realm, homesp, "FQDN", "fqdn");
+
+ xml_node_create(ctx->xml, c, NULL, "SubscriptionParameters");
+
+ cred = xml_node_create(ctx->xml, c, NULL, "Credential");
+ add_creation_date(ctx, cred);
+ if (imsi) {
+ xml_node_t *sim;
+ const char *type = "18"; /* default to EAP-SIM */
+
+ sim = xml_node_create(ctx->xml, cred, NULL, "SIM");
+ add_text_node(ctx, sim, "IMSI", imsi);
+ if (ctx->eap_method && os_strcmp(ctx->eap_method, "AKA") == 0)
+ type = "23";
+ else if (ctx->eap_method &&
+ os_strcmp(ctx->eap_method, "AKA'") == 0)
+ type = "50";
+ add_text_node(ctx, sim, "EAPType", type);
+ } else if (cert) {
+ xml_node_t *dc;
+ dc = xml_node_create(ctx->xml, cred, NULL,
+ "DigitalCertificate");
+ add_text_node(ctx, dc, "CertificateType", "x509v3");
+ add_text_node(ctx, dc, "CertSHA256Fingerprint", cert);
+ } else {
+ userpw = build_username_password(ctx, cred, user, pw);
+ add_text_node(ctx, userpw, "MachineManaged",
+ machine_managed ? "TRUE" : "FALSE");
+ eap = xml_node_create(ctx->xml, userpw, NULL, "EAPMethod");
+ add_text_node(ctx, eap, "EAPType", "21");
+ add_text_node(ctx, eap, "InnerMethod", "MS-CHAP-V2");
+ }
+ add_text_node(ctx, cred, "Realm", realm);
+
+ return pps;
+}
+
+
+static xml_node_t * spp_exec_get_certificate(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *user,
+ const char *realm,
+ int add_est_user)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *enroll, *exec_node;
+ char *val;
+ char password[11];
+ char *b64;
+
+ if (add_est_user && new_password(password, sizeof(password)) < 0)
+ return NULL;
+
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, "OK",
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ exec_node = xml_node_create(ctx->xml, spp_node, ns, "exec");
+
+ enroll = xml_node_create(ctx->xml, exec_node, ns, "getCertificate");
+ xml_node_add_attr(ctx->xml, enroll, NULL, "enrollmentProtocol", "EST");
+
+ val = db_get_osu_config_val(ctx, realm, "est_url");
+ xml_node_create_text(ctx->xml, enroll, ns, "enrollmentServerURI",
+ val ? val : "");
+ os_free(val);
+
+ if (!add_est_user)
+ return spp_node;
+
+ xml_node_create_text(ctx->xml, enroll, ns, "estUserID", user);
+
+ b64 = base64_encode(password, strlen(password), NULL);
+ if (b64 == NULL) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+ xml_node_create_text(ctx->xml, enroll, ns, "estPassword", b64);
+ free(b64);
+
+ db_update_session_password(ctx, user, realm, session_id, password);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_registration(struct hs20_svc *ctx,
+ const char *session_id,
+ int enrollment_done)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node = NULL;
+ xml_node_t *pps, *tnds;
+ char buf[400];
+ char *str;
+ char *user, *realm, *pw, *type, *mm, *test;
+ const char *status;
+ int cert = 0;
+ int machine_managed = 0;
+ char *fingerprint;
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
+
+ if (!user || !realm || !pw) {
+ debug_print(ctx, 1, "Could not find session info from DB for "
+ "the new subscription");
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+
+ mm = db_get_session_val(ctx, NULL, NULL, session_id, "machine_managed");
+ if (mm && atoi(mm))
+ machine_managed = 1;
+ free(mm);
+
+ type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
+ if (type && strcmp(type, "cert") == 0)
+ cert = 1;
+ free(type);
+
+ if (cert && !enrollment_done) {
+ xml_node_t *ret;
+ hs20_eventlog(ctx, user, realm, session_id,
+ "request client certificate enrollment", NULL);
+ ret = spp_exec_get_certificate(ctx, session_id, user, realm, 1);
+ free(user);
+ free(realm);
+ free(pw);
+ return ret;
+ }
+
+ if (!cert && strlen(pw) == 0) {
+ machine_managed = 1;
+ free(pw);
+ pw = malloc(11);
+ if (pw == NULL || new_password(pw, 11) < 0) {
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+ }
+
+ status = "Provisioning complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ test = db_get_session_val(ctx, NULL, NULL, session_id, "test");
+ if (test)
+ debug_print(ctx, 1, "TEST: Requested special behavior: %s",
+ test);
+ pps = build_pps(ctx, user, realm, pw,
+ fingerprint ? fingerprint : NULL, machine_managed,
+ test, NULL, NULL, NULL, NULL);
+ free(fingerprint);
+ free(test);
+ if (!pps) {
+ xml_node_free(ctx->xml, spp_node);
+ free(user);
+ free(realm);
+ free(pw);
+ return NULL;
+ }
+
+ debug_print(ctx, 1, "Request DB subscription registration on success "
+ "notification");
+ if (machine_managed) {
+ db_update_session_password(ctx, user, realm, session_id, pw);
+ db_update_session_machine_managed(ctx, user, realm, session_id,
+ machine_managed);
+ }
+ db_add_session_pps(ctx, user, realm, session_id, pps);
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "new subscription", pps);
+ free(user);
+ free(pw);
+
+ tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
+ xml_node_free(ctx->xml, pps);
+ if (!tnds) {
+ xml_node_free(ctx->xml, spp_node);
+ free(realm);
+ return NULL;
+ }
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (str == NULL) {
+ xml_node_free(ctx->xml, spp_node);
+ free(realm);
+ return NULL;
+ }
+
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
+ free(str);
+ snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
+ free(realm);
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_free_remediation(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm,
+ const char *session_id)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node;
+ xml_node_t *cred;
+ char buf[400];
+ char *status;
+ char *free_account, *pw;
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ if (free_account == NULL)
+ return NULL;
+ pw = db_get_val(ctx, free_account, realm, "password", 0);
+ if (pw == NULL) {
+ free(free_account);
+ return NULL;
+ }
+
+ cred = build_credential_pw(ctx, free_account, realm, pw, 1);
+ free(free_account);
+ free(pw);
+ if (!cred) {
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "free/public remediation", cred);
+ xml_node_free(ctx->xml, cred);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_user_input_complete(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+
+ val = db_get_session_val(ctx, user, realm, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper == USER_REMEDIATION) {
+ return hs20_user_input_remediation(ctx, user, realm, dmacc,
+ session_id);
+ }
+
+ if (oper == FREE_REMEDIATION) {
+ return hs20_user_input_free_remediation(ctx, user, realm,
+ session_id);
+ }
+
+ if (oper == SUBSCRIPTION_REGISTRATION) {
+ return hs20_user_input_registration(ctx, session_id, 0);
+ }
+
+ debug_print(ctx, 1, "User session %s not in state for user input "
+ "completion", session_id);
+ return NULL;
+}
+
+
+static xml_node_t * hs20_cert_reenroll_complete(struct hs20_svc *ctx,
+ const char *session_id)
+{
+ char *user, *realm, *cert;
+ char *status;
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *cred;
+ char buf[400];
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ cert = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ if (!user || !realm || !cert) {
+ debug_print(ctx, 1,
+ "Could not find session info from DB for certificate reenrollment");
+ free(user);
+ free(realm);
+ free(cert);
+ return NULL;
+ }
+
+ cred = build_credential_cert(ctx, user, realm, cert);
+ if (!cred) {
+ debug_print(ctx, 1, "Could not build credential");
+ free(user);
+ free(realm);
+ free(cert);
+ return NULL;
+ }
+
+ status = "Remediation complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL) {
+ debug_print(ctx, 1, "Could not build sppPostDevDataResponse");
+ free(user);
+ free(realm);
+ free(cert);
+ xml_node_free(ctx->xml, cred);
+ return NULL;
+ }
+
+ snprintf(buf, sizeof(buf),
+ "./Wi-Fi/%s/PerProviderSubscription/Cred01/Credential",
+ realm);
+
+ if (add_update_node(ctx, spp_node, ns, buf, cred) < 0) {
+ debug_print(ctx, 1, "Could not add update node");
+ xml_node_free(ctx->xml, spp_node);
+ free(user);
+ free(realm);
+ free(cert);
+ return NULL;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate reenrollment", cred);
+ xml_node_free(ctx->xml, cred);
+
+ free(user);
+ free(realm);
+ free(cert);
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_cert_enroll_completed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+
+ val = db_get_session_val(ctx, NULL, NULL, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper == SUBSCRIPTION_REGISTRATION)
+ return hs20_user_input_registration(ctx, session_id, 1);
+ if (oper == CERT_REENROLL)
+ return hs20_cert_reenroll_complete(ctx, session_id);
+
+ debug_print(ctx, 1, "User session %s not in state for certificate "
+ "enrollment completion", session_id);
+ return NULL;
+}
+
+
+static xml_node_t * hs20_cert_enroll_failed(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ char *val;
+ enum hs20_session_operation oper;
+ xml_node_t *spp_node, *node;
+ char *status;
+ xml_namespace_t *ns;
+
+ val = db_get_session_val(ctx, user, realm, session_id, "operation");
+ if (val == NULL) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ return NULL;
+ }
+ oper = atoi(val);
+ free(val);
+
+ if (oper != SUBSCRIPTION_REGISTRATION) {
+ debug_print(ctx, 1, "User session %s not in state for "
+ "enrollment failure", session_id);
+ return NULL;
+ }
+
+ status = "Error occurred";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (spp_node == NULL)
+ return NULL;
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ "Credentials cannot be provisioned at this time");
+ db_remove_session(ctx, user, realm, session_id);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_sim_provisioning(struct hs20_svc *ctx,
+ const char *user,
+ const char *realm, int dmacc,
+ const char *session_id)
+{
+ xml_namespace_t *ns;
+ xml_node_t *spp_node, *node = NULL;
+ xml_node_t *pps, *tnds;
+ char buf[400];
+ char *str;
+ const char *status;
+ char dmacc_username[32];
+ char dmacc_password[32];
+ char *policy;
+ xml_node_t *policy_node = NULL;
+
+ if (!ctx->imsi) {
+ debug_print(ctx, 1, "IMSI not available for SIM provisioning");
+ return NULL;
+ }
+
+ if (new_password(dmacc_username, sizeof(dmacc_username)) < 0 ||
+ new_password(dmacc_password, sizeof(dmacc_password)) < 0) {
+ debug_print(ctx, 1,
+ "Failed to generate DMAcc username/password");
+ return NULL;
+ }
+
+ status = "Provisioning complete, request sppUpdateResponse";
+ spp_node = build_post_dev_data_response(ctx, &ns, session_id, status,
+ NULL);
+ if (!spp_node)
+ return NULL;
+
+ policy = db_get_osu_config_val(ctx, realm, "sim_policy");
+ if (policy) {
+ policy_node = read_policy_file(ctx, policy);
+ os_free(policy);
+ if (!policy_node) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+ update_policy_update_uri(ctx, realm, policy_node);
+ node = get_node_uri(ctx->xml, policy_node,
+ "Policy/PolicyUpdate");
+ if (node)
+ build_username_password(ctx, node, dmacc_username,
+ dmacc_password);
+ }
+
+ pps = build_pps(ctx, NULL, realm, NULL, NULL, 0, NULL, ctx->imsi,
+ dmacc_username, dmacc_password, policy_node);
+ if (!pps) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ debug_print(ctx, 1,
+ "Request DB subscription registration on success notification");
+ if (!user || !user[0])
+ user = ctx->imsi;
+ db_add_session(ctx, user, realm, session_id, NULL, NULL,
+ SUBSCRIPTION_REGISTRATION, NULL);
+ db_add_session_dmacc(ctx, session_id, dmacc_username, dmacc_password);
+ if (ctx->eap_method)
+ db_add_session_eap_method(ctx, session_id, ctx->eap_method);
+ if (ctx->id_hash)
+ db_add_session_id_hash(ctx, session_id, ctx->id_hash);
+ db_add_session_pps(ctx, user, realm, session_id, pps);
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "new subscription", pps);
+
+ tnds = mo_to_tnds(ctx->xml, pps, 0, URN_HS20_PPS, NULL);
+ xml_node_free(ctx->xml, pps);
+ if (!tnds) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ str = xml_node_to_str(ctx->xml, tnds);
+ xml_node_free(ctx->xml, tnds);
+ if (!str) {
+ xml_node_free(ctx->xml, spp_node);
+ return NULL;
+ }
+
+ node = xml_node_create_text(ctx->xml, spp_node, ns, "addMO", str);
+ free(str);
+ snprintf(buf, sizeof(buf), "./Wi-Fi/%s/PerProviderSubscription", realm);
+ xml_node_add_attr(ctx->xml, node, ns, "managementTreeURI", buf);
+ xml_node_add_attr(ctx->xml, node, ns, "moURN", URN_HS20_PPS);
+
+ return spp_node;
+}
+
+
+static xml_node_t * hs20_spp_post_dev_data(struct hs20_svc *ctx,
+ xml_node_t *node,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc)
+{
+ const char *req_reason;
+ char *redirect_uri = NULL;
+ char *req_reason_buf = NULL;
+ char str[200];
+ xml_node_t *ret = NULL, *devinfo = NULL, *devdetail = NULL;
+ xml_node_t *mo, *macaddr;
+ char *version;
+ int valid;
+ char *supp, *pos;
+ char *err;
+ u8 wifi_mac_addr[ETH_ALEN];
+
+ version = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sppVersion");
+ if (version == NULL || strstr(version, "1.0") == NULL) {
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "SPP version not supported");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Unsupported sppVersion", ret);
+ xml_node_get_attr_value_free(ctx->xml, version);
+ return ret;
+ }
+ xml_node_get_attr_value_free(ctx->xml, version);
+
+ mo = get_node(ctx->xml, node, "supportedMOList");
+ if (mo == NULL) {
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No supportedMOList element", ret);
+ return ret;
+ }
+ supp = xml_node_get_text(ctx->xml, mo);
+ for (pos = supp; pos && *pos; pos++)
+ *pos = tolower(*pos);
+ if (supp == NULL ||
+ strstr(supp, URN_OMA_DM_DEVINFO) == NULL ||
+ strstr(supp, URN_OMA_DM_DEVDETAIL) == NULL ||
+ strstr(supp, URN_HS20_PPS) == NULL) {
+ xml_node_get_text_free(ctx->xml, supp);
+ ret = build_post_dev_data_response(
+ ctx, NULL, session_id, "Error occurred",
+ "One or more mandatory MOs not supported");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Unsupported MOs", ret);
+ return ret;
+ }
+ xml_node_get_text_free(ctx->xml, supp);
+
+ req_reason_buf = xml_node_get_attr_value(ctx->xml, node,
+ "requestReason");
+ if (req_reason_buf == NULL) {
+ debug_print(ctx, 1, "No requestReason attribute");
+ return NULL;
+ }
+ req_reason = req_reason_buf;
+
+ redirect_uri = xml_node_get_attr_value(ctx->xml, node, "redirectURI");
+
+ debug_print(ctx, 1, "requestReason: %s sessionID: %s redirectURI: %s",
+ req_reason, session_id, redirect_uri);
+ snprintf(str, sizeof(str), "sppPostDevData: requestReason=%s",
+ req_reason);
+ hs20_eventlog(ctx, user, realm, session_id, str, NULL);
+
+ devinfo = spp_get_mo(ctx, node, URN_OMA_DM_DEVINFO, &valid, &err);
+ if (devinfo == NULL) {
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No DevInfo moContainer in sppPostDevData",
+ ret);
+ os_free(err);
+ goto out;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received DevInfo MO", devinfo);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors in DevInfo MO",
+ err);
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ os_free(err);
+ goto out;
+ }
+ os_free(err);
+ if (user)
+ db_update_mo(ctx, user, realm, "devinfo", devinfo);
+
+ devdetail = spp_get_mo(ctx, node, URN_OMA_DM_DEVDETAIL, &valid, &err);
+ if (devdetail == NULL) {
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "No DevDetail moContainer in sppPostDevData",
+ ret);
+ os_free(err);
+ goto out;
+ }
+
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received DevDetail MO", devdetail);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors "
+ "in DevDetail MO", err);
+ ret = build_post_dev_data_response(ctx, NULL, session_id,
+ "Error occurred", "Other");
+ os_free(err);
+ goto out;
+ }
+ os_free(err);
+
+ os_memset(wifi_mac_addr, 0, ETH_ALEN);
+ macaddr = get_node(ctx->xml, devdetail,
+ "Ext/org.wi-fi/Wi-Fi/Wi-FiMACAddress");
+ if (macaddr) {
+ char *addr, buf[50];
+
+ addr = xml_node_get_text(ctx->xml, macaddr);
+ if (addr && hwaddr_compact_aton(addr, wifi_mac_addr) == 0) {
+ snprintf(buf, sizeof(buf), "DevDetail MAC address: "
+ MACSTR, MAC2STR(wifi_mac_addr));
+ hs20_eventlog(ctx, user, realm, session_id, buf, NULL);
+ xml_node_get_text_free(ctx->xml, addr);
+ } else {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "Could not extract MAC address from DevDetail",
+ NULL);
+ }
+ } else {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "No MAC address in DevDetail", NULL);
+ }
+
+ if (user)
+ db_update_mo(ctx, user, realm, "devdetail", devdetail);
+
+ if (user)
+ mo = spp_get_mo(ctx, node, URN_HS20_PPS, &valid, &err);
+ else {
+ mo = NULL;
+ err = NULL;
+ }
+ if (user && mo) {
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Received PPS MO", mo);
+ if (valid == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "OMA-DM DDF DTD validation errors "
+ "in PPS MO", err);
+ xml_node_get_attr_value_free(ctx->xml, redirect_uri);
+ os_free(err);
+ return build_post_dev_data_response(
+ ctx, NULL, session_id,
+ "Error occurred", "Other");
+ }
+ db_update_mo(ctx, user, realm, "pps", mo);
+ db_update_val(ctx, user, realm, "fetch_pps", "0", dmacc);
+ xml_node_free(ctx->xml, mo);
+ }
+ os_free(err);
+
+ if (user && !mo) {
+ char *fetch;
+ int fetch_pps;
+
+ fetch = db_get_val(ctx, user, realm, "fetch_pps", dmacc);
+ fetch_pps = fetch ? atoi(fetch) : 0;
+ free(fetch);
+
+ if (fetch_pps) {
+ enum hs20_session_operation oper;
+ if (strcasecmp(req_reason, "Subscription remediation")
+ == 0)
+ oper = CONTINUE_SUBSCRIPTION_REMEDIATION;
+ else if (strcasecmp(req_reason, "Policy update") == 0)
+ oper = CONTINUE_POLICY_UPDATE;
+ else
+ oper = NO_OPERATION;
+ if (db_add_session(ctx, user, realm, session_id, NULL,
+ NULL, oper, NULL) < 0)
+ goto out;
+
+ ret = spp_exec_upload_mo(ctx, session_id,
+ URN_HS20_PPS);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "request PPS MO upload",
+ ret);
+ goto out;
+ }
+ }
+
+ if (user && strcasecmp(req_reason, "MO upload") == 0) {
+ char *val = db_get_session_val(ctx, user, realm, session_id,
+ "operation");
+ enum hs20_session_operation oper;
+ if (!val) {
+ debug_print(ctx, 1, "No session %s found to continue",
+ session_id);
+ goto out;
+ }
+ oper = atoi(val);
+ free(val);
+ if (oper == CONTINUE_SUBSCRIPTION_REMEDIATION)
+ req_reason = "Subscription remediation";
+ else if (oper == CONTINUE_POLICY_UPDATE)
+ req_reason = "Policy update";
+ else {
+ debug_print(ctx, 1,
+ "No pending operation in session %s",
+ session_id);
+ goto out;
+ }
+ }
+
+ if (strcasecmp(req_reason, "Subscription registration") == 0) {
+ ret = hs20_subscription_registration(ctx, realm, session_id,
+ redirect_uri,
+ wifi_mac_addr);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription registration response",
+ ret);
+ goto out;
+ }
+ if (user && strcasecmp(req_reason, "Subscription remediation") == 0) {
+ ret = hs20_subscription_remediation(ctx, user, realm,
+ session_id, dmacc,
+ redirect_uri);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription remediation response",
+ ret);
+ goto out;
+ }
+ if (user && strcasecmp(req_reason, "Policy update") == 0) {
+ ret = hs20_policy_update(ctx, user, realm, session_id, dmacc);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "policy update response",
+ ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "User input completed") == 0) {
+ db_add_session_devinfo(ctx, session_id, devinfo);
+ db_add_session_devdetail(ctx, session_id, devdetail);
+ ret = hs20_user_input_complete(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "user input completed response", ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Certificate enrollment completed") == 0) {
+ ret = hs20_cert_enroll_completed(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate enrollment response", ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Certificate enrollment failed") == 0) {
+ ret = hs20_cert_enroll_failed(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "certificate enrollment failed response",
+ ret);
+ goto out;
+ }
+
+ if (strcasecmp(req_reason, "Subscription provisioning") == 0) {
+ ret = hs20_sim_provisioning(ctx, user, realm, dmacc,
+ session_id);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "subscription provisioning response",
+ ret);
+ goto out;
+ }
+
+ debug_print(ctx, 1, "Unsupported requestReason '%s' user '%s'",
+ req_reason, user);
+out:
+ xml_node_get_attr_value_free(ctx->xml, req_reason_buf);
+ xml_node_get_attr_value_free(ctx->xml, redirect_uri);
+ if (devinfo)
+ xml_node_free(ctx->xml, devinfo);
+ if (devdetail)
+ xml_node_free(ctx->xml, devdetail);
+ return ret;
+}
+
+
+static xml_node_t * build_spp_exchange_complete(struct hs20_svc *ctx,
+ const char *session_id,
+ const char *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,
+ "sppExchangeComplete");
+
+
+ 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", status);
+
+ if (error_code) {
+ node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+ xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+ error_code);
+ }
+
+ return spp_node;
+}
+
+
+static int add_subscription(struct hs20_svc *ctx, const char *session_id)
+{
+ char *user, *realm, *pw, *pw_mm, *pps, *str;
+ char *osu_user, *osu_password, *eap_method;
+ char *policy = NULL;
+ char *sql;
+ int ret = -1;
+ char *free_account;
+ int free_acc;
+ char *type;
+ int cert = 0;
+ char *cert_pem, *fingerprint;
+ const char *method;
+
+ user = db_get_session_val(ctx, NULL, NULL, session_id, "user");
+ realm = db_get_session_val(ctx, NULL, NULL, session_id, "realm");
+ pw = db_get_session_val(ctx, NULL, NULL, session_id, "password");
+ pw_mm = db_get_session_val(ctx, NULL, NULL, session_id,
+ "machine_managed");
+ pps = db_get_session_val(ctx, NULL, NULL, session_id, "pps");
+ cert_pem = db_get_session_val(ctx, NULL, NULL, session_id, "cert_pem");
+ fingerprint = db_get_session_val(ctx, NULL, NULL, session_id, "cert");
+ type = db_get_session_val(ctx, NULL, NULL, session_id, "type");
+ if (type && strcmp(type, "cert") == 0)
+ cert = 1;
+ free(type);
+ osu_user = db_get_session_val(ctx, NULL, NULL, session_id, "osu_user");
+ osu_password = db_get_session_val(ctx, NULL, NULL, session_id,
+ "osu_password");
+ eap_method = db_get_session_val(ctx, NULL, NULL, session_id,
+ "eap_method");
+
+ if (!user || !realm || !pw) {
+ debug_print(ctx, 1, "Could not find session info from DB for "
+ "the new subscription");
+ goto out;
+ }
+
+ free_account = db_get_osu_config_val(ctx, realm, "free_account");
+ free_acc = free_account && strcmp(free_account, user) == 0;
+ free(free_account);
+
+ policy = db_get_osu_config_val(ctx, realm, "sim_policy");
+
+ debug_print(ctx, 1,
+ "New subscription: user='%s' realm='%s' free_acc=%d",
+ user, realm, free_acc);
+ debug_print(ctx, 1, "New subscription: pps='%s'", pps);
+
+ sql = sqlite3_mprintf("UPDATE eventlog SET user=%Q, realm=%Q WHERE "
+ "sessionid=%Q AND (user='' OR user IS NULL)",
+ user, realm, session_id);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to update eventlog in "
+ "sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+
+ if (free_acc) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "completed shared free account registration",
+ NULL);
+ ret = 0;
+ goto out;
+ }
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "mac_addr");
+
+ if (eap_method && eap_method[0])
+ method = eap_method;
+ else
+ method = cert ? "TLS" : "TTLS-MSCHAPV2";
+ sql = sqlite3_mprintf("INSERT INTO users(identity,realm,phase2,methods,cert,cert_pem,machine_managed,mac_addr,osu_user,osu_password,policy) VALUES (%Q,%Q,%d,%Q,%Q,%Q,%d,%Q,%Q,%Q,%Q)",
+ user, realm, cert ? 0 : 1,
+ method,
+ fingerprint ? fingerprint : "",
+ cert_pem ? cert_pem : "",
+ pw_mm && atoi(pw_mm) ? 1 : 0,
+ str ? str : "",
+ osu_user ? osu_user : "",
+ osu_password ? osu_password : "",
+ policy ? policy : "");
+ free(str);
+ if (sql == NULL)
+ goto out;
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) != SQLITE_OK) {
+ debug_print(ctx, 1, "Failed to add user in sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ sqlite3_free(sql);
+ goto out;
+ }
+ sqlite3_free(sql);
+
+ if (cert)
+ ret = 0;
+ else
+ ret = update_password(ctx, user, realm, pw, 0);
+ if (ret < 0) {
+ sql = sqlite3_mprintf("DELETE FROM users WHERE identity=%Q AND realm=%Q AND (phase2=1 OR methods='TLS')",
+ user, realm);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ sqlite3_exec(ctx->db, sql, NULL, NULL, NULL);
+ sqlite3_free(sql);
+ }
+ }
+
+ if (pps)
+ db_update_mo_str(ctx, user, realm, "pps", pps);
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "devinfo");
+ if (str) {
+ db_update_mo_str(ctx, user, realm, "devinfo", str);
+ free(str);
+ }
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id, "devdetail");
+ if (str) {
+ db_update_mo_str(ctx, user, realm, "devdetail", str);
+ free(str);
+ }
+
+ if (cert && user) {
+ const char *serialnum;
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id,
+ "mac_addr");
+
+ if (os_strncmp(user, "cert-", 5) == 0)
+ serialnum = user + 5;
+ else
+ serialnum = "";
+ sql = sqlite3_mprintf("INSERT OR REPLACE INTO cert_enroll (mac_addr,user,realm,serialnum) VALUES(%Q,%Q,%Q,%Q)",
+ str ? str : "", user, realm ? realm : "",
+ serialnum);
+ free(str);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to add cert_enroll entry into sqlite database: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+ }
+
+ str = db_get_session_val(ctx, NULL, NULL, session_id,
+ "mobile_identifier_hash");
+ if (str) {
+ sql = sqlite3_mprintf("DELETE FROM sim_provisioning WHERE mobile_identifier_hash=%Q",
+ str);
+ if (sql) {
+ debug_print(ctx, 1, "DB: %s", sql);
+ if (sqlite3_exec(ctx->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ debug_print(ctx, 1,
+ "Failed to delete pending sim_provisioning entry: %s",
+ sqlite3_errmsg(ctx->db));
+ }
+ sqlite3_free(sql);
+ }
+ os_free(str);
+ }
+
+ if (ret == 0) {
+ hs20_eventlog(ctx, user, realm, session_id,
+ "completed subscription registration", NULL);
+ }
+
+out:
+ free(user);
+ free(realm);
+ free(pw);
+ free(pw_mm);
+ free(pps);
+ free(cert_pem);
+ free(fingerprint);
+ free(osu_user);
+ free(osu_password);
+ free(eap_method);
+ os_free(policy);
+ return ret;
+}
+
+
+static xml_node_t * hs20_spp_update_response(struct hs20_svc *ctx,
+ xml_node_t *node,
+ const char *user,
+ const char *realm,
+ const char *session_id,
+ int dmacc)
+{
+ char *status;
+ xml_node_t *ret;
+ char *val;
+ enum hs20_session_operation oper;
+
+ status = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sppStatus");
+ if (status == NULL) {
+ debug_print(ctx, 1, "No sppStatus attribute");
+ return NULL;
+ }
+
+ debug_print(ctx, 1, "sppUpdateResponse: sppStatus: %s sessionID: %s",
+ status, session_id);
+
+ val = db_get_session_val(ctx, NULL, NULL, session_id, "operation");
+ if (!val) {
+ debug_print(ctx, 1,
+ "No session active for sessionID: %s",
+ session_id);
+ oper = NO_OPERATION;
+ } else
+ oper = atoi(val);
+
+ if (strcasecmp(status, "OK") == 0) {
+ char *new_pw = NULL;
+
+ xml_node_get_attr_value_free(ctx->xml, status);
+
+ if (oper == USER_REMEDIATION) {
+ new_pw = db_get_session_val(ctx, user, realm,
+ session_id, "password");
+ if (new_pw == NULL || strlen(new_pw) == 0) {
+ free(new_pw);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "No password "
+ "had been assigned for "
+ "session", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ oper = UPDATE_PASSWORD;
+ }
+ if (oper == UPDATE_PASSWORD) {
+ if (!new_pw) {
+ new_pw = db_get_session_val(ctx, user, realm,
+ session_id,
+ "password");
+ if (!new_pw) {
+ db_remove_session(ctx, user, realm,
+ session_id);
+ return NULL;
+ }
+ }
+ debug_print(ctx, 1, "Update user '%s' password in DB",
+ user);
+ if (update_password(ctx, user, realm, new_pw, dmacc) <
+ 0) {
+ debug_print(ctx, 1, "Failed to update user "
+ "'%s' password in DB", user);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "Failed to "
+ "update database", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ hs20_eventlog(ctx, user, realm,
+ session_id, "Updated user password "
+ "in database", NULL);
+ }
+ if (oper == CLEAR_REMEDIATION) {
+ debug_print(ctx, 1,
+ "Clear remediation requirement for user '%s' in DB",
+ user);
+ if (clear_remediation(ctx, user, realm, dmacc) < 0) {
+ debug_print(ctx, 1,
+ "Failed to clear remediation requirement for user '%s' in DB",
+ user);
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id,
+ "Failed to update database",
+ ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ hs20_eventlog(ctx, user, realm,
+ session_id,
+ "Cleared remediation requirement in database",
+ NULL);
+ }
+ if (oper == SUBSCRIPTION_REGISTRATION) {
+ if (add_subscription(ctx, session_id) < 0) {
+ debug_print(ctx, 1, "Failed to add "
+ "subscription into DB");
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id, "Failed to "
+ "update database", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ return ret;
+ }
+ }
+ if (oper == POLICY_REMEDIATION || oper == POLICY_UPDATE) {
+ char *val;
+ val = db_get_val(ctx, user, realm, "remediation",
+ dmacc);
+ if (val && strcmp(val, "policy") == 0)
+ db_update_val(ctx, user, realm, "remediation",
+ "", dmacc);
+ free(val);
+ }
+ if (oper == POLICY_UPDATE)
+ db_update_val(ctx, user, realm, "polupd_done", "1",
+ dmacc);
+ if (oper == CERT_REENROLL) {
+ char *new_user;
+ char event[200];
+
+ new_user = db_get_session_val(ctx, NULL, NULL,
+ session_id, "user");
+ if (!new_user) {
+ debug_print(ctx, 1,
+ "Failed to find new user name (cert-serialnum)");
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id,
+ "Failed to find new user name (cert reenroll)",
+ ret);
+ db_remove_session(ctx, NULL, NULL, session_id);
+ return ret;
+ }
+
+ debug_print(ctx, 1,
+ "Update certificate user entry to use the new serial number (old=%s new=%s)",
+ user, new_user);
+ os_snprintf(event, sizeof(event), "renamed user to: %s",
+ new_user);
+ hs20_eventlog(ctx, user, realm, session_id, event,
+ NULL);
+
+ if (db_update_val(ctx, user, realm, "identity",
+ new_user, 0) < 0 ||
+ db_update_val(ctx, new_user, realm, "remediation",
+ "", 0) < 0) {
+ debug_print(ctx, 1,
+ "Failed to update user name (cert-serialnum)");
+ ret = build_spp_exchange_complete(
+ ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm,
+ session_id,
+ "Failed to update user name (cert reenroll)",
+ ret);
+ db_remove_session(ctx, NULL, NULL, session_id);
+ os_free(new_user);
+ return ret;
+ }
+
+ os_free(new_user);
+ }
+ ret = build_spp_exchange_complete(
+ ctx, session_id,
+ "Exchange complete, release TLS connection", NULL);
+ hs20_eventlog_node(ctx, user, realm, session_id,
+ "Exchange completed", ret);
+ db_remove_session(ctx, NULL, NULL, session_id);
+ return ret;
+ }
+
+ ret = build_spp_exchange_complete(ctx, session_id, "Error occurred",
+ "Other");
+ hs20_eventlog_node(ctx, user, realm, session_id, "Error occurred", ret);
+ db_remove_session(ctx, user, realm, session_id);
+ xml_node_get_attr_value_free(ctx->xml, status);
+ return ret;
+}
+
+
+#define SPP_SESSION_ID_LEN 16
+
+static char * gen_spp_session_id(void)
+{
+ FILE *f;
+ int i;
+ char *session;
+
+ session = os_malloc(SPP_SESSION_ID_LEN * 2 + 1);
+ if (session == NULL)
+ return NULL;
+
+ f = fopen("/dev/urandom", "r");
+ if (f == NULL) {
+ os_free(session);
+ return NULL;
+ }
+ for (i = 0; i < SPP_SESSION_ID_LEN; i++)
+ os_snprintf(session + i * 2, 3, "%02x", fgetc(f));
+
+ fclose(f);
+ return session;
+}
+
+xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
+ const char *auth_user,
+ const char *auth_realm, int dmacc)
+{
+ xml_node_t *ret = NULL;
+ char *session_id;
+ const char *op_name;
+ char *xml_err;
+ char fname[200];
+
+ debug_dump_node(ctx, "received request", node);
+
+ if (!dmacc && auth_user && auth_realm) {
+ char *real;
+ real = db_get_val(ctx, auth_user, auth_realm, "identity", 0);
+ if (!real) {
+ real = db_get_val(ctx, auth_user, auth_realm,
+ "identity", 1);
+ if (real)
+ dmacc = 1;
+ }
+ os_free(real);
+ }
+
+ snprintf(fname, sizeof(fname), "%s/spp/spp.xsd", ctx->root_dir);
+ if (xml_validate(ctx->xml, node, fname, &xml_err) < 0) {
+ /*
+ * We may not be able to extract the sessionID from invalid
+ * input, but well, we can try.
+ */
+ session_id = xml_node_get_attr_value_ns(ctx->xml, node,
+ SPP_NS_URI,
+ "sessionID");
+ debug_print(ctx, 1,
+ "SPP message failed validation, xsd file: %s xml-error: %s",
+ fname, xml_err);
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "SPP message failed validation", node);
+ hs20_eventlog(ctx, auth_user, auth_realm, session_id,
+ "Validation errors", xml_err);
+ os_free(xml_err);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppValidationError");
+ return ret;
+ }
+
+ session_id = xml_node_get_attr_value_ns(ctx->xml, node, SPP_NS_URI,
+ "sessionID");
+ if (session_id) {
+ char *tmp;
+ debug_print(ctx, 1, "Received sessionID %s", session_id);
+ tmp = os_strdup(session_id);
+ xml_node_get_attr_value_free(ctx->xml, session_id);
+ if (tmp == NULL)
+ return NULL;
+ session_id = tmp;
+ } else {
+ session_id = gen_spp_session_id();
+ if (session_id == NULL) {
+ debug_print(ctx, 1, "Failed to generate sessionID");
+ return NULL;
+ }
+ debug_print(ctx, 1, "Generated sessionID %s", session_id);
+ }
+
+ op_name = xml_node_get_localname(ctx->xml, node);
+ if (op_name == NULL) {
+ debug_print(ctx, 1, "Could not get op_name");
+ return NULL;
+ }
+
+ if (strcmp(op_name, "sppPostDevData") == 0) {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "sppPostDevData received and validated",
+ node);
+ ret = hs20_spp_post_dev_data(ctx, node, auth_user, auth_realm,
+ session_id, dmacc);
+ } else if (strcmp(op_name, "sppUpdateResponse") == 0) {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "sppUpdateResponse received and validated",
+ node);
+ ret = hs20_spp_update_response(ctx, node, auth_user,
+ auth_realm, session_id, dmacc);
+ } else {
+ hs20_eventlog_node(ctx, auth_user, auth_realm, session_id,
+ "Unsupported SPP message received and "
+ "validated", node);
+ debug_print(ctx, 1, "Unsupported operation '%s'", op_name);
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppUnknownCommandError");
+ }
+ os_free(session_id);
+
+ if (ret == NULL) {
+ /* TODO: what to return here? */
+ ret = xml_node_create_root(ctx->xml, NULL, NULL, NULL,
+ "SppInternalError");
+ }
+
+ return ret;
+}
+
+
+int hs20_spp_server_init(struct hs20_svc *ctx)
+{
+ char fname[200];
+ ctx->db = NULL;
+ snprintf(fname, sizeof(fname), "%s/AS/DB/eap_user.db", ctx->root_dir);
+ if (sqlite3_open(fname, &ctx->db)) {
+ printf("Failed to open sqlite database: %s\n",
+ sqlite3_errmsg(ctx->db));
+ sqlite3_close(ctx->db);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void hs20_spp_server_deinit(struct hs20_svc *ctx)
+{
+ sqlite3_close(ctx->db);
+ ctx->db = NULL;
+}
diff --git a/contrib/wpa/hs20/server/spp_server.h b/contrib/wpa/hs20/server/spp_server.h
new file mode 100644
index 000000000000..421974c607b8
--- /dev/null
+++ b/contrib/wpa/hs20/server/spp_server.h
@@ -0,0 +1,36 @@
+/*
+ * Hotspot 2.0 SPP server
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef SPP_SERVER_H
+#define SPP_SERVER_H
+
+struct hs20_svc {
+ const void *ctx;
+ struct xml_node_ctx *xml;
+ char *root_dir;
+ FILE *debug_log;
+ sqlite3 *db;
+ const char *addr;
+ const char *test;
+ const char *imsi;
+ const char *eap_method;
+ const char *id_hash;
+};
+
+
+void debug_print(struct hs20_svc *ctx, int print, const char *fmt, ...)
+ __attribute__ ((format (printf, 3, 4)));
+void debug_dump_node(struct hs20_svc *ctx, const char *title, xml_node_t *node);
+
+xml_node_t * hs20_spp_server_process(struct hs20_svc *ctx, xml_node_t *node,
+ const char *auth_user,
+ const char *auth_realm, int dmacc);
+int hs20_spp_server_init(struct hs20_svc *ctx);
+void hs20_spp_server_deinit(struct hs20_svc *ctx);
+
+#endif /* SPP_SERVER_H */
diff --git a/contrib/wpa/hs20/server/sql-example.txt b/contrib/wpa/hs20/server/sql-example.txt
new file mode 100644
index 000000000000..20dcf2f5c688
--- /dev/null
+++ b/contrib/wpa/hs20/server/sql-example.txt
@@ -0,0 +1,17 @@
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','fqdn','example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','friendly_name','Example Operator');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','spp_http_auth_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/spp-root-ca.der');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_url','https://osu-server.osu.example.com/hs20/files/aaa-root-ca.der');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','aaa_trust_root_cert_fingerprint','5b393a9246865569485c2605c3304e48212b449367858299beba9384c4cf4647');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_account','free');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','policy_url','https://subscription-server.osu.example.com/hs20/spp.php?realm=example.com');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','remediation_url','https://subscription-server.osu.example.com/hs20/remediation.php?session_id=');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','free_remediation_url','https://subscription-server.osu.example.com/hs20/free-remediation.php?session_id=');
+INSERT INTO osu_config(realm,field,value) VALUES('example.com','signup_url','https://subscription-server.osu.example.com/hs20/signup.php?session_id=');
+
+
+INSERT INTO users(identity,realm,methods,password,phase2,shared) VALUES('free','example.com','TTLS-MSCHAPV2','free',1,1);
+
+INSERT INTO wildcards(identity,methods) VALUES('','TTLS,TLS');
diff --git a/contrib/wpa/hs20/server/sql.txt b/contrib/wpa/hs20/server/sql.txt
new file mode 100644
index 000000000000..2cc6edea4063
--- /dev/null
+++ b/contrib/wpa/hs20/server/sql.txt
@@ -0,0 +1,108 @@
+CREATE TABLE eventlog(
+ user TEXT,
+ realm TEXT,
+ sessionid TEXT COLLATE NOCASE,
+ timestamp TEXT,
+ notes TEXT,
+ dump TEXT,
+ addr TEXT
+);
+
+CREATE TABLE sessions(
+ timestamp TEXT,
+ id TEXT COLLATE NOCASE,
+ user TEXT,
+ realm TEXT,
+ password TEXT,
+ machine_managed BOOLEAN,
+ operation INTEGER,
+ type TEXT,
+ pps TEXT,
+ redirect_uri TEXT,
+ devinfo TEXT,
+ devdetail TEXT,
+ cert TEXT,
+ cert_pem TEXT,
+ mac_addr TEXT,
+ osu_user TEXT,
+ osu_password TEXT,
+ eap_method TEXT,
+ mobile_identifier_hash TEXT,
+ test TEXT
+);
+
+CREATE index sessions_id_index ON sessions(id);
+
+CREATE TABLE osu_config(
+ realm TEXT,
+ field TEXT,
+ value TEXT
+);
+
+CREATE TABLE users(
+ identity TEXT PRIMARY KEY,
+ methods TEXT,
+ password TEXT,
+ machine_managed BOOLEAN,
+ remediation TEXT,
+ phase2 INTEGER,
+ realm TEXT,
+ policy TEXT,
+ devinfo TEXT,
+ devdetail TEXT,
+ pps TEXT,
+ fetch_pps INTEGER,
+ osu_user TEXT,
+ osu_password TEXT,
+ shared INTEGER,
+ cert TEXT,
+ cert_pem TEXT,
+ t_c_timestamp INTEGER,
+ mac_addr TEXT,
+ last_msk TEXT,
+ polupd_done TEXT,
+ subrem TEXT
+);
+
+CREATE TABLE wildcards(
+ identity TEXT PRIMARY KEY,
+ methods TEXT
+);
+
+CREATE TABLE authlog(
+ timestamp TEXT,
+ session TEXT,
+ nas_ip TEXT,
+ username TEXT,
+ note TEXT
+);
+
+CREATE TABLE pending_tc(
+ mac_addr TEXT PRIMARY KEY,
+ identity TEXT
+);
+
+CREATE TABLE current_sessions(
+ mac_addr TEXT PRIMARY KEY,
+ identity TEXT,
+ start_time TEXT,
+ nas TEXT,
+ hs20_t_c_filtering BOOLEAN,
+ waiting_coa_ack BOOLEAN,
+ coa_ack_received BOOLEAN
+);
+
+CREATE TABLE cert_enroll(
+ mac_addr TEXT PRIMARY KEY,
+ user TEXT,
+ realm TEXT,
+ serialnum TEXT
+);
+
+CREATE TABLE sim_provisioning(
+ mobile_identifier_hash TEXT PRIMARY KEY,
+ imsi TEXT,
+ mac_addr TEXT,
+ eap_method TEXT,
+ timestamp TEXT
+);
diff --git a/contrib/wpa/hs20/server/www/add-free.php b/contrib/wpa/hs20/server/www/add-free.php
new file mode 100644
index 000000000000..1efc65563274
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/add-free.php
@@ -0,0 +1,50 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_POST["id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
+else
+ die("Missing session id");
+if (strlen($id) < 32)
+ die("Invalid session id");
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+$realm = $row['realm'];
+
+$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
+if (!$row || strlen($row['value']) == 0) {
+ die("Free account disabled");
+}
+
+$user = $row['value'];
+
+$row = $db->query("SELECT password FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
+if (!$row)
+ die("Free account not found");
+
+$pw = $row['password'];
+
+if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', machine_managed='1' WHERE rowid=$rowid")) {
+ die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('$user', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'completed user input response for a new PPS MO')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/add-mo.php b/contrib/wpa/hs20/server/www/add-mo.php
new file mode 100644
index 000000000000..a3b4513531f8
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/add-mo.php
@@ -0,0 +1,56 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_POST["id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
+else
+ die("Missing session id");
+
+$user = $_POST["user"];
+$pw = $_POST["password"];
+if (strlen($id) < 32 || !isset($user) || !isset($pw)) {
+ die("Invalid POST data");
+}
+
+if (strlen($user) < 1 || strncasecmp($user, "cert-", 5) == 0) {
+ echo "<html><body><p><red>Invalid username</red></p>\n";
+ echo "<a href=\"signup.php?session_id=$id\">Try again</a>\n";
+ echo "</body></html>\n";
+ exit;
+}
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+$realm = $row['realm'];
+
+$userrow = $db->query("SELECT identity FROM users WHERE identity='$user' AND realm='$realm'")->fetch();
+if ($userrow) {
+ echo "<html><body><p><red>Selected username is not available</red></p>\n";
+ echo "<a href=\"signup.php?session_id=$id\">Try again</a>\n";
+ echo "</body></html>\n";
+ exit;
+}
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+
+if (!$db->exec("UPDATE sessions SET user='$user', password='$pw', realm='$realm', type='password' WHERE rowid=$rowid")) {
+ die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('$user', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'completed user input response for a new PPS MO')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/cert-enroll.php b/contrib/wpa/hs20/server/www/cert-enroll.php
new file mode 100644
index 000000000000..f023ca5a5b03
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/cert-enroll.php
@@ -0,0 +1,39 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_GET["id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["id"]);
+else
+ die("Missing session id");
+if (strlen($id) < 32)
+ die("Invalid session id");
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+$realm = $row['realm'];
+
+$user = sha1(mt_rand());
+
+if (!$db->exec("UPDATE sessions SET user='$user', type='cert' WHERE rowid=$rowid")) {
+ die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'completed user input response for client certificate enrollment')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/config.php b/contrib/wpa/hs20/server/www/config.php
new file mode 100644
index 000000000000..4272b102a88c
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/config.php
@@ -0,0 +1,7 @@
+<?php
+$osu_root = "/home/user/hs20-server";
+$osu_db = "sqlite:$osu_root/AS/DB/eap_user.db";
+$t_c_file = "$osu_root/terms-and-conditions";
+$t_c_timestamp = 123456789;
+$hostapd_ctrl = "udg:///home/user/hs20-server/AS/ctrl/as"
+?>
diff --git a/contrib/wpa/hs20/server/www/est.php b/contrib/wpa/hs20/server/www/est.php
new file mode 100644
index 000000000000..b7fb260d56c4
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/est.php
@@ -0,0 +1,232 @@
+<?php
+
+require('config.php');
+
+$params = explode("/", $_SERVER["PATH_INFO"], 3);
+$realm = $params[1];
+$cmd = $params[2];
+$method = $_SERVER["REQUEST_METHOD"];
+
+unset($user);
+unset($rowid);
+
+$db = new PDO($osu_db);
+if (!$db) {
+ error_log("EST: Could not access database");
+ die("Could not access database");
+}
+
+if (!empty($_SERVER['PHP_AUTH_DIGEST'])) {
+ $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1,
+ 'uri'=>1, 'response'=>1);
+ $data = array();
+ $keys = implode('|', array_keys($needed));
+ preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
+ $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);
+ foreach ($matches as $m) {
+ $data[$m[1]] = $m[3] ? $m[3] : $m[4];
+ unset($needed[$m[1]]);
+ }
+ if ($needed) {
+ error_log("EST: Missing auth parameter");
+ die('Authentication failed');
+ }
+ $user = $data['username'];
+ if (strlen($user) < 1) {
+ error_log("EST: Empty username");
+ die('Authentication failed');
+ }
+
+ $sql = "SELECT rowid,password,operation FROM sessions " .
+ "WHERE user='$user' AND realm='$realm'";
+ $q = $db->query($sql);
+ if (!$q) {
+ error_log("EST: Session not found for user=$user realm=$realm");
+ die("Session not found");
+ }
+ $row = $q->fetch();
+ if (!$row) {
+ error_log("EST: Session fetch failed for user=$user realm=$realm");
+ die('Session not found');
+ }
+ $rowid = $row['rowid'];
+
+ $oper = $row['operation'];
+ if ($oper != '5') {
+ error_log("EST: Unexpected operation $oper for user=$user realm=$realm");
+ die("Session not found");
+ }
+ $pw = $row['password'];
+ if (strlen($pw) < 1) {
+ error_log("EST: Empty password for user=$user realm=$realm");
+ die('Authentication failed');
+ }
+
+ $A1 = md5($user . ':' . $realm . ':' . $pw);
+ $A2 = md5($method . ':' . $data['uri']);
+ $resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' .
+ $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
+ if ($data['response'] != $resp) {
+ error_log("EST: Incorrect authentication response for user=$user realm=$realm");
+ die('Authentication failed');
+ }
+} else if (isset($_SERVER["SSL_CLIENT_VERIFY"]) &&
+ $_SERVER["SSL_CLIENT_VERIFY"] == "SUCCESS" &&
+ isset($_SERVER["SSL_CLIENT_M_SERIAL"])) {
+ $user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"];
+ $sql = "SELECT rowid,password,operation FROM sessions " .
+ "WHERE user='$user' AND realm='$realm'";
+ $q = $db->query($sql);
+ if (!$q) {
+ error_log("EST: Session not found for user=$user realm=$realm");
+ die("Session not found");
+ }
+ $row = $q->fetch();
+ if (!$row) {
+ error_log("EST: Session fetch failed for user=$user realm=$realm");
+ die('Session not found');
+ }
+ $rowid = $row['rowid'];
+
+ $oper = $row['operation'];
+ if ($oper != '10') {
+ error_log("EST: Unexpected operation $oper for user=$user realm=$realm");
+ die("Session not found");
+ }
+}
+
+
+if ($method == "GET" && $cmd == "cacerts") {
+ $fname = "$osu_root/est/$realm-cacerts.pkcs7";
+ if (!file_exists($fname)) {
+ error_log("EST: cacerts - unknown realm $realm");
+ die("Unknown realm");
+ }
+
+ header("Content-Transfer-Encoding: base64");
+ header("Content-Type: application/pkcs7-mime");
+
+ $data = file_get_contents($fname);
+ echo wordwrap(base64_encode($data), 72, "\n", true);
+ echo "\n";
+ error_log("EST: cacerts");
+} else if ($method == "GET" && $cmd == "csrattrs") {
+ header("Content-Transfer-Encoding: base64");
+ header("Content-Type: application/csrattrs");
+ readfile("$osu_root/est/est-attrs.b64");
+ error_log("EST: csrattrs");
+} else if ($method == "POST" &&
+ ($cmd == "simpleenroll" || $cmd == "simplereenroll")) {
+ $reenroll = $cmd == "simplereenroll";
+ if (!$reenroll && (!isset($user) || strlen($user) == 0)) {
+ header('HTTP/1.1 401 Unauthorized');
+ header('WWW-Authenticate: Digest realm="'.$realm.
+ '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+ error_log("EST: simpleenroll - require authentication");
+ die('Authentication required');
+ }
+ if ($reenroll &&
+ (!isset($user) ||
+ !isset($_SERVER["SSL_CLIENT_VERIFY"]) ||
+ $_SERVER["SSL_CLIENT_VERIFY"] != "SUCCESS")) {
+ header('HTTP/1.1 403 Forbidden');
+ error_log("EST: simplereenroll - require certificate authentication");
+ die('Authentication required');
+ }
+ if (!isset($_SERVER["CONTENT_TYPE"])) {
+ error_log("EST: simpleenroll without Content-Type");
+ die("Missing Content-Type");
+ }
+ if (!stristr($_SERVER["CONTENT_TYPE"], "application/pkcs10")) {
+ error_log("EST: simpleenroll - unexpected Content-Type: " .
+ $_SERVER["CONTENT_TYPE"]);
+ die("Unexpected Content-Type");
+ }
+
+ $data = file_get_contents("php://input");
+ error_log("EST: simpleenroll - POST data from php://input: " . $data);
+ $req = base64_decode($data);
+ if ($req == FALSE) {
+ error_log("EST: simpleenroll - Invalid base64-encoded PKCS#10 data");
+ die("Invalid base64-encoded PKCS#10 data");
+ }
+ $cadir = "$osu_root/est";
+ $reqfile = "$cadir/tmp/cert-req.pkcs10";
+ $f = fopen($reqfile, "wb");
+ fwrite($f, $req);
+ fclose($f);
+
+ $req_pem = "$reqfile.pem";
+ if (file_exists($req_pem))
+ unlink($req_pem);
+ exec("openssl req -in $reqfile -inform DER -out $req_pem -outform PEM");
+ if (!file_exists($req_pem)) {
+ error_log("EST: simpleenroll - Failed to parse certificate request");
+ die("Failed to parse certificate request");
+ }
+
+ /* FIX: validate request and add HS 2.0 extensions to cert */
+ $cert_pem = "$cadir/tmp/req-signed.pem";
+ if (file_exists($cert_pem))
+ unlink($cert_pem);
+ exec("openssl x509 -req -in $req_pem -CAkey $cadir/cakey.pem -out $cert_pem -CA $cadir/cacert.pem -CAserial $cadir/serial -days 365 -text");
+ if (!file_exists($cert_pem)) {
+ error_log("EST: simpleenroll - Failed to sign certificate");
+ die("Failed to sign certificate");
+ }
+
+ $cert = file_get_contents($cert_pem);
+ $handle = popen("openssl x509 -in $cert_pem -serial -noout", "r");
+ $serial = fread($handle, 200);
+ pclose($handle);
+ $pattern = "/serial=(?P<snhex>[0-9a-fA-F:]*)/m";
+ preg_match($pattern, $serial, $matches);
+ if (!isset($matches['snhex']) || strlen($matches['snhex']) < 1) {
+ error_log("EST: simpleenroll - Could not get serial number");
+ die("Could not get serial number");
+ }
+ $sn = str_replace(":", "", strtoupper($matches['snhex']));
+
+ $user = "cert-$sn";
+ error_log("EST: user = $user");
+
+ $cert_der = "$cadir/tmp/req-signed.der";
+ if (file_exists($cert_der))
+ unlink($cert_der);
+ exec("openssl x509 -in $cert_pem -inform PEM -out $cert_der -outform DER");
+ if (!file_exists($cert_der)) {
+ error_log("EST: simpleenroll - Failed to convert certificate");
+ die("Failed to convert certificate");
+ }
+ $der = file_get_contents($cert_der);
+ $fingerprint = hash("sha256", $der);
+ error_log("EST: sha256(DER cert): $fingerprint");
+
+ $pkcs7 = "$cadir/tmp/est-client.pkcs7";
+ if (file_exists($pkcs7))
+ unlink($pkcs7);
+ exec("openssl crl2pkcs7 -nocrl -certfile $cert_pem -out $pkcs7 -outform DER");
+ if (!file_exists($pkcs7)) {
+ error_log("EST: simpleenroll - Failed to prepare PKCS#7 file");
+ die("Failed to prepare PKCS#7 file");
+ }
+
+ if (!$db->exec("UPDATE sessions SET user='$user', cert='$fingerprint', cert_pem='$cert' WHERE rowid=$rowid")) {
+ error_log("EST: simpleenroll - Failed to update session database");
+ die("Failed to update session database");
+ }
+
+ header("Content-Transfer-Encoding: base64");
+ header("Content-Type: application/pkcs7-mime");
+
+ $data = file_get_contents($pkcs7);
+ $resp = wordwrap(base64_encode($data), 72, "\n", true);
+ echo $resp . "\n";
+ error_log("EST: simpleenroll - PKCS#7 response: " . $resp);
+} else {
+ header("HTTP/1.0 404 Not Found");
+ error_log("EST: Unexpected method or path");
+ die("Unexpected method or path");
+}
+
+?>
diff --git a/contrib/wpa/hs20/server/www/free-remediation.php b/contrib/wpa/hs20/server/www/free-remediation.php
new file mode 100644
index 000000000000..5648b30e8d6b
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/free-remediation.php
@@ -0,0 +1,19 @@
+<html>
+<head>
+<title>Hotspot 2.0 - public and free hotspot - remediation</title>
+</head>
+<body>
+
+<h3>Hotspot 2.0 - public and free hotspot</h3>
+
+<p>Terms and conditions have changed. You need to accept the new terms
+to continue using this network.</p>
+
+<p>Terms and conditions..</p>
+
+<?php
+echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Accept</a><br>\n";
+?>
+
+</body>
+</html>
diff --git a/contrib/wpa/hs20/server/www/free.php b/contrib/wpa/hs20/server/www/free.php
new file mode 100644
index 000000000000..8195069ed8ff
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/free.php
@@ -0,0 +1,23 @@
+<html>
+<head>
+<title>Hotspot 2.0 - public and free hotspot</title>
+</head>
+<body>
+
+<?php
+
+$id = $_GET["session_id"];
+
+echo "<h3>Hotspot 2.0 - public and free hotspot</h3>\n";
+
+echo "<form action=\"add-free.php\" method=\"POST\">\n";
+echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
+
+?>
+
+<p>Terms and conditions..</p>
+<input type="submit" value="Accept">
+</form>
+
+</body>
+</html>
diff --git a/contrib/wpa/hs20/server/www/redirect.php b/contrib/wpa/hs20/server/www/redirect.php
new file mode 100644
index 000000000000..8fc9cd644273
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/redirect.php
@@ -0,0 +1,32 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_GET["id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["id"]);
+else
+ $id = 0;
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+
+$uri = $row['redirect_uri'];
+
+header("Location: $uri", true, 302);
+
+$user = $row['user'];
+$realm = $row['realm'];
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('$user', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'redirected after user input')");
+
+?>
diff --git a/contrib/wpa/hs20/server/www/remediation-pw.php b/contrib/wpa/hs20/server/www/remediation-pw.php
new file mode 100644
index 000000000000..76fdccbdf9f7
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/remediation-pw.php
@@ -0,0 +1,41 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_POST["id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_POST["id"]);
+else
+ die("Missing session id");
+
+$pw = $_POST["password"];
+if (strlen($id) < 32 || !isset($pw)) {
+ die("Invalid POST data");
+}
+
+$row = $db->query("SELECT rowid,* FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+$user = $row['user'];
+$realm = $row['realm'];
+
+$uri = $row['redirect_uri'];
+$rowid = $row['rowid'];
+
+if (!$db->exec("UPDATE sessions SET password='$pw' WHERE rowid=$rowid")) {
+ die("Failed to update session database");
+}
+
+$db->exec("INSERT INTO eventlog(user,realm,sessionid,timestamp,notes) " .
+ "VALUES ('$user', '$realm', '$id', " .
+ "strftime('%Y-%m-%d %H:%M:%f','now'), " .
+ "'completed user input response for subscription remediation')");
+
+header("Location: $uri", true, 302);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/remediation.php b/contrib/wpa/hs20/server/www/remediation.php
new file mode 100644
index 000000000000..3628065ac225
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/remediation.php
@@ -0,0 +1,55 @@
+<html>
+<head>
+<title>Hotspot 2.0 subscription remediation</title>
+</head>
+<body>
+
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_GET["session_id"]))
+ $id = preg_replace("/[^a-fA-F0-9]/", "", $_GET["session_id"]);
+else
+ $id = 0;
+echo "SessionID: " . $id . "<br>\n";
+
+$row = $db->query("SELECT * FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found");
+}
+
+$username = $row['user'];
+echo "User: " . $username . "@" . $row['realm'] . "<br>\n";
+
+$user = $db->query("SELECT machine_managed,methods FROM users WHERE identity='$username'")->fetch();
+if ($user == false) {
+ die("User not found");
+}
+
+echo "<hr><br>\n";
+
+$cert = $user['methods'] == "TLS" || strncmp($username, "cert-", 5) == 0;
+
+if ($cert) {
+ echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
+} else if ($user['machine_managed'] == "1") {
+ echo "<a href=\"redirect.php?id=" . $_GET["session_id"] . "\">Complete user subscription remediation</a><br>\n";
+ echo "This will provide a new machine-generated password.<br>\n";
+} else {
+ echo "<form action=\"remediation-pw.php\" method=\"POST\">\n";
+ echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
+ echo "New password: <input type=\"password\" name=\"password\"><br>\n";
+ echo "<input type=\"submit\" value=\"Change password\">\n";
+ echo "</form>\n";
+}
+
+?>
+
+</body>
+</html>
diff --git a/contrib/wpa/hs20/server/www/signup.php b/contrib/wpa/hs20/server/www/signup.php
new file mode 100644
index 000000000000..80a9d403e8fc
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/signup.php
@@ -0,0 +1,59 @@
+<html>
+<head>
+<title>Hotspot 2.0 signup</title>
+</head>
+<body>
+
+<?php
+
+$id = $_GET["session_id"];
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+$row = $db->query("SELECT realm,test FROM sessions WHERE id='$id'")->fetch();
+if ($row == false) {
+ die("Session not found for id: $id");
+}
+$realm = $row['realm'];
+$test = $row['test'];
+
+if (strlen($test) > 0) {
+ echo "<p style=\"color:#FF0000\">Special test functionality: $test</red></big></p>\n";
+}
+
+echo "<h3>Sign up for a subscription - $realm</h3>\n";
+
+echo "<p>This page can be used to select between three different types of subscriptions for testing purposes.</p>\n";
+
+echo "<h4>Option 1 - shared free access credential</h4>\n";
+
+$row = $db->query("SELECT value FROM osu_config WHERE realm='$realm' AND field='free_account'")->fetch();
+if ($row && strlen($row['value']) > 0) {
+ echo "<p><a href=\"free.php?session_id=$id\">Sign up for free access</a></p>\n";
+}
+
+echo "<h4>Option 2 - username/password credential</h4>\n";
+
+echo "<form action=\"add-mo.php\" method=\"POST\">\n";
+echo "<input type=\"hidden\" name=\"id\" value=\"$id\">\n";
+?>
+Select a username and password. Leave password empty to get automatically
+generated and machine managed password.<br>
+Username: <input type="text" name="user"><br>
+Password: <input type="password" name="password"><br>
+<input type="submit" value="Complete subscription registration">
+</form>
+
+<?php
+echo "<h4>Option 3 - client certificate credential</h4>\n";
+
+echo "<p><a href=\"cert-enroll.php?id=$id\">Enroll a client certificate</a></p>\n"
+?>
+
+</body>
+</html>
diff --git a/contrib/wpa/hs20/server/www/spp.php b/contrib/wpa/hs20/server/www/spp.php
new file mode 100644
index 000000000000..c56d3d69e0ed
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/spp.php
@@ -0,0 +1,168 @@
+<?php
+
+require('config.php');
+
+if (!stristr($_SERVER["CONTENT_TYPE"], "application/soap+xml")) {
+ error_log("spp.php - Unexpected Content-Type " . $_SERVER["CONTENT_TYPE"]);
+ die("Unexpected Content-Type");
+}
+
+if ($_SERVER["REQUEST_METHOD"] != "POST") {
+ error_log("spp.php - Unexpected method " . $_SERVER["REQUEST_METHOD"]);
+ die("Unexpected method");
+}
+
+if (isset($_GET["realm"])) {
+ $realm = $_GET["realm"];
+ $realm = PREG_REPLACE("/[^0-9a-zA-Z\.\-]/i", '', $realm);
+} else {
+ error_log("spp.php - Realm not specified");
+ die("Realm not specified");
+}
+
+if (isset($_GET["test"]))
+ $test = PREG_REPLACE("/[^0-9a-zA-Z\_\-]/i", '', $_GET["test"]);
+else
+ $test = "";
+
+unset($user);
+putenv("HS20CERT");
+
+if (!empty($_SERVER['PHP_AUTH_DIGEST'])) {
+ $needed = array('nonce'=>1, 'nc'=>1, 'cnonce'=>1, 'qop'=>1, 'username'=>1,
+ 'uri'=>1, 'response'=>1);
+ $data = array();
+ $keys = implode('|', array_keys($needed));
+ preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@',
+ $_SERVER['PHP_AUTH_DIGEST'], $matches, PREG_SET_ORDER);
+ foreach ($matches as $m) {
+ $data[$m[1]] = $m[3] ? $m[3] : $m[4];
+ unset($needed[$m[1]]);
+ }
+ if ($needed) {
+ error_log("spp.php - Authentication failed - missing: " . print_r($needed));
+ die('Authentication failed');
+ }
+ $user = $data['username'];
+ if (strlen($user) < 1) {
+ error_log("spp.php - Authentication failed - empty username");
+ die('Authentication failed');
+ }
+
+
+ $db = new PDO($osu_db);
+ if (!$db) {
+ error_log("spp.php - Could not access database");
+ die("Could not access database");
+ }
+ $row = $db->query("SELECT password FROM users " .
+ "WHERE identity='$user' AND realm='$realm'")->fetch();
+ if (!$row) {
+ $row = $db->query("SELECT osu_password FROM users " .
+ "WHERE osu_user='$user' AND realm='$realm'")->fetch();
+ $pw = $row['osu_password'];
+ } else
+ $pw = $row['password'];
+ if (!$row) {
+ error_log("spp.php - Authentication failed - user '$user' not found");
+ die('Authentication failed');
+ }
+ if (strlen($pw) < 1) {
+ error_log("spp.php - Authentication failed - empty password");
+ die('Authentication failed');
+ }
+
+ $A1 = md5($user . ':' . $realm . ':' . $pw);
+ $A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
+ $resp = md5($A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' .
+ $data['cnonce'] . ':' . $data['qop'] . ':' . $A2);
+ if ($data['response'] != $resp) {
+ error_log("Authentication failure - response mismatch");
+ die('Authentication failed');
+ }
+} else if (isset($_SERVER["SSL_CLIENT_VERIFY"]) &&
+ $_SERVER["SSL_CLIENT_VERIFY"] == "SUCCESS" &&
+ isset($_SERVER["SSL_CLIENT_M_SERIAL"])) {
+ $user = "cert-" . $_SERVER["SSL_CLIENT_M_SERIAL"];
+ putenv("HS20CERT=yes");
+} else if (isset($_GET["hotspot2dot0-mobile-identifier-hash"])) {
+ $id_hash = $_GET["hotspot2dot0-mobile-identifier-hash"];
+ $id_hash = PREG_REPLACE("/[^0-9a-h]/i", '', $id_hash);
+
+ $db = new PDO($osu_db);
+ if (!$db) {
+ error_log("spp.php - Could not access database");
+ die("Could not access database");
+ }
+
+ $row = $db->query("SELECT * FROM sim_provisioning " .
+ "WHERE mobile_identifier_hash='$id_hash'")->fetch();
+ if (!$row) {
+ error_log("spp.php - SIM provisioning failed - mobile_identifier_hash not found");
+ die('SIM provisioning failed - mobile_identifier_hash not found');
+ }
+
+ $imsi = $row['imsi'];
+ $mac_addr = $row['mac_addr'];
+ $eap_method = $row['eap_method'];
+
+ $row = $db->query("SELECT COUNT(*) FROM osu_config " .
+ "WHERE realm='$realm'")->fetch();
+ if (!$row || intval($row[0]) < 1) {
+ error_log("spp.php - SIM provisioning failed - realm $realm not found");
+ die('SIM provisioning failed');
+ }
+
+ error_log("spp.php - SIM provisioning for IMSI $imsi");
+ putenv("HS20SIMPROV=yes");
+ putenv("HS20IMSI=$imsi");
+ putenv("HS20MACADDR=$mac_addr");
+ putenv("HS20EAPMETHOD=$eap_method");
+ putenv("HS20IDHASH=$id_hash");
+} else if (!isset($_SERVER["PATH_INFO"]) ||
+ $_SERVER["PATH_INFO"] != "/signup") {
+ header('HTTP/1.1 401 Unauthorized');
+ header('WWW-Authenticate: Digest realm="'.$realm.
+ '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+ error_log("spp.php - Authentication required (not signup)");
+ die('Authentication required (not signup)');
+}
+
+
+if (isset($user) && strlen($user) > 0)
+ putenv("HS20USER=$user");
+else
+ putenv("HS20USER");
+
+putenv("HS20REALM=$realm");
+$postdata = file_get_contents("php://input");
+putenv("HS20POST=$postdata");
+$addr = $_SERVER["REMOTE_ADDR"];
+putenv("HS20ADDR=$addr");
+putenv("HS20TEST=$test");
+
+$last = exec("$osu_root/spp/hs20_spp_server -r$osu_root -f/tmp/hs20_spp_server.log", $output, $ret);
+
+if ($ret == 2) {
+ if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
+ header('HTTP/1.1 401 Unauthorized');
+ header('WWW-Authenticate: Digest realm="'.$realm.
+ '",qop="auth",nonce="'.uniqid().'",opaque="'.md5($realm).'"');
+ error_log("spp.php - Authentication required (ret 2)");
+ die('Authentication required');
+ } else {
+ error_log("spp.php - Unexpected authentication error");
+ die("Unexpected authentication error");
+ }
+}
+if ($ret != 0) {
+ error_log("spp.php - Failed to process SPP request");
+ die("Failed to process SPP request");
+}
+//error_log("spp.php: Response: " . implode($output));
+
+header("Content-Type: application/soap+xml");
+
+echo implode($output);
+
+?>
diff --git a/contrib/wpa/hs20/server/www/terms.php b/contrib/wpa/hs20/server/www/terms.php
new file mode 100644
index 000000000000..acba23ef1ad7
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/terms.php
@@ -0,0 +1,87 @@
+<?php
+
+require('config.php');
+
+function print_header()
+{
+ echo "<html>\n";
+ echo "<head><title>HS 2.0 Terms and Conditions</title></head>\n";
+ echo "<body>\n";
+}
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (!isset($_GET["addr"])) {
+ die("Missing addr parameter");
+}
+$addr = $_GET["addr"];
+
+$accept = isset($_GET["accept"]) && $_GET["accept"] == "yes";
+
+$res = $db->prepare("SELECT identity FROM pending_tc WHERE mac_addr=?");
+$res->execute(array($addr));
+$row = $res->fetch();
+if (!$row) {
+ die("No pending session for the specified MAC address");
+}
+$identity = $row[0];
+
+if (!$accept) {
+ print_header();
+
+ echo "<p>Accept the following terms and conditions by clicking here: <a href=\"terms.php?addr=$addr&accept=yes\">Accept</a></p>\n<hr>\n";
+ readfile($t_c_file);
+} else {
+ $res = $db->prepare("UPDATE users SET t_c_timestamp=? WHERE identity=?");
+ if (!$res->execute(array($t_c_timestamp, $identity))) {
+ die("Failed to update user account.");
+ }
+
+ $res = $db->prepare("DELETE FROM pending_tc WHERE mac_addr=?");
+ $res->execute(array($addr));
+
+ $fp = fsockopen($hostapd_ctrl);
+ if (!$fp) {
+ die("Could not connect to hostapd(AS)");
+ }
+
+ fwrite($fp, "DAC_REQUEST coa $addr t_c_clear");
+ fclose($fp);
+
+ $waiting = true;
+ $ack = false;
+ for ($i = 1; $i <= 10; $i++) {
+ $res = $db->prepare("SELECT waiting_coa_ack,coa_ack_received FROM current_sessions WHERE mac_addr=?");
+ $res->execute(array($addr));
+ $row = $res->fetch();
+ if (!$row) {
+ die("No current session for the specified MAC address");
+ }
+ if (strlen($row[0]) > 0)
+ $waiting = $row[0] == 1;
+ if (strlen($row[1]) > 0)
+ $ack = $row[1] == 1;
+ $res->closeCursor();
+ if (!$waiting)
+ break;
+ sleep(1);
+ }
+ if ($ack) {
+ header('X-WFA-Hotspot20-Filtering: removed');
+ print_header();
+ echo "<p>Terms and conditions were accepted.</p>\n";
+
+ echo "<P>Filtering disabled.</P>\n";
+ } else {
+ print_header();
+ echo "<P>Failed to disable filtering.</P>\n";
+ }
+}
+
+?>
+
+</body>
+</html>
diff --git a/contrib/wpa/hs20/server/www/users.php b/contrib/wpa/hs20/server/www/users.php
new file mode 100644
index 000000000000..2bd555275dda
--- /dev/null
+++ b/contrib/wpa/hs20/server/www/users.php
@@ -0,0 +1,377 @@
+<?php
+
+require('config.php');
+
+$db = new PDO($osu_db);
+if (!$db) {
+ die($sqliteerror);
+}
+
+if (isset($_GET["id"])) {
+ $id = $_GET["id"];
+ if (!is_numeric($id))
+ $id = 0;
+} else
+ $id = 0;
+if (isset($_GET["cmd"]))
+ $cmd = $_GET["cmd"];
+else
+ $cmd = '';
+
+if ($cmd == 'eventlog' && $id > 0) {
+ $row = $db->query("SELECT dump FROM eventlog WHERE rowid=$id")->fetch();
+ $dump = $row['dump'];
+ if ($dump[0] == '<') {
+ header("Content-type: text/xml");
+ echo "<?xml version=\"1.0\"?>\n";
+ echo $dump;
+ } else {
+ header("Content-type: text/plain");
+ echo $dump;
+ }
+ exit;
+}
+
+if ($cmd == 'mo' && $id > 0) {
+ $mo = $_GET["mo"];
+ if (!isset($mo))
+ exit;
+ if ($mo != "devinfo" && $mo != "devdetail" && $mo != "pps")
+ exit;
+ $row = $db->query("SELECT $mo FROM users WHERE rowid=$id")->fetch();
+ header("Content-type: text/xml");
+ echo "<?xml version=\"1.0\"?>\n";
+ echo $row[$mo];
+ exit;
+}
+
+if ($cmd == 'cert' && $id > 0) {
+ $row = $db->query("SELECT cert_pem FROM users WHERE rowid=$id")->fetch();
+ header("Content-type: text/plain");
+ echo $row['cert_pem'];
+ exit;
+}
+
+?>
+
+<html>
+<head><title>HS 2.0 users</title></head>
+<body>
+
+<?php
+
+if ($cmd == 'subrem-clear' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-user' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='user' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-machine' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='machine' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-reenroll' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='reenroll' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-policy' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='policy' WHERE rowid=$id");
+}
+if ($cmd == 'subrem-add-free' && $id > 0) {
+ $db->exec("UPDATE users SET remediation='free' WHERE rowid=$id");
+}
+if ($cmd == 'fetch-pps-on' && $id > 0) {
+ $db->exec("UPDATE users SET fetch_pps=1 WHERE rowid=$id");
+}
+if ($cmd == 'fetch-pps-off' && $id > 0) {
+ $db->exec("UPDATE users SET fetch_pps=0 WHERE rowid=$id");
+}
+if ($cmd == 'reset-pw' && $id > 0) {
+ $db->exec("UPDATE users SET password='ChangeMe' WHERE rowid=$id");
+}
+if ($cmd == "policy" && $id > 0 && isset($_GET["policy"])) {
+ $policy = $_GET["policy"];
+ if ($policy == "no-policy" ||
+ is_readable("$osu_root/spp/policy/$policy.xml")) {
+ $db->exec("UPDATE users SET policy='$policy' WHERE rowid=$id");
+ }
+}
+if ($cmd == "account-type" && $id > 0 && isset($_GET["type"])) {
+ $type = $_GET["type"];
+ if ($type == "shared")
+ $db->exec("UPDATE users SET shared=1 WHERE rowid=$id");
+ if ($type == "default")
+ $db->exec("UPDATE users SET shared=0 WHERE rowid=$id");
+}
+
+if ($cmd == "set-osu-cred" && $id > 0) {
+ $osu_user = $_POST["osu_user"];
+ $osu_password = $_POST["osu_password"];
+ if (strlen($osu_user) == 0)
+ $osu_password = "";
+ $db->exec("UPDATE users SET osu_user='$osu_user', osu_password='$osu_password' WHERE rowid=$id");
+}
+
+if ($cmd == 'clear-t-c' && $id > 0) {
+ $db->exec("UPDATE users SET t_c_timestamp=NULL WHERE rowid=$id");
+}
+
+$dump = 0;
+
+if ($id > 0) {
+
+if (isset($_GET["dump"])) {
+ $dump = $_GET["dump"];
+ if (!is_numeric($dump))
+ $dump = 0;
+} else
+ $dump = 0;
+
+echo "[<a href=\"users.php\">All users</a>] ";
+if ($dump == 0)
+ echo "[<a href=\"users.php?id=$id&dump=1\">Include debug dump</a>] ";
+else
+ echo "[<a href=\"users.php?id=$id\">Without debug dump</a>] ";
+echo "<br>\n";
+
+$row = $db->query("SELECT rowid,* FROM users WHERE rowid=$id")->fetch();
+
+echo "<H3>" . $row['identity'] . "@" . $row['realm'] . "</H3>\n";
+
+echo "MO: ";
+if (strlen($row['devinfo']) > 0) {
+ echo "[<a href=\"users.php?cmd=mo&id=$id&mo=devinfo\">DevInfo</a>]\n";
+}
+if (strlen($row['devdetail']) > 0) {
+ echo "[<a href=\"users.php?cmd=mo&id=$id&mo=devdetail\">DevDetail</a>]\n";
+}
+if (strlen($row['pps']) > 0) {
+ echo "[<a href=\"users.php?cmd=mo&id=$id&mo=pps\">PPS</a>]\n";
+}
+if (strlen($row['cert_pem']) > 0) {
+ echo "[<a href=\"users.php?cmd=cert&id=$id\">Certificate</a>]\n";
+}
+echo "<BR>\n";
+
+echo "Fetch PPS MO: ";
+if ($row['fetch_pps'] == "1") {
+ echo "On next connection " .
+ "[<a href=\"users.php?cmd=fetch-pps-off&id=$id\">" .
+ "do not fetch</a>]<br>\n";
+} else {
+ echo "Do not fetch " .
+ "[<a href=\"users.php?cmd=fetch-pps-on&id=$id\">" .
+ "request fetch</a>]<br>\n";
+}
+
+$cert = $row['cert'];
+if (strlen($cert) > 0) {
+ echo "Certificate fingerprint: $cert<br>\n";
+}
+
+echo "Remediation: ";
+$rem = $row['remediation'];
+if ($rem == "") {
+ echo "Not required";
+ echo " [<a href=\"users.php?cmd=subrem-add-user&id=" .
+ $row['rowid'] . "\">add:user</a>]";
+ echo " [<a href=\"users.php?cmd=subrem-add-machine&id=" .
+ $row['rowid'] . "\">add:machine</a>]";
+ if ($row['methods'] == 'TLS') {
+ echo " [<a href=\"users.php?cmd=subrem-add-reenroll&id=" .
+ $row['rowid'] . "\">add:reenroll</a>]";
+ }
+ echo " [<a href=\"users.php?cmd=subrem-add-policy&id=" .
+ $row['rowid'] . "\">add:policy</a>]";
+ echo " [<a href=\"users.php?cmd=subrem-add-free&id=" .
+ $row['rowid'] . "\">add:free</a>]";
+} else if ($rem == "user") {
+ echo "User [<a href=\"users.php?cmd=subrem-clear&id=" .
+ $row['rowid'] . "\">clear</a>]";
+} else if ($rem == "policy") {
+ echo "Policy [<a href=\"users.php?cmd=subrem-clear&id=" .
+ $row['rowid'] . "\">clear</a>]";
+} else if ($rem == "free") {
+ echo "Free [<a href=\"users.php?cmd=subrem-clear&id=" .
+ $row['rowid'] . "\">clear</a>]";
+} else if ($rem == "reenroll") {
+ echo "Reenroll [<a href=\"users.php?cmd=subrem-clear&id=" .
+ $row['rowid'] . "\">clear</a>]";
+} else {
+ echo "Machine [<a href=\"users.php?cmd=subrem-clear&id=" .
+ $row['rowid'] . "\">clear</a>]";
+}
+echo "<br>\n";
+
+if (strncmp($row['identity'], "cert-", 5) != 0)
+ echo "Machine managed: " . ($row['machine_managed'] == "1" ? "TRUE" : "FALSE") . "<br>\n";
+
+echo "<form>Policy: <select name=\"policy\" " .
+ "onChange=\"window.location='users.php?cmd=policy&id=" .
+ $row['rowid'] . "&policy=' + this.value;\">\n";
+echo "<option value=\"" . $row['policy'] . "\" selected>" . $row['policy'] .
+ "</option>\n";
+$files = scandir("$osu_root/spp/policy");
+foreach ($files as $file) {
+ if (!preg_match("/.xml$/", $file))
+ continue;
+ if ($file == $row['policy'] . ".xml")
+ continue;
+ $p = substr($file, 0, -4);
+ echo "<option value=\"$p\">$p</option>\n";
+}
+echo "<option value=\"no-policy\">no policy</option>\n";
+echo "</select></form>\n";
+
+echo "<form>Account type: <select name=\"type\" " .
+ "onChange=\"window.location='users.php?cmd=account-type&id=" .
+ $row['rowid'] . "&type=' + this.value;\">\n";
+if ($row['shared'] > 0) {
+ $default_sel = "";
+ $shared_sel = " selected";
+} else {
+ $default_sel = " selected";
+ $shared_sel = "";
+}
+echo "<option value=\"default\"$default_sel>default</option>\n";
+echo "<option value=\"shared\"$shared_sel>shared</option>\n";
+echo "</select></form>\n";
+
+echo "Phase 2 method(s): " . $row['methods'] . "<br>\n";
+
+echo "<br>\n";
+echo "<a href=\"users.php?cmd=reset-pw&id=" .
+ $row['rowid'] . "\">Reset AAA password</a><br>\n";
+
+echo "<br>\n";
+echo "<form action=\"users.php?cmd=set-osu-cred&id=" . $row['rowid'] .
+ "\" method=\"POST\">\n";
+echo "OSU credentials (if username empty, AAA credentials are used):<br>\n";
+echo "username: <input type=\"text\" name=\"osu_user\" value=\"" .
+ $row['osu_user'] . "\">\n";
+echo "password: <input type=\"password\" name=\"osu_password\">\n";
+echo "<input type=\"submit\" value=\"Set OSU credentials\">\n";
+echo "</form>\n";
+
+if (strlen($row['t_c_timestamp']) > 0) {
+ echo "<br>\n";
+ echo "<a href=\"users.php?cmd=clear-t-c&id=" .
+ $row['rowid'] .
+ "\">Clear Terms and Conditions acceptance</a><br>\n";
+}
+
+echo "<hr>\n";
+
+$user = $row['identity'];
+$osu_user = $row['osu_user'];
+$realm = $row['realm'];
+}
+
+if ($id > 0 || ($id == 0 && $cmd == 'eventlog')) {
+
+ if ($id == 0) {
+ echo "[<a href=\"users.php\">All users</a>] ";
+ echo "<br>\n";
+ }
+
+echo "<table border=1>\n";
+echo "<tr>";
+if ($id == 0) {
+ echo "<th>user<th>realm";
+}
+echo "<th>time<th>address<th>sessionID<th>notes";
+if ($dump > 0)
+ echo "<th>dump";
+echo "\n";
+if (isset($_GET["limit"])) {
+ $limit = $_GET["limit"];
+ if (!is_numeric($limit))
+ $limit = 20;
+} else
+ $limit = 20;
+if ($id == 0)
+ $res = $db->query("SELECT rowid,* FROM eventlog ORDER BY timestamp DESC LIMIT $limit");
+else if (strlen($osu_user) > 0)
+ $res = $db->query("SELECT rowid,* FROM eventlog WHERE (user='$user' OR user='$osu_user') AND realm='$realm' ORDER BY timestamp DESC LIMIT $limit");
+else
+ $res = $db->query("SELECT rowid,* FROM eventlog WHERE user='$user' AND realm='$realm' ORDER BY timestamp DESC LIMIT $limit");
+foreach ($res as $row) {
+ echo "<tr>";
+ if ($id == 0) {
+ echo "<td>" . $row['user'] . "\n";
+ echo "<td>" . $row['realm'] . "\n";
+ }
+ echo "<td>" . $row['timestamp'] . "\n";
+ echo "<td>" . $row['addr'] . "\n";
+ echo "<td>" . $row['sessionid'] . "\n";
+ echo "<td>" . $row['notes'] . "\n";
+ $d = $row['dump'];
+ if (strlen($d) > 0) {
+ echo "[<a href=\"users.php?cmd=eventlog&id=" . $row['rowid'] .
+ "\">";
+ if ($d[0] == '<')
+ echo "XML";
+ else
+ echo "txt";
+ echo "</a>]\n";
+ if ($dump > 0)
+ echo "<td>" . htmlspecialchars($d) . "\n";
+ }
+}
+echo "</table>\n";
+
+}
+
+
+if ($id == 0 && $cmd != 'eventlog') {
+
+echo "[<a href=\"users.php?cmd=eventlog&limit=50\">Eventlog</a>] ";
+echo "<br>\n";
+
+echo "<table border=1 cellspacing=0 cellpadding=0>\n";
+echo "<tr><th>User<th>Realm<th><small>Remediation</small><th>Policy<th><small>Account type</small><th><small>Phase 2 method(s)</small><th>DevId<th>MAC Address<th>T&C\n";
+
+$res = $db->query('SELECT rowid,* FROM users WHERE (phase2=1 OR methods=\'TLS\') ORDER BY identity');
+foreach ($res as $row) {
+ echo "<tr><td><a href=\"users.php?id=" . $row['rowid'] . "\"> " .
+ $row['identity'] . " </a>";
+ echo "<td>" . $row['realm'];
+ $rem = $row['remediation'];
+ echo "<td>";
+ if ($rem == "") {
+ echo "-";
+ } else if ($rem == "user") {
+ echo "User";
+ } else if ($rem == "policy") {
+ echo "Policy";
+ } else if ($rem == "free") {
+ echo "Free";
+ } else if ($rem == "reenroll") {
+ echo "Reenroll";
+ } else {
+ echo "Machine";
+ }
+ echo "<td>" . $row['policy'];
+ if ($row['shared'] > 0)
+ echo "<td>shared";
+ else
+ echo "<td>default";
+ echo "<td><small>" . $row['methods'] . "</small>";
+ echo "<td>";
+ $xml = xml_parser_create();
+ xml_parse_into_struct($xml, $row['devinfo'], $devinfo);
+ foreach($devinfo as $k) {
+ if ($k['tag'] == 'DEVID') {
+ echo "<small>" . $k['value'] . "</small>";
+ break;
+ }
+ }
+ echo "<td><small>" . $row['mac_addr'] . "</small>";
+ echo "<td><small>" . $row['t_c_timestamp'] . "</small>";
+ echo "\n";
+}
+echo "</table>\n";
+
+}
+
+?>
+
+</html>
diff --git a/contrib/wpa/radius_example/.gitignore b/contrib/wpa/radius_example/.gitignore
new file mode 100644
index 000000000000..c43e0faab91c
--- /dev/null
+++ b/contrib/wpa/radius_example/.gitignore
@@ -0,0 +1,2 @@
+*.d
+radius_example
diff --git a/contrib/wpa/radius_example/Makefile b/contrib/wpa/radius_example/Makefile
new file mode 100644
index 000000000000..d58a82c340c6
--- /dev/null
+++ b/contrib/wpa/radius_example/Makefile
@@ -0,0 +1,28 @@
+ALL=radius_example
+
+include ../src/build.rules
+
+CFLAGS += -I.
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+LIBS = ../src/radius/libradius.a
+LIBS += ../src/crypto/libcrypto.a
+LIBS += ../src/utils/libutils.a
+LLIBS = -lrt
+
+#CLAGS += -DCONFIG_IPV6
+
+OBJS_ex = radius_example.o
+
+_OBJS_VAR := OBJS_ex
+include ../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../src/objs.mk
+
+radius_example: $(OBJS_ex) $(LIBS)
+ $(LDO) $(LDFLAGS) -o radius_example $(OBJS_ex) $(LIBS) $(LLIBS)
+
+clean: common-clean
+ rm -f core *~ *.o *.d
diff --git a/contrib/wpa/radius_example/README b/contrib/wpa/radius_example/README
new file mode 100644
index 000000000000..ec458e3ad7f5
--- /dev/null
+++ b/contrib/wpa/radius_example/README
@@ -0,0 +1,35 @@
+Example application using RADIUS client as a library
+Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+
+This software may be distributed under the terms of the BSD license.
+See the parent directory README for more details.
+
+
+This directory contains an example showing how the RADIUS client
+functionality from hostapd can be used as a library in another
+program. The example program initializes the RADIUS client and send a
+Access-Request using User-Name and User-Password attributes. A reply
+from the RADIUS authentication server will be processed and it is used
+as a trigger to terminate the example program.
+
+The RADIUS library links in couple of helper functions from src/utils and
+src/crypto directories. Most of these are suitable as-is, but it may
+be desirable to replace the debug output code in src/utils/wpa_debug.c
+by dropping this file from the library and re-implementing the
+functions there in a way that better fits in with the main
+application.
+
+RADIUS client implementation takes care of receiving messages,
+timeouts, and retransmissions of packets. Consequently, it requires
+functionality for registering timeouts and received packet
+notifications. This is implemented using the generic event loop
+implementation (see src/utils/eloop.h).
+
+The main application may either use the included event loop
+implementation or alternatively, implement eloop_* wrapper functions
+to use whatever event loop design is used in the main program. This
+would involve removing src/utils/eloop.o from the library and
+implementing following functions defines in src/utils/eloop.h:
+eloop_register_timeout(), eloop_cancel_timeout(),
+eloop_register_read_sock(), eloop_unregister_read_sock(), and
+eloop_terminated().
diff --git a/contrib/wpa/radius_example/radius_example.c b/contrib/wpa/radius_example/radius_example.c
new file mode 100644
index 000000000000..8b0f47586b05
--- /dev/null
+++ b/contrib/wpa/radius_example/radius_example.c
@@ -0,0 +1,153 @@
+/*
+ * Example application using RADIUS client as a library
+ * Copyright (c) 2007, 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 "common.h"
+#include "eloop.h"
+#include "radius/radius.h"
+#include "radius/radius_client.h"
+
+struct radius_ctx {
+ struct radius_client_data *radius;
+ struct hostapd_radius_servers conf;
+ u8 radius_identifier;
+ struct in_addr own_ip_addr;
+};
+
+
+static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
+ int level, const char *txt, size_t len)
+{
+ printf("%s\n", txt);
+}
+
+
+/* Process the RADIUS frames from Authentication Server */
+static RadiusRxResult receive_auth(struct radius_msg *msg,
+ struct radius_msg *req,
+ const u8 *shared_secret,
+ size_t shared_secret_len,
+ void *data)
+{
+ /* struct radius_ctx *ctx = data; */
+ printf("Received RADIUS Authentication message; code=%d\n",
+ radius_msg_get_hdr(msg)->code);
+
+ /* We're done for this example, so request eloop to terminate. */
+ eloop_terminate();
+
+ return RADIUS_RX_PROCESSED;
+}
+
+
+static void start_example(void *eloop_ctx, void *timeout_ctx)
+{
+ struct radius_ctx *ctx = eloop_ctx;
+ struct radius_msg *msg;
+
+ printf("Sending a RADIUS authentication message\n");
+
+ ctx->radius_identifier = radius_client_get_id(ctx->radius);
+ msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
+ ctx->radius_identifier);
+ if (msg == NULL) {
+ printf("Could not create net RADIUS packet\n");
+ return;
+ }
+
+ radius_msg_make_authenticator(msg);
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
+ (u8 *) "user", 4)) {
+ printf("Could not add User-Name\n");
+ radius_msg_free(msg);
+ return;
+ }
+
+ if (!radius_msg_add_attr_user_password(
+ msg, (u8 *) "password", 8,
+ ctx->conf.auth_server->shared_secret,
+ ctx->conf.auth_server->shared_secret_len)) {
+ printf("Could not add User-Password\n");
+ radius_msg_free(msg);
+ return;
+ }
+
+ if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+ (u8 *) &ctx->own_ip_addr, 4)) {
+ printf("Could not add NAS-IP-Address\n");
+ radius_msg_free(msg);
+ return;
+ }
+
+ if (radius_client_send(ctx->radius, msg, RADIUS_AUTH, NULL) < 0)
+ radius_msg_free(msg);
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct radius_ctx ctx;
+ struct hostapd_radius_server *srv;
+
+ if (os_program_init())
+ return -1;
+
+ hostapd_logger_register_cb(hostapd_logger_cb);
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ inet_aton("127.0.0.1", &ctx.own_ip_addr);
+
+ if (eloop_init()) {
+ printf("Failed to initialize event loop\n");
+ return -1;
+ }
+
+ srv = os_zalloc(sizeof(*srv));
+ if (srv == NULL)
+ return -1;
+
+ srv->addr.af = AF_INET;
+ srv->port = 1812;
+ if (hostapd_parse_ip_addr("127.0.0.1", &srv->addr) < 0) {
+ printf("Failed to parse IP address\n");
+ return -1;
+ }
+ srv->shared_secret = (u8 *) os_strdup("radius");
+ srv->shared_secret_len = 6;
+
+ ctx.conf.auth_server = ctx.conf.auth_servers = srv;
+ ctx.conf.num_auth_servers = 1;
+ ctx.conf.msg_dumps = 1;
+
+ ctx.radius = radius_client_init(&ctx, &ctx.conf);
+ if (ctx.radius == NULL) {
+ printf("Failed to initialize RADIUS client\n");
+ return -1;
+ }
+
+ if (radius_client_register(ctx.radius, RADIUS_AUTH, receive_auth,
+ &ctx) < 0) {
+ printf("Failed to register RADIUS authentication handler\n");
+ return -1;
+ }
+
+ eloop_register_timeout(0, 0, start_example, &ctx, NULL);
+
+ eloop_run();
+
+ radius_client_deinit(ctx.radius);
+ os_free(srv->shared_secret);
+ os_free(srv);
+
+ eloop_destroy();
+ os_program_deinit();
+
+ return 0;
+}
diff --git a/contrib/wpa/src/build.rules b/contrib/wpa/src/build.rules
new file mode 100644
index 000000000000..acda8847284d
--- /dev/null
+++ b/contrib/wpa/src/build.rules
@@ -0,0 +1,109 @@
+.PHONY: all
+all: _all
+
+# disable built-in rules
+.SUFFIXES:
+
+# setup some variables
+ROOTDIR := $(dir $(lastword $(MAKEFILE_LIST)))
+ROOTDIR := $(dir $(ROOTDIR:%../src/=%))../
+BUILDDIR ?= $(abspath $(ROOTDIR)build)
+BUILDDIR := $(BUILDDIR:%/=%)
+ABSROOT := $(abspath $(ROOTDIR))
+ifeq ($(origin OUT),command line)
+_PROJ := $(OUT:%/=%)
+_PROJ := $(_PROJ:$(BUILDDIR)/%=%)
+else
+_PROJ := $(abspath $(dir $(firstword $(MAKEFILE_LIST))))
+_PROJ := $(_PROJ:$(ABSROOT)/%=%)
+endif
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef RANLIB
+RANLIB=ranlib
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+ifneq ($(CONFIG_FILE),)
+-include $(CONFIG_FILE)
+
+# export for sub-makefiles
+export CONFIG_CODE_COVERAGE
+
+.PHONY: verify_config
+verify_config:
+ @if [ ! -r $(CONFIG_FILE) ]; then \
+ echo 'Building $(firstword $(ALL)) requires a configuration file'; \
+ echo '(.config). See README for more instructions. You can'; \
+ echo 'run "cp defconfig .config" to create an example'; \
+ echo 'configuration.'; \
+ exit 1; \
+ fi
+VERIFY := verify_config
+else
+VERIFY :=
+endif
+
+# default target
+.PHONY: _all
+_all: $(VERIFY) $(ALL) $(EXTRA_TARGETS)
+
+# continue setup
+COVSUFFIX := $(if $(CONFIG_CODE_COVERAGE),-cov,)
+PROJ := $(_PROJ)$(COVSUFFIX)
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
+
+ifeq ($(Q),@)
+MAKEFLAGS += --no-print-directory
+endif
+
+_DIRS := $(BUILDDIR)/$(PROJ)
+.PHONY: _make_dirs
+_make_dirs:
+ @mkdir -p $(_DIRS)
+
+$(BUILDDIR)/$(PROJ)/src/%.o: $(ROOTDIR)src/%.c $(CONFIG_FILE) | _make_dirs
+ $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+ @$(E) " CC " $<
+$(BUILDDIR)/$(PROJ)/%.o: %.c $(CONFIG_FILE) | _make_dirs
+ $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+ @$(E) " CC " $<
+# for the fuzzing tests
+$(BUILDDIR)/$(PROJ)/wpa_supplicant/%.o: $(ROOTDIR)wpa_supplicant/%.c $(CONFIG_FILE) | _make_dirs
+ $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+ @$(E) " CC " $<
+
+# libraries - they know how to build themselves
+# (lib_phony so we recurse all the time)
+.PHONY: lib_phony
+lib_phony:
+# nothing
+
+$(BUILDDIR)/$(PROJ)/%.a: $(CONFIG_FILE) lib_phony
+ $(Q)$(MAKE) -C $(ROOTDIR)$(dir $(@:$(BUILDDIR)/$(PROJ)/%=%)) OUT=$(abspath $(dir $@))/
+
+BUILDOBJ = $(patsubst %,$(BUILDDIR)/$(PROJ)/%,$(patsubst $(ROOTDIR)%,%,$(1)))
+
+.PHONY: common-clean
+common-clean:
+ $(Q)rm -rf $(ALL) $(BUILDDIR)/$(PROJ)
diff --git a/contrib/wpa/src/common/brcm_vendor.h b/contrib/wpa/src/common/brcm_vendor.h
new file mode 100644
index 000000000000..f163dea73768
--- /dev/null
+++ b/contrib/wpa/src/common/brcm_vendor.h
@@ -0,0 +1,156 @@
+/*
+ * Broadcom Corporation OUI and vendor specific assignments
+ * Copyright (c) 2020, Broadcom Corporation.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BRCM_VENDOR_H
+#define BRCM_VENDOR_H
+
+/*
+ * This file is a registry of identifier assignments from the Broadcom
+ * OUI 00:10:18 for purposes other than MAC address assignment. New identifiers
+ * can be assigned through normal review process for changes to the upstream
+ * hostap.git repository.
+ */
+
+#define OUI_BRCM 0x001018
+
+/**
+ * enum brcm_nl80211_vendor_subcmds - BRCM nl80211 vendor command identifiers
+ *
+ * @BRCM_VENDOR_SCMD_UNSPEC: Reserved value 0
+ *
+ * @BRCM_VENDOR_SCMD_PRIV_STR: Provide vendor private cmds to send to FW.
+ *
+ * @BRCM_VENDOR_SCMD_BCM_STR: Provide vendor cmds to BCMDHD driver.
+ *
+ * @BRCM_VENDOR_SCMD_BCM_PSK: Used to set SAE password.
+ *
+ * @BRCM_VENDOR_SCMD_SET_PMK: Command to check driver support
+ * for DFS offloading.
+ *
+ * @BRCM_VENDOR_SCMD_GET_FEATURES: Command to get the features
+ * supported by the driver.
+ *
+ * @BRCM_VENDOR_SCMD_SET_MAC: Set random mac address for P2P interface.
+ *
+ * @BRCM_VENDOR_SCMD_SET_CONNECT_PARAMS: Set some connect parameters.
+ * Used for the case that FW handle SAE.
+ *
+ * @BRCM_VENDOR_SCMD_SET_START_AP_PARAMS: Set SoftAP paramters.
+ * Used for the case that FW handle SAE.
+ *
+ * @BRCM_VENDOR_SCMD_ACS: ACS command/event which is used to
+ * invoke the ACS function in device and pass selected channels to
+ * hostapd. Uses enum qca_wlan_vendor_attr_acs_offload attributes.
+ *
+ * @BRCM_VENDOR_SCMD_MAX: This acts as a the tail of cmds list.
+ * Make sure it located at the end of the list.
+ *
+ */
+enum brcm_nl80211_vendor_subcmds {
+ BRCM_VENDOR_SCMD_UNSPEC = 0,
+ BRCM_VENDOR_SCMD_PRIV_STR = 1,
+ BRCM_VENDOR_SCMD_BCM_STR = 2,
+ BRCM_VENDOR_SCMD_BCM_PSK = 3,
+ BRCM_VENDOR_SCMD_SET_PMK = 4,
+ BRCM_VENDOR_SCMD_GET_FEATURES = 5,
+ BRCM_VENDOR_SCMD_SET_MAC = 6,
+ BRCM_VENDOR_SCMD_SET_CONNECT_PARAMS = 7,
+ BRCM_VENDOR_SCMD_SET_START_AP_PARAMS = 8,
+ BRCM_VENDOR_SCMD_ACS = 9,
+ BRCM_VENDOR_SCMD_MAX = 10
+};
+
+/**
+ * enum brcm_nl80211_vendor_events - BRCM nl80211 asynchoronous event identifiers
+ *
+ * @BRCM_VENDOR_EVENT_UNSPEC: Reserved value 0
+ *
+ * @BRCM_VENDOR_EVENT_PRIV_STR: String command/event
+ */
+enum brcm_nl80211_vendor_events {
+ BRCM_VENDOR_EVENT_UNSPEC = 0,
+ BRCM_VENDOR_EVENT_PRIV_STR = 1,
+ GOOGLE_GSCAN_SIGNIFICANT_EVENT = 2,
+ GOOGLE_GSCAN_GEOFENCE_FOUND_EVENT = 3,
+ GOOGLE_GSCAN_BATCH_SCAN_EVENT = 4,
+ GOOGLE_SCAN_FULL_RESULTS_EVENT = 5,
+ GOOGLE_RTT_COMPLETE_EVENT = 6,
+ GOOGLE_SCAN_COMPLETE_EVENT = 7,
+ GOOGLE_GSCAN_GEOFENCE_LOST_EVENT = 8,
+ GOOGLE_SCAN_EPNO_EVENT = 9,
+ GOOGLE_DEBUG_RING_EVENT = 10,
+ GOOGLE_FW_DUMP_EVENT = 11,
+ GOOGLE_PNO_HOTSPOT_FOUND_EVENT = 12,
+ GOOGLE_RSSI_MONITOR_EVENT = 13,
+ GOOGLE_MKEEP_ALIVE_EVENT = 14,
+
+ /*
+ * BRCM specific events should be placed after
+ * the Generic events so that enums don't mismatch
+ * between the DHD and HAL
+ */
+ GOOGLE_NAN_EVENT_ENABLED = 15,
+ GOOGLE_NAN_EVENT_DISABLED = 16,
+ GOOGLE_NAN_EVENT_SUBSCRIBE_MATCH = 17,
+ GOOGLE_NAN_EVENT_REPLIED = 18,
+ GOOGLE_NAN_EVENT_PUBLISH_TERMINATED = 19,
+ GOOGLE_NAN_EVENT_SUBSCRIBE_TERMINATED = 20,
+ GOOGLE_NAN_EVENT_DE_EVENT = 21,
+ GOOGLE_NAN_EVENT_FOLLOWUP = 22,
+ GOOGLE_NAN_EVENT_TRANSMIT_FOLLOWUP_IND = 23,
+ GOOGLE_NAN_EVENT_DATA_REQUEST = 24,
+ GOOGLE_NAN_EVENT_DATA_CONFIRMATION = 25,
+ GOOGLE_NAN_EVENT_DATA_END = 26,
+ GOOGLE_NAN_EVENT_BEACON = 27,
+ GOOGLE_NAN_EVENT_SDF = 28,
+ GOOGLE_NAN_EVENT_TCA = 29,
+ GOOGLE_NAN_EVENT_SUBSCRIBE_UNMATCH = 30,
+ GOOGLE_NAN_EVENT_UNKNOWN = 31,
+ GOOGLE_ROAM_EVENT_START = 32,
+ BRCM_VENDOR_EVENT_HANGED = 33,
+ BRCM_VENDOR_EVENT_SAE_KEY = 34,
+ BRCM_VENDOR_EVENT_BEACON_RECV = 35,
+ BRCM_VENDOR_EVENT_PORT_AUTHORIZED = 36,
+ GOOGLE_FILE_DUMP_EVENT = 37,
+ BRCM_VENDOR_EVENT_CU = 38,
+ BRCM_VENDOR_EVENT_WIPS = 39,
+ NAN_ASYNC_RESPONSE_DISABLED = 40,
+ BRCM_VENDOR_EVENT_RCC_INFO = 41,
+ BRCM_VENDOR_EVENT_ACS = 42,
+ BRCM_VENDOR_EVENT_LAST
+
+};
+
+#ifdef CONFIG_BRCM_SAE
+enum wifi_sae_key_attr {
+ BRCM_SAE_KEY_ATTR_BSSID,
+ BRCM_SAE_KEY_ATTR_PMK,
+ BRCM_SAE_KEY_ATTR_PMKID
+};
+#endif /* CONFIG_BRCM_SAE */
+
+enum wl_vendor_attr_acs_offload {
+ BRCM_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
+ BRCM_VENDOR_ATTR_ACS_PRIMARY_FREQ,
+ BRCM_VENDOR_ATTR_ACS_SECONDARY_FREQ,
+ BRCM_VENDOR_ATTR_ACS_VHT_SEG0_CENTER_CHANNEL,
+ BRCM_VENDOR_ATTR_ACS_VHT_SEG1_CENTER_CHANNEL,
+
+ BRCM_VENDOR_ATTR_ACS_HW_MODE,
+ BRCM_VENDOR_ATTR_ACS_HT_ENABLED,
+ BRCM_VENDOR_ATTR_ACS_HT40_ENABLED,
+ BRCM_VENDOR_ATTR_ACS_VHT_ENABLED,
+ BRCM_VENDOR_ATTR_ACS_CHWIDTH,
+ BRCM_VENDOR_ATTR_ACS_CH_LIST,
+ BRCM_VENDOR_ATTR_ACS_FREQ_LIST,
+
+ BRCM_VENDOR_ATTR_ACS_LAST
+};
+
+
+#endif /* BRCM_VENDOR_H */
diff --git a/contrib/wpa/src/common/dpp_auth.c b/contrib/wpa/src/common/dpp_auth.c
new file mode 100644
index 000000000000..0cabd647fb81
--- /dev/null
+++ b/contrib/wpa/src/common/dpp_auth.c
@@ -0,0 +1,1977 @@
+/*
+ * DPP authentication exchange
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * 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/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/random.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+u8 dpp_protocol_key_override[600];
+size_t dpp_protocol_key_override_len = 0;
+u8 dpp_nonce_override[DPP_MAX_NONCE_LEN];
+size_t dpp_nonce_override_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void dpp_build_attr_i_bootstrap_key_hash(struct wpabuf *msg,
+ const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: I-Bootstrap Key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_I_BOOTSTRAP_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+static void dpp_auth_success(struct dpp_authentication *auth)
+{
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication success - clear temporary keys");
+ os_memset(auth->Mx, 0, sizeof(auth->Mx));
+ auth->Mx_len = 0;
+ os_memset(auth->Nx, 0, sizeof(auth->Nx));
+ auth->Nx_len = 0;
+ os_memset(auth->Lx, 0, sizeof(auth->Lx));
+ auth->Lx_len = 0;
+ os_memset(auth->k1, 0, sizeof(auth->k1));
+ os_memset(auth->k2, 0, sizeof(auth->k2));
+
+ auth->auth_success = 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
+ const struct wpabuf *pi,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ unsigned int neg_freq)
+{
+ struct wpabuf *msg;
+ u8 clear[4 + DPP_MAX_NONCE_LEN + 4 + 1];
+ u8 wrapped_data[4 + DPP_MAX_NONCE_LEN + 4 + 1 + AES_BLOCK_SIZE];
+ u8 *pos;
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end;
+
+ /* Build DPP Authentication Request frame attributes */
+ attr_len = 2 * (4 + SHA256_MAC_LEN) + 4 + (pi ? wpabuf_len(pi) : 0) +
+ 4 + sizeof(wrapped_data);
+ if (neg_freq > 0)
+ attr_len += 4 + 2;
+#ifdef CONFIG_DPP2
+ attr_len += 5;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_REQ, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Initiator Protocol Key */
+ if (pi) {
+ wpabuf_put_le16(msg, DPP_ATTR_I_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pi));
+ wpabuf_put_buf(msg, pi);
+ }
+
+ /* Channel */
+ if (neg_freq > 0) {
+ u8 op_class, channel;
+
+ if (ieee80211_freq_to_channel_ext(neg_freq, 0, 0, &op_class,
+ &channel) ==
+ NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported negotiation frequency request: %d",
+ neg_freq);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_put_le16(msg, DPP_ATTR_CHANNEL);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_u8(msg, op_class);
+ wpabuf_put_u8(msg, channel);
+ }
+
+#ifdef CONFIG_DPP2
+ /* Protocol Version */
+ if (DPP_VERSION > 1) {
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+ }
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({I-nonce, I-capabilities}k1) */
+ pos = clear;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ goto skip_i_nonce;
+ }
+ if (dpp_test == DPP_TEST_INVALID_I_NONCE_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-nonce");
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len - 1);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len - 1);
+ pos += nonce_len - 1;
+ goto skip_i_nonce;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, auth->i_nonce, nonce_len);
+ pos += nonce_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_nonce:
+ if (dpp_test == DPP_TEST_NO_I_CAPAB_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-capab");
+ goto skip_i_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->i_capab = auth->allowed_roles;
+ *pos++ = auth->i_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_I_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero I-capabilities");
+ pos[-1] = 0;
+ }
+skip_i_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(auth->k1, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Request frame attributes", msg);
+
+ return msg;
+}
+
+
+static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
+ enum dpp_status_error status,
+ const struct wpabuf *pr,
+ size_t nonce_len,
+ const u8 *r_pubkey_hash,
+ const u8 *i_pubkey_hash,
+ const u8 *r_nonce, const u8 *i_nonce,
+ const u8 *wrapped_r_auth,
+ size_t wrapped_r_auth_len,
+ const u8 *siv_key)
+{
+ struct wpabuf *msg;
+#define DPP_AUTH_RESP_CLEAR_LEN 2 * (4 + DPP_MAX_NONCE_LEN) + 4 + 1 + \
+ 4 + 4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE
+ u8 clear[DPP_AUTH_RESP_CLEAR_LEN];
+ u8 wrapped_data[DPP_AUTH_RESP_CLEAR_LEN + AES_BLOCK_SIZE];
+ const u8 *addr[2];
+ size_t len[2], siv_len, attr_len;
+ u8 *attr_start, *attr_end, *pos;
+
+ auth->waiting_auth_conf = 1;
+ auth->auth_resp_status = status;
+ auth->auth_resp_tries = 0;
+
+ /* Build DPP Authentication Response frame attributes */
+ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+ 4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
+#ifdef CONFIG_DPP2
+ attr_len += 5;
+#endif /* CONFIG_DPP2 */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_RESP, attr_len);
+ if (!msg)
+ return NULL;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* DPP Status */
+ if (status != 255)
+ dpp_build_attr_status(msg, status);
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+ /* Responder Protocol Key */
+ if (pr) {
+ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pr));
+ wpabuf_put_buf(msg, pr);
+ }
+
+#ifdef CONFIG_DPP2
+ /* Protocol Version */
+ if (auth->peer_version >= 2) {
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+ }
+#endif /* CONFIG_DPP2 */
+
+ attr_end = wpabuf_put(msg, 0);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Wrapped data ({R-nonce, I-nonce, R-capabilities, {R-auth}ke}k2) */
+ pos = clear;
+
+ if (r_nonce) {
+ /* R-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, r_nonce, nonce_len);
+ pos += nonce_len;
+ }
+
+ if (i_nonce) {
+ /* I-nonce */
+ WPA_PUT_LE16(pos, DPP_ATTR_I_NONCE);
+ pos += 2;
+ WPA_PUT_LE16(pos, nonce_len);
+ pos += 2;
+ os_memcpy(pos, i_nonce, nonce_len);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_NONCE_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-nonce mismatch");
+ pos[nonce_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ pos += nonce_len;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-capab");
+ goto skip_r_capab;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* R-capabilities */
+ WPA_PUT_LE16(pos, DPP_ATTR_R_CAPABILITIES);
+ pos += 2;
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ auth->r_capab = auth->configurator ? DPP_CAPAB_CONFIGURATOR :
+ DPP_CAPAB_ENROLLEE;
+ *pos++ = auth->r_capab;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_ZERO_R_CAPAB) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - zero R-capabilities");
+ pos[-1] = 0;
+ } else if (dpp_test == DPP_TEST_INCOMPATIBLE_R_CAPAB_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - incompatible R-capabilities");
+ if ((auth->i_capab & DPP_CAPAB_ROLE_MASK) ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE))
+ pos[-1] = 0;
+ else
+ pos[-1] = auth->configurator ? DPP_CAPAB_ENROLLEE :
+ DPP_CAPAB_CONFIGURATOR;
+ }
+skip_r_capab:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (wrapped_r_auth) {
+ /* {R-auth}ke */
+ WPA_PUT_LE16(pos, DPP_ATTR_WRAPPED_DATA);
+ pos += 2;
+ WPA_PUT_LE16(pos, wrapped_r_auth_len);
+ pos += 2;
+ os_memcpy(pos, wrapped_r_auth, wrapped_r_auth_len);
+ pos += wrapped_r_auth_len;
+ }
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ siv_len = pos - clear;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext", clear, siv_len);
+ if (aes_siv_encrypt(siv_key, auth->curve->hash_len, clear, siv_len,
+ 2, addr, len, wrapped_data) < 0) {
+ wpabuf_free(msg);
+ return NULL;
+ }
+ siv_len += AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, siv_len);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, siv_len);
+ wpabuf_put_data(msg, wrapped_data, siv_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Response frame attributes", msg);
+ return msg;
+}
+
+
+static int dpp_auth_build_resp_ok(struct dpp_authentication *auth)
+{
+ size_t nonce_len;
+ size_t secret_len;
+ struct wpabuf *msg, *pr = NULL;
+ u8 r_auth[4 + DPP_MAX_HASH_LEN];
+ u8 wrapped_r_auth[4 + DPP_MAX_HASH_LEN + AES_BLOCK_SIZE], *w_r_auth;
+ size_t wrapped_r_auth_len;
+ int ret = -1;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *r_nonce, *i_nonce;
+ enum dpp_status_error status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+ if (!auth->own_bi)
+ return -1;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override R-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->r_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->r_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate R-nonce");
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", auth->r_nonce, nonce_len);
+
+ EVP_PKEY_free(auth->own_protocol_key);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pr)
+ goto fail;
+
+ /* ECDH: N = pR * PI */
+ if (dpp_ecdh(auth->own_protocol_key, auth->peer_protocol_key,
+ auth->Nx, &secret_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
+
+ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ if (auth->own_bi && auth->peer_bi) {
+ /* Mutual authentication */
+ if (dpp_auth_derive_l_responder(auth) < 0)
+ goto fail;
+ }
+
+ if (dpp_derive_bk_ke(auth) < 0)
+ goto fail;
+
+ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ WPA_PUT_LE16(r_auth, DPP_ATTR_R_AUTH_TAG);
+ WPA_PUT_LE16(&r_auth[2], auth->curve->hash_len);
+ if (dpp_gen_r_auth(auth, r_auth + 4) < 0)
+ goto fail;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_R_AUTH_MISMATCH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-auth mismatch");
+ r_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ r_auth, 4 + auth->curve->hash_len,
+ 0, NULL, NULL, wrapped_r_auth) < 0)
+ goto fail;
+ wrapped_r_auth_len = 4 + auth->curve->hash_len + AES_BLOCK_SIZE;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-auth}ke",
+ wrapped_r_auth, wrapped_r_auth_len);
+ w_r_auth = wrapped_r_auth;
+
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+ r_nonce = auth->r_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Proto Key");
+ wpabuf_free(pr);
+ pr = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_PROTO_KEY_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid R-Proto Key");
+ wpabuf_free(pr);
+ pr = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pr || dpp_test_gen_invalid_key(pr, auth->curve) < 0)
+ goto fail;
+ } else if (dpp_test == DPP_TEST_NO_R_AUTH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth");
+ w_r_auth = NULL;
+ wrapped_r_auth_len = 0;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ } else if (dpp_test == DPP_TEST_NO_R_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-nonce");
+ r_nonce = NULL;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, pr, nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ r_nonce, i_nonce,
+ w_r_auth, wrapped_r_auth_len,
+ auth->k2);
+ if (!msg)
+ goto fail;
+ wpabuf_free(auth->resp_msg);
+ auth->resp_msg = msg;
+ ret = 0;
+fail:
+ wpabuf_free(pr);
+ return ret;
+}
+
+
+static int dpp_auth_build_resp_status(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ const u8 *r_pubkey_hash, *i_pubkey_hash, *i_nonce;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->own_bi)
+ return -1;
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Response");
+
+ r_pubkey_hash = auth->own_bi->pubkey_hash;
+ if (auth->peer_bi)
+ i_pubkey_hash = auth->peer_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+ i_nonce = auth->i_nonce;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_STATUS_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ status = 255;
+ } else if (dpp_test == DPP_TEST_NO_I_NONCE_AUTH_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-nonce");
+ i_nonce = NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ msg = dpp_auth_build_resp(auth, status, NULL, auth->curve->nonce_len,
+ r_pubkey_hash, i_pubkey_hash,
+ NULL, i_nonce, NULL, 0, auth->k1);
+ if (!msg)
+ return -1;
+ wpabuf_free(auth->resp_msg);
+ auth->resp_msg = msg;
+ return 0;
+}
+
+
+struct dpp_authentication *
+dpp_auth_req_rx(struct dpp_global *dpp, void *msg_ctx, u8 dpp_allowed_roles,
+ int qr_mutual, struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ unsigned int freq, const u8 *hdr, const u8 *attr_start,
+ size_t attr_len)
+{
+ EVP_PKEY *pi = NULL;
+ EVP_PKEY_CTX *ctx = NULL;
+ size_t secret_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *wrapped_data, *i_proto, *i_nonce, *i_capab, *i_bootstrap,
+ *channel;
+ u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
+ i_bootstrap_len, channel_len;
+ struct dpp_authentication *auth = NULL;
+#ifdef CONFIG_DPP2
+ const u8 *version;
+ u16 version_len;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Request");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+ attr_len = wrapped_data - 4 - attr_start;
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ goto fail;
+ if (peer_bi && peer_bi->configurator_params &&
+ dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
+ goto fail;
+ auth->peer_bi = peer_bi;
+ auth->own_bi = own_bi;
+ auth->curve = own_bi->curve;
+ auth->curr_freq = freq;
+
+ auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (version && DPP_VERSION > 1) {
+ if (version_len < 1 || version[0] == 0) {
+ dpp_auth_fail(auth,
+ "Invalid Protocol Version attribute");
+ goto fail;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+ }
+#endif /* CONFIG_DPP2 */
+
+ channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
+ &channel_len);
+ if (channel) {
+ int neg_freq;
+
+ if (channel_len < 2) {
+ dpp_auth_fail(auth, "Too short Channel attribute");
+ goto fail;
+ }
+
+ neg_freq = ieee80211_chan_to_freq(NULL, channel[0], channel[1]);
+ wpa_printf(MSG_DEBUG,
+ "DPP: Initiator requested different channel for negotiation: op_class=%u channel=%u --> freq=%d",
+ channel[0], channel[1], neg_freq);
+ if (neg_freq < 0) {
+ dpp_auth_fail(auth,
+ "Unsupported Channel attribute value");
+ goto fail;
+ }
+
+ if (auth->curr_freq != (unsigned int) neg_freq) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Changing negotiation channel from %u MHz to %u MHz",
+ freq, neg_freq);
+ auth->curr_freq = neg_freq;
+ }
+ }
+
+ i_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_I_PROTOCOL_KEY,
+ &i_proto_len);
+ if (!i_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Initiator Protocol Key attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Protocol Key",
+ i_proto, i_proto_len);
+
+ /* M = bR * PI */
+ pi = dpp_set_pubkey_point(own_bi->pubkey, i_proto, i_proto_len);
+ if (!pi) {
+ dpp_auth_fail(auth, "Invalid Initiator Protocol Key");
+ goto fail;
+ }
+ dpp_debug_print_key("Peer (Initiator) Protocol Key", pi);
+
+ if (dpp_ecdh(own_bi->pubkey, pi, auth->Mx, &secret_len) < 0)
+ goto fail;
+ auth->secret_len = secret_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+ auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
+
+ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ os_memcpy(auth->i_nonce, i_nonce, i_nonce_len);
+
+ i_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_I_CAPABILITIES,
+ &i_capab_len);
+ if (!i_capab || i_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid I-capabilities");
+ goto fail;
+ }
+ auth->i_capab = i_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: I-capabilities: 0x%02x", auth->i_capab);
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ unwrapped = NULL;
+
+ switch (auth->i_capab & DPP_CAPAB_ROLE_MASK) {
+ case DPP_CAPAB_ENROLLEE:
+ if (!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator role");
+ goto not_compatible;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ break;
+ case DPP_CAPAB_CONFIGURATOR:
+ if (!(dpp_allowed_roles & DPP_CAPAB_ENROLLEE)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Enrollee role");
+ goto not_compatible;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ break;
+ case DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE:
+ if (dpp_allowed_roles & DPP_CAPAB_ENROLLEE) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Enrollee");
+ auth->configurator = 0;
+ } else if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR) {
+ wpa_printf(MSG_DEBUG, "DPP: Acting as Configurator");
+ auth->configurator = 1;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Local policy does not allow Configurator/Enrollee role");
+ goto not_compatible;
+ }
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected role in I-capabilities");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Invalid role in I-capabilities 0x%02x",
+ auth->i_capab & DPP_CAPAB_ROLE_MASK);
+ goto fail;
+ }
+
+ auth->peer_protocol_key = pi;
+ pi = NULL;
+ if (qr_mutual && !peer_bi && own_bi->type == DPP_BOOTSTRAP_QR_CODE) {
+ char hex[SHA256_MAC_LEN * 2 + 1];
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mutual authentication required with QR Codes, but peer info is not yet available - request more time");
+ if (dpp_auth_build_resp_status(auth,
+ DPP_STATUS_RESPONSE_PENDING) < 0)
+ goto fail;
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap && i_bootstrap_len == SHA256_MAC_LEN) {
+ auth->response_pending = 1;
+ os_memcpy(auth->waiting_pubkey_hash,
+ i_bootstrap, i_bootstrap_len);
+ wpa_snprintf_hex(hex, sizeof(hex), i_bootstrap,
+ i_bootstrap_len);
+ } else {
+ hex[0] = '\0';
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_SCAN_PEER_QR_CODE
+ "%s", hex);
+ return auth;
+ }
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ goto fail;
+
+ return auth;
+
+not_compatible:
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+ "i-capab=0x%02x", auth->i_capab);
+ if (dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR)
+ auth->configurator = 1;
+ else
+ auth->configurator = 0;
+ auth->peer_protocol_key = pi;
+ pi = NULL;
+ if (dpp_auth_build_resp_status(auth, DPP_STATUS_NOT_COMPATIBLE) < 0)
+ goto fail;
+
+ auth->remove_on_tx_status = 1;
+ return auth;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ EVP_PKEY_free(pi);
+ EVP_PKEY_CTX_free(ctx);
+ dpp_auth_deinit(auth);
+ return NULL;
+}
+
+
+int dpp_notify_new_qr_code(struct dpp_authentication *auth,
+ struct dpp_bootstrap_info *peer_bi)
+{
+ if (!auth || !auth->response_pending ||
+ os_memcmp(auth->waiting_pubkey_hash, peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0)
+ return 0;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: New scanned QR Code has matching public key that was needed to continue DPP Authentication exchange with "
+ MACSTR, MAC2STR(auth->peer_mac_addr));
+ auth->peer_bi = peer_bi;
+
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return -1;
+
+ return 1;
+}
+
+
+static struct wpabuf * dpp_auth_build_conf(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg;
+ u8 i_auth[4 + DPP_MAX_HASH_LEN];
+ size_t i_auth_len;
+ u8 r_nonce[4 + DPP_MAX_NONCE_LEN];
+ size_t r_nonce_len;
+ const u8 *addr[2];
+ size_t len[2], attr_len;
+ u8 *wrapped_i_auth;
+ u8 *wrapped_r_nonce;
+ u8 *attr_start, *attr_end;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Authentication Confirmation");
+
+ i_auth_len = 4 + auth->curve->hash_len;
+ r_nonce_len = 4 + auth->curve->nonce_len;
+ /* Build DPP Authentication Confirmation frame attributes */
+ attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
+ 4 + i_auth_len + r_nonce_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_AUTHENTICATION_CONF, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ if (auth->own_bi)
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+ else
+ i_pubkey_hash = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ } else if (dpp_test == DPP_TEST_INVALID_STATUS_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 254;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test ==
+ DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ if (i_pubkey_hash)
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ else
+ os_memset(test_hash, 0, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Responder Bootstrapping Key Hash */
+ dpp_build_attr_r_bootstrap_key_hash(msg, r_pubkey_hash);
+
+ /* Initiator Bootstrapping Key Hash (mutual authentication) */
+ dpp_build_attr_i_bootstrap_key_hash(msg, i_pubkey_hash);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_CONF)
+ goto skip_wrapped_data;
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ i_auth_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (status == DPP_STATUS_OK) {
+ /* I-auth wrapped with ke */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, i_auth_len + AES_BLOCK_SIZE);
+ wrapped_i_auth = wpabuf_put(msg, i_auth_len + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_I_AUTH_AUTH_CONF)
+ goto skip_i_auth;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |]
+ * 1) */
+ WPA_PUT_LE16(i_auth, DPP_ATTR_I_AUTH_TAG);
+ WPA_PUT_LE16(&i_auth[2], auth->curve->hash_len);
+ if (dpp_gen_i_auth(auth, i_auth + 4) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_I_AUTH_MISMATCH_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-auth mismatch");
+ i_auth[4 + auth->curve->hash_len / 2] ^= 0x01;
+ }
+skip_i_auth:
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ i_auth, i_auth_len,
+ 2, addr, len, wrapped_i_auth) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {I-auth}ke",
+ wrapped_i_auth, i_auth_len + AES_BLOCK_SIZE);
+ } else {
+ /* R-nonce wrapped with k2 */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, r_nonce_len + AES_BLOCK_SIZE);
+ wrapped_r_nonce = wpabuf_put(msg, r_nonce_len + AES_BLOCK_SIZE);
+
+ WPA_PUT_LE16(r_nonce, DPP_ATTR_R_NONCE);
+ WPA_PUT_LE16(&r_nonce[2], auth->curve->nonce_len);
+ os_memcpy(r_nonce + 4, auth->r_nonce, auth->curve->nonce_len);
+
+ if (aes_siv_encrypt(auth->k2, auth->curve->hash_len,
+ r_nonce, r_nonce_len,
+ 2, addr, len, wrapped_r_nonce) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: {R-nonce}k2",
+ wrapped_r_nonce, r_nonce_len + AES_BLOCK_SIZE);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_CONF) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Authentication Confirmation frame attributes",
+ msg);
+ if (status == DPP_STATUS_OK)
+ dpp_auth_success(auth);
+
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static int dpp_autogen_bootstrap_key(struct dpp_authentication *auth)
+{
+ struct dpp_bootstrap_info *bi;
+
+ if (auth->own_bi)
+ return 0; /* already generated */
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return -1;
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ if (dpp_keygen(bi, auth->peer_bi->curve->name, NULL, 0) < 0 ||
+ dpp_gen_uri(bi) < 0)
+ goto fail;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Auto-generated own bootstrapping key info: URI %s",
+ bi->uri);
+
+ auth->tmp_own_bi = auth->own_bi = bi;
+
+ return 0;
+fail:
+ dpp_bootstrap_info_free(bi);
+ return -1;
+}
+
+
+struct dpp_authentication * dpp_auth_init(struct dpp_global *dpp, void *msg_ctx,
+ struct dpp_bootstrap_info *peer_bi,
+ struct dpp_bootstrap_info *own_bi,
+ u8 dpp_allowed_roles,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes,
+ u16 num_modes)
+{
+ struct dpp_authentication *auth;
+ size_t nonce_len;
+ size_t secret_len;
+ struct wpabuf *pi = NULL;
+ const u8 *r_pubkey_hash, *i_pubkey_hash;
+#ifdef CONFIG_TESTING_OPTIONS
+ u8 test_hash[SHA256_MAC_LEN];
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ return NULL;
+ if (peer_bi->configurator_params &&
+ dpp_set_configurator(auth, peer_bi->configurator_params) < 0)
+ goto fail;
+ auth->initiator = 1;
+ auth->waiting_auth_resp = 1;
+ auth->allowed_roles = dpp_allowed_roles;
+ auth->configurator = !!(dpp_allowed_roles & DPP_CAPAB_CONFIGURATOR);
+ auth->peer_bi = peer_bi;
+ auth->own_bi = own_bi;
+ auth->curve = peer_bi->curve;
+
+ if (dpp_autogen_bootstrap_key(auth) < 0 ||
+ dpp_prepare_channel_list(auth, neg_freq, own_modes, num_modes) < 0)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_nonce_override_len > 0) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - override I-nonce");
+ nonce_len = dpp_nonce_override_len;
+ os_memcpy(auth->i_nonce, dpp_nonce_override, nonce_len);
+ } else {
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ nonce_len = auth->curve->nonce_len;
+ if (random_get_bytes(auth->i_nonce, nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate I-nonce");
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", auth->i_nonce, nonce_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_protocol_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override protocol key");
+ auth->own_protocol_key = dpp_set_keypair(
+ &tmp_curve, dpp_protocol_key_override,
+ dpp_protocol_key_override_len);
+ } else {
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ auth->own_protocol_key = dpp_gen_keypair(auth->curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ pi = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pi)
+ goto fail;
+
+ /* ECDH: M = pI * BR */
+ if (dpp_ecdh(auth->own_protocol_key, auth->peer_bi->pubkey,
+ auth->Mx, &secret_len) < 0)
+ goto fail;
+ auth->secret_len = secret_len;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (M.x)",
+ auth->Mx, auth->secret_len);
+ auth->Mx_len = auth->secret_len;
+
+ if (dpp_derive_k1(auth->Mx, auth->secret_len, auth->k1,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ r_pubkey_hash = auth->peer_bi->pubkey_hash;
+ i_pubkey_hash = auth->own_bi->pubkey_hash;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Bootstrap Key Hash");
+ r_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_R_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid R-Bootstrap Key Hash");
+ os_memcpy(test_hash, r_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ r_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Bootstrap Key Hash");
+ i_pubkey_hash = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_BOOTSTRAP_KEY_HASH_AUTH_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - invalid I-Bootstrap Key Hash");
+ os_memcpy(test_hash, i_pubkey_hash, SHA256_MAC_LEN);
+ test_hash[SHA256_MAC_LEN - 1] ^= 0x01;
+ i_pubkey_hash = test_hash;
+ } else if (dpp_test == DPP_TEST_NO_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Proto Key");
+ wpabuf_free(pi);
+ pi = NULL;
+ } else if (dpp_test == DPP_TEST_INVALID_I_PROTO_KEY_AUTH_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid I-Proto Key");
+ wpabuf_free(pi);
+ pi = wpabuf_alloc(2 * auth->curve->prime_len);
+ if (!pi || dpp_test_gen_invalid_key(pi, auth->curve) < 0)
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (neg_freq && auth->num_freq == 1 && auth->freq[0] == neg_freq)
+ neg_freq = 0;
+ auth->req_msg = dpp_auth_build_req(auth, pi, nonce_len, r_pubkey_hash,
+ i_pubkey_hash, neg_freq);
+ if (!auth->req_msg)
+ goto fail;
+
+out:
+ wpabuf_free(pi);
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+static void
+dpp_auth_resp_rx_status(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data, u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *i_nonce, *r_capab;
+ u16 i_nonce_len, r_capab_len;
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported incompatible roles");
+ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported more time needed");
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder reported failure (status %d)",
+ status);
+ dpp_auth_fail(auth, "Responder reported failure");
+ return;
+ }
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k1, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "I-nonce mismatch");
+ goto fail;
+ }
+
+ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_R_CAPABILITIES,
+ &r_capab_len);
+ if (!r_capab || r_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+ goto fail;
+ }
+ auth->r_capab = r_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+ if (status == DPP_STATUS_NOT_COMPATIBLE) {
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_NOT_COMPATIBLE
+ "r-capab=0x%02x", auth->r_capab);
+ } else if (status == DPP_STATUS_RESPONSE_PENDING) {
+ u8 role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+
+ if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_FAIL "Unexpected role in R-capabilities 0x%02x",
+ role);
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue waiting for full DPP Authentication Response");
+ wpa_msg(auth->msg_ctx, MSG_INFO,
+ DPP_EVENT_RESPONSE_PENDING "%s",
+ auth->tmp_own_bi ? auth->tmp_own_bi->uri : "");
+ }
+ }
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+}
+
+
+struct wpabuf *
+dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ EVP_PKEY *pr;
+ size_t secret_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL, *unwrapped2 = NULL;
+ size_t unwrapped_len = 0, unwrapped2_len = 0;
+ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *r_proto,
+ *r_nonce, *i_nonce, *r_capab, *wrapped2, *r_auth;
+ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+ r_proto_len, r_nonce_len, i_nonce_len, r_capab_len,
+ wrapped2_len, r_auth_len;
+ u8 r_auth2[DPP_MAX_HASH_LEN];
+ u8 role;
+#ifdef CONFIG_DPP2
+ const u8 *version;
+ u16 version_len;
+#endif /* CONFIG_DPP2 */
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Response");
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->initiator || !auth->peer_bi || auth->reconfig) {
+ dpp_auth_fail(auth, "Unexpected Authentication Response");
+ return NULL;
+ }
+
+ auth->waiting_auth_resp = 0;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ r_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ if (os_memcmp(r_bootstrap, auth->peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Unexpected Responder Bootstrapping Key Hash value");
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Expected Responder Bootstrapping Key Hash",
+ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ return NULL;
+ }
+
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap) {
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP,
+ "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+ if (!auth->own_bi ||
+ os_memcmp(i_bootstrap, auth->own_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash attribute did not match");
+ return NULL;
+ }
+ } else if (auth->own_bi && auth->own_bi->type == DPP_BOOTSTRAP_PKEX) {
+ /* PKEX bootstrapping mandates use of mutual authentication */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ } else if (auth->own_bi &&
+ auth->own_bi->type == DPP_BOOTSTRAP_NFC_URI &&
+ auth->own_bi->nfc_negotiated) {
+ /* NFC negotiated connection handover bootstrapping mandates
+ * use of mutual authentication */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return NULL;
+ }
+
+ auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (version && DPP_VERSION > 1) {
+ if (version_len < 1 || version[0] == 0) {
+ dpp_auth_fail(auth,
+ "Invalid Protocol Version attribute");
+ return NULL;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+ }
+#endif /* CONFIG_DPP2 */
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ auth->auth_resp_status = status[0];
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_resp_rx_status(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+ return NULL;
+ }
+
+ if (!i_bootstrap && auth->own_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Responder decided not to use mutual authentication");
+ auth->own_bi = NULL;
+ }
+
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_AUTH_DIRECTION "mutual=%d",
+ auth->own_bi != NULL);
+
+ r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+ &r_proto_len);
+ if (!r_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Responder Protocol Key attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+ r_proto, r_proto_len);
+
+ /* N = pI * PR */
+ pr = dpp_set_pubkey_point(auth->own_protocol_key, r_proto, r_proto_len);
+ if (!pr) {
+ dpp_auth_fail(auth, "Invalid Responder Protocol Key");
+ return NULL;
+ }
+ dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+
+ if (dpp_ecdh(auth->own_protocol_key, pr, auth->Nx, &secret_len) < 0) {
+ dpp_auth_fail(auth, "Failed to derive ECDH shared secret");
+ goto fail;
+ }
+ EVP_PKEY_free(auth->peer_protocol_key);
+ auth->peer_protocol_key = pr;
+ pr = NULL;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (N.x)",
+ auth->Nx, auth->secret_len);
+ auth->Nx_len = auth->secret_len;
+
+ if (dpp_derive_k2(auth->Nx, auth->secret_len, auth->k2,
+ auth->curve->hash_len) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: R-nonce", r_nonce, r_nonce_len);
+ os_memcpy(auth->r_nonce, r_nonce, r_nonce_len);
+
+ i_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_NONCE,
+ &i_nonce_len);
+ if (!i_nonce || i_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid I-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: I-nonce", i_nonce, i_nonce_len);
+ if (os_memcmp(auth->i_nonce, i_nonce, i_nonce_len) != 0) {
+ dpp_auth_fail(auth, "I-nonce mismatch");
+ goto fail;
+ }
+
+ if (auth->own_bi) {
+ /* Mutual authentication */
+ if (dpp_auth_derive_l_initiator(auth) < 0)
+ goto fail;
+ }
+
+ r_capab = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_R_CAPABILITIES,
+ &r_capab_len);
+ if (!r_capab || r_capab_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid R-capabilities");
+ goto fail;
+ }
+ auth->r_capab = r_capab[0];
+ wpa_printf(MSG_DEBUG, "DPP: R-capabilities: 0x%02x", auth->r_capab);
+ role = auth->r_capab & DPP_CAPAB_ROLE_MASK;
+ if ((auth->allowed_roles ==
+ (DPP_CAPAB_CONFIGURATOR | DPP_CAPAB_ENROLLEE)) &&
+ (role == DPP_CAPAB_CONFIGURATOR || role == DPP_CAPAB_ENROLLEE)) {
+ /* Peer selected its role, so move from "either role" to the
+ * role that is compatible with peer's selection. */
+ auth->configurator = role == DPP_CAPAB_ENROLLEE;
+ wpa_printf(MSG_DEBUG, "DPP: Acting as %s",
+ auth->configurator ? "Configurator" : "Enrollee");
+ } else if ((auth->configurator && role != DPP_CAPAB_ENROLLEE) ||
+ (!auth->configurator && role != DPP_CAPAB_CONFIGURATOR)) {
+ wpa_printf(MSG_DEBUG, "DPP: Incompatible role selection");
+ wpa_msg(auth->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Unexpected role in R-capabilities 0x%02x",
+ role);
+ if (role != DPP_CAPAB_ENROLLEE &&
+ role != DPP_CAPAB_CONFIGURATOR)
+ goto fail;
+ bin_clear_free(unwrapped, unwrapped_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_NOT_COMPATIBLE);
+ }
+
+ wrapped2 = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_WRAPPED_DATA, &wrapped2_len);
+ if (!wrapped2 || wrapped2_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Secondary Wrapped Data");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped2, wrapped2_len);
+
+ if (dpp_derive_bk_ke(auth) < 0)
+ goto fail;
+
+ unwrapped2_len = wrapped2_len - AES_BLOCK_SIZE;
+ unwrapped2 = os_malloc(unwrapped2_len);
+ if (!unwrapped2)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped2, wrapped2_len,
+ 0, NULL, NULL, unwrapped2) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped2, unwrapped2_len);
+
+ if (dpp_check_attrs(unwrapped2, unwrapped2_len) < 0) {
+ dpp_auth_fail(auth,
+ "Invalid attribute in secondary unwrapped data");
+ goto fail;
+ }
+
+ r_auth = dpp_get_attr(unwrapped2, unwrapped2_len, DPP_ATTR_R_AUTH_TAG,
+ &r_auth_len);
+ if (!r_auth || r_auth_len != auth->curve->hash_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Responder Authenticating Tag");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Received Responder Authenticating Tag",
+ r_auth, r_auth_len);
+ /* R-auth' = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ if (dpp_gen_r_auth(auth, r_auth2) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Responder Authenticating Tag",
+ r_auth2, r_auth_len);
+ if (os_memcmp(r_auth, r_auth2, r_auth_len) != 0) {
+ dpp_auth_fail(auth, "Mismatching Responder Authenticating Tag");
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ auth->remove_on_tx_status = 1;
+ return dpp_auth_build_conf(auth, DPP_STATUS_AUTH_FAILURE);
+ }
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AUTH_RESP_IN_PLACE_OF_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - Authentication Response in place of Confirm");
+ if (dpp_auth_build_resp_ok(auth) < 0)
+ return NULL;
+ return wpabuf_dup(auth->resp_msg);
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return dpp_auth_build_conf(auth, DPP_STATUS_OK);
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ bin_clear_free(unwrapped2, unwrapped2_len);
+ EVP_PKEY_free(pr);
+ return NULL;
+}
+
+
+static int dpp_auth_conf_rx_failure(struct dpp_authentication *auth,
+ const u8 *hdr,
+ const u8 *attr_start, size_t attr_len,
+ const u8 *wrapped_data,
+ u16 wrapped_data_len,
+ enum dpp_status_error status)
+{
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ const u8 *r_nonce;
+ u16 r_nonce_len;
+
+ /* Authentication Confirm failure cases are expected to include
+ * {R-nonce}k2 in the Wrapped Data attribute. */
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped) {
+ dpp_auth_fail(auth, "Authentication failed");
+ goto fail;
+ }
+ if (aes_siv_decrypt(auth->k2, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ r_nonce = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_NONCE,
+ &r_nonce_len);
+ if (!r_nonce || r_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "DPP: Missing or invalid R-nonce");
+ goto fail;
+ }
+ if (os_memcmp(r_nonce, auth->r_nonce, r_nonce_len) != 0) {
+ wpa_hexdump(MSG_DEBUG, "DPP: Received R-nonce",
+ r_nonce, r_nonce_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Expected R-nonce",
+ auth->r_nonce, r_nonce_len);
+ dpp_auth_fail(auth, "R-nonce mismatch");
+ goto fail;
+ }
+
+ if (status == DPP_STATUS_NOT_COMPATIBLE)
+ dpp_auth_fail(auth, "Peer reported incompatible R-capab role");
+ else if (status == DPP_STATUS_AUTH_FAILURE)
+ dpp_auth_fail(auth, "Peer reported authentication failure)");
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
+
+
+int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *r_bootstrap, *i_bootstrap, *wrapped_data, *status, *i_auth;
+ u16 r_bootstrap_len, i_bootstrap_len, wrapped_data_len, status_len,
+ i_auth_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ u8 i_auth2[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ return -1;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (auth->initiator || !auth->own_bi || !auth->waiting_auth_conf ||
+ auth->reconfig) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: initiator=%d own_bi=%d waiting_auth_conf=%d",
+ auth->initiator, !!auth->own_bi,
+ auth->waiting_auth_conf);
+ dpp_auth_fail(auth, "Unexpected Authentication Confirm");
+ return -1;
+ }
+
+ auth->waiting_auth_conf = 0;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ r_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ if (os_memcmp(r_bootstrap, auth->own_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Expected Responder Bootstrapping Key Hash",
+ auth->peer_bi->pubkey_hash, SHA256_MAC_LEN);
+ dpp_auth_fail(auth,
+ "Responder Bootstrapping Key Hash mismatch");
+ return -1;
+ }
+
+ i_bootstrap = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (i_bootstrap) {
+ if (i_bootstrap_len != SHA256_MAC_LEN) {
+ dpp_auth_fail(auth,
+ "Invalid Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP,
+ "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+ if (!auth->peer_bi ||
+ os_memcmp(i_bootstrap, auth->peer_bi->pubkey_hash,
+ SHA256_MAC_LEN) != 0) {
+ dpp_auth_fail(auth,
+ "Initiator Bootstrapping Key Hash mismatch");
+ return -1;
+ }
+ } else if (auth->peer_bi) {
+ /* Mutual authentication and peer did not include its
+ * Bootstrapping Key Hash attribute. */
+ dpp_auth_fail(auth,
+ "Missing Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ if (status[0] == DPP_STATUS_NOT_COMPATIBLE ||
+ status[0] == DPP_STATUS_AUTH_FAILURE)
+ return dpp_auth_conf_rx_failure(auth, hdr, attr_start,
+ attr_len, wrapped_data,
+ wrapped_data_len, status[0]);
+
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_fail(auth, "Authentication failed");
+ return -1;
+ }
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ return -1;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ i_auth = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+ &i_auth_len);
+ if (!i_auth || i_auth_len != auth->curve->hash_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Initiator Authenticating Tag");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Received Initiator Authenticating Tag",
+ i_auth, i_auth_len);
+ /* I-auth' = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+ if (dpp_gen_i_auth(auth, i_auth2) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated Initiator Authenticating Tag",
+ i_auth2, i_auth_len);
+ if (os_memcmp(i_auth, i_auth2, i_auth_len) != 0) {
+ dpp_auth_fail(auth, "Mismatching Initiator Authenticating Tag");
+ goto fail;
+ }
+
+ bin_clear_free(unwrapped, unwrapped_len);
+ dpp_auth_success(auth);
+ return 0;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return -1;
+}
diff --git a/contrib/wpa/src/common/dpp_backup.c b/contrib/wpa/src/common/dpp_backup.c
new file mode 100644
index 000000000000..947a5e9ea33e
--- /dev/null
+++ b/contrib/wpa/src/common/dpp_backup.c
@@ -0,0 +1,1265 @@
+/*
+ * DPP configurator backup
+ * Copyright (c) 2019-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "tls/asn1.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+#ifdef CONFIG_DPP2
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+ if (pkey->type != EVP_PKEY_EC)
+ return NULL;
+ return pkey->pkey.ec;
+}
+
+#endif
+
+
+void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key)
+{
+ while (key) {
+ struct dpp_asymmetric_key *next = key->next;
+
+ EVP_PKEY_free(key->csign);
+ EVP_PKEY_free(key->pp_key);
+ str_clear_free(key->config_template);
+ str_clear_free(key->connector_template);
+ os_free(key);
+ key = next;
+ }
+}
+
+
+static struct wpabuf * dpp_build_conf_params(struct dpp_configurator *conf)
+{
+ struct wpabuf *buf, *priv_key = NULL;
+ size_t len;
+ /* TODO: proper template values */
+ const char *conf_template = "{\"wi-fi_tech\":\"infra\",\"discovery\":{\"ssid\":\"test\"},\"cred\":{\"akm\":\"dpp\"}}";
+ const char *connector_template = NULL;
+ EC_KEY *eckey;
+ unsigned char *der = NULL;
+ int der_len;
+
+ if (!conf->pp_key)
+ return NULL;
+ eckey = EVP_PKEY_get0_EC_KEY(conf->pp_key);
+ if (!eckey)
+ return NULL;
+
+ EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ priv_key = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+ if (!priv_key)
+ goto fail;
+
+ len = 100 + os_strlen(conf_template);
+ if (connector_template)
+ len += os_strlen(connector_template);
+ if (priv_key)
+ len += wpabuf_len(priv_key);
+ buf = wpabuf_alloc(len);
+ if (!buf)
+ goto fail;
+
+ /*
+ * DPPConfigurationParameters ::= SEQUENCE {
+ * privacyProtectionKey PrivateKey,
+ * configurationTemplate UTF8String,
+ * connectorTemplate UTF8String OPTIONAL}
+ */
+
+ /* PrivateKey ::= OCTET STRING */
+ asn1_put_octet_string(buf, priv_key);
+
+ asn1_put_utf8string(buf, conf_template);
+ if (connector_template)
+ asn1_put_utf8string(buf, connector_template);
+ wpabuf_clear_free(priv_key);
+ return asn1_encaps(buf, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+fail:
+ wpabuf_clear_free(priv_key);
+ return NULL;
+}
+
+
+static struct wpabuf * dpp_build_attribute(struct dpp_configurator *conf)
+{
+ struct wpabuf *conf_params, *attr;
+
+ /*
+ * aa-DPPConfigurationParameters ATTRIBUTE ::=
+ * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
+ *
+ * Attribute ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * values SET SIZE(1..MAX) OF Type
+ */
+ conf_params = dpp_build_conf_params(conf);
+ conf_params = asn1_encaps(conf_params, ASN1_CLASS_UNIVERSAL,
+ ASN1_TAG_SET);
+ if (!conf_params)
+ return NULL;
+
+ attr = wpabuf_alloc(100 + wpabuf_len(conf_params));
+ if (!attr) {
+ wpabuf_clear_free(conf_params);
+ return NULL;
+ }
+
+ asn1_put_oid(attr, &asn1_dpp_config_params_oid);
+ wpabuf_put_buf(attr, conf_params);
+ wpabuf_clear_free(conf_params);
+
+ return asn1_encaps(attr, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_key_alg(const struct dpp_curve_params *curve)
+{
+ const struct asn1_oid *oid;
+ struct wpabuf *params, *res;
+
+ switch (curve->ike_group) {
+ case 19:
+ oid = &asn1_prime256v1_oid;
+ break;
+ case 20:
+ oid = &asn1_secp384r1_oid;
+ break;
+ case 21:
+ oid = &asn1_secp521r1_oid;
+ break;
+ case 28:
+ oid = &asn1_brainpoolP256r1_oid;
+ break;
+ case 29:
+ oid = &asn1_brainpoolP384r1_oid;
+ break;
+ case 30:
+ oid = &asn1_brainpoolP512r1_oid;
+ break;
+ default:
+ return NULL;
+ }
+
+ params = wpabuf_alloc(20);
+ if (!params)
+ return NULL;
+ asn1_put_oid(params, oid); /* namedCurve */
+
+ res = asn1_build_alg_id(&asn1_ec_public_key_oid, params);
+ wpabuf_free(params);
+ return res;
+}
+
+
+static struct wpabuf * dpp_build_key_pkg(struct dpp_authentication *auth)
+{
+ struct wpabuf *key = NULL, *attr, *alg, *priv_key = NULL;
+ EC_KEY *eckey;
+ unsigned char *der = NULL;
+ int der_len;
+
+ eckey = EVP_PKEY_get0_EC_KEY(auth->conf->csign);
+ if (!eckey)
+ return NULL;
+
+ EC_KEY_set_enc_flags(eckey, EC_PKEY_NO_PUBKEY);
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ priv_key = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+
+ alg = dpp_build_key_alg(auth->conf->curve);
+
+ /* 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)
+ goto fail;
+
+ /*
+ * OneAsymmetricKey ::= SEQUENCE {
+ * version Version,
+ * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ * privateKey PrivateKey,
+ * attributes [0] Attributes OPTIONAL,
+ * ...,
+ * [[2: publicKey [1] BIT STRING OPTIONAL ]],
+ * ...
+ * }
+ */
+
+ key = wpabuf_alloc(100 + wpabuf_len(alg) + wpabuf_len(priv_key) +
+ wpabuf_len(attr));
+ if (!key)
+ goto fail;
+
+ asn1_put_integer(key, 0); /* version = v1(0) */
+
+ /* PrivateKeyAlgorithmIdentifier */
+ wpabuf_put_buf(key, alg);
+
+ /* PrivateKey ::= OCTET STRING */
+ asn1_put_octet_string(key, priv_key);
+
+ /* [0] Attributes OPTIONAL */
+ asn1_put_hdr(key, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0, wpabuf_len(attr));
+ wpabuf_put_buf(key, attr);
+
+fail:
+ wpabuf_clear_free(attr);
+ wpabuf_clear_free(priv_key);
+ wpabuf_free(alg);
+
+ /*
+ * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
+ *
+ * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
+ *
+ * OneAsymmetricKey ::= SEQUENCE
+ */
+ return asn1_encaps(asn1_encaps(key,
+ ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE),
+ ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf * dpp_build_pbkdf2_alg_id(const struct wpabuf *salt,
+ size_t hash_len)
+{
+ struct wpabuf *params = NULL, *buf = NULL, *prf = NULL;
+ const struct asn1_oid *oid;
+
+ /*
+ * PBKDF2-params ::= SEQUENCE {
+ * salt CHOICE {
+ * specified OCTET STRING,
+ * otherSource AlgorithmIdentifier}
+ * iterationCount INTEGER (1..MAX),
+ * keyLength INTEGER (1..MAX),
+ * prf AlgorithmIdentifier}
+ *
+ * salt is an 64 octet value, iterationCount is 1000, keyLength is based
+ * on Configurator signing key length, prf is
+ * id-hmacWithSHA{256,384,512} based on Configurator signing key.
+ */
+
+ if (hash_len == 32)
+ oid = &asn1_pbkdf2_hmac_sha256_oid;
+ else if (hash_len == 48)
+ oid = &asn1_pbkdf2_hmac_sha384_oid;
+ else if (hash_len == 64)
+ oid = &asn1_pbkdf2_hmac_sha512_oid;
+ else
+ goto fail;
+ prf = asn1_build_alg_id(oid, NULL);
+ if (!prf)
+ goto fail;
+ params = wpabuf_alloc(100 + wpabuf_len(salt) + wpabuf_len(prf));
+ if (!params)
+ goto fail;
+ asn1_put_octet_string(params, salt); /* salt.specified */
+ asn1_put_integer(params, 1000); /* iterationCount */
+ asn1_put_integer(params, hash_len); /* keyLength */
+ wpabuf_put_buf(params, prf);
+ params = asn1_encaps(params, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+ if (!params)
+ goto fail;
+ buf = asn1_build_alg_id(&asn1_pbkdf2_oid, params);
+fail:
+ wpabuf_free(params);
+ wpabuf_free(prf);
+ return buf;
+}
+
+
+static struct wpabuf *
+dpp_build_pw_recipient_info(struct dpp_authentication *auth, size_t hash_len,
+ const struct wpabuf *cont_enc_key)
+{
+ struct wpabuf *pwri = NULL, *enc_key = NULL, *key_der_alg = NULL,
+ *key_enc_alg = NULL, *salt;
+ u8 kek[DPP_MAX_HASH_LEN];
+ u8 key[DPP_MAX_HASH_LEN];
+ size_t key_len;
+ int res;
+
+ salt = wpabuf_alloc(64);
+ if (!salt || os_get_random(wpabuf_put(salt, 64), 64) < 0)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: PBKDF2 salt", salt);
+
+ key_len = auth->curve->hash_len;
+ /* password = HKDF-Expand(bk, "Enveloped Data Password", length) */
+ res = dpp_hkdf_expand(key_len, auth->bk, key_len,
+ "Enveloped Data Password", key, key_len);
+ if (res < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
+
+ if (dpp_pbkdf2(hash_len, key, key_len, wpabuf_head(salt), 64, 1000,
+ kek, hash_len)) {
+ wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
+ kek, hash_len);
+
+ enc_key = wpabuf_alloc(hash_len + AES_BLOCK_SIZE);
+ if (!enc_key ||
+ aes_siv_encrypt(kek, hash_len, wpabuf_head(cont_enc_key),
+ wpabuf_len(cont_enc_key), 0, NULL, NULL,
+ wpabuf_put(enc_key, hash_len + AES_BLOCK_SIZE)) < 0)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: encryptedKey", enc_key);
+
+ /*
+ * PasswordRecipientInfo ::= SEQUENCE {
+ * version CMSVersion,
+ * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
+ * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ * encryptedKey EncryptedKey}
+ *
+ * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
+ * parameters contains PBKDF2-params SEQUENCE.
+ */
+
+ key_der_alg = dpp_build_pbkdf2_alg_id(salt, hash_len);
+ key_enc_alg = asn1_build_alg_id(&asn1_aes_siv_cmac_aead_256_oid, NULL);
+ if (!key_der_alg || !key_enc_alg)
+ goto fail;
+ pwri = wpabuf_alloc(100 + wpabuf_len(key_der_alg) +
+ wpabuf_len(key_enc_alg) + wpabuf_len(enc_key));
+ if (!pwri)
+ goto fail;
+
+ /* version = 0 */
+ asn1_put_integer(pwri, 0);
+
+ /* [0] KeyDerivationAlgorithmIdentifier */
+ asn1_put_hdr(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 1, 0,
+ wpabuf_len(key_der_alg));
+ wpabuf_put_buf(pwri, key_der_alg);
+
+ /* KeyEncryptionAlgorithmIdentifier */
+ wpabuf_put_buf(pwri, key_enc_alg);
+
+ /* EncryptedKey ::= OCTET STRING */
+ asn1_put_octet_string(pwri, enc_key);
+
+fail:
+ wpabuf_clear_free(key_der_alg);
+ wpabuf_free(key_enc_alg);
+ wpabuf_free(enc_key);
+ wpabuf_free(salt);
+ forced_memzero(kek, sizeof(kek));
+ return asn1_encaps(pwri, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+}
+
+
+static struct wpabuf *
+dpp_build_recipient_info(struct dpp_authentication *auth, size_t hash_len,
+ const struct wpabuf *cont_enc_key)
+{
+ struct wpabuf *pwri;
+
+ /*
+ * RecipientInfo ::= CHOICE {
+ * ktri KeyTransRecipientInfo,
+ * kari [1] KeyAgreeRecipientInfo,
+ * kekri [2] KEKRecipientInfo,
+ * pwri [3] PasswordRecipientInfo,
+ * ori [4] OtherRecipientInfo}
+ *
+ * Shall always use the pwri CHOICE.
+ */
+
+ pwri = dpp_build_pw_recipient_info(auth, hash_len, cont_enc_key);
+ return asn1_encaps(pwri, ASN1_CLASS_CONTEXT_SPECIFIC, 3);
+}
+
+
+static struct wpabuf *
+dpp_build_enc_cont_info(struct dpp_authentication *auth, size_t hash_len,
+ const struct wpabuf *cont_enc_key)
+{
+ struct wpabuf *key_pkg, *enc_cont_info = NULL, *enc_cont = NULL,
+ *enc_alg;
+ const struct asn1_oid *oid;
+ size_t enc_cont_len;
+
+ /*
+ * EncryptedContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
+ */
+
+ if (hash_len == 32)
+ oid = &asn1_aes_siv_cmac_aead_256_oid;
+ else if (hash_len == 48)
+ oid = &asn1_aes_siv_cmac_aead_384_oid;
+ else if (hash_len == 64)
+ oid = &asn1_aes_siv_cmac_aead_512_oid;
+ else
+ return NULL;
+
+ key_pkg = dpp_build_key_pkg(auth);
+ enc_alg = asn1_build_alg_id(oid, NULL);
+ if (!key_pkg || !enc_alg)
+ goto fail;
+
+ wpa_hexdump_buf_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
+ key_pkg);
+
+ enc_cont_len = wpabuf_len(key_pkg) + AES_BLOCK_SIZE;
+ enc_cont = wpabuf_alloc(enc_cont_len);
+ if (!enc_cont ||
+ aes_siv_encrypt(wpabuf_head(cont_enc_key), wpabuf_len(cont_enc_key),
+ wpabuf_head(key_pkg), wpabuf_len(key_pkg),
+ 0, NULL, NULL,
+ wpabuf_put(enc_cont, enc_cont_len)) < 0)
+ goto fail;
+
+ enc_cont_info = wpabuf_alloc(100 + wpabuf_len(enc_alg) +
+ wpabuf_len(enc_cont));
+ if (!enc_cont_info)
+ goto fail;
+
+ /* ContentType ::= OBJECT IDENTIFIER */
+ asn1_put_oid(enc_cont_info, &asn1_dpp_asymmetric_key_package_oid);
+
+ /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+ wpabuf_put_buf(enc_cont_info, enc_alg);
+
+ /* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+ * EncryptedContent ::= OCTET STRING */
+ asn1_put_hdr(enc_cont_info, ASN1_CLASS_CONTEXT_SPECIFIC, 0, 0,
+ wpabuf_len(enc_cont));
+ wpabuf_put_buf(enc_cont_info, enc_cont);
+
+fail:
+ wpabuf_clear_free(key_pkg);
+ wpabuf_free(enc_cont);
+ wpabuf_free(enc_alg);
+ return enc_cont_info;
+}
+
+
+static struct wpabuf * dpp_gen_random(size_t len)
+{
+ struct wpabuf *key;
+
+ key = wpabuf_alloc(len);
+ if (!key || os_get_random(wpabuf_put(key, len), len) < 0) {
+ wpabuf_free(key);
+ key = NULL;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "DPP: content-encryption key", key);
+ return key;
+}
+
+
+struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth)
+{
+ struct wpabuf *env = NULL;
+ struct wpabuf *recipient_info = NULL, *enc_cont_info = NULL;
+ struct wpabuf *cont_enc_key = NULL;
+ size_t hash_len;
+
+ if (!auth->conf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No Configurator instance selected for the session - cannot build DPPEnvelopedData");
+ return NULL;
+ }
+
+ if (!auth->provision_configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Configurator provisioning not allowed");
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Building DPPEnvelopedData");
+
+ hash_len = auth->conf->curve->hash_len;
+ cont_enc_key = dpp_gen_random(hash_len);
+ if (!cont_enc_key)
+ goto fail;
+ recipient_info = dpp_build_recipient_info(auth, hash_len, cont_enc_key);
+ enc_cont_info = dpp_build_enc_cont_info(auth, hash_len, cont_enc_key);
+ if (!recipient_info || !enc_cont_info)
+ goto fail;
+
+ env = wpabuf_alloc(wpabuf_len(recipient_info) +
+ wpabuf_len(enc_cont_info) +
+ 100);
+ if (!env)
+ goto fail;
+
+ /*
+ * DPPEnvelopedData ::= EnvelopedData
+ *
+ * EnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
+ *
+ * For DPP, version is 3, both originatorInfo and
+ * unprotectedAttrs are omitted, and recipientInfos contains a single
+ * RecipientInfo.
+ */
+
+ /* EnvelopedData.version = 3 */
+ asn1_put_integer(env, 3);
+
+ /* RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo */
+ asn1_put_set(env, recipient_info);
+
+ /* EncryptedContentInfo ::= SEQUENCE */
+ asn1_put_sequence(env, enc_cont_info);
+
+ env = asn1_encaps(env, ASN1_CLASS_UNIVERSAL, ASN1_TAG_SEQUENCE);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: DPPEnvelopedData", env);
+out:
+ wpabuf_clear_free(cont_enc_key);
+ wpabuf_clear_free(recipient_info);
+ wpabuf_free(enc_cont_info);
+ return env;
+fail:
+ wpabuf_free(env);
+ env = NULL;
+ goto out;
+}
+
+
+struct dpp_enveloped_data {
+ const u8 *enc_cont;
+ size_t enc_cont_len;
+ const u8 *enc_key;
+ size_t enc_key_len;
+ const u8 *salt;
+ size_t pbkdf2_key_len;
+ size_t prf_hash_len;
+};
+
+
+static int dpp_parse_recipient_infos(const u8 *pos, size_t len,
+ struct dpp_enveloped_data *data)
+{
+ struct asn1_hdr hdr;
+ const u8 *end = pos + len;
+ const u8 *next, *e_end;
+ struct asn1_oid oid;
+ int val;
+ const u8 *params;
+ size_t params_len;
+
+ wpa_hexdump(MSG_MSGDUMP, "DPP: RecipientInfos", pos, len);
+
+ /*
+ * RecipientInfo ::= CHOICE {
+ * ktri KeyTransRecipientInfo,
+ * kari [1] KeyAgreeRecipientInfo,
+ * kekri [2] KEKRecipientInfo,
+ * pwri [3] PasswordRecipientInfo,
+ * ori [4] OtherRecipientInfo}
+ *
+ * Shall always use the pwri CHOICE.
+ */
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
+ !asn1_is_cs_tag(&hdr, 3)) {
+ asn1_unexpected(&hdr, "DPP: Expected CHOICE [3] (pwri)");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: PasswordRecipientInfo",
+ hdr.payload, hdr.length);
+ pos = hdr.payload;
+ end = pos + hdr.length;
+
+ /*
+ * PasswordRecipientInfo ::= SEQUENCE {
+ * version CMSVersion,
+ * keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier OPTIONAL,
+ * keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ * encryptedKey EncryptedKey}
+ *
+ * version is 0, keyDerivationAlgorithm is id-PKBDF2, and the
+ * parameters contains PBKDF2-params SEQUENCE.
+ */
+
+ if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
+ return -1;
+ pos = hdr.payload;
+
+ if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: pwri.version != 0");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Remaining PasswordRecipientInfo after version",
+ pos, end - pos);
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
+ !asn1_is_cs_tag(&hdr, 0)) {
+ asn1_unexpected(&hdr,
+ "DPP: Expected keyDerivationAlgorithm [0]");
+ return -1;
+ }
+ pos = hdr.payload;
+ e_end = pos + hdr.length;
+
+ /* KeyDerivationAlgorithmIdentifier ::= AlgorithmIdentifier */
+ if (asn1_get_alg_id(pos, e_end - pos, &oid, &params, &params_len,
+ &next) < 0)
+ return -1;
+ if (!asn1_oid_equal(&oid, &asn1_pbkdf2_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected KeyDerivationAlgorithmIdentifier %s",
+ buf);
+ return -1;
+ }
+
+ /*
+ * PBKDF2-params ::= SEQUENCE {
+ * salt CHOICE {
+ * specified OCTET STRING,
+ * otherSource AlgorithmIdentifier}
+ * iterationCount INTEGER (1..MAX),
+ * keyLength INTEGER (1..MAX),
+ * prf AlgorithmIdentifier}
+ *
+ * salt is an 64 octet value, iterationCount is 1000, keyLength is based
+ * on Configurator signing key length, prf is
+ * id-hmacWithSHA{256,384,512} based on Configurator signing key.
+ */
+ if (!params ||
+ asn1_get_sequence(params, params_len, &hdr, &e_end) < 0)
+ return -1;
+ pos = hdr.payload;
+
+ if (asn1_get_next(pos, e_end - pos, &hdr) < 0 ||
+ !asn1_is_octetstring(&hdr)) {
+ asn1_unexpected(&hdr,
+ "DPP: Expected OCTETSTRING (salt.specified)");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: salt.specified",
+ hdr.payload, hdr.length);
+ if (hdr.length != 64) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected salt length %u",
+ hdr.length);
+ return -1;
+ }
+ data->salt = hdr.payload;
+ pos = hdr.payload + hdr.length;
+
+ if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 1000) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected iterationCount %d", val);
+ return -1;
+ }
+
+ if (asn1_get_integer(pos, e_end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 32 && val != 48 && val != 64) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected keyLength %d", val);
+ return -1;
+ }
+ data->pbkdf2_key_len = val;
+
+ if (asn1_get_sequence(pos, e_end - pos, &hdr, NULL) < 0 ||
+ asn1_get_oid(hdr.payload, hdr.length, &oid, &pos) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not parse prf");
+ return -1;
+ }
+ if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha256_oid)) {
+ data->prf_hash_len = 32;
+ } else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha384_oid)) {
+ data->prf_hash_len = 48;
+ } else if (asn1_oid_equal(&oid, &asn1_pbkdf2_hmac_sha512_oid)) {
+ data->prf_hash_len = 64;
+ } else {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected PBKDF2-params.prf %s",
+ buf);
+ return -1;
+ }
+
+ pos = next;
+
+ /* keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier
+ *
+ * KeyEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * id-alg-AES-SIV-CMAC-aed-256, id-alg-AES-SIV-CMAC-aed-384, or
+ * id-alg-AES-SIV-CMAC-aed-512. */
+ if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
+ return -1;
+ if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected KeyEncryptionAlgorithmIdentifier %s",
+ buf);
+ return -1;
+ }
+
+ /*
+ * encryptedKey EncryptedKey
+ *
+ * EncryptedKey ::= OCTET STRING
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ !asn1_is_octetstring(&hdr)) {
+ asn1_unexpected(&hdr,
+ "DPP: Expected OCTETSTRING (pwri.encryptedKey)");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: pwri.encryptedKey",
+ hdr.payload, hdr.length);
+ data->enc_key = hdr.payload;
+ data->enc_key_len = hdr.length;
+
+ return 0;
+}
+
+
+static int dpp_parse_encrypted_content_info(const u8 *pos, const u8 *end,
+ struct dpp_enveloped_data *data)
+{
+ struct asn1_hdr hdr;
+ struct asn1_oid oid;
+
+ /*
+ * EncryptedContentInfo ::= SEQUENCE {
+ * contentType ContentType,
+ * contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ * encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL}
+ */
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+ return -1;
+ wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContentInfo",
+ hdr.payload, hdr.length);
+ if (pos < end) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Unexpected extra data after EncryptedContentInfo",
+ pos, end - pos);
+ return -1;
+ }
+
+ end = pos;
+ pos = hdr.payload;
+
+ /* ContentType ::= OBJECT IDENTIFIER */
+ if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not parse ContentType");
+ return -1;
+ }
+ if (!asn1_oid_equal(&oid, &asn1_dpp_asymmetric_key_package_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected ContentType %s", buf);
+ return -1;
+ }
+
+ /* ContentEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier */
+ if (asn1_get_alg_id(pos, end - pos, &oid, NULL, NULL, &pos) < 0)
+ return -1;
+ if (!asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_256_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_384_oid) &&
+ !asn1_oid_equal(&oid, &asn1_aes_siv_cmac_aead_512_oid)) {
+ char buf[80];
+
+ asn1_oid_to_str(&oid, buf, sizeof(buf));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected ContentEncryptionAlgorithmIdentifier %s",
+ buf);
+ return -1;
+ }
+ /* ignore optional parameters */
+
+ /* encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+ * EncryptedContent ::= OCTET STRING */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.constructed ||
+ !asn1_is_cs_tag(&hdr, 0)) {
+ asn1_unexpected(&hdr,
+ "DPP: Expected [0] IMPLICIT (EncryptedContent)");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: EncryptedContent",
+ hdr.payload, hdr.length);
+ data->enc_cont = hdr.payload;
+ data->enc_cont_len = hdr.length;
+ return 0;
+}
+
+
+static int dpp_parse_enveloped_data(const u8 *env_data, size_t env_data_len,
+ struct dpp_enveloped_data *data)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos, *end;
+ int val;
+
+ os_memset(data, 0, sizeof(*data));
+
+ /*
+ * DPPEnvelopedData ::= EnvelopedData
+ *
+ * EnvelopedData ::= SEQUENCE {
+ * version CMSVersion,
+ * originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ * recipientInfos RecipientInfos,
+ * encryptedContentInfo EncryptedContentInfo,
+ * unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL}
+ *
+ * CMSVersion ::= INTEGER
+ *
+ * RecipientInfos ::= SET SIZE (1..MAX) OF RecipientInfo
+ *
+ * For DPP, version is 3, both originatorInfo and
+ * unprotectedAttrs are omitted, and recipientInfos contains a single
+ * RecipientInfo.
+ */
+ if (asn1_get_sequence(env_data, env_data_len, &hdr, &end) < 0)
+ return -1;
+ pos = hdr.payload;
+ if (end < env_data + env_data_len) {
+ wpa_hexdump(MSG_DEBUG,
+ "DPP: Unexpected extra data after DPPEnvelopedData",
+ end, env_data + env_data_len - end);
+ return -1;
+ }
+
+ if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+ return -1;
+ if (val != 3) {
+ wpa_printf(MSG_DEBUG, "DPP: EnvelopedData.version != 3");
+ return -1;
+ }
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_set(&hdr)) {
+ asn1_unexpected(&hdr,
+ "DPP: Expected SET (RecipientInfos)");
+ return -1;
+ }
+
+ if (dpp_parse_recipient_infos(hdr.payload, hdr.length, data) < 0)
+ return -1;
+ return dpp_parse_encrypted_content_info(hdr.payload + hdr.length, end,
+ data);
+}
+
+
+static struct dpp_asymmetric_key *
+dpp_parse_one_asymmetric_key(const u8 *buf, size_t len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos = buf, *end = buf + len, *next;
+ int val;
+ const u8 *params;
+ size_t params_len;
+ struct asn1_oid oid;
+ char txt[80];
+ struct dpp_asymmetric_key *key;
+ EC_KEY *eckey;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: OneAsymmetricKey", buf, len);
+
+ key = os_zalloc(sizeof(*key));
+ if (!key)
+ return NULL;
+
+ /*
+ * OneAsymmetricKey ::= SEQUENCE {
+ * version Version,
+ * privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
+ * privateKey PrivateKey,
+ * attributes [0] Attributes OPTIONAL,
+ * ...,
+ * [[2: publicKey [1] BIT STRING OPTIONAL ]],
+ * ...
+ * }
+ */
+ if (asn1_get_sequence(pos, end - pos, &hdr, &end) < 0)
+ goto fail;
+ pos = hdr.payload;
+
+ /* Version ::= INTEGER { v1(0), v2(1) } (v1, ..., v2) */
+ if (asn1_get_integer(pos, end - pos, &val, &pos) < 0)
+ goto fail;
+ if (val != 0 && val != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported DPPAsymmetricKeyPackage version %d",
+ val);
+ goto fail;
+ }
+
+ /* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier */
+ if (asn1_get_alg_id(pos, end - pos, &oid, &params, &params_len,
+ &pos) < 0)
+ goto fail;
+ if (!asn1_oid_equal(&oid, &asn1_ec_public_key_oid)) {
+ asn1_oid_to_str(&oid, txt, sizeof(txt));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported PrivateKeyAlgorithmIdentifier %s",
+ txt);
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: PrivateKeyAlgorithmIdentifier params",
+ params, params_len);
+ /*
+ * ECParameters ::= CHOICE {
+ * namedCurve OBJECT IDENTIFIER
+ * -- implicitCurve NULL
+ * -- specifiedCurve SpecifiedECDomain}
+ */
+ if (!params || asn1_get_oid(params, params_len, &oid, &next) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not parse ECParameters.namedCurve");
+ goto fail;
+ }
+ asn1_oid_to_str(&oid, txt, sizeof(txt));
+ wpa_printf(MSG_MSGDUMP, "DPP: namedCurve %s", txt);
+ /* Assume the curve is identified within ECPrivateKey, so that this
+ * separate indication is not really needed. */
+
+ /*
+ * PrivateKey ::= OCTET STRING
+ * (Contains DER encoding of ECPrivateKey)
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ !asn1_is_octetstring(&hdr)) {
+ asn1_unexpected(&hdr,
+ "DPP: Expected OCTETSTRING (PrivateKey)");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: PrivateKey",
+ hdr.payload, hdr.length);
+ pos = hdr.payload + hdr.length;
+ eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ key->csign = EVP_PKEY_new();
+ if (!key->csign || EVP_PKEY_assign_EC_KEY(key->csign, eckey) != 1) {
+ EC_KEY_free(eckey);
+ goto fail;
+ }
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("DPP: Received c-sign-key", key->csign);
+
+ /*
+ * Attributes ::= SET OF Attribute { { OneAsymmetricKeyAttributes } }
+ *
+ * Exactly one instance of type Attribute in OneAsymmetricKey.
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 || !hdr.constructed ||
+ !asn1_is_cs_tag(&hdr, 0)) {
+ asn1_unexpected(&hdr, "DPP: Expected [0] Attributes");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: Attributes",
+ hdr.payload, hdr.length);
+ if (hdr.payload + hdr.length < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data at the end of OneAsymmetricKey",
+ hdr.payload + hdr.length,
+ end - (hdr.payload + hdr.length));
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_set(&hdr)) {
+ asn1_unexpected(&hdr, "DPP: Expected SET (Attributes)");
+ goto fail;
+ }
+ if (hdr.payload + hdr.length < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data at the end of OneAsymmetricKey (after SET)",
+ hdr.payload + hdr.length,
+ end - (hdr.payload + hdr.length));
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ /*
+ * OneAsymmetricKeyAttributes ATTRIBUTE ::= {
+ * aa-DPPConfigurationParameters,
+ * ... -- For local profiles
+ * }
+ *
+ * aa-DPPConfigurationParameters ATTRIBUTE ::=
+ * { TYPE DPPConfigurationParameters IDENTIFIED BY id-DPPConfigParams }
+ *
+ * Attribute ::= SEQUENCE {
+ * type OBJECT IDENTIFIER,
+ * values SET SIZE(1..MAX) OF Type
+ *
+ * Exactly one instance of ATTRIBUTE in attrValues.
+ */
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+ goto fail;
+ if (pos < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data at the end of ATTRIBUTE",
+ pos, end - pos);
+ }
+ end = pos;
+ pos = hdr.payload;
+
+ if (asn1_get_oid(pos, end - pos, &oid, &pos) < 0)
+ goto fail;
+ if (!asn1_oid_equal(&oid, &asn1_dpp_config_params_oid)) {
+ asn1_oid_to_str(&oid, txt, sizeof(txt));
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected Attribute identifier %s", txt);
+ goto fail;
+ }
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 || !asn1_is_set(&hdr)) {
+ asn1_unexpected(&hdr, "DPP: Expected SET (Attribute)");
+ goto fail;
+ }
+ pos = hdr.payload;
+ end = hdr.payload + hdr.length;
+
+ /*
+ * DPPConfigurationParameters ::= SEQUENCE {
+ * privacyProtectionKey PrivateKey,
+ * configurationTemplate UTF8String,
+ * connectorTemplate UTF8String OPTIONAL}
+ */
+
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPConfigurationParameters",
+ pos, end - pos);
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0)
+ goto fail;
+ if (pos < end) {
+ wpa_hexdump_key(MSG_MSGDUMP,
+ "DPP: Ignore additional data after DPPConfigurationParameters",
+ pos, end - pos);
+ }
+ end = pos;
+ pos = hdr.payload;
+
+ /*
+ * PrivateKey ::= OCTET STRING
+ * (Contains DER encoding of ECPrivateKey)
+ */
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ !asn1_is_octetstring(&hdr)) {
+ asn1_unexpected(&hdr, "DPP: Expected OCTETSTRING (PrivateKey)");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: privacyProtectionKey",
+ hdr.payload, hdr.length);
+ pos = hdr.payload + hdr.length;
+ eckey = d2i_ECPrivateKey(NULL, &hdr.payload, hdr.length);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ key->pp_key = EVP_PKEY_new();
+ if (!key->pp_key || EVP_PKEY_assign_EC_KEY(key->pp_key, eckey) != 1) {
+ EC_KEY_free(eckey);
+ goto fail;
+ }
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("DPP: Received privacyProtectionKey",
+ key->pp_key);
+
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ !asn1_is_utf8string(&hdr)) {
+ asn1_unexpected(&hdr,
+ "DPP: Expected UTF8STRING (configurationTemplate)");
+ goto fail;
+ }
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: configurationTemplate",
+ hdr.payload, hdr.length);
+ key->config_template = os_zalloc(hdr.length + 1);
+ if (!key->config_template)
+ goto fail;
+ os_memcpy(key->config_template, hdr.payload, hdr.length);
+
+ pos = hdr.payload + hdr.length;
+
+ if (pos < end) {
+ if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+ !asn1_is_utf8string(&hdr)) {
+ asn1_unexpected(&hdr,
+ "DPP: Expected UTF8STRING (connectorTemplate)");
+ goto fail;
+ }
+ wpa_hexdump_ascii_key(MSG_MSGDUMP, "DPP: connectorTemplate",
+ hdr.payload, hdr.length);
+ key->connector_template = os_zalloc(hdr.length + 1);
+ if (!key->connector_template)
+ goto fail;
+ os_memcpy(key->connector_template, hdr.payload, hdr.length);
+ }
+
+ return key;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse OneAsymmetricKey");
+ dpp_free_asymmetric_key(key);
+ return NULL;
+}
+
+
+static struct dpp_asymmetric_key *
+dpp_parse_dpp_asymmetric_key_package(const u8 *key_pkg, size_t key_pkg_len)
+{
+ struct asn1_hdr hdr;
+ const u8 *pos = key_pkg, *end = key_pkg + key_pkg_len;
+ struct dpp_asymmetric_key *first = NULL, *last = NULL, *key;
+
+ wpa_hexdump_key(MSG_MSGDUMP, "DPP: DPPAsymmetricKeyPackage",
+ key_pkg, key_pkg_len);
+
+ /*
+ * DPPAsymmetricKeyPackage ::= AsymmetricKeyPackage
+ *
+ * AsymmetricKeyPackage ::= SEQUENCE SIZE (1..MAX) OF OneAsymmetricKey
+ */
+ while (pos < end) {
+ if (asn1_get_sequence(pos, end - pos, &hdr, &pos) < 0 ||
+ !(key = dpp_parse_one_asymmetric_key(hdr.payload,
+ hdr.length))) {
+ dpp_free_asymmetric_key(first);
+ return NULL;
+ }
+ if (!last) {
+ first = last = key;
+ } else {
+ last->next = key;
+ last = key;
+ }
+ }
+
+ return first;
+}
+
+
+int dpp_conf_resp_env_data(struct dpp_authentication *auth,
+ const u8 *env_data, size_t env_data_len)
+{
+ u8 key[DPP_MAX_HASH_LEN];
+ size_t key_len;
+ u8 kek[DPP_MAX_HASH_LEN];
+ u8 cont_encr_key[DPP_MAX_HASH_LEN];
+ size_t cont_encr_key_len;
+ int res;
+ u8 *key_pkg;
+ size_t key_pkg_len;
+ struct dpp_enveloped_data data;
+ struct dpp_asymmetric_key *keys;
+
+ wpa_hexdump(MSG_DEBUG, "DPP: DPPEnvelopedData", env_data, env_data_len);
+
+ if (dpp_parse_enveloped_data(env_data, env_data_len, &data) < 0)
+ return -1;
+
+ key_len = auth->curve->hash_len;
+ /* password = HKDF-Expand(bk, "Enveloped Data Password", length) */
+ res = dpp_hkdf_expand(key_len, auth->bk, key_len,
+ "Enveloped Data Password", key, key_len);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PBKDF2 key", key, key_len);
+
+ if (dpp_pbkdf2(data.prf_hash_len, key, key_len, data.salt, 64, 1000,
+ kek, data.pbkdf2_key_len)) {
+ wpa_printf(MSG_DEBUG, "DPP: PBKDF2 failed");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: key-encryption key from PBKDF2",
+ kek, data.pbkdf2_key_len);
+
+ if (data.enc_key_len < AES_BLOCK_SIZE ||
+ data.enc_key_len > sizeof(cont_encr_key) + AES_BLOCK_SIZE) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid encryptedKey length");
+ return -1;
+ }
+ res = aes_siv_decrypt(kek, data.pbkdf2_key_len,
+ data.enc_key, data.enc_key_len,
+ 0, NULL, NULL, cont_encr_key);
+ forced_memzero(kek, data.pbkdf2_key_len);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: AES-SIV decryption of encryptedKey failed");
+ return -1;
+ }
+ cont_encr_key_len = data.enc_key_len - AES_BLOCK_SIZE;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: content-encryption key",
+ cont_encr_key, cont_encr_key_len);
+
+ if (data.enc_cont_len < AES_BLOCK_SIZE)
+ return -1;
+ key_pkg_len = data.enc_cont_len - AES_BLOCK_SIZE;
+ key_pkg = os_malloc(key_pkg_len);
+ if (!key_pkg)
+ return -1;
+ res = aes_siv_decrypt(cont_encr_key, cont_encr_key_len,
+ data.enc_cont, data.enc_cont_len,
+ 0, NULL, NULL, key_pkg);
+ forced_memzero(cont_encr_key, cont_encr_key_len);
+ if (res < 0) {
+ bin_clear_free(key_pkg, key_pkg_len);
+ wpa_printf(MSG_DEBUG,
+ "DPP: AES-SIV decryption of encryptedContent failed");
+ return -1;
+ }
+
+ keys = dpp_parse_dpp_asymmetric_key_package(key_pkg, key_pkg_len);
+ bin_clear_free(key_pkg, key_pkg_len);
+ dpp_free_asymmetric_key(auth->conf_key_pkg);
+ auth->conf_key_pkg = keys;
+
+ return keys != NULL;
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/contrib/wpa/src/common/dpp_crypto.c b/contrib/wpa/src/common/dpp_crypto.c
new file mode 100644
index 000000000000..c75fc78711a8
--- /dev/null
+++ b/contrib/wpa/src/common/dpp_crypto.c
@@ -0,0 +1,3329 @@
+/*
+ * DPP crypto functionality
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/pem.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "utils/json.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/sha384.h"
+#include "crypto/sha512.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
+{
+ sig->r = r;
+ sig->s = s;
+ return 1;
+}
+
+
+static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
+ const BIGNUM **ps)
+{
+ if (pr)
+ *pr = sig->r;
+ if (ps)
+ *ps = sig->s;
+}
+
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+ if (pkey->type != EVP_PKEY_EC)
+ return NULL;
+ return pkey->pkey.ec;
+}
+
+#endif
+
+static const struct dpp_curve_params dpp_curves[] = {
+ /* The mandatory to support and the default NIST P-256 curve needs to
+ * be the first entry on this list. */
+ { "prime256v1", 32, 32, 16, 32, "P-256", 19, "ES256" },
+ { "secp384r1", 48, 48, 24, 48, "P-384", 20, "ES384" },
+ { "secp521r1", 64, 64, 32, 66, "P-521", 21, "ES512" },
+ { "brainpoolP256r1", 32, 32, 16, 32, "BP-256", 28, "BS256" },
+ { "brainpoolP384r1", 48, 48, 24, 48, "BP-384", 29, "BS384" },
+ { "brainpoolP512r1", 64, 64, 32, 64, "BP-512", 30, "BS512" },
+ { NULL, 0, 0, 0, 0, NULL, 0, NULL }
+};
+
+
+const struct dpp_curve_params * dpp_get_curve_name(const char *name)
+{
+ int i;
+
+ if (!name)
+ return &dpp_curves[0];
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (os_strcmp(name, dpp_curves[i].name) == 0 ||
+ (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0))
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name)
+{
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (dpp_curves[i].jwk_crv &&
+ os_strcmp(name, dpp_curves[i].jwk_crv) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+static const struct dpp_curve_params *
+dpp_get_curve_oid(const ASN1_OBJECT *poid)
+{
+ ASN1_OBJECT *oid;
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ oid = OBJ_txt2obj(dpp_curves[i].name, 0);
+ if (oid && OBJ_cmp(poid, oid) == 0)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+const struct dpp_curve_params * dpp_get_curve_nid(int nid)
+{
+ int i, tmp;
+
+ if (!nid)
+ return NULL;
+ for (i = 0; dpp_curves[i].name; i++) {
+ tmp = OBJ_txt2nid(dpp_curves[i].name);
+ if (tmp == nid)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+const struct dpp_curve_params * dpp_get_curve_ike_group(u16 group)
+{
+ int i;
+
+ for (i = 0; dpp_curves[i].name; i++) {
+ if (dpp_curves[i].ike_group == group)
+ return &dpp_curves[i];
+ }
+ return NULL;
+}
+
+
+void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+ const EC_POINT *point)
+{
+ BIGNUM *x, *y;
+ BN_CTX *ctx;
+ char *x_str = NULL, *y_str = NULL;
+
+ if (!wpa_debug_show_keys)
+ return;
+
+ ctx = BN_CTX_new();
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !x || !y ||
+ EC_POINT_get_affine_coordinates_GFp(group, point, x, y, ctx) != 1)
+ goto fail;
+
+ x_str = BN_bn2hex(x);
+ y_str = BN_bn2hex(y);
+ if (!x_str || !y_str)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "%s (%s,%s)", title, x_str, y_str);
+
+fail:
+ OPENSSL_free(x_str);
+ OPENSSL_free(y_str);
+ BN_free(x);
+ BN_free(y);
+ BN_CTX_free(ctx);
+}
+
+
+void dpp_debug_print_key(const char *title, EVP_PKEY *key)
+{
+ EC_KEY *eckey;
+ BIO *out;
+ size_t rlen;
+ char *txt;
+ int res;
+ unsigned char *der = NULL;
+ int der_len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ return;
+
+ EVP_PKEY_print_private(out, key, 0, NULL);
+ rlen = BIO_ctrl_pending(out);
+ txt = os_malloc(rlen + 1);
+ if (txt) {
+ res = BIO_read(out, txt, rlen);
+ if (res > 0) {
+ txt[res] = '\0';
+ wpa_printf(MSG_DEBUG, "%s: %s", title, txt);
+ }
+ os_free(txt);
+ }
+ BIO_free(out);
+
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!eckey)
+ return;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (group && point)
+ dpp_debug_print_point(title, group, point);
+
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECPrivateKey", der, der_len);
+ OPENSSL_free(der);
+ if (der_len <= 0) {
+ der = NULL;
+ der_len = i2d_EC_PUBKEY(eckey, &der);
+ if (der_len > 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: EC_PUBKEY", der, der_len);
+ OPENSSL_free(der);
+ }
+
+ EC_KEY_free(eckey);
+}
+
+
+static int dpp_hash_vector(const struct dpp_curve_params *curve,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ if (curve->hash_len == 32)
+ return sha256_vector(num_elem, addr, len, mac);
+ if (curve->hash_len == 48)
+ return sha384_vector(num_elem, addr, len, mac);
+ if (curve->hash_len == 64)
+ return sha512_vector(num_elem, addr, len, mac);
+ return -1;
+}
+
+
+int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+ const char *label, u8 *out, size_t outlen)
+{
+ if (hash_len == 32)
+ return hmac_sha256_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ if (hash_len == 48)
+ return hmac_sha384_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ if (hash_len == 64)
+ return hmac_sha512_kdf(secret, secret_len, NULL,
+ (const u8 *) label, os_strlen(label),
+ out, outlen);
+ return -1;
+}
+
+
+int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac)
+{
+ if (hash_len == 32)
+ return hmac_sha256_vector(key, key_len, num_elem, addr, len,
+ mac);
+ if (hash_len == 48)
+ return hmac_sha384_vector(key, key_len, num_elem, addr, len,
+ mac);
+ if (hash_len == 64)
+ return hmac_sha512_vector(key, key_len, num_elem, addr, len,
+ mac);
+ return -1;
+}
+
+
+static int dpp_hmac(size_t hash_len, const u8 *key, size_t key_len,
+ const u8 *data, size_t data_len, u8 *mac)
+{
+ if (hash_len == 32)
+ return hmac_sha256(key, key_len, data, data_len, mac);
+ if (hash_len == 48)
+ return hmac_sha384(key, key_len, data, data_len, mac);
+ if (hash_len == 64)
+ return hmac_sha512(key, key_len, data, data_len, mac);
+ return -1;
+}
+
+
+#ifdef CONFIG_DPP2
+
+static int dpp_pbkdf2_f(size_t hash_len,
+ const u8 *password, size_t password_len,
+ const u8 *salt, size_t salt_len,
+ unsigned int iterations, unsigned int count, u8 *digest)
+{
+ unsigned char tmp[DPP_MAX_HASH_LEN], tmp2[DPP_MAX_HASH_LEN];
+ unsigned int i;
+ size_t j;
+ u8 count_buf[4];
+ const u8 *addr[2];
+ size_t len[2];
+
+ addr[0] = salt;
+ len[0] = salt_len;
+ addr[1] = count_buf;
+ len[1] = 4;
+
+ /* F(P, S, c, i) = U1 xor U2 xor ... Uc
+ * U1 = PRF(P, S || i)
+ * U2 = PRF(P, U1)
+ * Uc = PRF(P, Uc-1)
+ */
+
+ WPA_PUT_BE32(count_buf, count);
+ if (dpp_hmac_vector(hash_len, password, password_len, 2, addr, len,
+ tmp))
+ return -1;
+ os_memcpy(digest, tmp, hash_len);
+
+ for (i = 1; i < iterations; i++) {
+ if (dpp_hmac(hash_len, password, password_len, tmp, hash_len,
+ tmp2))
+ return -1;
+ os_memcpy(tmp, tmp2, hash_len);
+ for (j = 0; j < hash_len; j++)
+ digest[j] ^= tmp2[j];
+ }
+
+ return 0;
+}
+
+
+int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
+ const u8 *salt, size_t salt_len, unsigned int iterations,
+ u8 *buf, size_t buflen)
+{
+ unsigned int count = 0;
+ unsigned char *pos = buf;
+ size_t left = buflen, plen;
+ unsigned char digest[DPP_MAX_HASH_LEN];
+
+ while (left > 0) {
+ count++;
+ if (dpp_pbkdf2_f(hash_len, password, password_len,
+ salt, salt_len, iterations, count, digest))
+ return -1;
+ plen = left > hash_len ? hash_len : left;
+ os_memcpy(pos, digest, plen);
+ pos += plen;
+ left -= plen;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len)
+{
+ int num_bytes, offset;
+
+ num_bytes = BN_num_bytes(bn);
+ if ((size_t) num_bytes > len)
+ return -1;
+ offset = len - num_bytes;
+ os_memset(pos, 0, offset);
+ BN_bn2bin(bn, pos + offset);
+ return 0;
+}
+
+
+struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix)
+{
+ int len, res;
+ EC_KEY *eckey;
+ struct wpabuf *buf;
+ unsigned char *pos;
+
+ eckey = EVP_PKEY_get1_EC_KEY(pkey);
+ if (!eckey)
+ return NULL;
+ EC_KEY_set_conv_form(eckey, POINT_CONVERSION_UNCOMPRESSED);
+ len = i2o_ECPublicKey(eckey, NULL);
+ if (len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to determine public key encoding length");
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ buf = wpabuf_alloc(len);
+ if (!buf) {
+ EC_KEY_free(eckey);
+ return NULL;
+ }
+
+ pos = wpabuf_put(buf, len);
+ res = i2o_ECPublicKey(eckey, &pos);
+ EC_KEY_free(eckey);
+ if (res != len) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to encode public key (res=%d/%d)",
+ res, len);
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ if (!prefix) {
+ /* Remove 0x04 prefix to match DPP definition */
+ pos = wpabuf_mhead(buf);
+ os_memmove(pos, pos + 1, len - 1);
+ buf->used--;
+ }
+
+ return buf;
+}
+
+
+EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+ const u8 *buf_x, const u8 *buf_y,
+ size_t len)
+{
+ EC_KEY *eckey = NULL;
+ BN_CTX *ctx;
+ EC_POINT *point = NULL;
+ BIGNUM *x = NULL, *y = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ ctx = BN_CTX_new();
+ if (!ctx) {
+ wpa_printf(MSG_ERROR, "DPP: Out of memory");
+ return NULL;
+ }
+
+ point = EC_POINT_new(group);
+ x = BN_bin2bn(buf_x, len, NULL);
+ y = BN_bin2bn(buf_y, len, NULL);
+ if (!point || !x || !y) {
+ wpa_printf(MSG_ERROR, "DPP: Out of memory");
+ goto fail;
+ }
+
+ if (!EC_POINT_set_affine_coordinates_GFp(group, point, x, y, ctx)) {
+ wpa_printf(MSG_ERROR,
+ "DPP: OpenSSL: EC_POINT_set_affine_coordinates_GFp failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (!EC_POINT_is_on_curve(group, point, ctx) ||
+ EC_POINT_is_at_infinity(group, point)) {
+ wpa_printf(MSG_ERROR, "DPP: Invalid point");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: dpp_set_pubkey_point_group", group, point);
+
+ eckey = EC_KEY_new();
+ if (!eckey ||
+ EC_KEY_set_group(eckey, group) != 1 ||
+ EC_KEY_set_public_key(eckey, point) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to set EC_KEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+ pkey = EVP_PKEY_new();
+ if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+ goto fail;
+ }
+
+out:
+ BN_free(x);
+ BN_free(y);
+ EC_KEY_free(eckey);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+ return pkey;
+fail:
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto out;
+}
+
+
+EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len)
+{
+ const EC_KEY *eckey;
+ const EC_GROUP *group;
+ EVP_PKEY *pkey = NULL;
+
+ if (len & 1)
+ return NULL;
+
+ eckey = EVP_PKEY_get0_EC_KEY(group_key);
+ if (!eckey) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Could not get EC_KEY from group_key");
+ return NULL;
+ }
+
+ group = EC_KEY_get0_group(eckey);
+ if (group)
+ pkey = dpp_set_pubkey_point_group(group, buf, buf + len / 2,
+ len / 2);
+ else
+ wpa_printf(MSG_ERROR, "DPP: Could not get EC group");
+
+ return pkey;
+}
+
+
+EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve)
+{
+ EVP_PKEY_CTX *kctx = NULL;
+ EC_KEY *ec_params = NULL;
+ EVP_PKEY *params = NULL, *key = NULL;
+ int nid;
+
+ wpa_printf(MSG_DEBUG, "DPP: Generating a keypair");
+
+ nid = OBJ_txt2nid(curve->name);
+ if (nid == NID_undef) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve %s", curve->name);
+ return NULL;
+ }
+
+ ec_params = EC_KEY_new_by_curve_name(nid);
+ if (!ec_params) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EC_KEY parameters");
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(ec_params, OPENSSL_EC_NAMED_CURVE);
+ params = EVP_PKEY_new();
+ if (!params || EVP_PKEY_set1_EC_KEY(params, ec_params) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to generate EVP_PKEY parameters");
+ goto fail;
+ }
+
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (!kctx ||
+ EVP_PKEY_keygen_init(kctx) != 1 ||
+ EVP_PKEY_keygen(kctx, &key) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate EC key");
+ key = NULL;
+ goto fail;
+ }
+
+ if (wpa_debug_show_keys)
+ dpp_debug_print_key("Own generated key", key);
+
+fail:
+ EC_KEY_free(ec_params);
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(kctx);
+ return key;
+}
+
+
+EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ EVP_PKEY *pkey;
+ EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ pkey = EVP_PKEY_new();
+ if (!pkey)
+ return NULL;
+ eckey = d2i_ECPrivateKey(NULL, &privkey, privkey_len);
+ if (!eckey) {
+ wpa_printf(MSG_INFO,
+ "DPP: OpenSSL: d2i_ECPrivateKey() failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ group = EC_KEY_get0_group(eckey);
+ if (!group) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ nid = EC_GROUP_get_curve_name(group);
+ *curve = dpp_get_curve_nid(nid);
+ if (!*curve) {
+ wpa_printf(MSG_INFO,
+ "DPP: Unsupported curve (nid=%d) in pre-assigned key",
+ nid);
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+
+ if (EVP_PKEY_assign_EC_KEY(pkey, eckey) != 1) {
+ EC_KEY_free(eckey);
+ EVP_PKEY_free(pkey);
+ return NULL;
+ }
+ return pkey;
+}
+
+
+typedef struct {
+ /* AlgorithmIdentifier ecPublicKey with optional parameters present
+ * as an OID identifying the curve */
+ X509_ALGOR *alg;
+ /* Compressed format public key per ANSI X9.63 */
+ ASN1_BIT_STRING *pub_key;
+} DPP_BOOTSTRAPPING_KEY;
+
+ASN1_SEQUENCE(DPP_BOOTSTRAPPING_KEY) = {
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, alg, X509_ALGOR),
+ ASN1_SIMPLE(DPP_BOOTSTRAPPING_KEY, pub_key, ASN1_BIT_STRING)
+} ASN1_SEQUENCE_END(DPP_BOOTSTRAPPING_KEY);
+
+IMPLEMENT_ASN1_FUNCTIONS(DPP_BOOTSTRAPPING_KEY);
+
+
+static struct wpabuf * dpp_bootstrap_key_der(EVP_PKEY *key)
+{
+ unsigned char *der = NULL;
+ int der_len;
+ const EC_KEY *eckey;
+ struct wpabuf *ret = NULL;
+ size_t len;
+ const EC_GROUP *group;
+ const EC_POINT *point;
+ BN_CTX *ctx;
+ DPP_BOOTSTRAPPING_KEY *bootstrap = NULL;
+ int nid;
+
+ ctx = BN_CTX_new();
+ eckey = EVP_PKEY_get0_EC_KEY(key);
+ if (!ctx || !eckey)
+ goto fail;
+
+ group = EC_KEY_get0_group(eckey);
+ point = EC_KEY_get0_public_key(eckey);
+ if (!group || !point)
+ goto fail;
+ dpp_debug_print_point("DPP: bootstrap public key", group, point);
+ nid = EC_GROUP_get_curve_name(group);
+
+ bootstrap = DPP_BOOTSTRAPPING_KEY_new();
+ if (!bootstrap ||
+ X509_ALGOR_set0(bootstrap->alg, OBJ_nid2obj(EVP_PKEY_EC),
+ V_ASN1_OBJECT, (void *) OBJ_nid2obj(nid)) != 1)
+ goto fail;
+
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ NULL, 0, ctx);
+ if (len == 0)
+ goto fail;
+
+ der = OPENSSL_malloc(len);
+ if (!der)
+ goto fail;
+ len = EC_POINT_point2oct(group, point, POINT_CONVERSION_COMPRESSED,
+ der, len, ctx);
+
+ OPENSSL_free(bootstrap->pub_key->data);
+ bootstrap->pub_key->data = der;
+ der = NULL;
+ bootstrap->pub_key->length = len;
+ /* No unused bits */
+ bootstrap->pub_key->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
+ bootstrap->pub_key->flags |= ASN1_STRING_FLAG_BITS_LEFT;
+
+ der_len = i2d_DPP_BOOTSTRAPPING_KEY(bootstrap, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_ERROR,
+ "DDP: Failed to build DER encoded public key");
+ goto fail;
+ }
+
+ ret = wpabuf_alloc_copy(der, der_len);
+fail:
+ DPP_BOOTSTRAPPING_KEY_free(bootstrap);
+ OPENSSL_free(der);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi)
+{
+ struct wpabuf *der;
+ int res;
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ return -1;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+ res = dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der));
+ if (res < 0)
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ wpabuf_free(der);
+ return res;
+}
+
+
+int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+ const u8 *privkey, size_t privkey_len)
+{
+ char *base64 = NULL;
+ char *pos, *end;
+ size_t len;
+ struct wpabuf *der = NULL;
+
+ bi->curve = dpp_get_curve_name(curve);
+ if (!bi->curve) {
+ wpa_printf(MSG_INFO, "DPP: Unsupported curve: %s", curve);
+ return -1;
+ }
+
+ if (privkey)
+ bi->pubkey = dpp_set_keypair(&bi->curve, privkey, privkey_len);
+ else
+ bi->pubkey = dpp_gen_keypair(bi->curve);
+ if (!bi->pubkey)
+ goto fail;
+ bi->own = 1;
+
+ der = dpp_bootstrap_key_der(bi->pubkey);
+ if (!der)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Compressed public key (DER)",
+ der);
+
+ if (dpp_bi_pubkey_hash(bi, wpabuf_head(der), wpabuf_len(der)) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ goto fail;
+ }
+
+ base64 = base64_encode(wpabuf_head(der), wpabuf_len(der), &len);
+ wpabuf_free(der);
+ der = NULL;
+ if (!base64)
+ goto fail;
+ pos = base64;
+ end = pos + len;
+ for (;;) {
+ pos = os_strchr(pos, '\n');
+ if (!pos)
+ break;
+ os_memmove(pos, pos + 1, end - pos);
+ }
+ os_free(bi->pk);
+ bi->pk = base64;
+ return 0;
+fail:
+ os_free(base64);
+ wpabuf_free(der);
+ return -1;
+}
+
+
+int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "first intermediate key";
+ int res;
+
+ /* k1 = HKDF(<>, "first intermediate key", M.x) */
+
+ /* HKDF-Extract(<>, M.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Mx, Mx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=M.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k1, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: k1 = HKDF-Expand(PRK, info, L)",
+ k1, hash_len);
+ return 0;
+}
+
+
+int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "second intermediate key";
+ int res;
+
+ /* k2 = HKDF(<>, "second intermediate key", N.x) */
+
+ /* HKDF-Extract(<>, N.x) */
+ os_memset(salt, 0, hash_len);
+ res = dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, k2, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: k2 = HKDF-Expand(PRK, info, L)",
+ k2, hash_len);
+ return 0;
+}
+
+
+int dpp_derive_bk_ke(struct dpp_authentication *auth)
+{
+ unsigned int hash_len = auth->curve->hash_len;
+ size_t nonce_len = auth->curve->nonce_len;
+ u8 nonces[2 * DPP_MAX_NONCE_LEN];
+ const char *info_ke = "DPP Key";
+ int res;
+ const u8 *addr[3];
+ size_t len[3];
+ size_t num_elem = 0;
+
+ if (!auth->Mx_len || !auth->Nx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mx/Nx not available - cannot derive ke");
+ return -1;
+ }
+
+ /* bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x]) */
+ os_memcpy(nonces, auth->i_nonce, nonce_len);
+ os_memcpy(&nonces[nonce_len], auth->r_nonce, nonce_len);
+ addr[num_elem] = auth->Mx;
+ len[num_elem] = auth->Mx_len;
+ num_elem++;
+ addr[num_elem] = auth->Nx;
+ len[num_elem] = auth->Nx_len;
+ num_elem++;
+ if (auth->peer_bi && auth->own_bi) {
+ if (!auth->Lx_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Lx not available - cannot derive ke");
+ return -1;
+ }
+ addr[num_elem] = auth->Lx;
+ len[num_elem] = auth->secret_len;
+ num_elem++;
+ }
+ res = dpp_hmac_vector(hash_len, nonces, 2 * nonce_len,
+ num_elem, addr, len, auth->bk);
+ if (res < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: bk = HKDF-Extract(I-nonce | R-nonce, M.x | N.x [| L.x])",
+ auth->bk, hash_len);
+
+ /* ke = HKDF-Expand(bk, "DPP Key", length) */
+ res = dpp_hkdf_expand(hash_len, auth->bk, hash_len, info_ke, auth->ke,
+ hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: ke = HKDF-Expand(bk, \"DPP Key\", length)",
+ auth->ke, hash_len);
+
+ return 0;
+}
+
+
+int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, u8 *secret, size_t *secret_len)
+{
+ EVP_PKEY_CTX *ctx;
+ int ret = -1;
+
+ ERR_clear_error();
+ *secret_len = 0;
+
+ ctx = EVP_PKEY_CTX_new(own, NULL);
+ if (!ctx) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_CTX_new failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return -1;
+ }
+
+ if (EVP_PKEY_derive_init(ctx) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive_init failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (EVP_PKEY_derive_set_peer(ctx, peer) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: EVP_PKEY_derive_set_peet failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (EVP_PKEY_derive(ctx, NULL, secret_len) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive(NULL) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
+ u8 buf[200];
+ int level = *secret_len > 200 ? MSG_ERROR : MSG_DEBUG;
+
+ /* It looks like OpenSSL can return unexpectedly large buffer
+ * need for shared secret from EVP_PKEY_derive(NULL) in some
+ * cases. For example, group 19 has shown cases where secret_len
+ * is set to 72 even though the actual length ends up being
+ * updated to 32 when EVP_PKEY_derive() is called with a buffer
+ * for the value. Work around this by trying to fetch the value
+ * and continue if it is within supported range even when the
+ * initial buffer need is claimed to be larger. */
+ wpa_printf(level,
+ "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
+ (int) *secret_len);
+ if (*secret_len > 200)
+ goto fail;
+ if (EVP_PKEY_derive(ctx, buf, secret_len) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (*secret_len > DPP_MAX_SHARED_SECRET_LEN) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Unexpected secret_len=%d from EVP_PKEY_derive()",
+ (int) *secret_len);
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: Unexpected secret_len change",
+ buf, *secret_len);
+ os_memcpy(secret, buf, *secret_len);
+ forced_memzero(buf, sizeof(buf));
+ goto done;
+ }
+
+ if (EVP_PKEY_derive(ctx, secret, secret_len) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: EVP_PKEY_derive failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+done:
+ ret = 0;
+
+fail:
+ EVP_PKEY_CTX_free(ctx);
+ return ret;
+}
+
+
+int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len)
+{
+ const u8 *addr[2];
+ size_t len[2];
+
+ addr[0] = data;
+ len[0] = data_len;
+ if (sha256_vector(1, addr, len, bi->pubkey_hash) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash",
+ bi->pubkey_hash, SHA256_MAC_LEN);
+
+ addr[0] = (const u8 *) "chirp";
+ len[0] = 5;
+ addr[1] = data;
+ len[1] = data_len;
+ if (sha256_vector(2, addr, len, bi->pubkey_hash_chirp) < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "DPP: Public key hash (chirp)",
+ bi->pubkey_hash_chirp, SHA256_MAC_LEN);
+
+ return 0;
+}
+
+
+int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len)
+{
+ EVP_PKEY *pkey;
+ const unsigned char *p;
+ int res;
+ X509_PUBKEY *pub = NULL;
+ ASN1_OBJECT *ppkalg;
+ const unsigned char *pk;
+ int ppklen;
+ X509_ALGOR *pa;
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20800000L)
+ ASN1_OBJECT *pa_oid;
+#else
+ const ASN1_OBJECT *pa_oid;
+#endif
+ const void *pval;
+ int ptype;
+ const ASN1_OBJECT *poid;
+ char buf[100];
+
+ if (dpp_bi_pubkey_hash(bi, data, data_len) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to hash public key");
+ return -1;
+ }
+
+ /* DER encoded ASN.1 SubjectPublicKeyInfo
+ *
+ * SubjectPublicKeyInfo ::= SEQUENCE {
+ * algorithm AlgorithmIdentifier,
+ * subjectPublicKey BIT STRING }
+ *
+ * AlgorithmIdentifier ::= SEQUENCE {
+ * algorithm OBJECT IDENTIFIER,
+ * parameters ANY DEFINED BY algorithm OPTIONAL }
+ *
+ * subjectPublicKey = compressed format public key per ANSI X9.63
+ * algorithm = ecPublicKey (1.2.840.10045.2.1)
+ * parameters = shall be present and shall be OBJECT IDENTIFIER; e.g.,
+ * prime256v1 (1.2.840.10045.3.1.7)
+ */
+
+ p = data;
+ pkey = d2i_PUBKEY(NULL, &p, data_len);
+
+ if (!pkey) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not parse URI public-key SubjectPublicKeyInfo");
+ return -1;
+ }
+
+ if (EVP_PKEY_type(EVP_PKEY_id(pkey)) != EVP_PKEY_EC) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo does not describe an EC key");
+ EVP_PKEY_free(pkey);
+ return -1;
+ }
+
+ res = X509_PUBKEY_set(&pub, pkey);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not set pubkey");
+ goto fail;
+ }
+
+ res = X509_PUBKEY_get0_param(&ppkalg, &pk, &ppklen, &pa, pub);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters");
+ goto fail;
+ }
+ res = OBJ_obj2txt(buf, sizeof(buf), ppkalg, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey algorithm: %s", buf);
+ if (os_strcmp(buf, "id-ecPublicKey") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo algorithm");
+ goto fail;
+ }
+
+ X509_ALGOR_get0(&pa_oid, &ptype, (void *) &pval, pa);
+ if (ptype != V_ASN1_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: SubjectPublicKeyInfo parameters did not contain an OID");
+ goto fail;
+ }
+ poid = pval;
+ res = OBJ_obj2txt(buf, sizeof(buf), poid, 0);
+ if (res < 0 || (size_t) res >= sizeof(buf)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not extract SubjectPublicKeyInfo parameters OID");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: URI subjectPublicKey parameters: %s", buf);
+ bi->curve = dpp_get_curve_oid(poid);
+ if (!bi->curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported SubjectPublicKeyInfo curve: %s",
+ buf);
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: URI subjectPublicKey", pk, ppklen);
+
+ X509_PUBKEY_free(pub);
+ bi->pubkey = pkey;
+ return 0;
+fail:
+ X509_PUBKEY_free(pub);
+ EVP_PKEY_free(pkey);
+ return -1;
+}
+
+
+static struct wpabuf *
+dpp_parse_jws_prot_hdr(const struct dpp_curve_params *curve,
+ const u8 *prot_hdr, u16 prot_hdr_len,
+ const EVP_MD **ret_md)
+{
+ struct json_token *root, *token;
+ struct wpabuf *kid = NULL;
+
+ root = json_parse((const char *) prot_hdr, prot_hdr_len);
+ if (!root) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: JSON parsing failed for JWS Protected Header");
+ goto fail;
+ }
+
+ if (root->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: JWS Protected Header root is not an object");
+ goto fail;
+ }
+
+ token = json_get_member(root, "typ");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No typ string value found");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header typ=%s",
+ token->string);
+ if (os_strcmp(token->string, "dppCon") != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported JWS Protected Header typ=%s",
+ token->string);
+ goto fail;
+ }
+
+ token = json_get_member(root, "alg");
+ if (!token || token->type != JSON_STRING) {
+ wpa_printf(MSG_DEBUG, "DPP: No alg string value found");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: JWS Protected Header alg=%s",
+ token->string);
+ if (os_strcmp(token->string, curve->jws_alg) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected JWS Protected Header alg=%s (expected %s based on C-sign-key)",
+ token->string, curve->jws_alg);
+ goto fail;
+ }
+ if (os_strcmp(token->string, "ES256") == 0 ||
+ os_strcmp(token->string, "BS256") == 0)
+ *ret_md = EVP_sha256();
+ else if (os_strcmp(token->string, "ES384") == 0 ||
+ os_strcmp(token->string, "BS384") == 0)
+ *ret_md = EVP_sha384();
+ else if (os_strcmp(token->string, "ES512") == 0 ||
+ os_strcmp(token->string, "BS512") == 0)
+ *ret_md = EVP_sha512();
+ else
+ *ret_md = NULL;
+ if (!*ret_md) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported JWS Protected Header alg=%s",
+ token->string);
+ goto fail;
+ }
+
+ kid = json_get_member_base64url(root, "kid");
+ if (!kid) {
+ wpa_printf(MSG_DEBUG, "DPP: No kid string value found");
+ goto fail;
+ }
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: JWS Protected Header kid (decoded)",
+ kid);
+
+fail:
+ json_free(root);
+ return kid;
+}
+
+
+static int dpp_check_pubkey_match(EVP_PKEY *pub, struct wpabuf *r_hash)
+{
+ struct wpabuf *uncomp;
+ int res;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[1];
+ size_t len[1];
+
+ if (wpabuf_len(r_hash) != SHA256_MAC_LEN)
+ return -1;
+ uncomp = dpp_get_pubkey_point(pub, 1);
+ if (!uncomp)
+ return -1;
+ addr[0] = wpabuf_head(uncomp);
+ len[0] = wpabuf_len(uncomp);
+ wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed public key",
+ addr[0], len[0]);
+ res = sha256_vector(1, addr, len, hash);
+ wpabuf_free(uncomp);
+ if (res < 0)
+ return -1;
+ if (os_memcmp(hash, wpabuf_head(r_hash), SHA256_MAC_LEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received hash value does not match calculated public key hash value");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated hash",
+ hash, SHA256_MAC_LEN);
+ return -1;
+ }
+ return 0;
+}
+
+
+enum dpp_status_error
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+ EVP_PKEY *csign_pub, const char *connector)
+{
+ enum dpp_status_error ret = 255;
+ const char *pos, *end, *signed_start, *signed_end;
+ struct wpabuf *kid = NULL;
+ unsigned char *prot_hdr = NULL, *signature = NULL;
+ size_t prot_hdr_len = 0, signature_len = 0;
+ const EVP_MD *sign_md = NULL;
+ unsigned char *der = NULL;
+ int der_len;
+ int res;
+ EVP_MD_CTX *md_ctx = NULL;
+ ECDSA_SIG *sig = NULL;
+ BIGNUM *r = NULL, *s = NULL;
+ const struct dpp_curve_params *curve;
+ const EC_KEY *eckey;
+ const EC_GROUP *group;
+ int nid;
+
+ eckey = EVP_PKEY_get0_EC_KEY(csign_pub);
+ if (!eckey)
+ goto fail;
+ group = EC_KEY_get0_group(eckey);
+ if (!group)
+ goto fail;
+ nid = EC_GROUP_get_curve_name(group);
+ curve = dpp_get_curve_nid(nid);
+ if (!curve)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "DPP: C-sign-key group: %s", curve->jwk_crv);
+ os_memset(info, 0, sizeof(*info));
+
+ signed_start = pos = connector;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG, "DPP: Missing dot(1) in signedConnector");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ prot_hdr = base64_url_decode(pos, end - pos, &prot_hdr_len);
+ if (!prot_hdr) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector JWS Protected Header");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: signedConnector - JWS Protected Header",
+ prot_hdr, prot_hdr_len);
+ kid = dpp_parse_jws_prot_hdr(curve, prot_hdr, prot_hdr_len, &sign_md);
+ if (!kid) {
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ if (wpabuf_len(kid) != SHA256_MAC_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected signedConnector JWS Protected Header kid length: %u (expected %u)",
+ (unsigned int) wpabuf_len(kid), SHA256_MAC_LEN);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ pos = end + 1;
+ end = os_strchr(pos, '.');
+ if (!end) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing dot(2) in signedConnector");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ signed_end = end - 1;
+ info->payload = base64_url_decode(pos, end - pos, &info->payload_len);
+ if (!info->payload) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector JWS Payload");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG,
+ "DPP: signedConnector - JWS Payload",
+ info->payload, info->payload_len);
+ pos = end + 1;
+ signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
+ if (!signature) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to base64url decode signedConnector signature");
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector - signature",
+ signature, signature_len);
+
+ if (dpp_check_pubkey_match(csign_pub, kid) < 0) {
+ ret = DPP_STATUS_NO_MATCH;
+ goto fail;
+ }
+
+ if (signature_len & 0x01) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected signedConnector signature length (%d)",
+ (int) signature_len);
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ /* JWS Signature encodes the signature (r,s) as two octet strings. Need
+ * to convert that to DER encoded ECDSA_SIG for OpenSSL EVP routines. */
+ r = BN_bin2bn(signature, signature_len / 2, NULL);
+ s = BN_bin2bn(signature + signature_len / 2, signature_len / 2, NULL);
+ sig = ECDSA_SIG_new();
+ if (!r || !s || !sig || ECDSA_SIG_set0(sig, r, s) != 1)
+ goto fail;
+ r = NULL;
+ s = NULL;
+
+ der_len = i2d_ECDSA_SIG(sig, &der);
+ if (der_len <= 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Could not DER encode signature");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: DER encoded signature", der, der_len);
+ md_ctx = EVP_MD_CTX_create();
+ if (!md_ctx)
+ goto fail;
+
+ ERR_clear_error();
+ if (EVP_DigestVerifyInit(md_ctx, NULL, sign_md, NULL, csign_pub) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyInit failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestVerifyUpdate(md_ctx, signed_start,
+ signed_end - signed_start + 1) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestVerifyUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ res = EVP_DigestVerifyFinal(md_ctx, der, der_len);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: EVP_DigestVerifyFinal failed (res=%d): %s",
+ res, ERR_error_string(ERR_get_error(), NULL));
+ ret = DPP_STATUS_INVALID_CONNECTOR;
+ goto fail;
+ }
+
+ ret = DPP_STATUS_OK;
+fail:
+ EVP_MD_CTX_destroy(md_ctx);
+ os_free(prot_hdr);
+ wpabuf_free(kid);
+ os_free(signature);
+ ECDSA_SIG_free(sig);
+ BN_free(r);
+ BN_free(s);
+ OPENSSL_free(der);
+ return ret;
+}
+
+
+enum dpp_status_error
+dpp_check_signed_connector(struct dpp_signed_connector_info *info,
+ const u8 *csign_key, size_t csign_key_len,
+ const u8 *peer_connector, size_t peer_connector_len)
+{
+ const unsigned char *p;
+ EVP_PKEY *csign = NULL;
+ char *signed_connector = NULL;
+ enum dpp_status_error res = DPP_STATUS_INVALID_CONNECTOR;
+
+ p = csign_key;
+ csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ if (!csign) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to parse local C-sign-key information");
+ goto fail;
+ }
+
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: Peer signedConnector",
+ peer_connector, peer_connector_len);
+ signed_connector = os_malloc(peer_connector_len + 1);
+ if (!signed_connector)
+ goto fail;
+ os_memcpy(signed_connector, peer_connector, peer_connector_len);
+ signed_connector[peer_connector_len] = '\0';
+ res = dpp_process_signed_connector(info, csign, signed_connector);
+fail:
+ os_free(signed_connector);
+ EVP_PKEY_free(csign);
+ return res;
+}
+
+
+int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth)
+{
+ struct wpabuf *pix, *prx, *bix, *brx;
+ const u8 *addr[7];
+ size_t len[7];
+ size_t i, num_elem = 0;
+ size_t nonce_len;
+ u8 zero = 0;
+ int res = -1;
+
+ /* R-auth = H(I-nonce | R-nonce | PI.x | PR.x | [BI.x |] BR.x | 0) */
+ nonce_len = auth->curve->nonce_len;
+
+ if (auth->initiator) {
+ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ if (auth->own_bi)
+ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ else
+ bix = NULL;
+ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ } else {
+ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (auth->peer_bi)
+ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ else
+ bix = NULL;
+ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ }
+ if (!pix || !prx || !brx)
+ goto fail;
+
+ addr[num_elem] = auth->i_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = auth->r_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(pix);
+ len[num_elem] = wpabuf_len(pix) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(prx);
+ len[num_elem] = wpabuf_len(prx) / 2;
+ num_elem++;
+
+ if (bix) {
+ addr[num_elem] = wpabuf_head(bix);
+ len[num_elem] = wpabuf_len(bix) / 2;
+ num_elem++;
+ }
+
+ addr[num_elem] = wpabuf_head(brx);
+ len[num_elem] = wpabuf_len(brx) / 2;
+ num_elem++;
+
+ addr[num_elem] = &zero;
+ len[num_elem] = 1;
+ num_elem++;
+
+ wpa_printf(MSG_DEBUG, "DPP: R-auth hash components");
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+ res = dpp_hash_vector(auth->curve, num_elem, addr, len, r_auth);
+ if (res == 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: R-auth", r_auth,
+ auth->curve->hash_len);
+fail:
+ wpabuf_free(pix);
+ wpabuf_free(prx);
+ wpabuf_free(bix);
+ wpabuf_free(brx);
+ return res;
+}
+
+
+int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth)
+{
+ struct wpabuf *pix = NULL, *prx = NULL, *bix = NULL, *brx = NULL;
+ const u8 *addr[7];
+ size_t len[7];
+ size_t i, num_elem = 0;
+ size_t nonce_len;
+ u8 one = 1;
+ int res = -1;
+
+ /* I-auth = H(R-nonce | I-nonce | PR.x | PI.x | BR.x | [BI.x |] 1) */
+ nonce_len = auth->curve->nonce_len;
+
+ if (auth->initiator) {
+ pix = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ if (auth->own_bi)
+ bix = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ else
+ bix = NULL;
+ if (!auth->peer_bi)
+ goto fail;
+ brx = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ } else {
+ pix = dpp_get_pubkey_point(auth->peer_protocol_key, 0);
+ prx = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (auth->peer_bi)
+ bix = dpp_get_pubkey_point(auth->peer_bi->pubkey, 0);
+ else
+ bix = NULL;
+ if (!auth->own_bi)
+ goto fail;
+ brx = dpp_get_pubkey_point(auth->own_bi->pubkey, 0);
+ }
+ if (!pix || !prx || !brx)
+ goto fail;
+
+ addr[num_elem] = auth->r_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = auth->i_nonce;
+ len[num_elem] = nonce_len;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(prx);
+ len[num_elem] = wpabuf_len(prx) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(pix);
+ len[num_elem] = wpabuf_len(pix) / 2;
+ num_elem++;
+
+ addr[num_elem] = wpabuf_head(brx);
+ len[num_elem] = wpabuf_len(brx) / 2;
+ num_elem++;
+
+ if (bix) {
+ addr[num_elem] = wpabuf_head(bix);
+ len[num_elem] = wpabuf_len(bix) / 2;
+ num_elem++;
+ }
+
+ addr[num_elem] = &one;
+ len[num_elem] = 1;
+ num_elem++;
+
+ wpa_printf(MSG_DEBUG, "DPP: I-auth hash components");
+ for (i = 0; i < num_elem; i++)
+ wpa_hexdump(MSG_DEBUG, "DPP: hash component", addr[i], len[i]);
+ res = dpp_hash_vector(auth->curve, num_elem, addr, len, i_auth);
+ if (res == 0)
+ wpa_hexdump(MSG_DEBUG, "DPP: I-auth", i_auth,
+ auth->curve->hash_len);
+fail:
+ wpabuf_free(pix);
+ wpabuf_free(prx);
+ wpabuf_free(bix);
+ wpabuf_free(brx);
+ return res;
+}
+
+
+int dpp_auth_derive_l_responder(struct dpp_authentication *auth)
+{
+ const EC_GROUP *group;
+ EC_POINT *l = NULL;
+ const EC_KEY *BI, *bR, *pR;
+ const EC_POINT *BI_point;
+ BN_CTX *bnctx;
+ BIGNUM *lx, *sum, *q;
+ const BIGNUM *bR_bn, *pR_bn;
+ int ret = -1;
+
+ /* L = ((bR + pR) modulo q) * BI */
+
+ bnctx = BN_CTX_new();
+ sum = BN_new();
+ q = BN_new();
+ lx = BN_new();
+ if (!bnctx || !sum || !q || !lx)
+ goto fail;
+ BI = EVP_PKEY_get0_EC_KEY(auth->peer_bi->pubkey);
+ if (!BI)
+ goto fail;
+ BI_point = EC_KEY_get0_public_key(BI);
+ group = EC_KEY_get0_group(BI);
+ if (!group)
+ goto fail;
+
+ bR = EVP_PKEY_get0_EC_KEY(auth->own_bi->pubkey);
+ pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
+ if (!bR || !pR)
+ goto fail;
+ bR_bn = EC_KEY_get0_private_key(bR);
+ pR_bn = EC_KEY_get0_private_key(pR);
+ if (!bR_bn || !pR_bn)
+ goto fail;
+ if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+ BN_mod_add(sum, bR_bn, pR_bn, q, bnctx) != 1)
+ goto fail;
+ l = EC_POINT_new(group);
+ if (!l ||
+ EC_POINT_mul(group, l, NULL, BI_point, sum, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
+ ret = 0;
+fail:
+ EC_POINT_clear_free(l);
+ BN_clear_free(lx);
+ BN_clear_free(sum);
+ BN_free(q);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+
+int dpp_auth_derive_l_initiator(struct dpp_authentication *auth)
+{
+ const EC_GROUP *group;
+ EC_POINT *l = NULL, *sum = NULL;
+ const EC_KEY *bI, *BR, *PR;
+ const EC_POINT *BR_point, *PR_point;
+ BN_CTX *bnctx;
+ BIGNUM *lx;
+ const BIGNUM *bI_bn;
+ int ret = -1;
+
+ /* L = bI * (BR + PR) */
+
+ bnctx = BN_CTX_new();
+ lx = BN_new();
+ if (!bnctx || !lx)
+ goto fail;
+ BR = EVP_PKEY_get0_EC_KEY(auth->peer_bi->pubkey);
+ PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
+ if (!BR || !PR)
+ goto fail;
+ BR_point = EC_KEY_get0_public_key(BR);
+ PR_point = EC_KEY_get0_public_key(PR);
+
+ bI = EVP_PKEY_get0_EC_KEY(auth->own_bi->pubkey);
+ if (!bI)
+ goto fail;
+ group = EC_KEY_get0_group(bI);
+ bI_bn = EC_KEY_get0_private_key(bI);
+ if (!group || !bI_bn)
+ goto fail;
+ sum = EC_POINT_new(group);
+ l = EC_POINT_new(group);
+ if (!sum || !l ||
+ EC_POINT_add(group, sum, BR_point, PR_point, bnctx) != 1 ||
+ EC_POINT_mul(group, l, NULL, sum, bI_bn, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, l, lx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(lx, auth->Lx, auth->secret_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: L.x", auth->Lx, auth->secret_len);
+ auth->Lx_len = auth->secret_len;
+ ret = 0;
+fail:
+ EC_POINT_clear_free(l);
+ EC_POINT_clear_free(sum);
+ BN_clear_free(lx);
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+
+int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ const char *info = "DPP PMK";
+ int res;
+
+ /* PMK = HKDF(<>, "DPP PMK", N.x) */
+
+ /* HKDF-Extract(<>, N.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Nx, Nx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM=N.x)",
+ prk, hash_len);
+
+ /* HKDF-Expand(PRK, info, L) */
+ res = dpp_hkdf_expand(hash_len, prk, hash_len, info, pmk, hash_len);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PMK = HKDF-Expand(PRK, info, L)",
+ pmk, hash_len);
+ return 0;
+}
+
+
+int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+ EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid)
+{
+ struct wpabuf *nkx, *pkx;
+ int ret = -1, res;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 hash[SHA256_MAC_LEN];
+
+ /* PMKID = Truncate-128(H(min(NK.x, PK.x) | max(NK.x, PK.x))) */
+ nkx = dpp_get_pubkey_point(own_key, 0);
+ pkx = dpp_get_pubkey_point(peer_key, 0);
+ if (!nkx || !pkx)
+ goto fail;
+ addr[0] = wpabuf_head(nkx);
+ len[0] = wpabuf_len(nkx) / 2;
+ addr[1] = wpabuf_head(pkx);
+ len[1] = wpabuf_len(pkx) / 2;
+ if (len[0] != len[1])
+ goto fail;
+ if (os_memcmp(addr[0], addr[1], len[0]) > 0) {
+ addr[0] = wpabuf_head(pkx);
+ addr[1] = wpabuf_head(nkx);
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 1", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash payload 2", addr[1], len[1]);
+ res = sha256_vector(2, addr, len, hash);
+ if (res < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID hash output", hash, SHA256_MAC_LEN);
+ os_memcpy(pmkid, hash, PMKID_LEN);
+ wpa_hexdump(MSG_DEBUG, "DPP: PMKID", pmkid, PMKID_LEN);
+ ret = 0;
+fail:
+ wpabuf_free(nkx);
+ wpabuf_free(pkx);
+ return ret;
+}
+
+
+/* Role-specific elements for PKEX */
+
+/* NIST P-256 */
+static const u8 pkex_init_x_p256[32] = {
+ 0x56, 0x26, 0x12, 0xcf, 0x36, 0x48, 0xfe, 0x0b,
+ 0x07, 0x04, 0xbb, 0x12, 0x22, 0x50, 0xb2, 0x54,
+ 0xb1, 0x94, 0x64, 0x7e, 0x54, 0xce, 0x08, 0x07,
+ 0x2e, 0xec, 0xca, 0x74, 0x5b, 0x61, 0x2d, 0x25
+ };
+static const u8 pkex_init_y_p256[32] = {
+ 0x3e, 0x44, 0xc7, 0xc9, 0x8c, 0x1c, 0xa1, 0x0b,
+ 0x20, 0x09, 0x93, 0xb2, 0xfd, 0xe5, 0x69, 0xdc,
+ 0x75, 0xbc, 0xad, 0x33, 0xc1, 0xe7, 0xc6, 0x45,
+ 0x4d, 0x10, 0x1e, 0x6a, 0x3d, 0x84, 0x3c, 0xa4
+ };
+static const u8 pkex_resp_x_p256[32] = {
+ 0x1e, 0xa4, 0x8a, 0xb1, 0xa4, 0xe8, 0x42, 0x39,
+ 0xad, 0x73, 0x07, 0xf2, 0x34, 0xdf, 0x57, 0x4f,
+ 0xc0, 0x9d, 0x54, 0xbe, 0x36, 0x1b, 0x31, 0x0f,
+ 0x59, 0x91, 0x52, 0x33, 0xac, 0x19, 0x9d, 0x76
+};
+static const u8 pkex_resp_y_p256[32] = {
+ 0xd9, 0xfb, 0xf6, 0xb9, 0xf5, 0xfa, 0xdf, 0x19,
+ 0x58, 0xd8, 0x3e, 0xc9, 0x89, 0x7a, 0x35, 0xc1,
+ 0xbd, 0xe9, 0x0b, 0x77, 0x7a, 0xcb, 0x91, 0x2a,
+ 0xe8, 0x21, 0x3f, 0x47, 0x52, 0x02, 0x4d, 0x67
+};
+
+/* NIST P-384 */
+static const u8 pkex_init_x_p384[48] = {
+ 0x95, 0x3f, 0x42, 0x9e, 0x50, 0x7f, 0xf9, 0xaa,
+ 0xac, 0x1a, 0xf2, 0x85, 0x2e, 0x64, 0x91, 0x68,
+ 0x64, 0xc4, 0x3c, 0xb7, 0x5c, 0xf8, 0xc9, 0x53,
+ 0x6e, 0x58, 0x4c, 0x7f, 0xc4, 0x64, 0x61, 0xac,
+ 0x51, 0x8a, 0x6f, 0xfe, 0xab, 0x74, 0xe6, 0x12,
+ 0x81, 0xac, 0x38, 0x5d, 0x41, 0xe6, 0xb9, 0xa3
+};
+static const u8 pkex_init_y_p384[48] = {
+ 0x76, 0x2f, 0x68, 0x84, 0xa6, 0xb0, 0x59, 0x29,
+ 0x83, 0xa2, 0x6c, 0xa4, 0x6c, 0x3b, 0xf8, 0x56,
+ 0x76, 0x11, 0x2a, 0x32, 0x90, 0xbd, 0x07, 0xc7,
+ 0x37, 0x39, 0x9d, 0xdb, 0x96, 0xf3, 0x2b, 0xb6,
+ 0x27, 0xbb, 0x29, 0x3c, 0x17, 0x33, 0x9d, 0x94,
+ 0xc3, 0xda, 0xac, 0x46, 0xb0, 0x8e, 0x07, 0x18
+};
+static const u8 pkex_resp_x_p384[48] = {
+ 0xad, 0xbe, 0xd7, 0x1d, 0x3a, 0x71, 0x64, 0x98,
+ 0x5f, 0xb4, 0xd6, 0x4b, 0x50, 0xd0, 0x84, 0x97,
+ 0x4b, 0x7e, 0x57, 0x70, 0xd2, 0xd9, 0xf4, 0x92,
+ 0x2a, 0x3f, 0xce, 0x99, 0xc5, 0x77, 0x33, 0x44,
+ 0x14, 0x56, 0x92, 0xcb, 0xae, 0x46, 0x64, 0xdf,
+ 0xe0, 0xbb, 0xd7, 0xb1, 0x29, 0x20, 0x72, 0xdf
+};
+static const u8 pkex_resp_y_p384[48] = {
+ 0xab, 0xa7, 0xdf, 0x52, 0xaa, 0xe2, 0x35, 0x0c,
+ 0xe3, 0x75, 0x32, 0xe6, 0xbf, 0x06, 0xc8, 0x7c,
+ 0x38, 0x29, 0x4c, 0xec, 0x82, 0xac, 0xd7, 0xa3,
+ 0x09, 0xd2, 0x0e, 0x22, 0x5a, 0x74, 0x52, 0xa1,
+ 0x7e, 0x54, 0x4e, 0xfe, 0xc6, 0x29, 0x33, 0x63,
+ 0x15, 0xe1, 0x7b, 0xe3, 0x40, 0x1c, 0xca, 0x06
+};
+
+/* NIST P-521 */
+static const u8 pkex_init_x_p521[66] = {
+ 0x00, 0x16, 0x20, 0x45, 0x19, 0x50, 0x95, 0x23,
+ 0x0d, 0x24, 0xbe, 0x00, 0x87, 0xdc, 0xfa, 0xf0,
+ 0x58, 0x9a, 0x01, 0x60, 0x07, 0x7a, 0xca, 0x76,
+ 0x01, 0xab, 0x2d, 0x5a, 0x46, 0xcd, 0x2c, 0xb5,
+ 0x11, 0x9a, 0xff, 0xaa, 0x48, 0x04, 0x91, 0x38,
+ 0xcf, 0x86, 0xfc, 0xa4, 0xa5, 0x0f, 0x47, 0x01,
+ 0x80, 0x1b, 0x30, 0xa3, 0xae, 0xe8, 0x1c, 0x2e,
+ 0xea, 0xcc, 0xf0, 0x03, 0x9f, 0x77, 0x4c, 0x8d,
+ 0x97, 0x76
+};
+static const u8 pkex_init_y_p521[66] = {
+ 0x00, 0xb3, 0x8e, 0x02, 0xe4, 0x2a, 0x63, 0x59,
+ 0x12, 0xc6, 0x10, 0xba, 0x3a, 0xf9, 0x02, 0x99,
+ 0x3f, 0x14, 0xf0, 0x40, 0xde, 0x5c, 0xc9, 0x8b,
+ 0x02, 0x55, 0xfa, 0x91, 0xb1, 0xcc, 0x6a, 0xbd,
+ 0xe5, 0x62, 0xc0, 0xc5, 0xe3, 0xa1, 0x57, 0x9f,
+ 0x08, 0x1a, 0xa6, 0xe2, 0xf8, 0x55, 0x90, 0xbf,
+ 0xf5, 0xa6, 0xc3, 0xd8, 0x52, 0x1f, 0xb7, 0x02,
+ 0x2e, 0x7c, 0xc8, 0xb3, 0x20, 0x1e, 0x79, 0x8d,
+ 0x03, 0xa8
+};
+static const u8 pkex_resp_x_p521[66] = {
+ 0x00, 0x79, 0xe4, 0x4d, 0x6b, 0x5e, 0x12, 0x0a,
+ 0x18, 0x2c, 0xb3, 0x05, 0x77, 0x0f, 0xc3, 0x44,
+ 0x1a, 0xcd, 0x78, 0x46, 0x14, 0xee, 0x46, 0x3f,
+ 0xab, 0xc9, 0x59, 0x7c, 0x85, 0xa0, 0xc2, 0xfb,
+ 0x02, 0x32, 0x99, 0xde, 0x5d, 0xe1, 0x0d, 0x48,
+ 0x2d, 0x71, 0x7d, 0x8d, 0x3f, 0x61, 0x67, 0x9e,
+ 0x2b, 0x8b, 0x12, 0xde, 0x10, 0x21, 0x55, 0x0a,
+ 0x5b, 0x2d, 0xe8, 0x05, 0x09, 0xf6, 0x20, 0x97,
+ 0x84, 0xb4
+};
+static const u8 pkex_resp_y_p521[66] = {
+ 0x00, 0x46, 0x63, 0x39, 0xbe, 0xcd, 0xa4, 0x2d,
+ 0xca, 0x27, 0x74, 0xd4, 0x1b, 0x91, 0x33, 0x20,
+ 0x83, 0xc7, 0x3b, 0xa4, 0x09, 0x8b, 0x8e, 0xa3,
+ 0x88, 0xe9, 0x75, 0x7f, 0x56, 0x7b, 0x38, 0x84,
+ 0x62, 0x02, 0x7c, 0x90, 0x51, 0x07, 0xdb, 0xe9,
+ 0xd0, 0xde, 0xda, 0x9a, 0x5d, 0xe5, 0x94, 0xd2,
+ 0xcf, 0x9d, 0x4c, 0x33, 0x91, 0xa6, 0xc3, 0x80,
+ 0xa7, 0x6e, 0x7e, 0x8d, 0xf8, 0x73, 0x6e, 0x53,
+ 0xce, 0xe1
+};
+
+/* Brainpool P-256r1 */
+static const u8 pkex_init_x_bp_p256r1[32] = {
+ 0x46, 0x98, 0x18, 0x6c, 0x27, 0xcd, 0x4b, 0x10,
+ 0x7d, 0x55, 0xa3, 0xdd, 0x89, 0x1f, 0x9f, 0xca,
+ 0xc7, 0x42, 0x5b, 0x8a, 0x23, 0xed, 0xf8, 0x75,
+ 0xac, 0xc7, 0xe9, 0x8d, 0xc2, 0x6f, 0xec, 0xd8
+};
+static const u8 pkex_init_y_bp_p256r1[32] = {
+ 0x93, 0xca, 0xef, 0xa9, 0x66, 0x3e, 0x87, 0xcd,
+ 0x52, 0x6e, 0x54, 0x13, 0xef, 0x31, 0x67, 0x30,
+ 0x15, 0x13, 0x9d, 0x6d, 0xc0, 0x95, 0x32, 0xbe,
+ 0x4f, 0xab, 0x5d, 0xf7, 0xbf, 0x5e, 0xaa, 0x0b
+};
+static const u8 pkex_resp_x_bp_p256r1[32] = {
+ 0x90, 0x18, 0x84, 0xc9, 0xdc, 0xcc, 0xb5, 0x2f,
+ 0x4a, 0x3f, 0x4f, 0x18, 0x0a, 0x22, 0x56, 0x6a,
+ 0xa9, 0xef, 0xd4, 0xe6, 0xc3, 0x53, 0xc2, 0x1a,
+ 0x23, 0x54, 0xdd, 0x08, 0x7e, 0x10, 0xd8, 0xe3
+};
+static const u8 pkex_resp_y_bp_p256r1[32] = {
+ 0x2a, 0xfa, 0x98, 0x9b, 0xe3, 0xda, 0x30, 0xfd,
+ 0x32, 0x28, 0xcb, 0x66, 0xfb, 0x40, 0x7f, 0xf2,
+ 0xb2, 0x25, 0x80, 0x82, 0x44, 0x85, 0x13, 0x7e,
+ 0x4b, 0xb5, 0x06, 0xc0, 0x03, 0x69, 0x23, 0x64
+};
+
+/* Brainpool P-384r1 */
+static const u8 pkex_init_x_bp_p384r1[48] = {
+ 0x0a, 0x2c, 0xeb, 0x49, 0x5e, 0xb7, 0x23, 0xbd,
+ 0x20, 0x5b, 0xe0, 0x49, 0xdf, 0xcf, 0xcf, 0x19,
+ 0x37, 0x36, 0xe1, 0x2f, 0x59, 0xdb, 0x07, 0x06,
+ 0xb5, 0xeb, 0x2d, 0xae, 0xc2, 0xb2, 0x38, 0x62,
+ 0xa6, 0x73, 0x09, 0xa0, 0x6c, 0x0a, 0xa2, 0x30,
+ 0x99, 0xeb, 0xf7, 0x1e, 0x47, 0xb9, 0x5e, 0xbe
+};
+static const u8 pkex_init_y_bp_p384r1[48] = {
+ 0x54, 0x76, 0x61, 0x65, 0x75, 0x5a, 0x2f, 0x99,
+ 0x39, 0x73, 0xca, 0x6c, 0xf9, 0xf7, 0x12, 0x86,
+ 0x54, 0xd5, 0xd4, 0xad, 0x45, 0x7b, 0xbf, 0x32,
+ 0xee, 0x62, 0x8b, 0x9f, 0x52, 0xe8, 0xa0, 0xc9,
+ 0xb7, 0x9d, 0xd1, 0x09, 0xb4, 0x79, 0x1c, 0x3e,
+ 0x1a, 0xbf, 0x21, 0x45, 0x66, 0x6b, 0x02, 0x52
+};
+static const u8 pkex_resp_x_bp_p384r1[48] = {
+ 0x03, 0xa2, 0x57, 0xef, 0xe8, 0x51, 0x21, 0xa0,
+ 0xc8, 0x9e, 0x21, 0x02, 0xb5, 0x9a, 0x36, 0x25,
+ 0x74, 0x22, 0xd1, 0xf2, 0x1b, 0xa8, 0x9a, 0x9b,
+ 0x97, 0xbc, 0x5a, 0xeb, 0x26, 0x15, 0x09, 0x71,
+ 0x77, 0x59, 0xec, 0x8b, 0xb7, 0xe1, 0xe8, 0xce,
+ 0x65, 0xb8, 0xaf, 0xf8, 0x80, 0xae, 0x74, 0x6c
+};
+static const u8 pkex_resp_y_bp_p384r1[48] = {
+ 0x2f, 0xd9, 0x6a, 0xc7, 0x3e, 0xec, 0x76, 0x65,
+ 0x2d, 0x38, 0x7f, 0xec, 0x63, 0x26, 0x3f, 0x04,
+ 0xd8, 0x4e, 0xff, 0xe1, 0x0a, 0x51, 0x74, 0x70,
+ 0xe5, 0x46, 0x63, 0x7f, 0x5c, 0xc0, 0xd1, 0x7c,
+ 0xfb, 0x2f, 0xea, 0xe2, 0xd8, 0x0f, 0x84, 0xcb,
+ 0xe9, 0x39, 0x5c, 0x64, 0xfe, 0xcb, 0x2f, 0xf1
+};
+
+/* Brainpool P-512r1 */
+static const u8 pkex_init_x_bp_p512r1[64] = {
+ 0x4c, 0xe9, 0xb6, 0x1c, 0xe2, 0x00, 0x3c, 0x9c,
+ 0xa9, 0xc8, 0x56, 0x52, 0xaf, 0x87, 0x3e, 0x51,
+ 0x9c, 0xbb, 0x15, 0x31, 0x1e, 0xc1, 0x05, 0xfc,
+ 0x7c, 0x77, 0xd7, 0x37, 0x61, 0x27, 0xd0, 0x95,
+ 0x98, 0xee, 0x5d, 0xa4, 0x3d, 0x09, 0xdb, 0x3d,
+ 0xfa, 0x89, 0x9e, 0x7f, 0xa6, 0xa6, 0x9c, 0xff,
+ 0x83, 0x5c, 0x21, 0x6c, 0x3e, 0xf2, 0xfe, 0xdc,
+ 0x63, 0xe4, 0xd1, 0x0e, 0x75, 0x45, 0x69, 0x0f
+};
+static const u8 pkex_init_y_bp_p512r1[64] = {
+ 0x50, 0xb5, 0x9b, 0xfa, 0x45, 0x67, 0x75, 0x94,
+ 0x44, 0xe7, 0x68, 0xb0, 0xeb, 0x3e, 0xb3, 0xb8,
+ 0xf9, 0x99, 0x05, 0xef, 0xae, 0x6c, 0xbc, 0xe3,
+ 0xe1, 0xd2, 0x51, 0x54, 0xdf, 0x59, 0xd4, 0x45,
+ 0x41, 0x3a, 0xa8, 0x0b, 0x76, 0x32, 0x44, 0x0e,
+ 0x07, 0x60, 0x3a, 0x6e, 0xbe, 0xfe, 0xe0, 0x58,
+ 0x52, 0xa0, 0xaa, 0x8b, 0xd8, 0x5b, 0xf2, 0x71,
+ 0x11, 0x9a, 0x9e, 0x8f, 0x1a, 0xd1, 0xc9, 0x99
+};
+static const u8 pkex_resp_x_bp_p512r1[64] = {
+ 0x2a, 0x60, 0x32, 0x27, 0xa1, 0xe6, 0x94, 0x72,
+ 0x1c, 0x48, 0xbe, 0xc5, 0x77, 0x14, 0x30, 0x76,
+ 0xe4, 0xbf, 0xf7, 0x7b, 0xc5, 0xfd, 0xdf, 0x19,
+ 0x1e, 0x0f, 0xdf, 0x1c, 0x40, 0xfa, 0x34, 0x9e,
+ 0x1f, 0x42, 0x24, 0xa3, 0x2c, 0xd5, 0xc7, 0xc9,
+ 0x7b, 0x47, 0x78, 0x96, 0xf1, 0x37, 0x0e, 0x88,
+ 0xcb, 0xa6, 0x52, 0x29, 0xd7, 0xa8, 0x38, 0x29,
+ 0x8e, 0x6e, 0x23, 0x47, 0xd4, 0x4b, 0x70, 0x3e
+};
+static const u8 pkex_resp_y_bp_p512r1[64] = {
+ 0x80, 0x1f, 0x43, 0xd2, 0x17, 0x35, 0xec, 0x81,
+ 0xd9, 0x4b, 0xdc, 0x81, 0x19, 0xd9, 0x5f, 0x68,
+ 0x16, 0x84, 0xfe, 0x63, 0x4b, 0x8d, 0x5d, 0xaa,
+ 0x88, 0x4a, 0x47, 0x48, 0xd4, 0xea, 0xab, 0x7d,
+ 0x6a, 0xbf, 0xe1, 0x28, 0x99, 0x6a, 0x87, 0x1c,
+ 0x30, 0xb4, 0x44, 0x2d, 0x75, 0xac, 0x35, 0x09,
+ 0x73, 0x24, 0x3d, 0xb4, 0x43, 0xb1, 0xc1, 0x56,
+ 0x56, 0xad, 0x30, 0x87, 0xf4, 0xc3, 0x00, 0xc7
+};
+
+
+static EVP_PKEY * dpp_pkex_get_role_elem(const struct dpp_curve_params *curve,
+ int init)
+{
+ EC_GROUP *group;
+ size_t len = curve->prime_len;
+ const u8 *x, *y;
+ EVP_PKEY *res;
+
+ switch (curve->ike_group) {
+ case 19:
+ x = init ? pkex_init_x_p256 : pkex_resp_x_p256;
+ y = init ? pkex_init_y_p256 : pkex_resp_y_p256;
+ break;
+ case 20:
+ x = init ? pkex_init_x_p384 : pkex_resp_x_p384;
+ y = init ? pkex_init_y_p384 : pkex_resp_y_p384;
+ break;
+ case 21:
+ x = init ? pkex_init_x_p521 : pkex_resp_x_p521;
+ y = init ? pkex_init_y_p521 : pkex_resp_y_p521;
+ break;
+ case 28:
+ x = init ? pkex_init_x_bp_p256r1 : pkex_resp_x_bp_p256r1;
+ y = init ? pkex_init_y_bp_p256r1 : pkex_resp_y_bp_p256r1;
+ break;
+ case 29:
+ x = init ? pkex_init_x_bp_p384r1 : pkex_resp_x_bp_p384r1;
+ y = init ? pkex_init_y_bp_p384r1 : pkex_resp_y_bp_p384r1;
+ break;
+ case 30:
+ x = init ? pkex_init_x_bp_p512r1 : pkex_resp_x_bp_p512r1;
+ y = init ? pkex_init_y_bp_p512r1 : pkex_resp_y_bp_p512r1;
+ break;
+ default:
+ return NULL;
+ }
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group)
+ return NULL;
+ res = dpp_set_pubkey_point_group(group, x, y, len);
+ EC_GROUP_free(group);
+ return res;
+}
+
+
+EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+ const u8 *mac_init, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group)
+{
+ u8 hash[DPP_MAX_HASH_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ unsigned int num_elem = 0;
+ EC_POINT *Qi = NULL;
+ EVP_PKEY *Pi = NULL;
+ const EC_KEY *Pi_ec;
+ const EC_POINT *Pi_point;
+ BIGNUM *hash_bn = NULL;
+ const EC_GROUP *group = NULL;
+ EC_GROUP *group2 = NULL;
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+
+ wpa_printf(MSG_DEBUG, "DPP: MAC-Initiator: " MACSTR, MAC2STR(mac_init));
+ addr[num_elem] = mac_init;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+ if (identifier) {
+ wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+ identifier);
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+ addr[num_elem] = (const u8 *) code;
+ len[num_elem] = os_strlen(code);
+ num_elem++;
+ if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: H(MAC-Initiator | [identifier |] code)",
+ hash, curve->hash_len);
+ Pi = dpp_pkex_get_role_elem(curve, 1);
+ if (!Pi)
+ goto fail;
+ dpp_debug_print_key("DPP: Pi", Pi);
+ Pi_ec = EVP_PKEY_get0_EC_KEY(Pi);
+ if (!Pi_ec)
+ goto fail;
+ Pi_point = EC_KEY_get0_public_key(Pi_ec);
+
+ group = EC_KEY_get0_group(Pi_ec);
+ if (!group)
+ goto fail;
+ group2 = EC_GROUP_dup(group);
+ if (!group2)
+ goto fail;
+ Qi = EC_POINT_new(group2);
+ if (!Qi) {
+ EC_GROUP_free(group2);
+ goto fail;
+ }
+ hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+ if (!hash_bn ||
+ EC_POINT_mul(group2, Qi, NULL, Pi_point, hash_bn, bnctx) != 1)
+ goto fail;
+ if (EC_POINT_is_at_infinity(group, Qi)) {
+ wpa_printf(MSG_INFO, "DPP: Qi is the point-at-infinity");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: Qi", group, Qi);
+out:
+ EVP_PKEY_free(Pi);
+ BN_clear_free(hash_bn);
+ if (ret_group && Qi)
+ *ret_group = group2;
+ else
+ EC_GROUP_free(group2);
+ return Qi;
+fail:
+ EC_POINT_free(Qi);
+ Qi = NULL;
+ goto out;
+}
+
+
+EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+ const u8 *mac_resp, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group)
+{
+ u8 hash[DPP_MAX_HASH_LEN];
+ const u8 *addr[3];
+ size_t len[3];
+ unsigned int num_elem = 0;
+ EC_POINT *Qr = NULL;
+ EVP_PKEY *Pr = NULL;
+ const EC_KEY *Pr_ec;
+ const EC_POINT *Pr_point;
+ BIGNUM *hash_bn = NULL;
+ const EC_GROUP *group = NULL;
+ EC_GROUP *group2 = NULL;
+
+ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+
+ wpa_printf(MSG_DEBUG, "DPP: MAC-Responder: " MACSTR, MAC2STR(mac_resp));
+ addr[num_elem] = mac_resp;
+ len[num_elem] = ETH_ALEN;
+ num_elem++;
+ if (identifier) {
+ wpa_printf(MSG_DEBUG, "DPP: code identifier: %s",
+ identifier);
+ addr[num_elem] = (const u8 *) identifier;
+ len[num_elem] = os_strlen(identifier);
+ num_elem++;
+ }
+ wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: code", code, os_strlen(code));
+ addr[num_elem] = (const u8 *) code;
+ len[num_elem] = os_strlen(code);
+ num_elem++;
+ if (dpp_hash_vector(curve, num_elem, addr, len, hash) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: H(MAC-Responder | [identifier |] code)",
+ hash, curve->hash_len);
+ Pr = dpp_pkex_get_role_elem(curve, 0);
+ if (!Pr)
+ goto fail;
+ dpp_debug_print_key("DPP: Pr", Pr);
+ Pr_ec = EVP_PKEY_get0_EC_KEY(Pr);
+ if (!Pr_ec)
+ goto fail;
+ Pr_point = EC_KEY_get0_public_key(Pr_ec);
+
+ group = EC_KEY_get0_group(Pr_ec);
+ if (!group)
+ goto fail;
+ group2 = EC_GROUP_dup(group);
+ if (!group2)
+ goto fail;
+ Qr = EC_POINT_new(group2);
+ if (!Qr) {
+ EC_GROUP_free(group2);
+ goto fail;
+ }
+ hash_bn = BN_bin2bn(hash, curve->hash_len, NULL);
+ if (!hash_bn ||
+ EC_POINT_mul(group2, Qr, NULL, Pr_point, hash_bn, bnctx) != 1)
+ goto fail;
+ if (EC_POINT_is_at_infinity(group, Qr)) {
+ wpa_printf(MSG_INFO, "DPP: Qr is the point-at-infinity");
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: Qr", group, Qr);
+out:
+ EVP_PKEY_free(Pr);
+ BN_clear_free(hash_bn);
+ if (ret_group && Qr)
+ *ret_group = group2;
+ else
+ EC_GROUP_free(group2);
+ return Qr;
+fail:
+ EC_POINT_free(Qr);
+ Qr = NULL;
+ goto out;
+}
+
+
+int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+ const u8 *Mx, size_t Mx_len,
+ const u8 *Nx, size_t Nx_len,
+ const char *code,
+ const u8 *Kx, size_t Kx_len,
+ u8 *z, unsigned int hash_len)
+{
+ u8 salt[DPP_MAX_HASH_LEN], prk[DPP_MAX_HASH_LEN];
+ int res;
+ u8 *info, *pos;
+ size_t info_len;
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+
+ /* HKDF-Extract(<>, IKM=K.x) */
+ os_memset(salt, 0, hash_len);
+ if (dpp_hmac(hash_len, salt, hash_len, Kx, Kx_len, prk) < 0)
+ return -1;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK = HKDF-Extract(<>, IKM)",
+ prk, hash_len);
+ info_len = 2 * ETH_ALEN + Mx_len + Nx_len + os_strlen(code);
+ info = os_malloc(info_len);
+ if (!info)
+ return -1;
+ pos = info;
+ os_memcpy(pos, mac_init, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, mac_resp, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, Mx, Mx_len);
+ pos += Mx_len;
+ os_memcpy(pos, Nx, Nx_len);
+ pos += Nx_len;
+ os_memcpy(pos, code, os_strlen(code));
+
+ /* HKDF-Expand(PRK, info, L) */
+ if (hash_len == 32)
+ res = hmac_sha256_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else if (hash_len == 48)
+ res = hmac_sha384_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else if (hash_len == 64)
+ res = hmac_sha512_kdf(prk, hash_len, NULL, info, info_len,
+ z, hash_len);
+ else
+ res = -1;
+ os_free(info);
+ os_memset(prk, 0, hash_len);
+ if (res < 0)
+ return -1;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: z = HKDF-Expand(PRK, info, L)",
+ z, hash_len);
+ return 0;
+}
+
+
+int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
+ const u8 *net_access_key,
+ size_t net_access_key_len,
+ struct json_token *peer_net_access_key)
+{
+ BN_CTX *bnctx = NULL;
+ EVP_PKEY *own_key = NULL, *peer_key = NULL;
+ BIGNUM *sum = NULL, *q = NULL, *mx = NULL;
+ EC_POINT *m = NULL;
+ const EC_KEY *cR, *pR;
+ const EC_GROUP *group;
+ const BIGNUM *cR_bn, *pR_bn;
+ const EC_POINT *CI_point;
+ const EC_KEY *CI;
+ u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 prk[DPP_MAX_HASH_LEN];
+ const struct dpp_curve_params *curve;
+ int res = -1;
+ u8 nonces[2 * DPP_MAX_NONCE_LEN];
+
+ own_key = dpp_set_keypair(&auth->curve, net_access_key,
+ net_access_key_len);
+ if (!own_key) {
+ dpp_auth_fail(auth, "Failed to parse own netAccessKey");
+ goto fail;
+ }
+
+ peer_key = dpp_parse_jwk(peer_net_access_key, &curve);
+ if (!peer_key)
+ goto fail;
+ dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+
+ if (auth->curve != curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mismatching netAccessKey curves (own=%s != peer=%s)",
+ auth->curve->name, curve->name);
+ goto fail;
+ }
+
+ auth->own_protocol_key = dpp_gen_keypair(curve);
+ if (!auth->own_protocol_key)
+ goto fail;
+
+ if (random_get_bytes(auth->e_nonce, auth->curve->nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate E-nonce");
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: E-nonce",
+ auth->e_nonce, auth->curve->nonce_len);
+
+ /* M = { cR + pR } * CI */
+ cR = EVP_PKEY_get0_EC_KEY(own_key);
+ pR = EVP_PKEY_get0_EC_KEY(auth->own_protocol_key);
+ if (!pR)
+ goto fail;
+ group = EC_KEY_get0_group(pR);
+ bnctx = BN_CTX_new();
+ sum = BN_new();
+ mx = BN_new();
+ q = BN_new();
+ m = EC_POINT_new(group);
+ if (!cR || !bnctx || !sum || !mx || !q || !m)
+ goto fail;
+ cR_bn = EC_KEY_get0_private_key(cR);
+ pR_bn = EC_KEY_get0_private_key(pR);
+ if (!cR_bn || !pR_bn)
+ goto fail;
+ CI = EVP_PKEY_get0_EC_KEY(peer_key);
+ CI_point = EC_KEY_get0_public_key(CI);
+ if (EC_GROUP_get_order(group, q, bnctx) != 1 ||
+ BN_mod_add(sum, cR_bn, pR_bn, q, bnctx) != 1 ||
+ EC_POINT_mul(group, m, NULL, CI_point, sum, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
+ bnctx) != 1) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+
+ if (dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
+
+ /* ke = HKDF(C-nonce | E-nonce, "dpp reconfig key", M.x) */
+
+ /* HKDF-Extract(C-nonce | E-nonce, M.x) */
+ os_memcpy(nonces, auth->c_nonce, curve->nonce_len);
+ os_memcpy(&nonces[curve->nonce_len], auth->e_nonce, curve->nonce_len);
+ if (dpp_hmac(curve->hash_len, nonces, 2 * curve->nonce_len,
+ Mx, curve->prime_len, prk) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
+
+ /* HKDF-Expand(PRK, "dpp reconfig key", L) */
+ if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
+ "dpp reconfig key", auth->ke, curve->hash_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: ke = HKDF(C-nonce | E-nonce, \"dpp reconfig key\", M.x)",
+ auth->ke, curve->hash_len);
+
+ res = 0;
+ EVP_PKEY_free(auth->reconfig_old_protocol_key);
+ auth->reconfig_old_protocol_key = own_key;
+ own_key = NULL;
+fail:
+ forced_memzero(prk, sizeof(prk));
+ forced_memzero(Mx, sizeof(Mx));
+ EC_POINT_clear_free(m);
+ BN_free(q);
+ BN_clear_free(mx);
+ BN_clear_free(sum);
+ EVP_PKEY_free(own_key);
+ EVP_PKEY_free(peer_key);
+ BN_CTX_free(bnctx);
+ return res;
+}
+
+
+int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
+ const u8 *r_proto, u16 r_proto_len,
+ struct json_token *net_access_key)
+{
+ BN_CTX *bnctx = NULL;
+ EVP_PKEY *pr = NULL, *peer_key = NULL;
+ EC_POINT *sum = NULL, *m = NULL;
+ BIGNUM *mx = NULL;
+ const EC_KEY *cI, *CR, *PR;
+ const EC_GROUP *group;
+ const EC_POINT *CR_point, *PR_point;
+ const BIGNUM *cI_bn;
+ u8 Mx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 prk[DPP_MAX_HASH_LEN];
+ int res = -1;
+ const struct dpp_curve_params *curve;
+ u8 nonces[2 * DPP_MAX_NONCE_LEN];
+
+ pr = dpp_set_pubkey_point(auth->conf->connector_key,
+ r_proto, r_proto_len);
+ if (!pr) {
+ dpp_auth_fail(auth, "Invalid Responder Protocol Key");
+ goto fail;
+ }
+ dpp_debug_print_key("Peer (Responder) Protocol Key", pr);
+ EVP_PKEY_free(auth->peer_protocol_key);
+ auth->peer_protocol_key = pr;
+ pr = NULL;
+
+ peer_key = dpp_parse_jwk(net_access_key, &curve);
+ if (!peer_key)
+ goto fail;
+ dpp_debug_print_key("DPP: Received netAccessKey", peer_key);
+ if (auth->curve != curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Mismatching netAccessKey curves (own=%s != peer=%s)",
+ auth->curve->name, curve->name);
+ goto fail;
+ }
+
+ /* M = cI * { CR + PR } */
+ cI = EVP_PKEY_get0_EC_KEY(auth->conf->connector_key);
+ cI_bn = EC_KEY_get0_private_key(cI);
+ group = EC_KEY_get0_group(cI);
+ bnctx = BN_CTX_new();
+ sum = EC_POINT_new(group);
+ m = EC_POINT_new(group);
+ mx = BN_new();
+ CR = EVP_PKEY_get0_EC_KEY(peer_key);
+ PR = EVP_PKEY_get0_EC_KEY(auth->peer_protocol_key);
+ CR_point = EC_KEY_get0_public_key(CR);
+ PR_point = EC_KEY_get0_public_key(PR);
+ if (!bnctx || !sum || !m || !mx ||
+ EC_POINT_add(group, sum, CR_point, PR_point, bnctx) != 1 ||
+ EC_POINT_mul(group, m, NULL, sum, cI_bn, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, m, mx, NULL,
+ bnctx) != 1 ||
+ dpp_bn2bin_pad(mx, Mx, curve->prime_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: M.x", Mx, curve->prime_len);
+
+ /* ke = HKDF(C-nonce | E-nonce, "dpp reconfig key", M.x) */
+
+ /* HKDF-Extract(C-nonce | E-nonce, M.x) */
+ os_memcpy(nonces, auth->c_nonce, curve->nonce_len);
+ os_memcpy(&nonces[curve->nonce_len], auth->e_nonce, curve->nonce_len);
+ if (dpp_hmac(curve->hash_len, nonces, 2 * curve->nonce_len,
+ Mx, curve->prime_len, prk) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG, "DPP: PRK", prk, curve->hash_len);
+
+ /* HKDF-Expand(PRK, "dpp reconfig key", L) */
+ if (dpp_hkdf_expand(curve->hash_len, prk, curve->hash_len,
+ "dpp reconfig key", auth->ke, curve->hash_len) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: ke = HKDF(C-nonce | E-nonce, \"dpp reconfig key\", M.x)",
+ auth->ke, curve->hash_len);
+
+ res = 0;
+fail:
+ forced_memzero(prk, sizeof(prk));
+ forced_memzero(Mx, sizeof(Mx));
+ EVP_PKEY_free(pr);
+ EVP_PKEY_free(peer_key);
+ EC_POINT_clear_free(sum);
+ EC_POINT_clear_free(m);
+ BN_clear_free(mx);
+ BN_CTX_free(bnctx);
+ return res;
+}
+
+
+static char *
+dpp_build_jws_prot_hdr(struct dpp_configurator *conf, size_t *signed1_len)
+{
+ struct wpabuf *jws_prot_hdr;
+ char *signed1;
+
+ jws_prot_hdr = wpabuf_alloc(100);
+ if (!jws_prot_hdr)
+ return NULL;
+ json_start_object(jws_prot_hdr, NULL);
+ json_add_string(jws_prot_hdr, "typ", "dppCon");
+ json_value_sep(jws_prot_hdr);
+ json_add_string(jws_prot_hdr, "kid", conf->kid);
+ json_value_sep(jws_prot_hdr);
+ json_add_string(jws_prot_hdr, "alg", conf->curve->jws_alg);
+ json_end_object(jws_prot_hdr);
+ signed1 = base64_url_encode(wpabuf_head(jws_prot_hdr),
+ wpabuf_len(jws_prot_hdr),
+ signed1_len);
+ wpabuf_free(jws_prot_hdr);
+ return signed1;
+}
+
+
+static char *
+dpp_build_conn_signature(struct dpp_configurator *conf,
+ const char *signed1, size_t signed1_len,
+ const char *signed2, size_t signed2_len,
+ size_t *signed3_len)
+{
+ const struct dpp_curve_params *curve;
+ char *signed3 = NULL;
+ unsigned char *signature = NULL;
+ const unsigned char *p;
+ size_t signature_len;
+ EVP_MD_CTX *md_ctx = NULL;
+ ECDSA_SIG *sig = NULL;
+ char *dot = ".";
+ const EVP_MD *sign_md;
+ const BIGNUM *r, *s;
+
+ curve = conf->curve;
+ if (curve->hash_len == SHA256_MAC_LEN) {
+ sign_md = EVP_sha256();
+ } else if (curve->hash_len == SHA384_MAC_LEN) {
+ sign_md = EVP_sha384();
+ } else if (curve->hash_len == SHA512_MAC_LEN) {
+ sign_md = EVP_sha512();
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+ goto fail;
+ }
+
+ md_ctx = EVP_MD_CTX_create();
+ if (!md_ctx)
+ goto fail;
+
+ ERR_clear_error();
+ if (EVP_DigestSignInit(md_ctx, NULL, sign_md, NULL, conf->csign) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignInit failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestSignUpdate(md_ctx, signed1, signed1_len) != 1 ||
+ EVP_DigestSignUpdate(md_ctx, dot, 1) != 1 ||
+ EVP_DigestSignUpdate(md_ctx, signed2, signed2_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ if (EVP_DigestSignFinal(md_ctx, NULL, &signature_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ signature = os_malloc(signature_len);
+ if (!signature)
+ goto fail;
+ if (EVP_DigestSignFinal(md_ctx, signature, &signature_len) != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: EVP_DigestSignFinal failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (DER)",
+ signature, signature_len);
+ /* Convert to raw coordinates r,s */
+ p = signature;
+ sig = d2i_ECDSA_SIG(NULL, &p, signature_len);
+ if (!sig)
+ goto fail;
+ ECDSA_SIG_get0(sig, &r, &s);
+ if (dpp_bn2bin_pad(r, signature, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(s, signature + curve->prime_len,
+ curve->prime_len) < 0)
+ goto fail;
+ signature_len = 2 * curve->prime_len;
+ wpa_hexdump(MSG_DEBUG, "DPP: signedConnector ECDSA signature (raw r,s)",
+ signature, signature_len);
+ signed3 = base64_url_encode(signature, signature_len, signed3_len);
+fail:
+ EVP_MD_CTX_destroy(md_ctx);
+ ECDSA_SIG_free(sig);
+ os_free(signature);
+ return signed3;
+}
+
+char * dpp_sign_connector(struct dpp_configurator *conf,
+ const struct wpabuf *dppcon)
+{
+ char *signed1 = NULL, *signed2 = NULL, *signed3 = NULL;
+ char *signed_conn = NULL, *pos;
+ size_t signed1_len, signed2_len, signed3_len;
+
+ signed1 = dpp_build_jws_prot_hdr(conf, &signed1_len);
+ signed2 = base64_url_encode(wpabuf_head(dppcon), wpabuf_len(dppcon),
+ &signed2_len);
+ if (!signed1 || !signed2)
+ goto fail;
+
+ signed3 = dpp_build_conn_signature(conf, signed1, signed1_len,
+ signed2, signed2_len, &signed3_len);
+ if (!signed3)
+ goto fail;
+
+ signed_conn = os_malloc(signed1_len + signed2_len + signed3_len + 3);
+ if (!signed_conn)
+ goto fail;
+ pos = signed_conn;
+ os_memcpy(pos, signed1, signed1_len);
+ pos += signed1_len;
+ *pos++ = '.';
+ os_memcpy(pos, signed2, signed2_len);
+ pos += signed2_len;
+ *pos++ = '.';
+ os_memcpy(pos, signed3, signed3_len);
+ pos += signed3_len;
+ *pos = '\0';
+
+fail:
+ os_free(signed1);
+ os_free(signed2);
+ os_free(signed3);
+ return signed_conn;
+}
+
+
+#ifdef CONFIG_DPP2
+
+struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
+ size_t net_access_key_len)
+{
+ struct wpabuf *pub = NULL;
+ EVP_PKEY *own_key;
+ struct dpp_pfs *pfs;
+
+ pfs = os_zalloc(sizeof(*pfs));
+ if (!pfs)
+ return NULL;
+
+ own_key = dpp_set_keypair(&pfs->curve, net_access_key,
+ net_access_key_len);
+ if (!own_key) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+ goto fail;
+ }
+ EVP_PKEY_free(own_key);
+
+ pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
+ if (!pfs->ecdh)
+ goto fail;
+
+ pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
+ pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
+ if (!pub)
+ goto fail;
+
+ pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
+ if (!pfs->ie)
+ goto fail;
+ wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
+ wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
+ wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
+ wpabuf_put_buf(pfs->ie, pub);
+ wpabuf_free(pub);
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
+ pfs->ie);
+
+ return pfs;
+fail:
+ wpabuf_free(pub);
+ dpp_pfs_free(pfs);
+ return NULL;
+}
+
+
+int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
+{
+ if (peer_ie_len < 2)
+ return -1;
+ if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
+ wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
+ return -1;
+ }
+
+ pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
+ peer_ie_len - 2);
+ pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
+ if (!pfs->secret) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
+ return -1;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
+ return 0;
+}
+
+
+void dpp_pfs_free(struct dpp_pfs *pfs)
+{
+ if (!pfs)
+ return;
+ crypto_ecdh_deinit(pfs->ecdh);
+ wpabuf_free(pfs->ie);
+ wpabuf_clear_free(pfs->secret);
+ os_free(pfs);
+}
+
+
+struct wpabuf * dpp_build_csr(struct dpp_authentication *auth, const char *name)
+{
+ X509_REQ *req = NULL;
+ struct wpabuf *buf = NULL;
+ unsigned char *der;
+ int der_len;
+ EVP_PKEY *key;
+ const EVP_MD *sign_md;
+ unsigned int hash_len = auth->curve->hash_len;
+ EC_KEY *eckey;
+ BIO *out = NULL;
+ u8 cp[DPP_CP_LEN];
+ char *password;
+ size_t password_len;
+ int res;
+
+ /* TODO: use auth->csrattrs */
+
+ /* TODO: support generation of a new private key if csrAttrs requests
+ * a specific group to be used */
+ key = auth->own_protocol_key;
+
+ eckey = EVP_PKEY_get1_EC_KEY(key);
+ if (!eckey)
+ goto fail;
+ der = NULL;
+ der_len = i2d_ECPrivateKey(eckey, &der);
+ if (der_len <= 0)
+ goto fail;
+ wpabuf_free(auth->priv_key);
+ auth->priv_key = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+ if (!auth->priv_key)
+ goto fail;
+
+ req = X509_REQ_new();
+ if (!req || !X509_REQ_set_pubkey(req, key))
+ goto fail;
+
+ if (name) {
+ X509_NAME *n;
+
+ n = X509_REQ_get_subject_name(req);
+ if (!n)
+ goto fail;
+
+ if (X509_NAME_add_entry_by_txt(
+ n, "CN", MBSTRING_UTF8,
+ (const unsigned char *) name, -1, -1, 0) != 1)
+ goto fail;
+ }
+
+ /* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */
+ if (dpp_hkdf_expand(hash_len, auth->bk, hash_len,
+ "CSR challengePassword", cp, DPP_CP_LEN) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)",
+ cp, DPP_CP_LEN);
+ password = base64_encode_no_lf(cp, DPP_CP_LEN, &password_len);
+ forced_memzero(cp, DPP_CP_LEN);
+ if (!password)
+ goto fail;
+
+ res = X509_REQ_add1_attr_by_NID(req, NID_pkcs9_challengePassword,
+ V_ASN1_UTF8STRING,
+ (const unsigned char *) password,
+ password_len);
+ bin_clear_free(password, password_len);
+ if (!res)
+ goto fail;
+
+ /* TODO */
+
+ /* TODO: hash func selection based on csrAttrs */
+ if (hash_len == SHA256_MAC_LEN) {
+ sign_md = EVP_sha256();
+ } else if (hash_len == SHA384_MAC_LEN) {
+ sign_md = EVP_sha384();
+ } else if (hash_len == SHA512_MAC_LEN) {
+ sign_md = EVP_sha512();
+ } else {
+ wpa_printf(MSG_DEBUG, "DPP: Unknown signature algorithm");
+ goto fail;
+ }
+
+ if (!X509_REQ_sign(req, key, sign_md))
+ goto fail;
+
+ der = NULL;
+ der_len = i2d_X509_REQ(req, &der);
+ if (der_len < 0)
+ goto fail;
+ buf = wpabuf_alloc_copy(der, der_len);
+ OPENSSL_free(der);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: CSR", buf);
+
+fail:
+ BIO_free_all(out);
+ X509_REQ_free(req);
+ return buf;
+}
+
+
+struct wpabuf * dpp_pkcs7_certs(const struct wpabuf *pkcs7)
+{
+#ifdef OPENSSL_IS_BORINGSSL
+ CBS pkcs7_cbs;
+#else /* OPENSSL_IS_BORINGSSL */
+ PKCS7 *p7 = NULL;
+ const unsigned char *p = wpabuf_head(pkcs7);
+#endif /* OPENSSL_IS_BORINGSSL */
+ STACK_OF(X509) *certs;
+ int i, num;
+ BIO *out = NULL;
+ size_t rlen;
+ struct wpabuf *pem = NULL;
+ int res;
+
+#ifdef OPENSSL_IS_BORINGSSL
+ certs = sk_X509_new_null();
+ if (!certs)
+ goto fail;
+ CBS_init(&pkcs7_cbs, wpabuf_head(pkcs7), wpabuf_len(pkcs7));
+ if (!PKCS7_get_certificates(certs, &pkcs7_cbs)) {
+ wpa_printf(MSG_INFO, "DPP: Could not parse PKCS#7 object: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+#else /* OPENSSL_IS_BORINGSSL */
+ p7 = d2i_PKCS7(NULL, &p, wpabuf_len(pkcs7));
+ if (!p7) {
+ wpa_printf(MSG_INFO, "DPP: Could not parse PKCS#7 object: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ 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,
+ "DPP: No certificates found in PKCS#7 object");
+ goto fail;
+ }
+
+ out = BIO_new(BIO_s_mem());
+ if (!out)
+ goto fail;
+
+ for (i = 0; i < num; i++) {
+ X509 *cert = sk_X509_value(certs, i);
+
+ PEM_write_bio_X509(out, cert);
+ }
+
+ rlen = BIO_ctrl_pending(out);
+ pem = wpabuf_alloc(rlen);
+ if (!pem)
+ goto fail;
+ res = BIO_read(out, wpabuf_put(pem, 0), rlen);
+ if (res <= 0) {
+ wpabuf_free(pem);
+ pem = NULL;
+ goto fail;
+ }
+ wpabuf_put(pem, res);
+
+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 pem;
+}
+
+
+int dpp_validate_csr(struct dpp_authentication *auth, const struct wpabuf *csr)
+{
+ X509_REQ *req;
+ const unsigned char *pos;
+ EVP_PKEY *pkey;
+ int res, loc, ret = -1;
+ X509_ATTRIBUTE *attr;
+ ASN1_TYPE *type;
+ ASN1_STRING *str;
+ unsigned char *utf8 = NULL;
+ unsigned char *cp = NULL;
+ size_t cp_len;
+ u8 exp_cp[DPP_CP_LEN];
+ unsigned int hash_len = auth->curve->hash_len;
+
+ pos = wpabuf_head(csr);
+ req = d2i_X509_REQ(NULL, &pos, wpabuf_len(csr));
+ if (!req) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to parse CSR");
+ return -1;
+ }
+
+ pkey = X509_REQ_get_pubkey(req);
+ if (!pkey) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to get public key from CSR");
+ goto fail;
+ }
+
+ res = X509_REQ_verify(req, pkey);
+ EVP_PKEY_free(pkey);
+ if (res != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: CSR does not have a valid signature");
+ goto fail;
+ }
+
+ loc = X509_REQ_get_attr_by_NID(req, NID_pkcs9_challengePassword, -1);
+ if (loc < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: CSR does not include challengePassword");
+ goto fail;
+ }
+
+ attr = X509_REQ_get_attr(req, loc);
+ if (!attr) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not get challengePassword attribute");
+ goto fail;
+ }
+
+ type = X509_ATTRIBUTE_get0_type(attr, 0);
+ if (!type) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not get challengePassword attribute type");
+ goto fail;
+ }
+
+ res = ASN1_TYPE_get(type);
+ /* This is supposed to be UTF8String, but allow other strings as well
+ * since challengePassword is using ASCII (base64 encoded). */
+ if (res != V_ASN1_UTF8STRING && res != V_ASN1_PRINTABLESTRING &&
+ res != V_ASN1_IA5STRING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected challengePassword attribute type %d",
+ res);
+ goto fail;
+ }
+
+ str = X509_ATTRIBUTE_get0_data(attr, 0, res, NULL);
+ if (!str) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not get ASN.1 string for challengePassword");
+ goto fail;
+ }
+
+ res = ASN1_STRING_to_UTF8(&utf8, str);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not get UTF8 version of challengePassword");
+ goto fail;
+ }
+
+ cp = base64_decode((const char *) utf8, res, &cp_len);
+ OPENSSL_free(utf8);
+ if (!cp) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not base64 decode challengePassword");
+ goto fail;
+ }
+ if (cp_len != DPP_CP_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected cp length (%zu) in CSR challengePassword",
+ cp_len);
+ goto fail;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "DPP: cp from CSR challengePassword",
+ cp, cp_len);
+
+ /* cp = HKDF-Expand(bk, "CSR challengePassword", 64) */
+ if (dpp_hkdf_expand(hash_len, auth->bk, hash_len,
+ "CSR challengePassword", exp_cp, DPP_CP_LEN) < 0)
+ goto fail;
+ wpa_hexdump_key(MSG_DEBUG,
+ "DPP: cp = HKDF-Expand(bk, \"CSR challengePassword\", 64)",
+ exp_cp, DPP_CP_LEN);
+ if (os_memcmp_const(cp, exp_cp, DPP_CP_LEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: CSR challengePassword does not match calculated cp");
+ goto fail;
+ }
+
+ ret = 0;
+fail:
+ os_free(cp);
+ X509_REQ_free(req);
+ return ret;
+}
+
+
+struct dpp_reconfig_id * dpp_gen_reconfig_id(const u8 *csign_key,
+ size_t csign_key_len,
+ const u8 *pp_key,
+ size_t pp_key_len)
+{
+ const unsigned char *p;
+ EVP_PKEY *csign = NULL, *ppkey = NULL;
+ struct dpp_reconfig_id *id = NULL;
+ BN_CTX *ctx = NULL;
+ BIGNUM *bn = NULL, *q = NULL;
+ const EC_KEY *eckey;
+ const EC_GROUP *group;
+ EC_POINT *e_id = NULL;
+
+ p = csign_key;
+ csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ if (!csign)
+ goto fail;
+
+ if (!pp_key)
+ goto fail;
+ p = pp_key;
+ ppkey = d2i_PUBKEY(NULL, &p, pp_key_len);
+ if (!ppkey)
+ goto fail;
+
+ eckey = EVP_PKEY_get0_EC_KEY(csign);
+ if (!eckey)
+ goto fail;
+ group = EC_KEY_get0_group(eckey);
+ if (!group)
+ goto fail;
+
+ e_id = EC_POINT_new(group);
+ ctx = BN_CTX_new();
+ bn = BN_new();
+ q = BN_new();
+ if (!e_id || !ctx || !bn || !q ||
+ !EC_GROUP_get_order(group, q, ctx) ||
+ !BN_rand_range(bn, q) ||
+ !EC_POINT_mul(group, e_id, bn, NULL, NULL, ctx))
+ goto fail;
+
+ dpp_debug_print_point("DPP: Generated random point E-id", group, e_id);
+
+ id = os_zalloc(sizeof(*id));
+ if (!id)
+ goto fail;
+ id->group = group;
+ id->e_id = e_id;
+ e_id = NULL;
+ id->csign = csign;
+ csign = NULL;
+ id->pp_key = ppkey;
+ ppkey = NULL;
+fail:
+ EC_POINT_free(e_id);
+ EVP_PKEY_free(csign);
+ EVP_PKEY_free(ppkey);
+ BN_clear_free(bn);
+ BN_CTX_free(ctx);
+ return id;
+}
+
+
+static EVP_PKEY * dpp_pkey_from_point(const EC_GROUP *group,
+ const EC_POINT *point)
+{
+ EC_KEY *eckey;
+ EVP_PKEY *pkey = NULL;
+
+ eckey = EC_KEY_new();
+ if (!eckey ||
+ EC_KEY_set_group(eckey, group) != 1 ||
+ EC_KEY_set_public_key(eckey, point) != 1) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to set EC_KEY: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ goto fail;
+ }
+ EC_KEY_set_asn1_flag(eckey, OPENSSL_EC_NAMED_CURVE);
+
+ pkey = EVP_PKEY_new();
+ if (!pkey || EVP_PKEY_set1_EC_KEY(pkey, eckey) != 1) {
+ wpa_printf(MSG_ERROR, "DPP: Could not create EVP_PKEY");
+ EVP_PKEY_free(pkey);
+ pkey = NULL;
+ goto fail;
+ }
+
+fail:
+ EC_KEY_free(eckey);
+ return pkey;
+}
+
+
+int dpp_update_reconfig_id(struct dpp_reconfig_id *id)
+{
+ BN_CTX *ctx = NULL;
+ BIGNUM *bn = NULL, *q = NULL;
+ EC_POINT *e_prime_id = NULL, *a_nonce = NULL;
+ int ret = -1;
+ const EC_KEY *pp;
+ const EC_POINT *pp_point;
+
+ pp = EVP_PKEY_get0_EC_KEY(id->pp_key);
+ if (!pp)
+ goto fail;
+ pp_point = EC_KEY_get0_public_key(pp);
+ e_prime_id = EC_POINT_new(id->group);
+ a_nonce = EC_POINT_new(id->group);
+ ctx = BN_CTX_new();
+ bn = BN_new();
+ q = BN_new();
+ /* Generate random 0 <= a-nonce < q
+ * A-NONCE = a-nonce * G
+ * E'-id = E-id + a-nonce * P_pk */
+ if (!pp_point || !e_prime_id || !a_nonce || !ctx || !bn || !q ||
+ !EC_GROUP_get_order(id->group, q, ctx) ||
+ !BN_rand_range(bn, q) || /* bn = a-nonce */
+ !EC_POINT_mul(id->group, a_nonce, bn, NULL, NULL, ctx) ||
+ !EC_POINT_mul(id->group, e_prime_id, NULL, pp_point, bn, ctx) ||
+ !EC_POINT_add(id->group, e_prime_id, id->e_id, e_prime_id, ctx))
+ goto fail;
+
+ dpp_debug_print_point("DPP: Generated A-NONCE", id->group, a_nonce);
+ dpp_debug_print_point("DPP: Encrypted E-id to E'-id",
+ id->group, e_prime_id);
+
+ EVP_PKEY_free(id->a_nonce);
+ EVP_PKEY_free(id->e_prime_id);
+ id->a_nonce = dpp_pkey_from_point(id->group, a_nonce);
+ id->e_prime_id = dpp_pkey_from_point(id->group, e_prime_id);
+ if (!id->a_nonce || !id->e_prime_id)
+ goto fail;
+
+ ret = 0;
+
+fail:
+ EC_POINT_free(e_prime_id);
+ EC_POINT_free(a_nonce);
+ BN_clear_free(bn);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
+
+void dpp_free_reconfig_id(struct dpp_reconfig_id *id)
+{
+ if (id) {
+ EC_POINT_clear_free(id->e_id);
+ EVP_PKEY_free(id->csign);
+ EVP_PKEY_free(id->a_nonce);
+ EVP_PKEY_free(id->e_prime_id);
+ EVP_PKEY_free(id->pp_key);
+ os_free(id);
+ }
+}
+
+
+EC_POINT * dpp_decrypt_e_id(EVP_PKEY *ppkey, EVP_PKEY *a_nonce,
+ EVP_PKEY *e_prime_id)
+{
+ const EC_KEY *pp_ec, *a_nonce_ec, *e_prime_id_ec;
+ const BIGNUM *pp_bn;
+ const EC_GROUP *group;
+ EC_POINT *e_id = NULL;
+ const EC_POINT *a_nonce_point, *e_prime_id_point;
+ BN_CTX *ctx = NULL;
+
+ if (!ppkey)
+ return NULL;
+
+ /* E-id = E'-id - s_C * A-NONCE */
+ pp_ec = EVP_PKEY_get0_EC_KEY(ppkey);
+ a_nonce_ec = EVP_PKEY_get0_EC_KEY(a_nonce);
+ e_prime_id_ec = EVP_PKEY_get0_EC_KEY(e_prime_id);
+ if (!pp_ec || !a_nonce_ec || !e_prime_id_ec)
+ return NULL;
+ pp_bn = EC_KEY_get0_private_key(pp_ec);
+ group = EC_KEY_get0_group(pp_ec);
+ a_nonce_point = EC_KEY_get0_public_key(a_nonce_ec);
+ e_prime_id_point = EC_KEY_get0_public_key(e_prime_id_ec);
+ ctx = BN_CTX_new();
+ if (!pp_bn || !group || !a_nonce_point || !e_prime_id_point || !ctx)
+ goto fail;
+ e_id = EC_POINT_new(group);
+ if (!e_id ||
+ !EC_POINT_mul(group, e_id, NULL, a_nonce_point, pp_bn, ctx) ||
+ !EC_POINT_invert(group, e_id, ctx) ||
+ !EC_POINT_add(group, e_id, e_prime_id_point, e_id, ctx)) {
+ EC_POINT_clear_free(e_id);
+ goto fail;
+ }
+
+ dpp_debug_print_point("DPP: Decrypted E-id", group, e_id);
+
+fail:
+ BN_CTX_free(ctx);
+ return e_id;
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve)
+{
+ BN_CTX *ctx;
+ BIGNUM *x, *y;
+ int ret = -1;
+ EC_GROUP *group;
+ EC_POINT *point;
+
+ group = EC_GROUP_new_by_curve_name(OBJ_txt2nid(curve->name));
+ if (!group)
+ return -1;
+
+ ctx = BN_CTX_new();
+ point = EC_POINT_new(group);
+ x = BN_new();
+ y = BN_new();
+ if (!ctx || !point || !x || !y)
+ goto fail;
+
+ if (BN_rand(x, curve->prime_len * 8, 0, 0) != 1)
+ goto fail;
+
+ /* Generate a random y coordinate that results in a point that is not
+ * on the curve. */
+ for (;;) {
+ if (BN_rand(y, curve->prime_len * 8, 0, 0) != 1)
+ goto fail;
+
+ if (EC_POINT_set_affine_coordinates_GFp(group, point, x, y,
+ ctx) != 1) {
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L || defined(OPENSSL_IS_BORINGSSL)
+ /* Unlike older OpenSSL versions, OpenSSL 1.1.1 and BoringSSL
+ * return an error from EC_POINT_set_affine_coordinates_GFp()
+ * when the point is not on the curve. */
+ break;
+#else /* >=1.1.0 or OPENSSL_IS_BORINGSSL */
+ goto fail;
+#endif /* >= 1.1.0 or OPENSSL_IS_BORINGSSL */
+ }
+
+ if (!EC_POINT_is_on_curve(group, point, ctx))
+ break;
+ }
+
+ if (dpp_bn2bin_pad(x, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(y, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+ ret = 0;
+fail:
+ if (ret < 0)
+ wpa_printf(MSG_INFO, "DPP: Failed to generate invalid key");
+ BN_free(x);
+ BN_free(y);
+ EC_POINT_free(point);
+ BN_CTX_free(ctx);
+ EC_GROUP_free(group);
+
+ return ret;
+}
+
+
+char * dpp_corrupt_connector_signature(const char *connector)
+{
+ char *tmp, *pos, *signed3 = NULL;
+ unsigned char *signature = NULL;
+ size_t signature_len = 0, signed3_len;
+
+ tmp = os_zalloc(os_strlen(connector) + 5);
+ if (!tmp)
+ goto fail;
+ os_memcpy(tmp, connector, os_strlen(connector));
+
+ pos = os_strchr(tmp, '.');
+ if (!pos)
+ goto fail;
+
+ pos = os_strchr(pos + 1, '.');
+ if (!pos)
+ goto fail;
+ pos++;
+
+ wpa_printf(MSG_DEBUG, "DPP: Original base64url encoded signature: %s",
+ pos);
+ signature = base64_url_decode(pos, os_strlen(pos), &signature_len);
+ if (!signature || signature_len == 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: Original Connector signature",
+ signature, signature_len);
+ signature[signature_len - 1] ^= 0x01;
+ wpa_hexdump(MSG_DEBUG, "DPP: Corrupted Connector signature",
+ signature, signature_len);
+ signed3 = base64_url_encode(signature, signature_len, &signed3_len);
+ if (!signed3)
+ goto fail;
+ os_memcpy(pos, signed3, signed3_len);
+ pos[signed3_len] = '\0';
+ wpa_printf(MSG_DEBUG, "DPP: Corrupted base64url encoded signature: %s",
+ pos);
+
+out:
+ os_free(signature);
+ os_free(signed3);
+ return tmp;
+fail:
+ os_free(tmp);
+ tmp = NULL;
+ goto out;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/contrib/wpa/src/common/dpp_i.h b/contrib/wpa/src/common/dpp_i.h
new file mode 100644
index 000000000000..af12467a5d92
--- /dev/null
+++ b/contrib/wpa/src/common/dpp_i.h
@@ -0,0 +1,160 @@
+/*
+ * DPP module internal definitions
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DPP_I_H
+#define DPP_I_H
+
+#ifdef CONFIG_DPP
+
+struct dpp_global {
+ void *msg_ctx;
+ struct dl_list bootstrap; /* struct dpp_bootstrap_info */
+ struct dl_list configurator; /* struct dpp_configurator */
+#ifdef CONFIG_DPP2
+ struct dl_list controllers; /* struct dpp_relay_controller */
+ struct dpp_controller *controller;
+ struct dl_list tcp_init; /* struct dpp_connection */
+ void *cb_ctx;
+ int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
+ void (*remove_bi)(void *ctx, struct dpp_bootstrap_info *bi);
+#endif /* CONFIG_DPP2 */
+};
+
+/* dpp.c */
+
+void dpp_build_attr_status(struct wpabuf *msg, enum dpp_status_error status);
+void dpp_build_attr_r_bootstrap_key_hash(struct wpabuf *msg, const u8 *hash);
+unsigned int dpp_next_id(struct dpp_global *dpp);
+struct wpabuf * dpp_build_conn_status(enum dpp_status_error result,
+ const u8 *ssid, size_t ssid_len,
+ const char *channel_list);
+struct json_token * dpp_parse_own_connector(const char *own_connector);
+int dpp_connector_match_groups(struct json_token *own_root,
+ struct json_token *peer_root, bool reconfig);
+int dpp_build_jwk(struct wpabuf *buf, const char *name, EVP_PKEY *key,
+ const char *kid, const struct dpp_curve_params *curve);
+EVP_PKEY * dpp_parse_jwk(struct json_token *jwk,
+ const struct dpp_curve_params **key_curve);
+int dpp_prepare_channel_list(struct dpp_authentication *auth,
+ unsigned int neg_freq,
+ struct hostapd_hw_modes *own_modes, u16 num_modes);
+void dpp_auth_fail(struct dpp_authentication *auth, const char *txt);
+int dpp_gen_uri(struct dpp_bootstrap_info *bi);
+void dpp_write_adv_proto(struct wpabuf *buf);
+void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query);
+
+/* dpp_backup.c */
+
+void dpp_free_asymmetric_key(struct dpp_asymmetric_key *key);
+struct wpabuf * dpp_build_enveloped_data(struct dpp_authentication *auth);
+int dpp_conf_resp_env_data(struct dpp_authentication *auth,
+ const u8 *env_data, size_t env_data_len);
+
+/* dpp_crypto.c */
+
+struct dpp_signed_connector_info {
+ unsigned char *payload;
+ size_t payload_len;
+};
+
+enum dpp_status_error
+dpp_process_signed_connector(struct dpp_signed_connector_info *info,
+ EVP_PKEY *csign_pub, const char *connector);
+enum dpp_status_error
+dpp_check_signed_connector(struct dpp_signed_connector_info *info,
+ const u8 *csign_key, size_t csign_key_len,
+ const u8 *peer_connector, size_t peer_connector_len);
+const struct dpp_curve_params * dpp_get_curve_name(const char *name);
+const struct dpp_curve_params * dpp_get_curve_jwk_crv(const char *name);
+const struct dpp_curve_params * dpp_get_curve_nid(int nid);
+const struct dpp_curve_params * dpp_get_curve_ike_group(u16 group);
+int dpp_bi_pubkey_hash(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len);
+struct wpabuf * dpp_get_pubkey_point(EVP_PKEY *pkey, int prefix);
+EVP_PKEY * dpp_set_pubkey_point_group(const EC_GROUP *group,
+ const u8 *buf_x, const u8 *buf_y,
+ size_t len);
+EVP_PKEY * dpp_set_pubkey_point(EVP_PKEY *group_key, const u8 *buf, size_t len);
+int dpp_bn2bin_pad(const BIGNUM *bn, u8 *pos, size_t len);
+int dpp_hkdf_expand(size_t hash_len, const u8 *secret, size_t secret_len,
+ const char *label, u8 *out, size_t outlen);
+int dpp_hmac_vector(size_t hash_len, const u8 *key, size_t key_len,
+ size_t num_elem, const u8 *addr[], const size_t *len,
+ u8 *mac);
+int dpp_ecdh(EVP_PKEY *own, EVP_PKEY *peer, u8 *secret, size_t *secret_len);
+void dpp_debug_print_point(const char *title, const EC_GROUP *group,
+ const EC_POINT *point);
+void dpp_debug_print_key(const char *title, EVP_PKEY *key);
+int dpp_pbkdf2(size_t hash_len, const u8 *password, size_t password_len,
+ const u8 *salt, size_t salt_len, unsigned int iterations,
+ u8 *buf, size_t buflen);
+int dpp_get_subject_public_key(struct dpp_bootstrap_info *bi,
+ const u8 *data, size_t data_len);
+int dpp_bootstrap_key_hash(struct dpp_bootstrap_info *bi);
+int dpp_keygen(struct dpp_bootstrap_info *bi, const char *curve,
+ const u8 *privkey, size_t privkey_len);
+EVP_PKEY * dpp_set_keypair(const struct dpp_curve_params **curve,
+ const u8 *privkey, size_t privkey_len);
+EVP_PKEY * dpp_gen_keypair(const struct dpp_curve_params *curve);
+int dpp_derive_k1(const u8 *Mx, size_t Mx_len, u8 *k1, unsigned int hash_len);
+int dpp_derive_k2(const u8 *Nx, size_t Nx_len, u8 *k2, unsigned int hash_len);
+int dpp_derive_bk_ke(struct dpp_authentication *auth);
+int dpp_gen_r_auth(struct dpp_authentication *auth, u8 *r_auth);
+int dpp_gen_i_auth(struct dpp_authentication *auth, u8 *i_auth);
+int dpp_auth_derive_l_responder(struct dpp_authentication *auth);
+int dpp_auth_derive_l_initiator(struct dpp_authentication *auth);
+int dpp_derive_pmk(const u8 *Nx, size_t Nx_len, u8 *pmk, unsigned int hash_len);
+int dpp_derive_pmkid(const struct dpp_curve_params *curve,
+ EVP_PKEY *own_key, EVP_PKEY *peer_key, u8 *pmkid);
+EC_POINT * dpp_pkex_derive_Qi(const struct dpp_curve_params *curve,
+ const u8 *mac_init, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group);
+EC_POINT * dpp_pkex_derive_Qr(const struct dpp_curve_params *curve,
+ const u8 *mac_resp, const char *code,
+ const char *identifier, BN_CTX *bnctx,
+ EC_GROUP **ret_group);
+int dpp_pkex_derive_z(const u8 *mac_init, const u8 *mac_resp,
+ const u8 *Mx, size_t Mx_len,
+ const u8 *Nx, size_t Nx_len,
+ const char *code,
+ const u8 *Kx, size_t Kx_len,
+ u8 *z, unsigned int hash_len);
+int dpp_reconfig_derive_ke_responder(struct dpp_authentication *auth,
+ const u8 *net_access_key,
+ size_t net_access_key_len,
+ struct json_token *peer_net_access_key);
+int dpp_reconfig_derive_ke_initiator(struct dpp_authentication *auth,
+ const u8 *r_proto, u16 r_proto_len,
+ struct json_token *net_access_key);
+EC_POINT * dpp_decrypt_e_id(EVP_PKEY *ppkey, EVP_PKEY *a_nonce,
+ EVP_PKEY *e_prime_id);
+char * dpp_sign_connector(struct dpp_configurator *conf,
+ const struct wpabuf *dppcon);
+int dpp_test_gen_invalid_key(struct wpabuf *msg,
+ const struct dpp_curve_params *curve);
+
+struct dpp_reconfig_id {
+ const EC_GROUP *group;
+ EC_POINT *e_id; /* E-id */
+ EVP_PKEY *csign;
+ EVP_PKEY *a_nonce; /* A-NONCE */
+ EVP_PKEY *e_prime_id; /* E'-id */
+ EVP_PKEY *pp_key;
+};
+
+/* dpp_tcp.c */
+
+void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx);
+void dpp_tcp_init_flush(struct dpp_global *dpp);
+void dpp_relay_flush_controllers(struct dpp_global *dpp);
+
+#endif /* CONFIG_DPP */
+#endif /* DPP_I_H */
diff --git a/contrib/wpa/src/common/dpp_pkex.c b/contrib/wpa/src/common/dpp_pkex.c
new file mode 100644
index 000000000000..807ab7d0a1ce
--- /dev/null
+++ b/contrib/wpa/src/common/dpp_pkex.c
@@ -0,0 +1,1324 @@
+/*
+ * DPP PKEX functionality
+ * Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/crypto.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+u8 dpp_pkex_own_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_peer_mac_override[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
+u8 dpp_pkex_ephemeral_key_override[600];
+size_t dpp_pkex_ephemeral_key_override_len = 0;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
+/* Compatibility wrappers for older versions. */
+
+static EC_KEY * EVP_PKEY_get0_EC_KEY(EVP_PKEY *pkey)
+{
+ if (pkey->type != EVP_PKEY_EC)
+ return NULL;
+ return pkey->pkey.ec;
+}
+
+#endif
+
+
+static struct wpabuf * dpp_pkex_build_exchange_req(struct dpp_pkex *pkex)
+{
+ const EC_KEY *X_ec;
+ const EC_POINT *X_point;
+ BN_CTX *bnctx = NULL;
+ EC_GROUP *group = NULL;
+ EC_POINT *Qi = NULL, *M = NULL;
+ struct wpabuf *M_buf = NULL;
+ BIGNUM *Mx = NULL, *My = NULL;
+ struct wpabuf *msg = NULL;
+ size_t attr_len;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build PKEX Exchange Request");
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qi = dpp_pkex_derive_Qi(curve, pkex->own_mac, pkex->code,
+ pkex->identifier, bnctx, &group);
+ if (!Qi)
+ goto fail;
+
+ /* Generate a random ephemeral keypair x/X */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_pkex_ephemeral_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override ephemeral key x/X");
+ pkex->x = dpp_set_keypair(&tmp_curve,
+ dpp_pkex_ephemeral_key_override,
+ dpp_pkex_ephemeral_key_override_len);
+ } else {
+ pkex->x = dpp_gen_keypair(curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ pkex->x = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!pkex->x)
+ goto fail;
+
+ /* M = X + Qi */
+ X_ec = EVP_PKEY_get0_EC_KEY(pkex->x);
+ if (!X_ec)
+ goto fail;
+ X_point = EC_KEY_get0_public_key(X_ec);
+ if (!X_point)
+ goto fail;
+ dpp_debug_print_point("DPP: X", group, X_point);
+ M = EC_POINT_new(group);
+ Mx = BN_new();
+ My = BN_new();
+ if (!M || !Mx || !My ||
+ EC_POINT_add(group, M, X_point, Qi, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1)
+ goto fail;
+ dpp_debug_print_point("DPP: M", group, M);
+
+ /* Initiator -> Responder: group, [identifier,] M */
+ attr_len = 4 + 2;
+ if (pkex->identifier)
+ attr_len += 4 + os_strlen(pkex->identifier);
+ attr_len += 4 + 2 * curve->prime_len;
+ msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_REQ, attr_len);
+ if (!msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_FINITE_CYCLIC_GROUP_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Finite Cyclic Group");
+ goto skip_finite_cyclic_group;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Finite Cyclic Group attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, curve->ike_group);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_finite_cyclic_group:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Code Identifier attribute */
+ if (pkex->identifier) {
+ wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+ wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+ wpabuf_put_str(msg, pkex->identifier);
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+ goto out;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* M in Encrypted Key attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+ wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+ if (dpp_test_gen_invalid_key(msg, curve) < 0)
+ goto fail;
+ goto out;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_bn2bin_pad(Mx, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Mx, pkex->Mx, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(My, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+out:
+ wpabuf_free(M_buf);
+ EC_POINT_free(M);
+ EC_POINT_free(Qi);
+ BN_clear_free(Mx);
+ BN_clear_free(My);
+ BN_CTX_free(bnctx);
+ EC_GROUP_free(group);
+ return msg;
+fail:
+ wpa_printf(MSG_INFO, "DPP: Failed to build PKEX Exchange Request");
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+static void dpp_pkex_fail(struct dpp_pkex *pkex, const char *txt)
+{
+ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL "%s", txt);
+}
+
+
+struct dpp_pkex * dpp_pkex_init(void *msg_ctx, struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const char *identifier,
+ const char *code)
+{
+ struct dpp_pkex *pkex;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+ MAC2STR(dpp_pkex_own_mac_override));
+ own_mac = dpp_pkex_own_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ return NULL;
+ pkex->msg_ctx = msg_ctx;
+ pkex->initiator = 1;
+ pkex->own_bi = bi;
+ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+ if (identifier) {
+ pkex->identifier = os_strdup(identifier);
+ if (!pkex->identifier)
+ goto fail;
+ }
+ pkex->code = os_strdup(code);
+ if (!pkex->code)
+ goto fail;
+ pkex->exchange_req = dpp_pkex_build_exchange_req(pkex);
+ if (!pkex->exchange_req)
+ goto fail;
+ return pkex;
+fail:
+ dpp_pkex_free(pkex);
+ return NULL;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_exchange_resp(struct dpp_pkex *pkex,
+ enum dpp_status_error status,
+ const BIGNUM *Nx, const BIGNUM *Ny)
+{
+ struct wpabuf *msg = NULL;
+ size_t attr_len;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+
+ /* Initiator -> Responder: DPP Status, [identifier,] N */
+ attr_len = 4 + 1;
+ if (pkex->identifier)
+ attr_len += 4 + os_strlen(pkex->identifier);
+ attr_len += 4 + 2 * curve->prime_len;
+ msg = dpp_alloc_msg(DPP_PA_PKEX_EXCHANGE_RESP, attr_len);
+ if (!msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_STATUS_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Status");
+ goto skip_status;
+ }
+
+ if (dpp_test == DPP_TEST_INVALID_STATUS_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Status");
+ status = 255;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, status);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_status:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* Code Identifier attribute */
+ if (pkex->identifier) {
+ wpabuf_put_le16(msg, DPP_ATTR_CODE_IDENTIFIER);
+ wpabuf_put_le16(msg, os_strlen(pkex->identifier));
+ wpabuf_put_str(msg, pkex->identifier);
+ }
+
+ if (status != DPP_STATUS_OK)
+ goto skip_encrypted_key;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Encrypted Key");
+ goto skip_encrypted_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* N in Encrypted Key attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_ENCRYPTED_KEY);
+ wpabuf_put_le16(msg, 2 * curve->prime_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_INVALID_ENCRYPTED_KEY_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Encrypted Key");
+ if (dpp_test_gen_invalid_key(msg, curve) < 0)
+ goto fail;
+ goto skip_encrypted_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (dpp_bn2bin_pad(Nx, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Nx, pkex->Nx, curve->prime_len) < 0 ||
+ dpp_bn2bin_pad(Ny, wpabuf_put(msg, curve->prime_len),
+ curve->prime_len) < 0)
+ goto fail;
+
+skip_encrypted_key:
+ if (status == DPP_STATUS_BAD_GROUP) {
+ /* Finite Cyclic Group attribute */
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, curve->ike_group);
+ }
+
+ return msg;
+fail:
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
+static int dpp_pkex_identifier_match(const u8 *attr_id, u16 attr_id_len,
+ const char *identifier)
+{
+ if (!attr_id && identifier) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No PKEX code identifier received, but expected one");
+ return 0;
+ }
+
+ if (attr_id && !identifier) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX code identifier received, but not expecting one");
+ return 0;
+ }
+
+ if (attr_id && identifier &&
+ (os_strlen(identifier) != attr_id_len ||
+ os_memcmp(identifier, attr_id, attr_id_len) != 0)) {
+ wpa_printf(MSG_DEBUG, "DPP: PKEX code identifier mismatch");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+struct dpp_pkex * dpp_pkex_rx_exchange_req(void *msg_ctx,
+ struct dpp_bootstrap_info *bi,
+ const u8 *own_mac,
+ const u8 *peer_mac,
+ const char *identifier,
+ const char *code,
+ const u8 *buf, size_t len)
+{
+ const u8 *attr_group, *attr_id, *attr_key;
+ u16 attr_group_len, attr_id_len, attr_key_len;
+ const struct dpp_curve_params *curve = bi->curve;
+ u16 ike_group;
+ struct dpp_pkex *pkex = NULL;
+ EC_POINT *Qi = NULL, *Qr = NULL, *M = NULL, *X = NULL, *N = NULL;
+ BN_CTX *bnctx = NULL;
+ EC_GROUP *group = NULL;
+ BIGNUM *Mx = NULL, *My = NULL;
+ const EC_KEY *Y_ec;
+ EC_KEY *X_ec = NULL;
+ const EC_POINT *Y_point;
+ BIGNUM *Nx = NULL, *Ny = NULL;
+ u8 Kx[DPP_MAX_SHARED_SECRET_LEN];
+ size_t Kx_len;
+ int res;
+
+ if (bi->pkex_t >= PKEX_COUNTER_T_LIMIT) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "PKEX counter t limit reached - ignore message");
+ return NULL;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+ MAC2STR(dpp_pkex_peer_mac_override));
+ peer_mac = dpp_pkex_peer_mac_override;
+ }
+ if (!is_zero_ether_addr(dpp_pkex_own_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - own_mac override " MACSTR,
+ MAC2STR(dpp_pkex_own_mac_override));
+ own_mac = dpp_pkex_own_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ attr_id_len = 0;
+ attr_id = dpp_get_attr(buf, len, DPP_ATTR_CODE_IDENTIFIER,
+ &attr_id_len);
+ if (!dpp_pkex_identifier_match(attr_id, attr_id_len, identifier))
+ return NULL;
+
+ attr_group = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &attr_group_len);
+ if (!attr_group || attr_group_len != 2) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid Finite Cyclic Group attribute");
+ return NULL;
+ }
+ ike_group = WPA_GET_LE16(attr_group);
+ if (ike_group != curve->ike_group) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Mismatching PKEX curve: peer=%u own=%u",
+ ike_group, curve->ike_group);
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ goto fail;
+ pkex->own_bi = bi;
+ pkex->failed = 1;
+ pkex->exchange_resp = dpp_pkex_build_exchange_resp(
+ pkex, DPP_STATUS_BAD_GROUP, NULL, NULL);
+ if (!pkex->exchange_resp)
+ goto fail;
+ return pkex;
+ }
+
+ /* M in Encrypted Key attribute */
+ attr_key = dpp_get_attr(buf, len, DPP_ATTR_ENCRYPTED_KEY,
+ &attr_key_len);
+ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2 ||
+ attr_key_len / 2 > DPP_MAX_SHARED_SECRET_LEN) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing Encrypted Key attribute");
+ return NULL;
+ }
+
+ /* Qi = H(MAC-Initiator | [identifier |] code) * Pi */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qi = dpp_pkex_derive_Qi(curve, peer_mac, code, identifier, bnctx,
+ &group);
+ if (!Qi)
+ goto fail;
+
+ /* X' = M - Qi */
+ X = EC_POINT_new(group);
+ M = EC_POINT_new(group);
+ Mx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+ My = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+ if (!X || !M || !Mx || !My ||
+ EC_POINT_set_affine_coordinates_GFp(group, M, Mx, My, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, M) ||
+ !EC_POINT_is_on_curve(group, M, bnctx) ||
+ EC_POINT_invert(group, Qi, bnctx) != 1 ||
+ EC_POINT_add(group, X, M, Qi, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, X) ||
+ !EC_POINT_is_on_curve(group, X, bnctx)) {
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Invalid Encrypted Key value");
+ bi->pkex_t++;
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: M", group, M);
+ dpp_debug_print_point("DPP: X'", group, X);
+
+ pkex = os_zalloc(sizeof(*pkex));
+ if (!pkex)
+ goto fail;
+ pkex->t = bi->pkex_t;
+ pkex->msg_ctx = msg_ctx;
+ pkex->own_bi = bi;
+ os_memcpy(pkex->own_mac, own_mac, ETH_ALEN);
+ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+ if (identifier) {
+ pkex->identifier = os_strdup(identifier);
+ if (!pkex->identifier)
+ goto fail;
+ }
+ pkex->code = os_strdup(code);
+ if (!pkex->code)
+ goto fail;
+
+ os_memcpy(pkex->Mx, attr_key, attr_key_len / 2);
+
+ X_ec = EC_KEY_new();
+ if (!X_ec ||
+ EC_KEY_set_group(X_ec, group) != 1 ||
+ EC_KEY_set_public_key(X_ec, X) != 1)
+ goto fail;
+ pkex->x = EVP_PKEY_new();
+ if (!pkex->x ||
+ EVP_PKEY_set1_EC_KEY(pkex->x, X_ec) != 1)
+ goto fail;
+
+ /* Qr = H(MAC-Responder | | [identifier | ] code) * Pr */
+ Qr = dpp_pkex_derive_Qr(curve, own_mac, code, identifier, bnctx, NULL);
+ if (!Qr)
+ goto fail;
+
+ /* Generate a random ephemeral keypair y/Y */
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_pkex_ephemeral_key_override_len) {
+ const struct dpp_curve_params *tmp_curve;
+
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - override ephemeral key y/Y");
+ pkex->y = dpp_set_keypair(&tmp_curve,
+ dpp_pkex_ephemeral_key_override,
+ dpp_pkex_ephemeral_key_override_len);
+ } else {
+ pkex->y = dpp_gen_keypair(curve);
+ }
+#else /* CONFIG_TESTING_OPTIONS */
+ pkex->y = dpp_gen_keypair(curve);
+#endif /* CONFIG_TESTING_OPTIONS */
+ if (!pkex->y)
+ goto fail;
+
+ /* N = Y + Qr */
+ Y_ec = EVP_PKEY_get0_EC_KEY(pkex->y);
+ if (!Y_ec)
+ goto fail;
+ Y_point = EC_KEY_get0_public_key(Y_ec);
+ if (!Y_point)
+ goto fail;
+ dpp_debug_print_point("DPP: Y", group, Y_point);
+ N = EC_POINT_new(group);
+ Nx = BN_new();
+ Ny = BN_new();
+ if (!N || !Nx || !Ny ||
+ EC_POINT_add(group, N, Y_point, Qr, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1)
+ goto fail;
+ dpp_debug_print_point("DPP: N", group, N);
+
+ pkex->exchange_resp = dpp_pkex_build_exchange_resp(pkex, DPP_STATUS_OK,
+ Nx, Ny);
+ if (!pkex->exchange_resp)
+ goto fail;
+
+ /* K = y * X' */
+ if (dpp_ecdh(pkex->y, pkex->x, Kx, &Kx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+ Kx, Kx_len);
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+ res = dpp_pkex_derive_z(pkex->peer_mac, pkex->own_mac,
+ pkex->Mx, curve->prime_len,
+ pkex->Nx, curve->prime_len, pkex->code,
+ Kx, Kx_len, pkex->z, curve->hash_len);
+ os_memset(Kx, 0, Kx_len);
+ if (res < 0)
+ goto fail;
+
+ pkex->exchange_done = 1;
+
+out:
+ BN_CTX_free(bnctx);
+ EC_POINT_free(Qi);
+ EC_POINT_free(Qr);
+ BN_free(Mx);
+ BN_free(My);
+ BN_free(Nx);
+ BN_free(Ny);
+ EC_POINT_free(M);
+ EC_POINT_free(N);
+ EC_POINT_free(X);
+ EC_KEY_free(X_ec);
+ EC_GROUP_free(group);
+ return pkex;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Request processing failed");
+ dpp_pkex_free(pkex);
+ pkex = NULL;
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_req(struct dpp_pkex *pkex,
+ const struct wpabuf *A_pub, const u8 *u)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ struct wpabuf *msg = NULL;
+ size_t clear_len, attr_len;
+ struct wpabuf *clear = NULL;
+ u8 *wrapped;
+ u8 octet;
+ const u8 *addr[2];
+ size_t len[2];
+
+ /* {A, u, [bootstrapping info]}z */
+ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_REQ, attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+ goto skip_bootstrap_key;
+ }
+ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, 2 * curve->prime_len);
+ if (dpp_test_gen_invalid_key(clear, curve) < 0)
+ goto fail;
+ goto skip_bootstrap_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* A in Bootstrap Key attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, wpabuf_len(A_pub));
+ wpabuf_put_buf(clear, A_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+ if (dpp_test == DPP_TEST_NO_I_AUTH_TAG_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no I-Auth tag");
+ goto skip_i_auth_tag;
+ }
+ if (dpp_test == DPP_TEST_I_AUTH_TAG_MISMATCH_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - I-Auth tag mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, u, curve->hash_len - 1);
+ wpabuf_put_u8(clear, u[curve->hash_len - 1] ^ 0x01);
+ goto skip_i_auth_tag;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* u in I-Auth tag attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_I_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, u, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_i_auth_tag:
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = DPP_HDR_LEN;
+ octet = 0;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(pkex->z, curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+out:
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_exchange_resp(struct dpp_pkex *pkex,
+ const u8 *peer_mac,
+ const u8 *buf, size_t buflen)
+{
+ const u8 *attr_status, *attr_id, *attr_key, *attr_group;
+ u16 attr_status_len, attr_id_len, attr_key_len, attr_group_len;
+ EC_GROUP *group = NULL;
+ BN_CTX *bnctx = NULL;
+ struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ EC_POINT *Qr = NULL, *Y = NULL, *N = NULL;
+ BIGNUM *Nx = NULL, *Ny = NULL;
+ EC_KEY *Y_ec = NULL;
+ size_t Jx_len, Kx_len;
+ u8 Jx[DPP_MAX_SHARED_SECRET_LEN], Kx[DPP_MAX_SHARED_SECRET_LEN];
+ const u8 *addr[4];
+ size_t len[4];
+ u8 u[DPP_MAX_HASH_LEN];
+ int res;
+
+ if (pkex->failed || pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+ return NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_EXCHANGE_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX Exchange Response");
+ pkex->failed = 1;
+ return NULL;
+ }
+
+ if (!is_zero_ether_addr(dpp_pkex_peer_mac_override)) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - peer_mac override " MACSTR,
+ MAC2STR(dpp_pkex_peer_mac_override));
+ peer_mac = dpp_pkex_peer_mac_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memcpy(pkex->peer_mac, peer_mac, ETH_ALEN);
+
+ attr_status = dpp_get_attr(buf, buflen, DPP_ATTR_STATUS,
+ &attr_status_len);
+ if (!attr_status || attr_status_len != 1) {
+ dpp_pkex_fail(pkex, "No DPP Status attribute");
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", attr_status[0]);
+
+ if (attr_status[0] == DPP_STATUS_BAD_GROUP) {
+ attr_group = dpp_get_attr(buf, buflen,
+ DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &attr_group_len);
+ if (attr_group && attr_group_len == 2) {
+ wpa_msg(pkex->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Peer indicated mismatching PKEX group - proposed %u",
+ WPA_GET_LE16(attr_group));
+ return NULL;
+ }
+ }
+
+ if (attr_status[0] != DPP_STATUS_OK) {
+ dpp_pkex_fail(pkex, "PKEX failed (peer indicated failure)");
+ return NULL;
+ }
+
+ attr_id_len = 0;
+ attr_id = dpp_get_attr(buf, buflen, DPP_ATTR_CODE_IDENTIFIER,
+ &attr_id_len);
+ if (!dpp_pkex_identifier_match(attr_id, attr_id_len,
+ pkex->identifier)) {
+ dpp_pkex_fail(pkex, "PKEX code identifier mismatch");
+ return NULL;
+ }
+
+ /* N in Encrypted Key attribute */
+ attr_key = dpp_get_attr(buf, buflen, DPP_ATTR_ENCRYPTED_KEY,
+ &attr_key_len);
+ if (!attr_key || attr_key_len & 0x01 || attr_key_len < 2) {
+ dpp_pkex_fail(pkex, "Missing Encrypted Key attribute");
+ return NULL;
+ }
+
+ /* Qr = H(MAC-Responder | [identifier |] code) * Pr */
+ bnctx = BN_CTX_new();
+ if (!bnctx)
+ goto fail;
+ Qr = dpp_pkex_derive_Qr(curve, pkex->peer_mac, pkex->code,
+ pkex->identifier, bnctx, &group);
+ if (!Qr)
+ goto fail;
+
+ /* Y' = N - Qr */
+ Y = EC_POINT_new(group);
+ N = EC_POINT_new(group);
+ Nx = BN_bin2bn(attr_key, attr_key_len / 2, NULL);
+ Ny = BN_bin2bn(attr_key + attr_key_len / 2, attr_key_len / 2, NULL);
+ if (!Y || !N || !Nx || !Ny ||
+ EC_POINT_set_affine_coordinates_GFp(group, N, Nx, Ny, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, N) ||
+ !EC_POINT_is_on_curve(group, N, bnctx) ||
+ EC_POINT_invert(group, Qr, bnctx) != 1 ||
+ EC_POINT_add(group, Y, N, Qr, bnctx) != 1 ||
+ EC_POINT_is_at_infinity(group, Y) ||
+ !EC_POINT_is_on_curve(group, Y, bnctx)) {
+ dpp_pkex_fail(pkex, "Invalid Encrypted Key value");
+ pkex->t++;
+ goto fail;
+ }
+ dpp_debug_print_point("DPP: N", group, N);
+ dpp_debug_print_point("DPP: Y'", group, Y);
+
+ pkex->exchange_done = 1;
+
+ /* ECDH: J = a * Y' */
+ Y_ec = EC_KEY_new();
+ if (!Y_ec ||
+ EC_KEY_set_group(Y_ec, group) != 1 ||
+ EC_KEY_set_public_key(Y_ec, Y) != 1)
+ goto fail;
+ pkex->y = EVP_PKEY_new();
+ if (!pkex->y ||
+ EVP_PKEY_set1_EC_KEY(pkex->y, Y_ec) != 1)
+ goto fail;
+ if (dpp_ecdh(pkex->own_bi->pubkey, pkex->y, Jx, &Jx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+ Jx, Jx_len);
+
+ /* u = HMAC(J.x, MAC-Initiator | A.x | Y'.x | X.x) */
+ A_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ if (!A_pub || !Y_pub || !X_pub)
+ goto fail;
+ addr[0] = pkex->own_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(A_pub);
+ len[1] = wpabuf_len(A_pub) / 2;
+ addr[2] = wpabuf_head(Y_pub);
+ len[2] = wpabuf_len(Y_pub) / 2;
+ addr[3] = wpabuf_head(X_pub);
+ len[3] = wpabuf_len(X_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: u", u, curve->hash_len);
+
+ /* K = x * Y' */
+ if (dpp_ecdh(pkex->x, pkex->y, Kx, &Kx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (K.x)",
+ Kx, Kx_len);
+
+ /* z = HKDF(<>, MAC-Initiator | MAC-Responder | M.x | N.x | code, K.x)
+ */
+ res = dpp_pkex_derive_z(pkex->own_mac, pkex->peer_mac,
+ pkex->Mx, curve->prime_len,
+ attr_key /* N.x */, attr_key_len / 2,
+ pkex->code, Kx, Kx_len,
+ pkex->z, curve->hash_len);
+ os_memset(Kx, 0, Kx_len);
+ if (res < 0)
+ goto fail;
+
+ msg = dpp_pkex_build_commit_reveal_req(pkex, A_pub, u);
+ if (!msg)
+ goto fail;
+
+out:
+ wpabuf_free(A_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ EC_POINT_free(Qr);
+ EC_POINT_free(Y);
+ EC_POINT_free(N);
+ BN_free(Nx);
+ BN_free(Ny);
+ EC_KEY_free(Y_ec);
+ BN_CTX_free(bnctx);
+ EC_GROUP_free(group);
+ return msg;
+fail:
+ wpa_printf(MSG_DEBUG, "DPP: PKEX Exchange Response processing failed");
+ goto out;
+}
+
+
+static struct wpabuf *
+dpp_pkex_build_commit_reveal_resp(struct dpp_pkex *pkex,
+ const struct wpabuf *B_pub, const u8 *v)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ struct wpabuf *msg = NULL;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 octet;
+ u8 *wrapped;
+ struct wpabuf *clear = NULL;
+ size_t clear_len, attr_len;
+
+ /* {B, v [bootstrapping info]}z */
+ clear_len = 4 + 2 * curve->prime_len + 4 + curve->hash_len;
+ clear = wpabuf_alloc(clear_len);
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP)
+ attr_len += 5;
+#endif /* CONFIG_TESTING_OPTIONS */
+ msg = dpp_alloc_msg(DPP_PA_PKEX_COMMIT_REVEAL_RESP, attr_len);
+ if (!clear || !msg)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_NO_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Bootstrap Key");
+ goto skip_bootstrap_key;
+ }
+ if (dpp_test == DPP_TEST_INVALID_BOOTSTRAP_KEY_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - invalid Bootstrap Key");
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, 2 * curve->prime_len);
+ if (dpp_test_gen_invalid_key(clear, curve) < 0)
+ goto fail;
+ goto skip_bootstrap_key;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* B in Bootstrap Key attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_BOOTSTRAP_KEY);
+ wpabuf_put_le16(clear, wpabuf_len(B_pub));
+ wpabuf_put_buf(clear, B_pub);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_bootstrap_key:
+ if (dpp_test == DPP_TEST_NO_R_AUTH_TAG_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no R-Auth tag");
+ goto skip_r_auth_tag;
+ }
+ if (dpp_test == DPP_TEST_R_AUTH_TAG_MISMATCH_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - R-Auth tag mismatch");
+ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, v, curve->hash_len - 1);
+ wpabuf_put_u8(clear, v[curve->hash_len - 1] ^ 0x01);
+ goto skip_r_auth_tag;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ /* v in R-Auth tag attribute */
+ wpabuf_put_le16(clear, DPP_ATTR_R_AUTH_TAG);
+ wpabuf_put_le16(clear, curve->hash_len);
+ wpabuf_put_data(clear, v, curve->hash_len);
+
+#ifdef CONFIG_TESTING_OPTIONS
+skip_r_auth_tag:
+ if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
+ goto skip_wrapped_data;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = DPP_HDR_LEN;
+ octet = 1;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(pkex->z, curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - attr after Wrapped Data");
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+ }
+skip_wrapped_data:
+#endif /* CONFIG_TESTING_OPTIONS */
+
+out:
+ wpabuf_free(clear);
+ return msg;
+
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf * dpp_pkex_rx_commit_reveal_req(struct dpp_pkex *pkex,
+ const u8 *hdr,
+ const u8 *buf, size_t buflen)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ size_t Jx_len, Lx_len;
+ u8 Jx[DPP_MAX_SHARED_SECRET_LEN];
+ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+ const u8 *wrapped_data, *b_key, *peer_u;
+ u16 wrapped_data_len, b_key_len, peer_u_len = 0;
+ const u8 *addr[4];
+ size_t len[4];
+ u8 octet;
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ struct wpabuf *msg = NULL, *A_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+ struct wpabuf *B_pub = NULL;
+ u8 u[DPP_MAX_HASH_LEN], v[DPP_MAX_HASH_LEN];
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_REQ) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX CR Request");
+ pkex->failed = 1;
+ return NULL;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!pkex->exchange_done || pkex->failed ||
+ pkex->t >= PKEX_COUNTER_T_LIMIT || pkex->initiator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_pkex_fail(pkex,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ octet = 0;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (aes_siv_decrypt(pkex->z, curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_pkex_fail(pkex,
+ "AES-SIV decryption failed - possible PKEX code mismatch");
+ pkex->failed = 1;
+ pkex->t++;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+ &b_key_len);
+ if (!b_key || b_key_len != 2 * curve->prime_len) {
+ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
+ goto fail;
+ }
+ pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+ b_key_len);
+ if (!pkex->peer_bootstrap_key) {
+ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Peer bootstrap public key",
+ pkex->peer_bootstrap_key);
+
+ /* ECDH: J' = y * A' */
+ if (dpp_ecdh(pkex->y, pkex->peer_bootstrap_key, Jx, &Jx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (J.x)",
+ Jx, Jx_len);
+
+ /* u' = HMAC(J'.x, MAC-Initiator | A'.x | Y.x | X'.x) */
+ A_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ if (!A_pub || !Y_pub || !X_pub)
+ goto fail;
+ addr[0] = pkex->peer_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(A_pub);
+ len[1] = wpabuf_len(A_pub) / 2;
+ addr[2] = wpabuf_head(Y_pub);
+ len[2] = wpabuf_len(Y_pub) / 2;
+ addr[3] = wpabuf_head(X_pub);
+ len[3] = wpabuf_len(X_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Jx, Jx_len, 4, addr, len, u) < 0)
+ goto fail;
+
+ peer_u = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_I_AUTH_TAG,
+ &peer_u_len);
+ if (!peer_u || peer_u_len != curve->hash_len ||
+ os_memcmp(peer_u, u, curve->hash_len) != 0) {
+ dpp_pkex_fail(pkex, "No valid u (I-Auth tag) found");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated u'",
+ u, curve->hash_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Received u", peer_u, peer_u_len);
+ pkex->t++;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Valid u (I-Auth tag) received");
+
+ /* ECDH: L = b * X' */
+ if (dpp_ecdh(pkex->own_bi->pubkey, pkex->x, Lx, &Lx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+ Lx, Lx_len);
+
+ /* v = HMAC(L.x, MAC-Responder | B.x | X'.x | Y.x) */
+ B_pub = dpp_get_pubkey_point(pkex->own_bi->pubkey, 0);
+ if (!B_pub)
+ goto fail;
+ addr[0] = pkex->own_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(B_pub);
+ len[1] = wpabuf_len(B_pub) / 2;
+ addr[2] = wpabuf_head(X_pub);
+ len[2] = wpabuf_len(X_pub) / 2;
+ addr[3] = wpabuf_head(Y_pub);
+ len[3] = wpabuf_len(Y_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: v", v, curve->hash_len);
+
+ msg = dpp_pkex_build_commit_reveal_resp(pkex, B_pub, v);
+ if (!msg)
+ goto fail;
+
+out:
+ os_free(unwrapped);
+ wpabuf_free(A_pub);
+ wpabuf_free(B_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ return msg;
+fail:
+ wpa_printf(MSG_DEBUG,
+ "DPP: PKEX Commit-Reveal Request processing failed");
+ goto out;
+}
+
+
+int dpp_pkex_rx_commit_reveal_resp(struct dpp_pkex *pkex, const u8 *hdr,
+ const u8 *buf, size_t buflen)
+{
+ const struct dpp_curve_params *curve = pkex->own_bi->curve;
+ const u8 *wrapped_data, *b_key, *peer_v;
+ u16 wrapped_data_len, b_key_len, peer_v_len = 0;
+ const u8 *addr[4];
+ size_t len[4];
+ u8 octet;
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ int ret = -1;
+ u8 v[DPP_MAX_HASH_LEN];
+ size_t Lx_len;
+ u8 Lx[DPP_MAX_SHARED_SECRET_LEN];
+ struct wpabuf *B_pub = NULL, *X_pub = NULL, *Y_pub = NULL;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_PKEX_CR_RESP) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at PKEX CR Response");
+ pkex->failed = 1;
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!pkex->exchange_done || pkex->failed ||
+ pkex->t >= PKEX_COUNTER_T_LIMIT || !pkex->initiator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(buf, buflen, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_pkex_fail(pkex,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ octet = 1;
+ addr[1] = &octet;
+ len[1] = sizeof(octet);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ if (aes_siv_decrypt(pkex->z, curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_pkex_fail(pkex,
+ "AES-SIV decryption failed - possible PKEX code mismatch");
+ pkex->t++;
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_pkex_fail(pkex, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ b_key = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_BOOTSTRAP_KEY,
+ &b_key_len);
+ if (!b_key || b_key_len != 2 * curve->prime_len) {
+ dpp_pkex_fail(pkex, "No valid peer bootstrapping key found");
+ goto fail;
+ }
+ pkex->peer_bootstrap_key = dpp_set_pubkey_point(pkex->x, b_key,
+ b_key_len);
+ if (!pkex->peer_bootstrap_key) {
+ dpp_pkex_fail(pkex, "Peer bootstrapping key is invalid");
+ goto fail;
+ }
+ dpp_debug_print_key("DPP: Peer bootstrap public key",
+ pkex->peer_bootstrap_key);
+
+ /* ECDH: L' = x * B' */
+ if (dpp_ecdh(pkex->x, pkex->peer_bootstrap_key, Lx, &Lx_len) < 0)
+ goto fail;
+
+ wpa_hexdump_key(MSG_DEBUG, "DPP: ECDH shared secret (L.x)",
+ Lx, Lx_len);
+
+ /* v' = HMAC(L.x, MAC-Responder | B'.x | X.x | Y'.x) */
+ B_pub = dpp_get_pubkey_point(pkex->peer_bootstrap_key, 0);
+ X_pub = dpp_get_pubkey_point(pkex->x, 0);
+ Y_pub = dpp_get_pubkey_point(pkex->y, 0);
+ if (!B_pub || !X_pub || !Y_pub)
+ goto fail;
+ addr[0] = pkex->peer_mac;
+ len[0] = ETH_ALEN;
+ addr[1] = wpabuf_head(B_pub);
+ len[1] = wpabuf_len(B_pub) / 2;
+ addr[2] = wpabuf_head(X_pub);
+ len[2] = wpabuf_len(X_pub) / 2;
+ addr[3] = wpabuf_head(Y_pub);
+ len[3] = wpabuf_len(Y_pub) / 2;
+ if (dpp_hmac_vector(curve->hash_len, Lx, Lx_len, 4, addr, len, v) < 0)
+ goto fail;
+
+ peer_v = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_R_AUTH_TAG,
+ &peer_v_len);
+ if (!peer_v || peer_v_len != curve->hash_len ||
+ os_memcmp(peer_v, v, curve->hash_len) != 0) {
+ dpp_pkex_fail(pkex, "No valid v (R-Auth tag) found");
+ wpa_hexdump(MSG_DEBUG, "DPP: Calculated v'",
+ v, curve->hash_len);
+ wpa_hexdump(MSG_DEBUG, "DPP: Received v", peer_v, peer_v_len);
+ pkex->t++;
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Valid v (R-Auth tag) received");
+
+ ret = 0;
+out:
+ wpabuf_free(B_pub);
+ wpabuf_free(X_pub);
+ wpabuf_free(Y_pub);
+ os_free(unwrapped);
+ return ret;
+fail:
+ goto out;
+}
+
+
+struct dpp_bootstrap_info *
+dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
+ unsigned int freq)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return NULL;
+ bi->id = dpp_next_id(dpp);
+ bi->type = DPP_BOOTSTRAP_PKEX;
+ os_memcpy(bi->mac_addr, peer, ETH_ALEN);
+ bi->num_freq = 1;
+ bi->freq[0] = freq;
+ bi->curve = pkex->own_bi->curve;
+ bi->pubkey = pkex->peer_bootstrap_key;
+ pkex->peer_bootstrap_key = NULL;
+ if (dpp_bootstrap_key_hash(bi) < 0) {
+ dpp_bootstrap_info_free(bi);
+ return NULL;
+ }
+ dpp_pkex_free(pkex);
+ dl_list_add(&dpp->bootstrap, &bi->list);
+ return bi;
+}
+
+
+void dpp_pkex_free(struct dpp_pkex *pkex)
+{
+ if (!pkex)
+ return;
+
+ os_free(pkex->identifier);
+ os_free(pkex->code);
+ EVP_PKEY_free(pkex->x);
+ EVP_PKEY_free(pkex->y);
+ EVP_PKEY_free(pkex->peer_bootstrap_key);
+ wpabuf_free(pkex->exchange_req);
+ wpabuf_free(pkex->exchange_resp);
+ os_free(pkex);
+}
diff --git a/contrib/wpa/src/common/dpp_reconfig.c b/contrib/wpa/src/common/dpp_reconfig.c
new file mode 100644
index 000000000000..c4a027363fce
--- /dev/null
+++ b/contrib/wpa/src/common/dpp_reconfig.c
@@ -0,0 +1,958 @@
+/*
+ * DPP reconfiguration
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <openssl/opensslv.h>
+#include <openssl/err.h>
+
+#include "utils/common.h"
+#include "utils/json.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+
+#ifdef CONFIG_DPP2
+
+static void dpp_build_attr_csign_key_hash(struct wpabuf *msg, const u8 *hash)
+{
+ if (hash) {
+ wpa_printf(MSG_DEBUG, "DPP: Configurator C-sign key Hash");
+ wpabuf_put_le16(msg, DPP_ATTR_C_SIGN_KEY_HASH);
+ wpabuf_put_le16(msg, SHA256_MAC_LEN);
+ wpabuf_put_data(msg, hash, SHA256_MAC_LEN);
+ }
+}
+
+
+struct wpabuf * dpp_build_reconfig_announcement(const u8 *csign_key,
+ size_t csign_key_len,
+ const u8 *net_access_key,
+ size_t net_access_key_len,
+ struct dpp_reconfig_id *id)
+{
+ struct wpabuf *msg = NULL;
+ EVP_PKEY *csign = NULL;
+ const unsigned char *p;
+ struct wpabuf *uncomp;
+ u8 hash[SHA256_MAC_LEN];
+ const u8 *addr[1];
+ size_t len[1];
+ int res;
+ size_t attr_len;
+ const struct dpp_curve_params *own_curve;
+ EVP_PKEY *own_key;
+ struct wpabuf *a_nonce = NULL, *e_id = NULL;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build Reconfig Announcement frame");
+
+ own_key = dpp_set_keypair(&own_curve, net_access_key,
+ net_access_key_len);
+ if (!own_key) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+ goto fail;
+ }
+
+ p = csign_key;
+ csign = d2i_PUBKEY(NULL, &p, csign_key_len);
+ if (!csign) {
+ wpa_printf(MSG_ERROR,
+ "DPP: Failed to parse local C-sign-key information");
+ goto fail;
+ }
+
+ uncomp = dpp_get_pubkey_point(csign, 1);
+ EVP_PKEY_free(csign);
+ if (!uncomp)
+ goto fail;
+ addr[0] = wpabuf_head(uncomp);
+ len[0] = wpabuf_len(uncomp);
+ wpa_hexdump(MSG_DEBUG, "DPP: Uncompressed C-sign key", addr[0], len[0]);
+ res = sha256_vector(1, addr, len, hash);
+ wpabuf_free(uncomp);
+ if (res < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "DPP: kid = SHA256(uncompressed C-sign key)",
+ hash, SHA256_MAC_LEN);
+
+ if (dpp_update_reconfig_id(id) < 0) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate E'-id");
+ goto fail;
+ }
+
+ a_nonce = dpp_get_pubkey_point(id->a_nonce, 0);
+ e_id = dpp_get_pubkey_point(id->e_prime_id, 0);
+ if (!a_nonce || !e_id)
+ goto fail;
+
+ attr_len = 4 + SHA256_MAC_LEN;
+ attr_len += 4 + 2;
+ attr_len += 4 + wpabuf_len(a_nonce);
+ attr_len += 4 + wpabuf_len(e_id);
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_ANNOUNCEMENT, attr_len);
+ if (!msg)
+ goto fail;
+
+ /* Configurator C-sign key Hash */
+ dpp_build_attr_csign_key_hash(msg, hash);
+
+ /* Finite Cyclic Group attribute */
+ wpa_printf(MSG_DEBUG, "DPP: Finite Cyclic Group: %u",
+ own_curve->ike_group);
+ wpabuf_put_le16(msg, DPP_ATTR_FINITE_CYCLIC_GROUP);
+ wpabuf_put_le16(msg, 2);
+ wpabuf_put_le16(msg, own_curve->ike_group);
+
+ /* A-NONCE */
+ wpabuf_put_le16(msg, DPP_ATTR_A_NONCE);
+ wpabuf_put_le16(msg, wpabuf_len(a_nonce));
+ wpabuf_put_buf(msg, a_nonce);
+
+ /* E'-id */
+ wpabuf_put_le16(msg, DPP_ATTR_E_PRIME_ID);
+ wpabuf_put_le16(msg, wpabuf_len(e_id));
+ wpabuf_put_buf(msg, e_id);
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Announcement frame attributes", msg);
+fail:
+ wpabuf_free(a_nonce);
+ wpabuf_free(e_id);
+ EVP_PKEY_free(own_key);
+ return msg;
+}
+
+
+static struct wpabuf * dpp_reconfig_build_req(struct dpp_authentication *auth)
+{
+ struct wpabuf *msg;
+ size_t attr_len;
+
+ /* Build DPP Reconfig Authentication Request frame attributes */
+ attr_len = 4 + 1 + 4 + 1 + 4 + os_strlen(auth->conf->connector) +
+ 4 + auth->curve->nonce_len;
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_REQ, attr_len);
+ if (!msg)
+ return NULL;
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, auth->transaction_id);
+
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+
+ /* DPP Connector */
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(auth->conf->connector));
+ wpabuf_put_str(msg, auth->conf->connector);
+
+ /* C-nonce */
+ wpabuf_put_le16(msg, DPP_ATTR_CONFIGURATOR_NONCE);
+ wpabuf_put_le16(msg, auth->curve->nonce_len);
+ wpabuf_put_data(msg, auth->c_nonce, auth->curve->nonce_len);
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Authentication Request frame attributes",
+ msg);
+
+ return msg;
+}
+
+
+static int
+dpp_configurator_build_own_connector(struct dpp_configurator *conf,
+ const struct dpp_curve_params *curve)
+{
+ struct wpabuf *dppcon = NULL;
+ int ret = -1;
+
+ if (conf->connector)
+ return 0; /* already generated */
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Sign own Configurator Connector for reconfiguration with curve %s",
+ conf->curve->name);
+ conf->connector_key = dpp_gen_keypair(curve);
+ if (!conf->connector_key)
+ goto fail;
+
+ /* Connector (JSON dppCon object) */
+ dppcon = wpabuf_alloc(1000 + 2 * curve->prime_len * 4 / 3);
+ if (!dppcon)
+ goto fail;
+ json_start_object(dppcon, NULL);
+ json_start_array(dppcon, "groups");
+ json_start_object(dppcon, NULL);
+ json_add_string(dppcon, "groupId", "*");
+ json_value_sep(dppcon);
+ json_add_string(dppcon, "netRole", "configurator");
+ json_end_object(dppcon);
+ json_end_array(dppcon);
+ json_value_sep(dppcon);
+ if (dpp_build_jwk(dppcon, "netAccessKey", conf->connector_key, NULL,
+ curve) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to build netAccessKey JWK");
+ goto fail;
+ }
+ json_end_object(dppcon);
+ wpa_printf(MSG_DEBUG, "DPP: dppCon: %s",
+ (const char *) wpabuf_head(dppcon));
+
+ conf->connector = dpp_sign_connector(conf, dppcon);
+ if (!conf->connector)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "DPP: signedConnector: %s", conf->connector);
+
+ ret = 0;
+fail:
+ wpabuf_free(dppcon);
+ return ret;
+}
+
+
+struct dpp_authentication *
+dpp_reconfig_init(struct dpp_global *dpp, void *msg_ctx,
+ struct dpp_configurator *conf, unsigned int freq, u16 group,
+ const u8 *a_nonce_attr, size_t a_nonce_len,
+ const u8 *e_id_attr, size_t e_id_len)
+{
+ struct dpp_authentication *auth;
+ const struct dpp_curve_params *curve;
+ EVP_PKEY *a_nonce, *e_prime_id;
+ EC_POINT *e_id;
+
+ curve = dpp_get_curve_ike_group(group);
+ if (!curve) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported group %u - cannot reconfigure",
+ group);
+ return NULL;
+ }
+
+ if (!a_nonce_attr) {
+ wpa_printf(MSG_INFO, "DPP: Missing required A-NONCE attribute");
+ return NULL;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: A-NONCE", a_nonce_attr, a_nonce_len);
+ a_nonce = dpp_set_pubkey_point(conf->csign, a_nonce_attr, a_nonce_len);
+ if (!a_nonce) {
+ wpa_printf(MSG_INFO, "DPP: Invalid A-NONCE");
+ return NULL;
+ }
+ dpp_debug_print_key("A-NONCE", a_nonce);
+
+ if (!e_id_attr) {
+ wpa_printf(MSG_INFO, "DPP: Missing required E'-id attribute");
+ return NULL;
+ }
+ e_prime_id = dpp_set_pubkey_point(conf->csign, e_id_attr, e_id_len);
+ if (!e_prime_id) {
+ wpa_printf(MSG_INFO, "DPP: Invalid E'-id");
+ EVP_PKEY_free(a_nonce);
+ return NULL;
+ }
+ dpp_debug_print_key("E'-id", e_prime_id);
+ e_id = dpp_decrypt_e_id(conf->pp_key, a_nonce, e_prime_id);
+ EVP_PKEY_free(a_nonce);
+ EVP_PKEY_free(e_prime_id);
+ if (!e_id) {
+ wpa_printf(MSG_INFO, "DPP: Could not decrypt E'-id");
+ return NULL;
+ }
+ /* TODO: could use E-id to determine whether reconfiguration with this
+ * Enrollee has already been started and is waiting for updated
+ * configuration instead of replying again before such configuration
+ * becomes available */
+ EC_POINT_clear_free(e_id);
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ return NULL;
+
+ auth->conf = conf;
+ auth->reconfig = 1;
+ auth->initiator = 1;
+ auth->waiting_auth_resp = 1;
+ auth->allowed_roles = DPP_CAPAB_CONFIGURATOR;
+ auth->configurator = 1;
+ auth->curve = curve;
+ auth->transaction_id = 1;
+ if (freq && dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
+ goto fail;
+
+ if (dpp_configurator_build_own_connector(conf, curve) < 0)
+ goto fail;
+
+ if (random_get_bytes(auth->c_nonce, auth->curve->nonce_len)) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to generate C-nonce");
+ goto fail;
+ }
+
+ auth->reconfig_req_msg = dpp_reconfig_build_req(auth);
+ if (!auth->reconfig_req_msg)
+ goto fail;
+
+out:
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+
+
+static int dpp_reconfig_build_resp(struct dpp_authentication *auth,
+ const char *own_connector,
+ struct wpabuf *conn_status)
+{
+ struct wpabuf *msg = NULL, *clear, *pr = NULL;
+ u8 *attr_start, *attr_end;
+ size_t clear_len, attr_len, len[2];
+ const u8 *addr[2];
+ u8 *wrapped;
+ int res = -1;
+
+ /* Build DPP Reconfig Authentication Response frame attributes */
+ clear_len = 4 + auth->curve->nonce_len +
+ 4 + wpabuf_len(conn_status);
+ clear = wpabuf_alloc(clear_len);
+ if (!clear)
+ goto fail;
+
+ /* C-nonce (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE);
+ wpabuf_put_le16(clear, auth->curve->nonce_len);
+ wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len);
+
+ /* Connection Status (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_CONN_STATUS);
+ wpabuf_put_le16(clear, wpabuf_len(conn_status));
+ wpabuf_put_buf(clear, conn_status);
+
+ pr = dpp_get_pubkey_point(auth->own_protocol_key, 0);
+ if (!pr)
+ goto fail;
+
+ attr_len = 4 + 1 + 4 + 1 +
+ 4 + os_strlen(own_connector) +
+ 4 + auth->curve->nonce_len +
+ 4 + wpabuf_len(pr) +
+ 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_RESP, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* Transaction ID */
+ wpabuf_put_le16(msg, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, auth->transaction_id);
+
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, DPP_VERSION);
+
+ /* R-Connector */
+ wpabuf_put_le16(msg, DPP_ATTR_CONNECTOR);
+ wpabuf_put_le16(msg, os_strlen(own_connector));
+ wpabuf_put_str(msg, own_connector);
+
+ /* E-nonce */
+ wpabuf_put_le16(msg, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(msg, auth->curve->nonce_len);
+ wpabuf_put_data(msg, auth->e_nonce, auth->curve->nonce_len);
+
+ /* Responder Protocol Key (Pr) */
+ wpabuf_put_le16(msg, DPP_ATTR_R_PROTOCOL_KEY);
+ wpabuf_put_le16(msg, wpabuf_len(pr));
+ wpabuf_put_buf(msg, pr);
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ /* Wrapped Data: {C-nonce, E-nonce, Connection Status}ke */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Authentication Response frame attributes",
+ msg);
+
+ wpabuf_free(auth->reconfig_resp_msg);
+ auth->reconfig_resp_msg = msg;
+
+ res = 0;
+out:
+ wpabuf_free(clear);
+ wpabuf_free(pr);
+ return res;
+fail:
+ wpabuf_free(msg);
+ goto out;
+}
+
+
+struct dpp_authentication *
+dpp_reconfig_auth_req_rx(struct dpp_global *dpp, void *msg_ctx,
+ const char *own_connector,
+ const u8 *net_access_key, size_t net_access_key_len,
+ const u8 *csign_key, size_t csign_key_len,
+ unsigned int freq, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ struct dpp_authentication *auth = NULL;
+ const u8 *trans_id, *version, *i_connector, *c_nonce;
+ u16 trans_id_len, version_len, i_connector_len, c_nonce_len;
+ struct dpp_signed_connector_info info;
+ enum dpp_status_error res;
+ struct json_token *root = NULL, *own_root = NULL, *token;
+ unsigned char *own_conn = NULL;
+ struct wpabuf *conn_status = NULL;
+
+ os_memset(&info, 0, sizeof(info));
+
+ trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
+ &trans_id_len);
+ if (!trans_id || trans_id_len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Peer did not include Transaction ID");
+ goto fail;
+ }
+
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (!version || version_len < 1 || version[0] < 2) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing or invalid Protocol Version attribute");
+ goto fail;
+ }
+
+ i_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
+ &i_connector_len);
+ if (!i_connector) {
+ wpa_printf(MSG_DEBUG, "DPP: Missing I-Connector attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: I-Connector",
+ i_connector, i_connector_len);
+
+ c_nonce = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+ if (!c_nonce || c_nonce_len > DPP_MAX_NONCE_LEN) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Missing or invalid C-nonce attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
+
+ res = dpp_check_signed_connector(&info, csign_key, csign_key_len,
+ i_connector, i_connector_len);
+ if (res != DPP_STATUS_OK) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid I-Connector");
+ goto fail;
+ }
+
+ root = json_parse((const char *) info.payload, info.payload_len);
+ own_root = dpp_parse_own_connector(own_connector);
+ if (!root || !own_root ||
+ !dpp_connector_match_groups(own_root, root, true)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: I-Connector does not include compatible group netrole with own connector");
+ goto fail;
+ }
+
+ token = json_get_member(root, "expiry");
+ if (token && token->type == JSON_STRING &&
+ dpp_key_expired(token->string, NULL)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: I-Connector (netAccessKey) has expired");
+ goto fail;
+ }
+
+ token = json_get_member(root, "netAccessKey");
+ if (!token || token->type != JSON_OBJECT) {
+ wpa_printf(MSG_DEBUG, "DPP: No netAccessKey object found");
+ goto fail;
+ }
+
+ auth = dpp_alloc_auth(dpp, msg_ctx);
+ if (!auth)
+ return NULL;
+
+ auth->reconfig = 1;
+ auth->allowed_roles = DPP_CAPAB_ENROLLEE;
+ if (dpp_prepare_channel_list(auth, freq, NULL, 0) < 0)
+ goto fail;
+
+ auth->transaction_id = trans_id[0];
+
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+
+ os_memcpy(auth->c_nonce, c_nonce, c_nonce_len);
+
+ if (dpp_reconfig_derive_ke_responder(auth, net_access_key,
+ net_access_key_len, token) < 0)
+ goto fail;
+
+ if (c_nonce_len != auth->curve->nonce_len) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unexpected C-nonce length %u (curve nonce len %zu)",
+ c_nonce_len, auth->curve->nonce_len);
+ goto fail;
+ }
+
+ /* Build Connection Status object */
+ /* TODO: Get appropriate result value */
+ /* TODO: ssid64 and channelList */
+ conn_status = dpp_build_conn_status(DPP_STATUS_NO_AP, NULL, 0, NULL);
+ if (!conn_status)
+ goto fail;
+
+ if (dpp_reconfig_build_resp(auth, own_connector, conn_status) < 0)
+ goto fail;
+
+out:
+ os_free(info.payload);
+ os_free(own_conn);
+ json_free(root);
+ json_free(own_root);
+ wpabuf_free(conn_status);
+ return auth;
+fail:
+ dpp_auth_deinit(auth);
+ auth = NULL;
+ goto out;
+}
+
+
+struct wpabuf *
+dpp_reconfig_build_conf(struct dpp_authentication *auth)
+{
+ struct wpabuf *msg = NULL, *clear;
+ u8 *attr_start, *attr_end;
+ size_t clear_len, attr_len, len[2];
+ const u8 *addr[2];
+ u8 *wrapped;
+ u8 flags;
+
+ /* Build DPP Reconfig Authentication Confirm frame attributes */
+ clear_len = 4 + 1 + 4 + 1 + 2 * (4 + auth->curve->nonce_len) +
+ 4 + 1;
+ clear = wpabuf_alloc(clear_len);
+ if (!clear)
+ goto fail;
+
+ /* Transaction ID */
+ wpabuf_put_le16(clear, DPP_ATTR_TRANSACTION_ID);
+ wpabuf_put_le16(clear, 1);
+ wpabuf_put_u8(clear, auth->transaction_id);
+
+ /* Protocol Version */
+ wpabuf_put_le16(clear, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(clear, 1);
+ wpabuf_put_u8(clear, auth->peer_version);
+
+ /* C-nonce (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_CONFIGURATOR_NONCE);
+ wpabuf_put_le16(clear, auth->curve->nonce_len);
+ wpabuf_put_data(clear, auth->c_nonce, auth->curve->nonce_len);
+
+ /* E-nonce (wrapped) */
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, auth->curve->nonce_len);
+ wpabuf_put_data(clear, auth->e_nonce, auth->curve->nonce_len);
+
+ /* Reconfig-Flags (wrapped) */
+ flags = DPP_CONFIG_REPLACEKEY;
+ wpabuf_put_le16(clear, DPP_ATTR_RECONFIG_FLAGS);
+ wpabuf_put_le16(clear, 1);
+ wpabuf_put_u8(clear, flags);
+
+ attr_len = 4 + wpabuf_len(clear) + AES_BLOCK_SIZE;
+ attr_len += 4 + 1;
+ msg = dpp_alloc_msg(DPP_PA_RECONFIG_AUTH_CONF, attr_len);
+ if (!msg)
+ goto fail;
+
+ attr_start = wpabuf_put(msg, 0);
+
+ /* DPP Status */
+ dpp_build_attr_status(msg, DPP_STATUS_OK);
+
+ attr_end = wpabuf_put(msg, 0);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data */
+ addr[1] = attr_start;
+ len[1] = attr_end - attr_start;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ /* Wrapped Data */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+
+ wpa_hexdump_buf(MSG_DEBUG,
+ "DPP: Reconfig Authentication Confirm frame attributes",
+ msg);
+
+out:
+ wpabuf_free(clear);
+ return msg;
+fail:
+ wpabuf_free(msg);
+ msg = NULL;
+ goto out;
+}
+
+
+struct wpabuf *
+dpp_reconfig_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *trans_id, *version, *r_connector, *r_proto, *wrapped_data,
+ *c_nonce, *e_nonce, *conn_status;
+ u16 trans_id_len, version_len, r_connector_len, r_proto_len,
+ wrapped_data_len, c_nonce_len, e_nonce_len, conn_status_len;
+ struct wpabuf *conf = NULL;
+ char *signed_connector = NULL;
+ struct dpp_signed_connector_info info;
+ enum dpp_status_error res;
+ struct json_token *root = NULL, *token, *conn_status_json = NULL;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+
+ os_memset(&info, 0, sizeof(info));
+
+ if (!auth->reconfig || !auth->configurator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+ attr_len = wrapped_data - 4 - attr_start;
+
+ trans_id = dpp_get_attr(attr_start, attr_len, DPP_ATTR_TRANSACTION_ID,
+ &trans_id_len);
+ if (!trans_id || trans_id_len != 1) {
+ dpp_auth_fail(auth, "Peer did not include Transaction ID");
+ goto fail;
+ }
+ if (trans_id[0] != auth->transaction_id) {
+ dpp_auth_fail(auth, "Transaction ID mismatch");
+ goto fail;
+ }
+
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (!version || version_len < 1 || version[0] < 2) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Protocol Version attribute");
+ goto fail;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+
+ r_connector = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CONNECTOR,
+ &r_connector_len);
+ if (!r_connector) {
+ dpp_auth_fail(auth, " Missing R-Connector attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: R-Connector",
+ r_connector, r_connector_len);
+
+ e_nonce = dpp_get_attr(attr_start, attr_len,
+ DPP_ATTR_ENROLLEE_NONCE, &e_nonce_len);
+ if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth, "Missing or invalid E-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len);
+ os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
+
+ r_proto = dpp_get_attr(attr_start, attr_len, DPP_ATTR_R_PROTOCOL_KEY,
+ &r_proto_len);
+ if (!r_proto) {
+ dpp_auth_fail(auth,
+ "Missing required Responder Protocol Key attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Protocol Key",
+ r_proto, r_proto_len);
+
+ signed_connector = os_malloc(r_connector_len + 1);
+ if (!signed_connector)
+ goto fail;
+ os_memcpy(signed_connector, r_connector, r_connector_len);
+ signed_connector[r_connector_len] = '\0';
+
+ res = dpp_process_signed_connector(&info, auth->conf->csign,
+ signed_connector);
+ if (res != DPP_STATUS_OK) {
+ dpp_auth_fail(auth, "Invalid R-Connector");
+ goto fail;
+ }
+
+ root = json_parse((const char *) info.payload, info.payload_len);
+ if (!root) {
+ dpp_auth_fail(auth, "Invalid Connector payload");
+ goto fail;
+ }
+
+ /* Do not check netAccessKey expiration for reconfiguration to allow
+ * expired Connector to be updated. */
+
+ token = json_get_member(root, "netAccessKey");
+ if (!token || token->type != JSON_OBJECT) {
+ dpp_auth_fail(auth, "No netAccessKey object found");
+ goto fail;
+ }
+
+ if (dpp_reconfig_derive_ke_initiator(auth, r_proto, r_proto_len,
+ token) < 0)
+ goto fail;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ c_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+ if (!c_nonce || c_nonce_len != auth->curve->nonce_len ||
+ os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Missing or invalid C-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
+
+ conn_status = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_CONN_STATUS, &conn_status_len);
+ if (!conn_status) {
+ dpp_auth_fail(auth, "Missing Connection Status attribute");
+ goto fail;
+ }
+ wpa_hexdump_ascii(MSG_DEBUG, "DPP: connStatus",
+ conn_status, conn_status_len);
+
+ conn_status_json = json_parse((const char *) conn_status,
+ conn_status_len);
+ if (!conn_status_json) {
+ dpp_auth_fail(auth, "Could not parse connStatus");
+ goto fail;
+ }
+ /* TODO: use connStatus information */
+
+ conf = dpp_reconfig_build_conf(auth);
+ if (conf)
+ auth->reconfig_success = true;
+
+out:
+ json_free(root);
+ json_free(conn_status_json);
+ bin_clear_free(unwrapped, unwrapped_len);
+ os_free(info.payload);
+ os_free(signed_connector);
+ return conf;
+fail:
+ wpabuf_free(conf);
+ conf = NULL;
+ goto out;
+}
+
+
+int dpp_reconfig_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *trans_id, *version, *wrapped_data, *c_nonce, *e_nonce,
+ *reconfig_flags, *status;
+ u16 trans_id_len, version_len, wrapped_data_len, c_nonce_len,
+ e_nonce_len, reconfig_flags_len, status_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ int res = -1;
+ u8 flags;
+
+ if (!auth->reconfig || auth->configurator)
+ goto fail;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Wrapped Data",
+ wrapped_data, wrapped_data_len);
+ attr_len = wrapped_data - 4 - attr_start;
+
+ status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ if (status[0] != DPP_STATUS_OK) {
+ dpp_auth_fail(auth,
+ "Reconfiguration did not complete successfully");
+ goto fail;
+ }
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ trans_id = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_TRANSACTION_ID, &trans_id_len);
+ if (!trans_id || trans_id_len != 1 ||
+ trans_id[0] != auth->transaction_id) {
+ dpp_auth_fail(auth,
+ "Peer did not include valid Transaction ID");
+ goto fail;
+ }
+
+ version = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_PROTOCOL_VERSION, &version_len);
+ if (!version || version_len < 1 || version[0] != DPP_VERSION) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Protocol Version attribute");
+ goto fail;
+ }
+
+ c_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_CONFIGURATOR_NONCE, &c_nonce_len);
+ if (!c_nonce || c_nonce_len != auth->curve->nonce_len ||
+ os_memcmp(c_nonce, auth->c_nonce, c_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Missing or invalid C-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: C-nonce", c_nonce, c_nonce_len);
+
+ e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_ENROLLEE_NONCE, &e_nonce_len);
+ if (!e_nonce || e_nonce_len != auth->curve->nonce_len ||
+ os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Missing or invalid E-nonce");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: E-nonce", e_nonce, e_nonce_len);
+
+ reconfig_flags = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_RECONFIG_FLAGS,
+ &reconfig_flags_len);
+ if (!reconfig_flags || reconfig_flags_len < 1) {
+ dpp_auth_fail(auth, "Missing or invalid Reconfig-Flags");
+ goto fail;
+ }
+ flags = reconfig_flags[0] & BIT(0);
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Flags connectorKey=%u", flags);
+ auth->reconfig_connector_key = flags;
+
+ auth->reconfig_success = true;
+ res = 0;
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return res;
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/contrib/wpa/src/common/dpp_tcp.c b/contrib/wpa/src/common/dpp_tcp.c
new file mode 100644
index 000000000000..609c243a6856
--- /dev/null
+++ b/contrib/wpa/src/common/dpp_tcp.c
@@ -0,0 +1,1794 @@
+/*
+ * DPP over TCP
+ * Copyright (c) 2019-2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "utils/ip_addr.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "dpp.h"
+#include "dpp_i.h"
+
+#ifdef CONFIG_DPP2
+
+struct dpp_connection {
+ struct dl_list list;
+ struct dpp_controller *ctrl;
+ struct dpp_relay_controller *relay;
+ struct dpp_global *global;
+ struct dpp_authentication *auth;
+ void *msg_ctx;
+ void *cb_ctx;
+ int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
+ int sock;
+ u8 mac_addr[ETH_ALEN];
+ unsigned int freq;
+ u8 msg_len[4];
+ size_t msg_len_octets;
+ struct wpabuf *msg;
+ struct wpabuf *msg_out;
+ size_t msg_out_pos;
+ unsigned int read_eloop:1;
+ unsigned int write_eloop:1;
+ unsigned int on_tcp_tx_complete_gas_done:1;
+ unsigned int on_tcp_tx_complete_remove:1;
+ unsigned int on_tcp_tx_complete_auth_ok:1;
+ unsigned int gas_comeback_in_progress:1;
+ u8 gas_dialog_token;
+ char *name;
+ enum dpp_netrole netrole;
+};
+
+/* Remote Controller */
+struct dpp_relay_controller {
+ struct dl_list list;
+ struct dpp_global *global;
+ u8 pkhash[SHA256_MAC_LEN];
+ struct hostapd_ip_addr ipaddr;
+ void *msg_ctx;
+ void *cb_ctx;
+ void (*tx)(void *ctx, const u8 *addr, unsigned int freq, const u8 *msg,
+ size_t len);
+ void (*gas_resp_tx)(void *ctx, const u8 *addr, u8 dialog_token,
+ int prot, struct wpabuf *buf);
+ struct dl_list conn; /* struct dpp_connection */
+};
+
+/* Local Controller */
+struct dpp_controller {
+ struct dpp_global *global;
+ u8 allowed_roles;
+ int qr_mutual;
+ int sock;
+ struct dl_list conn; /* struct dpp_connection */
+ char *configurator_params;
+ enum dpp_netrole netrole;
+ void *msg_ctx;
+ void *cb_ctx;
+ int (*process_conf_obj)(void *ctx, struct dpp_authentication *auth);
+};
+
+static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx);
+static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx);
+static void dpp_controller_auth_success(struct dpp_connection *conn,
+ int initiator);
+static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx);
+static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx);
+
+
+static void dpp_connection_free(struct dpp_connection *conn)
+{
+ if (conn->sock >= 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Close Controller socket %d",
+ conn->sock);
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_READ);
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+ close(conn->sock);
+ }
+ eloop_cancel_timeout(dpp_controller_conn_status_result_wait_timeout,
+ conn, NULL);
+ eloop_cancel_timeout(dpp_tcp_build_csr, conn, NULL);
+ eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
+ wpabuf_free(conn->msg);
+ wpabuf_free(conn->msg_out);
+ dpp_auth_deinit(conn->auth);
+ os_free(conn->name);
+ os_free(conn);
+}
+
+
+static void dpp_connection_remove(struct dpp_connection *conn)
+{
+ dl_list_del(&conn->list);
+ dpp_connection_free(conn);
+}
+
+
+int dpp_relay_add_controller(struct dpp_global *dpp,
+ struct dpp_relay_config *config)
+{
+ struct dpp_relay_controller *ctrl;
+
+ if (!dpp)
+ return -1;
+
+ ctrl = os_zalloc(sizeof(*ctrl));
+ if (!ctrl)
+ return -1;
+ dl_list_init(&ctrl->conn);
+ ctrl->global = dpp;
+ os_memcpy(&ctrl->ipaddr, config->ipaddr, sizeof(*config->ipaddr));
+ os_memcpy(ctrl->pkhash, config->pkhash, SHA256_MAC_LEN);
+ ctrl->msg_ctx = config->msg_ctx;
+ ctrl->cb_ctx = config->cb_ctx;
+ ctrl->tx = config->tx;
+ ctrl->gas_resp_tx = config->gas_resp_tx;
+ dl_list_add(&dpp->controllers, &ctrl->list);
+ return 0;
+}
+
+
+static struct dpp_relay_controller *
+dpp_relay_controller_get(struct dpp_global *dpp, const u8 *pkhash)
+{
+ struct dpp_relay_controller *ctrl;
+
+ if (!dpp)
+ return NULL;
+
+ dl_list_for_each(ctrl, &dpp->controllers, struct dpp_relay_controller,
+ list) {
+ if (os_memcmp(pkhash, ctrl->pkhash, SHA256_MAC_LEN) == 0)
+ return ctrl;
+ }
+
+ return NULL;
+}
+
+
+static void dpp_controller_gas_done(struct dpp_connection *conn)
+{
+ struct dpp_authentication *auth = conn->auth;
+
+ if (auth->waiting_csr) {
+ wpa_printf(MSG_DEBUG, "DPP: Waiting for CSR");
+ conn->on_tcp_tx_complete_gas_done = 0;
+ return;
+ }
+
+ if (auth->peer_version >= 2 &&
+ auth->conf_resp_status == DPP_STATUS_OK) {
+ wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+ auth->waiting_conf_result = 1;
+ return;
+ }
+
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ dpp_connection_remove(conn);
+}
+
+
+static int dpp_tcp_send(struct dpp_connection *conn)
+{
+ int res;
+
+ if (!conn->msg_out) {
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+ conn->write_eloop = 0;
+ return -1;
+ }
+ res = send(conn->sock,
+ wpabuf_head_u8(conn->msg_out) + conn->msg_out_pos,
+ wpabuf_len(conn->msg_out) - conn->msg_out_pos, 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to send buffer: %s",
+ strerror(errno));
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ conn->msg_out_pos += res;
+ if (wpabuf_len(conn->msg_out) > conn->msg_out_pos) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: %u/%u bytes of message sent to Controller",
+ (unsigned int) conn->msg_out_pos,
+ (unsigned int) wpabuf_len(conn->msg_out));
+ if (!conn->write_eloop &&
+ eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready, conn, NULL) == 0)
+ conn->write_eloop = 1;
+ return 1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Full message sent over TCP");
+ wpabuf_free(conn->msg_out);
+ conn->msg_out = NULL;
+ conn->msg_out_pos = 0;
+ eloop_unregister_sock(conn->sock, EVENT_TYPE_WRITE);
+ conn->write_eloop = 0;
+ if (!conn->read_eloop &&
+ eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+ dpp_controller_rx, conn, NULL) == 0)
+ conn->read_eloop = 1;
+ if (conn->on_tcp_tx_complete_remove) {
+ dpp_connection_remove(conn);
+ } else if (conn->auth && (conn->ctrl || conn->auth->configurator) &&
+ conn->on_tcp_tx_complete_gas_done) {
+ dpp_controller_gas_done(conn);
+ } else if (conn->on_tcp_tx_complete_auth_ok) {
+ conn->on_tcp_tx_complete_auth_ok = 0;
+ dpp_controller_auth_success(conn, 1);
+ }
+
+ return 0;
+}
+
+
+static int dpp_tcp_send_msg(struct dpp_connection *conn,
+ const struct wpabuf *msg)
+{
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = wpabuf_alloc(4 + wpabuf_len(msg) - 1);
+ if (!conn->msg_out)
+ return -1;
+ wpabuf_put_be32(conn->msg_out, wpabuf_len(msg) - 1);
+ wpabuf_put_data(conn->msg_out, wpabuf_head_u8(msg) + 1,
+ wpabuf_len(msg) - 1);
+
+ if (dpp_tcp_send(conn) == 1) {
+ if (!conn->write_eloop) {
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready,
+ conn, NULL) < 0)
+ return -1;
+ conn->write_eloop = 1;
+ }
+ }
+
+ return 0;
+}
+
+
+static void dpp_controller_start_gas_client(struct dpp_connection *conn)
+{
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *buf;
+ const char *dpp_name;
+
+ dpp_name = conn->name ? conn->name : "Test";
+ buf = dpp_build_conf_req_helper(auth, dpp_name, conn->netrole, NULL,
+ NULL);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No configuration request data available");
+ return;
+ }
+
+ dpp_tcp_send_msg(conn, buf);
+ wpabuf_free(buf);
+}
+
+
+static void dpp_controller_auth_success(struct dpp_connection *conn,
+ int initiator)
+{
+ struct dpp_authentication *auth = conn->auth;
+
+ if (!auth)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication succeeded");
+ wpa_msg(conn->msg_ctx, MSG_INFO,
+ DPP_EVENT_AUTH_SUCCESS "init=%d", initiator);
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_STOP_AT_AUTH_CONF) {
+ wpa_printf(MSG_INFO,
+ "DPP: TESTING - stop at Authentication Confirm");
+ if (auth->configurator) {
+ /* Prevent GAS response */
+ auth->auth_success = 0;
+ }
+ return;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (!auth->configurator)
+ dpp_controller_start_gas_client(conn);
+}
+
+
+static void dpp_conn_tx_ready(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "DPP: TCP socket %d ready for TX", sock);
+ dpp_tcp_send(conn);
+}
+
+
+static int dpp_ipaddr_to_sockaddr(struct sockaddr *addr, socklen_t *addrlen,
+ const struct hostapd_ip_addr *ipaddr,
+ int port)
+{
+ struct sockaddr_in *dst;
+#ifdef CONFIG_IPV6
+ struct sockaddr_in6 *dst6;
+#endif /* CONFIG_IPV6 */
+
+ switch (ipaddr->af) {
+ case AF_INET:
+ dst = (struct sockaddr_in *) addr;
+ os_memset(dst, 0, sizeof(*dst));
+ dst->sin_family = AF_INET;
+ dst->sin_addr.s_addr = ipaddr->u.v4.s_addr;
+ dst->sin_port = htons(port);
+ *addrlen = sizeof(*dst);
+ break;
+#ifdef CONFIG_IPV6
+ case AF_INET6:
+ dst6 = (struct sockaddr_in6 *) addr;
+ os_memset(dst6, 0, sizeof(*dst6));
+ dst6->sin6_family = AF_INET6;
+ os_memcpy(&dst6->sin6_addr, &ipaddr->u.v6,
+ sizeof(struct in6_addr));
+ dst6->sin6_port = htons(port);
+ *addrlen = sizeof(*dst6);
+ break;
+#endif /* CONFIG_IPV6 */
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static struct dpp_connection *
+dpp_relay_new_conn(struct dpp_relay_controller *ctrl, const u8 *src,
+ unsigned int freq)
+{
+ struct dpp_connection *conn;
+ struct sockaddr_storage addr;
+ socklen_t addrlen;
+ char txt[100];
+
+ if (dl_list_len(&ctrl->conn) >= 15) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Too many ongoing Relay connections to the Controller - cannot start a new one");
+ return NULL;
+ }
+
+ if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &addr, &addrlen,
+ &ctrl->ipaddr, DPP_TCP_PORT) < 0)
+ return NULL;
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn)
+ return NULL;
+
+ conn->global = ctrl->global;
+ conn->relay = ctrl;
+ conn->msg_ctx = ctrl->msg_ctx;
+ conn->cb_ctx = ctrl->global->cb_ctx;
+ os_memcpy(conn->mac_addr, src, ETH_ALEN);
+ conn->freq = freq;
+
+ conn->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (conn->sock < 0)
+ goto fail;
+ wpa_printf(MSG_DEBUG, "DPP: TCP relay socket %d connection to %s",
+ conn->sock, hostapd_ip_txt(&ctrl->ipaddr, txt, sizeof(txt)));
+
+ if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (connect(conn->sock, (struct sockaddr *) &addr, addrlen) < 0) {
+ if (errno != EINPROGRESS) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /*
+ * Continue connecting in the background; eloop will call us
+ * once the connection is ready (or failed).
+ */
+ }
+
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready, conn, NULL) < 0)
+ goto fail;
+ conn->write_eloop = 1;
+
+ /* TODO: eloop timeout to clear a connection if it does not complete
+ * properly */
+
+ dl_list_add(&ctrl->conn, &conn->list);
+ return conn;
+fail:
+ dpp_connection_free(conn);
+ return NULL;
+}
+
+
+static struct wpabuf * dpp_tcp_encaps(const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct wpabuf *msg;
+
+ msg = wpabuf_alloc(4 + 1 + DPP_HDR_LEN + len);
+ if (!msg)
+ return NULL;
+ wpabuf_put_be32(msg, 1 + DPP_HDR_LEN + len);
+ wpabuf_put_u8(msg, WLAN_PA_VENDOR_SPECIFIC);
+ wpabuf_put_data(msg, hdr, DPP_HDR_LEN);
+ wpabuf_put_data(msg, buf, len);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+ return msg;
+}
+
+
+static int dpp_relay_tx(struct dpp_connection *conn, const u8 *hdr,
+ const u8 *buf, size_t len)
+{
+ u8 type = hdr[DPP_HDR_LEN - 1];
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Continue already established Relay/Controller connection for this session");
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
+ if (!conn->msg_out) {
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ /* TODO: for proto ver 1, need to do remove connection based on GAS Resp
+ * TX status */
+ if (type == DPP_PA_CONFIGURATION_RESULT)
+ conn->on_tcp_tx_complete_remove = 1;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+int dpp_relay_rx_action(struct dpp_global *dpp, const u8 *src, const u8 *hdr,
+ const u8 *buf, size_t len, unsigned int freq,
+ const u8 *i_bootstrap, const u8 *r_bootstrap)
+{
+ struct dpp_relay_controller *ctrl;
+ struct dpp_connection *conn;
+ u8 type = hdr[DPP_HDR_LEN - 1];
+
+ /* Check if there is an already started session for this peer and if so,
+ * continue that session (send this over TCP) and return 0.
+ */
+ if (type != DPP_PA_PEER_DISCOVERY_REQ &&
+ type != DPP_PA_PEER_DISCOVERY_RESP &&
+ type != DPP_PA_PRESENCE_ANNOUNCEMENT &&
+ type != DPP_PA_RECONFIG_ANNOUNCEMENT) {
+ dl_list_for_each(ctrl, &dpp->controllers,
+ struct dpp_relay_controller, list) {
+ dl_list_for_each(conn, &ctrl->conn,
+ struct dpp_connection, list) {
+ if (os_memcmp(src, conn->mac_addr,
+ ETH_ALEN) == 0)
+ return dpp_relay_tx(conn, hdr, buf, len);
+ }
+ }
+ }
+
+ if (type == DPP_PA_PRESENCE_ANNOUNCEMENT ||
+ type == DPP_PA_RECONFIG_ANNOUNCEMENT) {
+ /* TODO: Could send this to all configured Controllers. For now,
+ * only the first Controller is supported. */
+ ctrl = dl_list_first(&dpp->controllers,
+ struct dpp_relay_controller, list);
+ } else {
+ if (!r_bootstrap)
+ return -1;
+ ctrl = dpp_relay_controller_get(dpp, r_bootstrap);
+ }
+ if (!ctrl)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Authentication Request for a configured Controller");
+ conn = dpp_relay_new_conn(ctrl, src, freq);
+ if (!conn)
+ return -1;
+
+ conn->msg_out = dpp_tcp_encaps(hdr, buf, len);
+ if (!conn->msg_out) {
+ dpp_connection_remove(conn);
+ return -1;
+ }
+ /* Message will be sent in dpp_conn_tx_ready() */
+
+ return 0;
+}
+
+
+int dpp_relay_rx_gas_req(struct dpp_global *dpp, const u8 *src, const u8 *data,
+ size_t data_len)
+{
+ struct dpp_relay_controller *ctrl;
+ struct dpp_connection *conn, *found = NULL;
+ struct wpabuf *msg;
+
+ /* Check if there is a successfully completed authentication for this
+ * and if so, continue that session (send this over TCP) and return 0.
+ */
+ dl_list_for_each(ctrl, &dpp->controllers,
+ struct dpp_relay_controller, list) {
+ if (found)
+ break;
+ dl_list_for_each(conn, &ctrl->conn,
+ struct dpp_connection, list) {
+ if (os_memcmp(src, conn->mac_addr,
+ ETH_ALEN) == 0) {
+ found = conn;
+ break;
+ }
+ }
+ }
+
+ if (!found)
+ return -1;
+
+ msg = wpabuf_alloc(4 + 1 + data_len);
+ if (!msg)
+ return -1;
+ wpabuf_put_be32(msg, 1 + data_len);
+ wpabuf_put_u8(msg, WLAN_PA_GAS_INITIAL_REQ);
+ wpabuf_put_data(msg, data, data_len);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = msg;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+static void dpp_controller_free(struct dpp_controller *ctrl)
+{
+ struct dpp_connection *conn, *tmp;
+
+ if (!ctrl)
+ return;
+
+ dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
+ list)
+ dpp_connection_remove(conn);
+
+ if (ctrl->sock >= 0) {
+ close(ctrl->sock);
+ eloop_unregister_sock(ctrl->sock, EVENT_TYPE_READ);
+ }
+ os_free(ctrl->configurator_params);
+ os_free(ctrl);
+}
+
+
+static int dpp_controller_rx_auth_req(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ const u8 *r_bootstrap, *i_bootstrap;
+ u16 r_bootstrap_len, i_bootstrap_len;
+ struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
+
+ if (!conn->ctrl)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Request");
+
+ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_printf(MSG_INFO,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+
+ i_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_I_BOOTSTRAP_KEY_HASH,
+ &i_bootstrap_len);
+ if (!i_bootstrap || i_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_printf(MSG_INFO,
+ "Missing or invalid required Initiator Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Initiator Bootstrapping Key Hash",
+ i_bootstrap, i_bootstrap_len);
+
+ /* Try to find own and peer bootstrapping key matches based on the
+ * received hash values */
+ dpp_bootstrap_find_pair(conn->ctrl->global, i_bootstrap, r_bootstrap,
+ &own_bi, &peer_bi);
+ if (!own_bi) {
+ wpa_printf(MSG_INFO,
+ "No matching own bootstrapping key found - ignore message");
+ return -1;
+ }
+
+ if (conn->auth) {
+ wpa_printf(MSG_INFO,
+ "Already in DPP authentication exchange - ignore new one");
+ return 0;
+ }
+
+ conn->auth = dpp_auth_req_rx(conn->ctrl->global, conn->msg_ctx,
+ conn->ctrl->allowed_roles,
+ conn->ctrl->qr_mutual,
+ peer_bi, own_bi, -1, hdr, buf, len);
+ if (!conn->auth) {
+ wpa_printf(MSG_DEBUG, "DPP: No response generated");
+ return -1;
+ }
+
+ if (dpp_set_configurator(conn->auth,
+ conn->ctrl->configurator_params) < 0) {
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ return dpp_tcp_send_msg(conn, conn->auth->resp_msg);
+}
+
+
+static int dpp_controller_rx_auth_resp(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *msg;
+ int res;
+
+ if (!auth)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Response");
+
+ msg = dpp_auth_resp_rx(auth, hdr, buf, len);
+ if (!msg) {
+ if (auth->auth_resp_status == DPP_STATUS_RESPONSE_PENDING) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Start wait for full response");
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: No confirm generated");
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ conn->on_tcp_tx_complete_auth_ok = 1;
+ res = dpp_tcp_send_msg(conn, msg);
+ wpabuf_free(msg);
+ return res;
+}
+
+
+static int dpp_controller_rx_auth_conf(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+
+ wpa_printf(MSG_DEBUG, "DPP: Authentication Confirmation");
+
+ if (!auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Authentication in progress - drop");
+ return -1;
+ }
+
+ if (dpp_auth_conf_rx(auth, hdr, buf, len) < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Authentication failed");
+ return -1;
+ }
+
+ dpp_controller_auth_success(conn, 0);
+ return 0;
+}
+
+
+void dpp_controller_conn_status_result_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+
+ if (!conn->auth->waiting_conf_result)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Timeout while waiting for Connection Status Result");
+ wpa_msg(conn->msg_ctx, MSG_INFO,
+ DPP_EVENT_CONN_STATUS_RESULT "timeout");
+ dpp_connection_remove(conn);
+}
+
+
+static int dpp_controller_rx_conf_result(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ enum dpp_status_error status;
+ void *msg_ctx = conn->msg_ctx;
+
+ if (!conn->ctrl && (!auth || !auth->configurator))
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Configuration Result");
+
+ if (!auth || !auth->waiting_conf_result) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Configuration waiting for result - drop");
+ return -1;
+ }
+
+ status = dpp_conf_result_rx(auth, hdr, buf, len);
+ if (status == DPP_STATUS_OK && auth->send_conn_status) {
+ wpa_msg(msg_ctx, MSG_INFO,
+ DPP_EVENT_CONF_SENT "wait_conn_status=1");
+ wpa_printf(MSG_DEBUG, "DPP: Wait for Connection Status Result");
+ eloop_cancel_timeout(
+ dpp_controller_conn_status_result_wait_timeout,
+ conn, NULL);
+ eloop_register_timeout(
+ 16, 0, dpp_controller_conn_status_result_wait_timeout,
+ conn, NULL);
+ return 0;
+ }
+ if (status == DPP_STATUS_OK)
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ else
+ wpa_msg(msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ return -1; /* to remove the completed connection */
+}
+
+
+static int dpp_controller_rx_conn_status_result(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ enum dpp_status_error status;
+ u8 ssid[SSID_MAX_LEN];
+ size_t ssid_len = 0;
+ char *channel_list = NULL;
+
+ if (!conn->ctrl)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Connection Status Result");
+
+ if (!auth || !auth->waiting_conn_status_result) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Configuration waiting for connection status result - drop");
+ return -1;
+ }
+
+ status = dpp_conn_status_result_rx(auth, hdr, buf, len,
+ ssid, &ssid_len, &channel_list);
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_CONN_STATUS_RESULT
+ "result=%d ssid=%s channel_list=%s",
+ status, wpa_ssid_txt(ssid, ssid_len),
+ channel_list ? channel_list : "N/A");
+ os_free(channel_list);
+ return -1; /* to remove the completed connection */
+}
+
+
+static int dpp_controller_rx_presence_announcement(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ const u8 *r_bootstrap;
+ u16 r_bootstrap_len;
+ struct dpp_bootstrap_info *peer_bi;
+ struct dpp_authentication *auth;
+ struct dpp_global *dpp = conn->ctrl->global;
+
+ if (conn->auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Presence Announcement during ongoing Authentication");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Presence Announcement");
+
+ r_bootstrap = dpp_get_attr(buf, len, DPP_ATTR_R_BOOTSTRAP_KEY_HASH,
+ &r_bootstrap_len);
+ if (!r_bootstrap || r_bootstrap_len != SHA256_MAC_LEN) {
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Responder Bootstrapping Key Hash",
+ r_bootstrap, r_bootstrap_len);
+ peer_bi = dpp_bootstrap_find_chirp(dpp, r_bootstrap);
+ if (!peer_bi) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching bootstrapping information found");
+ return -1;
+ }
+
+ auth = dpp_auth_init(dpp, conn->msg_ctx, peer_bi, NULL,
+ DPP_CAPAB_CONFIGURATOR, -1, NULL, 0);
+ if (!auth)
+ return -1;
+ if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ dpp_connection_remove(conn);
+ return -1;
+ }
+
+ conn->auth = auth;
+ return dpp_tcp_send_msg(conn, conn->auth->req_msg);
+}
+
+
+static int dpp_controller_rx_reconfig_announcement(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ const u8 *csign_hash, *fcgroup, *a_nonce, *e_id;
+ u16 csign_hash_len, fcgroup_len, a_nonce_len, e_id_len;
+ struct dpp_configurator *conf;
+ struct dpp_global *dpp = conn->ctrl->global;
+ struct dpp_authentication *auth;
+ u16 group;
+
+ if (conn->auth) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Ignore Reconfig Announcement during ongoing Authentication");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Announcement");
+
+ csign_hash = dpp_get_attr(buf, len, DPP_ATTR_C_SIGN_KEY_HASH,
+ &csign_hash_len);
+ if (!csign_hash || csign_hash_len != SHA256_MAC_LEN) {
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Configurator C-sign key Hash attribute");
+ return -1;
+ }
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Configurator C-sign key Hash (kid)",
+ csign_hash, csign_hash_len);
+ conf = dpp_configurator_find_kid(dpp, csign_hash);
+ if (!conf) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No matching Configurator information found");
+ return -1;
+ }
+
+ fcgroup = dpp_get_attr(buf, len, DPP_ATTR_FINITE_CYCLIC_GROUP,
+ &fcgroup_len);
+ if (!fcgroup || fcgroup_len != 2) {
+ wpa_msg(conn->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
+ "Missing or invalid required Finite Cyclic Group attribute");
+ return -1;
+ }
+ group = WPA_GET_LE16(fcgroup);
+ wpa_printf(MSG_DEBUG, "DPP: Enrollee finite cyclic group: %u", group);
+
+ a_nonce = dpp_get_attr(buf, len, DPP_ATTR_A_NONCE, &a_nonce_len);
+ e_id = dpp_get_attr(buf, len, DPP_ATTR_E_PRIME_ID, &e_id_len);
+
+ auth = dpp_reconfig_init(dpp, conn->msg_ctx, conf, 0, group,
+ a_nonce, a_nonce_len, e_id, e_id_len);
+ if (!auth)
+ return -1;
+ if (dpp_set_configurator(auth, conn->ctrl->configurator_params) < 0) {
+ dpp_auth_deinit(auth);
+ return -1;
+ }
+
+ conn->auth = auth;
+ return dpp_tcp_send_msg(conn, auth->reconfig_req_msg);
+}
+
+
+static int dpp_controller_rx_reconfig_auth_resp(struct dpp_connection *conn,
+ const u8 *hdr, const u8 *buf,
+ size_t len)
+{
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *conf;
+ int res;
+
+ wpa_printf(MSG_DEBUG, "DPP: Reconfig Authentication Response");
+
+ if (!auth || !auth->reconfig || !auth->configurator) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Reconfig Authentication in progress - drop");
+ return -1;
+ }
+
+ conf = dpp_reconfig_auth_resp_rx(auth, hdr, buf, len);
+ if (!conf)
+ return -1;
+
+ res = dpp_tcp_send_msg(conn, conf);
+ wpabuf_free(conf);
+ return res;
+}
+
+
+static int dpp_controller_rx_action(struct dpp_connection *conn, const u8 *msg,
+ size_t len)
+{
+ const u8 *pos, *end;
+ u8 type;
+
+ wpa_printf(MSG_DEBUG, "DPP: Received DPP Action frame over TCP");
+ pos = msg;
+ end = msg + len;
+
+ if (end - pos < DPP_HDR_LEN ||
+ WPA_GET_BE24(pos) != OUI_WFA ||
+ pos[3] != DPP_OUI_TYPE) {
+ wpa_printf(MSG_DEBUG, "DPP: Unrecognized header");
+ return -1;
+ }
+
+ if (pos[4] != 1) {
+ wpa_printf(MSG_DEBUG, "DPP: Unsupported Crypto Suite %u",
+ pos[4]);
+ return -1;
+ }
+ type = pos[5];
+ wpa_printf(MSG_DEBUG, "DPP: Received message type %u", type);
+ pos += DPP_HDR_LEN;
+
+ wpa_hexdump(MSG_MSGDUMP, "DPP: Received message attributes",
+ pos, end - pos);
+ if (dpp_check_attrs(pos, end - pos) < 0)
+ return -1;
+
+ if (conn->relay) {
+ wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
+ conn->relay->tx(conn->relay->cb_ctx, conn->mac_addr,
+ conn->freq, msg, len);
+ return 0;
+ }
+
+ switch (type) {
+ case DPP_PA_AUTHENTICATION_REQ:
+ return dpp_controller_rx_auth_req(conn, msg, pos, end - pos);
+ case DPP_PA_AUTHENTICATION_RESP:
+ return dpp_controller_rx_auth_resp(conn, msg, pos, end - pos);
+ case DPP_PA_AUTHENTICATION_CONF:
+ return dpp_controller_rx_auth_conf(conn, msg, pos, end - pos);
+ case DPP_PA_CONFIGURATION_RESULT:
+ return dpp_controller_rx_conf_result(conn, msg, pos, end - pos);
+ case DPP_PA_CONNECTION_STATUS_RESULT:
+ return dpp_controller_rx_conn_status_result(conn, msg, pos,
+ end - pos);
+ case DPP_PA_PRESENCE_ANNOUNCEMENT:
+ return dpp_controller_rx_presence_announcement(conn, msg, pos,
+ end - pos);
+ case DPP_PA_RECONFIG_ANNOUNCEMENT:
+ return dpp_controller_rx_reconfig_announcement(conn, msg, pos,
+ end - pos);
+ case DPP_PA_RECONFIG_AUTH_RESP:
+ return dpp_controller_rx_reconfig_auth_resp(conn, msg, pos,
+ end - pos);
+ default:
+ /* TODO: missing messages types */
+ wpa_printf(MSG_DEBUG,
+ "DPP: Unsupported frame subtype %d", type);
+ return -1;
+ }
+}
+
+
+static int dpp_tcp_send_comeback_delay(struct dpp_connection *conn, u8 action)
+{
+ struct wpabuf *buf;
+ size_t len = 18;
+
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ len++;
+
+ buf = wpabuf_alloc(4 + len);
+ if (!buf)
+ return -1;
+
+ wpabuf_put_be32(buf, len);
+
+ wpabuf_put_u8(buf, action);
+ wpabuf_put_u8(buf, conn->gas_dialog_token);
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 500); /* GAS Comeback Delay */
+
+ dpp_write_adv_proto(buf);
+ wpabuf_put_le16(buf, 0); /* Query Response Length */
+
+ /* Send Config Response over TCP */
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = buf;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+static int dpp_tcp_send_gas_resp(struct dpp_connection *conn, u8 action,
+ struct wpabuf *resp)
+{
+ struct wpabuf *buf;
+ size_t len;
+
+ if (!resp)
+ return -1;
+
+ len = 18 + wpabuf_len(resp);
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ len++;
+
+ buf = wpabuf_alloc(4 + len);
+ if (!buf) {
+ wpabuf_free(resp);
+ return -1;
+ }
+
+ wpabuf_put_be32(buf, len);
+
+ wpabuf_put_u8(buf, action);
+ wpabuf_put_u8(buf, conn->gas_dialog_token);
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+ if (action == WLAN_PA_GAS_COMEBACK_RESP)
+ wpabuf_put_u8(buf, 0);
+ wpabuf_put_le16(buf, 0); /* GAS Comeback Delay */
+
+ dpp_write_adv_proto(buf);
+ dpp_write_gas_query(buf, resp);
+ wpabuf_free(resp);
+
+ /* Send Config Response over TCP; GAS fragmentation is taken care of by
+ * the Relay */
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", buf);
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = buf;
+ conn->on_tcp_tx_complete_gas_done = 1;
+ dpp_tcp_send(conn);
+ return 0;
+}
+
+
+static int dpp_controller_rx_gas_req(struct dpp_connection *conn, const u8 *msg,
+ size_t len)
+{
+ const u8 *pos, *end, *next;
+ const u8 *adv_proto;
+ u16 slen;
+ struct wpabuf *resp;
+ struct dpp_authentication *auth = conn->auth;
+
+ if (len < 1 + 2)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received DPP Configuration Request over TCP");
+
+ if (!auth || (!conn->ctrl && !auth->configurator) ||
+ (!auth->auth_success && !auth->reconfig_success)) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return -1;
+ }
+
+ pos = msg;
+ end = msg + len;
+
+ conn->gas_dialog_token = *pos++;
+ adv_proto = pos++;
+ slen = *pos++;
+ if (*adv_proto != WLAN_EID_ADV_PROTO ||
+ slen > end - pos || slen < 2)
+ return -1;
+
+ next = pos + slen;
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
+ pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
+ pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
+ return -1;
+
+ pos = next;
+ /* Query Request */
+ if (end - pos < 2)
+ return -1;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (slen > end - pos)
+ return -1;
+
+ resp = dpp_conf_req_rx(auth, pos, slen);
+ if (!resp && auth->waiting_cert) {
+ wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
+ conn->gas_comeback_in_progress = 1;
+ return dpp_tcp_send_comeback_delay(conn,
+ WLAN_PA_GAS_INITIAL_RESP);
+ }
+
+ return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_INITIAL_RESP, resp);
+}
+
+
+static int dpp_controller_rx_gas_comeback_req(struct dpp_connection *conn,
+ const u8 *msg, size_t len)
+{
+ u8 dialog_token;
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *resp;
+
+ if (len < 1)
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received DPP Configuration Request over TCP (comeback)");
+
+ if (!auth || (!conn->ctrl && !auth->configurator) ||
+ (!auth->auth_success && !auth->reconfig_success) ||
+ !conn->gas_comeback_in_progress) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ return -1;
+ }
+
+ dialog_token = msg[0];
+ if (dialog_token != conn->gas_dialog_token) {
+ wpa_printf(MSG_DEBUG, "DPP: Dialog token mismatch (%u != %u)",
+ dialog_token, conn->gas_dialog_token);
+ return -1;
+ }
+
+ if (!auth->conf_resp_tcp) {
+ wpa_printf(MSG_DEBUG, "DPP: Certificate not yet ready");
+ return dpp_tcp_send_comeback_delay(conn,
+ WLAN_PA_GAS_COMEBACK_RESP);
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Configuration response is ready to be sent out");
+ resp = auth->conf_resp_tcp;
+ auth->conf_resp_tcp = NULL;
+ return dpp_tcp_send_gas_resp(conn, WLAN_PA_GAS_COMEBACK_RESP, resp);
+}
+
+
+static void dpp_tcp_build_csr(void *eloop_ctx, void *timeout_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+ struct dpp_authentication *auth = conn->auth;
+
+ if (!auth || !auth->csrattrs)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Build CSR");
+ wpabuf_free(auth->csr);
+ /* TODO: Additional information needed for CSR based on csrAttrs */
+ auth->csr = dpp_build_csr(auth, conn->name ? conn->name : "Test");
+ if (!auth->csr) {
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ dpp_controller_start_gas_client(conn);
+}
+
+
+static int dpp_tcp_rx_gas_resp(struct dpp_connection *conn, struct wpabuf *resp)
+{
+ struct dpp_authentication *auth = conn->auth;
+ int res;
+ struct wpabuf *msg;
+ enum dpp_status_error status;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Configuration Response for local stack from TCP");
+
+ if (auth)
+ res = dpp_conf_resp_rx(auth, resp);
+ else
+ res = -1;
+ wpabuf_free(resp);
+ if (res == -2) {
+ wpa_printf(MSG_DEBUG, "DPP: CSR needed");
+ eloop_register_timeout(0, 0, dpp_tcp_build_csr, conn, NULL);
+ return 0;
+ }
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: Configuration attempt failed");
+ return -1;
+ }
+
+ if (conn->process_conf_obj)
+ res = conn->process_conf_obj(conn->cb_ctx, auth);
+ else
+ res = 0;
+
+ if (auth->peer_version < 2 || auth->conf_resp_status != DPP_STATUS_OK)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+ status = res < 0 ? DPP_STATUS_CONFIG_REJECTED : DPP_STATUS_OK;
+ msg = dpp_build_conf_result(auth, status);
+ if (!msg)
+ return -1;
+
+ conn->on_tcp_tx_complete_remove = 1;
+ res = dpp_tcp_send_msg(conn, msg);
+ wpabuf_free(msg);
+
+ /* This exchange will be terminated in the TX status handler */
+
+ return res;
+}
+
+
+static void dpp_tcp_gas_query_comeback(void *eloop_ctx, void *timeout_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+ struct dpp_authentication *auth = conn->auth;
+ struct wpabuf *msg;
+
+ if (!auth)
+ return;
+
+ wpa_printf(MSG_DEBUG, "DPP: Send GAS Comeback Request");
+ msg = wpabuf_alloc(4 + 2);
+ if (!msg)
+ return;
+ wpabuf_put_be32(msg, 2);
+ wpabuf_put_u8(msg, WLAN_PA_GAS_COMEBACK_REQ);
+ wpabuf_put_u8(msg, conn->gas_dialog_token);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: Outgoing TCP message", msg);
+
+ wpabuf_free(conn->msg_out);
+ conn->msg_out_pos = 0;
+ conn->msg_out = msg;
+ dpp_tcp_send(conn);
+}
+
+
+static int dpp_rx_gas_resp(struct dpp_connection *conn, const u8 *msg,
+ size_t len, bool comeback)
+{
+ struct wpabuf *buf;
+ u8 dialog_token;
+ const u8 *pos, *end, *next, *adv_proto;
+ u16 status, slen, comeback_delay;
+
+ if (len < (size_t) (5 + 2 + (comeback ? 1 : 0)))
+ return -1;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received DPP Configuration Response over TCP");
+
+ pos = msg;
+ end = msg + len;
+
+ dialog_token = *pos++;
+ status = WPA_GET_LE16(pos);
+ if (status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "DPP: Unexpected Status Code %u", status);
+ return -1;
+ }
+ pos += 2;
+ if (comeback)
+ pos++; /* ignore Fragment ID */
+ comeback_delay = WPA_GET_LE16(pos);
+ pos += 2;
+
+ adv_proto = pos++;
+ slen = *pos++;
+ if (*adv_proto != WLAN_EID_ADV_PROTO ||
+ slen > end - pos || slen < 2)
+ return -1;
+
+ next = pos + slen;
+ pos++; /* skip QueryRespLenLimit and PAME-BI */
+
+ if (slen != 8 || *pos != WLAN_EID_VENDOR_SPECIFIC ||
+ pos[1] != 5 || WPA_GET_BE24(&pos[2]) != OUI_WFA ||
+ pos[5] != DPP_OUI_TYPE || pos[6] != 0x01)
+ return -1;
+
+ pos = next;
+ /* Query Response */
+ if (end - pos < 2)
+ return -1;
+ slen = WPA_GET_LE16(pos);
+ pos += 2;
+ if (slen > end - pos)
+ return -1;
+
+ if (comeback_delay) {
+ unsigned int secs, usecs;
+
+ conn->gas_dialog_token = dialog_token;
+ secs = (comeback_delay * 1024) / 1000000;
+ usecs = comeback_delay * 1024 - secs * 1000000;
+ wpa_printf(MSG_DEBUG, "DPP: Comeback delay: %u",
+ comeback_delay);
+ eloop_cancel_timeout(dpp_tcp_gas_query_comeback, conn, NULL);
+ eloop_register_timeout(secs, usecs, dpp_tcp_gas_query_comeback,
+ conn, NULL);
+ return 0;
+ }
+
+ buf = wpabuf_alloc(slen);
+ if (!buf)
+ return -1;
+ wpabuf_put_data(buf, pos, slen);
+
+ if (!conn->relay &&
+ (!conn->ctrl || (conn->ctrl->allowed_roles & DPP_CAPAB_ENROLLEE)))
+ return dpp_tcp_rx_gas_resp(conn, buf);
+
+ if (!conn->relay) {
+ wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
+ wpabuf_free(buf);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Relay - send over WLAN");
+ conn->relay->gas_resp_tx(conn->relay->cb_ctx, conn->mac_addr,
+ dialog_token, 0, buf);
+
+ return 0;
+}
+
+
+static void dpp_controller_rx(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct dpp_connection *conn = eloop_ctx;
+ int res;
+ const u8 *pos;
+
+ wpa_printf(MSG_DEBUG, "DPP: TCP data available for reading (sock %d)",
+ sd);
+
+ if (conn->msg_len_octets < 4) {
+ u32 msglen;
+
+ res = recv(sd, &conn->msg_len[conn->msg_len_octets],
+ 4 - conn->msg_len_octets, 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: recv failed: %s",
+ strerror(errno));
+ dpp_connection_remove(conn);
+ return;
+ }
+ if (res == 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No more data available over TCP");
+ dpp_connection_remove(conn);
+ return;
+ }
+ wpa_printf(MSG_DEBUG,
+ "DPP: Received %d/%d octet(s) of message length field",
+ res, (int) (4 - conn->msg_len_octets));
+ conn->msg_len_octets += res;
+
+ if (conn->msg_len_octets < 4) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Need %d more octets of message length field",
+ (int) (4 - conn->msg_len_octets));
+ return;
+ }
+
+ msglen = WPA_GET_BE32(conn->msg_len);
+ wpa_printf(MSG_DEBUG, "DPP: Message length: %u", msglen);
+ if (msglen > 65535) {
+ wpa_printf(MSG_INFO, "DPP: Unexpectedly long message");
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ wpabuf_free(conn->msg);
+ conn->msg = wpabuf_alloc(msglen);
+ }
+
+ if (!conn->msg) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No buffer available for receiving the message");
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "DPP: Need %u more octets of message payload",
+ (unsigned int) wpabuf_tailroom(conn->msg));
+
+ res = recv(sd, wpabuf_put(conn->msg, 0), wpabuf_tailroom(conn->msg), 0);
+ if (res < 0) {
+ wpa_printf(MSG_DEBUG, "DPP: recv failed: %s", strerror(errno));
+ dpp_connection_remove(conn);
+ return;
+ }
+ if (res == 0) {
+ wpa_printf(MSG_DEBUG, "DPP: No more data available over TCP");
+ dpp_connection_remove(conn);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Received %d octets", res);
+ wpabuf_put(conn->msg, res);
+
+ if (wpabuf_tailroom(conn->msg) > 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Need %u more octets of message payload",
+ (unsigned int) wpabuf_tailroom(conn->msg));
+ return;
+ }
+
+ conn->msg_len_octets = 0;
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Received TCP message", conn->msg);
+ if (wpabuf_len(conn->msg) < 1) {
+ dpp_connection_remove(conn);
+ return;
+ }
+
+ pos = wpabuf_head(conn->msg);
+ switch (*pos) {
+ case WLAN_PA_VENDOR_SPECIFIC:
+ if (dpp_controller_rx_action(conn, pos + 1,
+ wpabuf_len(conn->msg) - 1) < 0)
+ dpp_connection_remove(conn);
+ break;
+ case WLAN_PA_GAS_INITIAL_REQ:
+ if (dpp_controller_rx_gas_req(conn, pos + 1,
+ wpabuf_len(conn->msg) - 1) < 0)
+ dpp_connection_remove(conn);
+ break;
+ case WLAN_PA_GAS_INITIAL_RESP:
+ case WLAN_PA_GAS_COMEBACK_RESP:
+ if (dpp_rx_gas_resp(conn, pos + 1,
+ wpabuf_len(conn->msg) - 1,
+ *pos == WLAN_PA_GAS_COMEBACK_RESP) < 0)
+ dpp_connection_remove(conn);
+ break;
+ case WLAN_PA_GAS_COMEBACK_REQ:
+ if (dpp_controller_rx_gas_comeback_req(
+ conn, pos + 1, wpabuf_len(conn->msg) - 1) < 0)
+ dpp_connection_remove(conn);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "DPP: Ignore unsupported message type %u",
+ *pos);
+ break;
+ }
+}
+
+
+static void dpp_controller_tcp_cb(int sd, void *eloop_ctx, void *sock_ctx)
+{
+ struct dpp_controller *ctrl = eloop_ctx;
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+ int fd;
+ struct dpp_connection *conn;
+
+ wpa_printf(MSG_DEBUG, "DPP: New TCP connection");
+
+ fd = accept(ctrl->sock, (struct sockaddr *) &addr, &addr_len);
+ if (fd < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Failed to accept new connection: %s",
+ strerror(errno));
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Connection from %s:%d",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn)
+ goto fail;
+
+ conn->global = ctrl->global;
+ conn->ctrl = ctrl;
+ conn->msg_ctx = ctrl->msg_ctx;
+ conn->cb_ctx = ctrl->cb_ctx;
+ conn->process_conf_obj = ctrl->process_conf_obj;
+ conn->sock = fd;
+ conn->netrole = ctrl->netrole;
+
+ if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_READ,
+ dpp_controller_rx, conn, NULL) < 0)
+ goto fail;
+ conn->read_eloop = 1;
+
+ /* TODO: eloop timeout to expire connections that do not complete in
+ * reasonable time */
+ dl_list_add(&ctrl->conn, &conn->list);
+ return;
+
+fail:
+ close(fd);
+ os_free(conn);
+}
+
+
+int dpp_tcp_init(struct dpp_global *dpp, struct dpp_authentication *auth,
+ const struct hostapd_ip_addr *addr, int port, const char *name,
+ enum dpp_netrole netrole, void *msg_ctx, void *cb_ctx,
+ int (*process_conf_obj)(void *ctx,
+ struct dpp_authentication *auth))
+{
+ struct dpp_connection *conn;
+ struct sockaddr_storage saddr;
+ socklen_t addrlen;
+ const u8 *hdr, *pos, *end;
+ char txt[100];
+
+ wpa_printf(MSG_DEBUG, "DPP: Initialize TCP connection to %s port %d",
+ hostapd_ip_txt(addr, txt, sizeof(txt)), port);
+ if (dpp_ipaddr_to_sockaddr((struct sockaddr *) &saddr, &addrlen,
+ addr, port) < 0) {
+ dpp_auth_deinit(auth);
+ return -1;
+ }
+
+ conn = os_zalloc(sizeof(*conn));
+ if (!conn) {
+ dpp_auth_deinit(auth);
+ return -1;
+ }
+
+ conn->msg_ctx = msg_ctx;
+ conn->cb_ctx = cb_ctx;
+ conn->process_conf_obj = process_conf_obj;
+ conn->name = os_strdup(name ? name : "Test");
+ conn->netrole = netrole;
+ conn->global = dpp;
+ conn->auth = auth;
+ conn->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (conn->sock < 0)
+ goto fail;
+
+ if (fcntl(conn->sock, F_SETFL, O_NONBLOCK) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ if (connect(conn->sock, (struct sockaddr *) &saddr, addrlen) < 0) {
+ if (errno != EINPROGRESS) {
+ wpa_printf(MSG_DEBUG, "DPP: Failed to connect: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /*
+ * Continue connecting in the background; eloop will call us
+ * once the connection is ready (or failed).
+ */
+ }
+
+ if (eloop_register_sock(conn->sock, EVENT_TYPE_WRITE,
+ dpp_conn_tx_ready, conn, NULL) < 0)
+ goto fail;
+ conn->write_eloop = 1;
+
+ hdr = wpabuf_head(auth->req_msg);
+ end = hdr + wpabuf_len(auth->req_msg);
+ hdr += 2; /* skip Category and Actiom */
+ pos = hdr + DPP_HDR_LEN;
+ conn->msg_out = dpp_tcp_encaps(hdr, pos, end - pos);
+ if (!conn->msg_out)
+ goto fail;
+ /* Message will be sent in dpp_conn_tx_ready() */
+
+ /* TODO: eloop timeout to clear a connection if it does not complete
+ * properly */
+ dl_list_add(&dpp->tcp_init, &conn->list);
+ return 0;
+fail:
+ dpp_connection_free(conn);
+ return -1;
+}
+
+
+int dpp_controller_start(struct dpp_global *dpp,
+ struct dpp_controller_config *config)
+{
+ struct dpp_controller *ctrl;
+ int on = 1;
+ struct sockaddr_in sin;
+ int port;
+
+ if (!dpp || dpp->controller)
+ return -1;
+
+ ctrl = os_zalloc(sizeof(*ctrl));
+ if (!ctrl)
+ return -1;
+ ctrl->global = dpp;
+ if (config->configurator_params)
+ ctrl->configurator_params =
+ os_strdup(config->configurator_params);
+ dl_list_init(&ctrl->conn);
+ ctrl->allowed_roles = config->allowed_roles;
+ ctrl->qr_mutual = config->qr_mutual;
+ ctrl->netrole = config->netrole;
+ ctrl->msg_ctx = config->msg_ctx;
+ ctrl->cb_ctx = config->cb_ctx;
+ ctrl->process_conf_obj = config->process_conf_obj;
+
+ ctrl->sock = socket(AF_INET, SOCK_STREAM, 0);
+ if (ctrl->sock < 0)
+ goto fail;
+
+ if (setsockopt(ctrl->sock, SOL_SOCKET, SO_REUSEADDR,
+ &on, sizeof(on)) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: setsockopt(SO_REUSEADDR) failed: %s",
+ strerror(errno));
+ /* try to continue anyway */
+ }
+
+ if (fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0) {
+ wpa_printf(MSG_INFO, "DPP: fnctl(O_NONBLOCK) failed: %s",
+ strerror(errno));
+ goto fail;
+ }
+
+ /* TODO: IPv6 */
+ os_memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+ port = config->tcp_port ? config->tcp_port : DPP_TCP_PORT;
+ sin.sin_port = htons(port);
+ if (bind(ctrl->sock, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ wpa_printf(MSG_INFO,
+ "DPP: Failed to bind Controller TCP port: %s",
+ strerror(errno));
+ goto fail;
+ }
+ if (listen(ctrl->sock, 10 /* max backlog */) < 0 ||
+ fcntl(ctrl->sock, F_SETFL, O_NONBLOCK) < 0 ||
+ eloop_register_sock(ctrl->sock, EVENT_TYPE_READ,
+ dpp_controller_tcp_cb, ctrl, NULL))
+ goto fail;
+
+ dpp->controller = ctrl;
+ wpa_printf(MSG_DEBUG, "DPP: Controller started on TCP port %d", port);
+ return 0;
+fail:
+ dpp_controller_free(ctrl);
+ return -1;
+}
+
+
+void dpp_controller_stop(struct dpp_global *dpp)
+{
+ if (dpp) {
+ dpp_controller_free(dpp->controller);
+ dpp->controller = NULL;
+ }
+}
+
+
+static bool dpp_tcp_peer_id_match(struct dpp_authentication *auth,
+ unsigned int id)
+{
+ return auth &&
+ ((auth->peer_bi && auth->peer_bi->id == id) ||
+ (auth->tmp_peer_bi && auth->tmp_peer_bi->id == id));
+}
+
+
+static struct dpp_authentication * dpp_tcp_get_auth(struct dpp_global *dpp,
+ unsigned int id)
+{
+ struct dpp_connection *conn;
+
+ dl_list_for_each(conn, &dpp->tcp_init, struct dpp_connection, list) {
+ if (dpp_tcp_peer_id_match(conn->auth, id))
+ return conn->auth;
+ }
+
+ return NULL;
+}
+
+
+struct dpp_authentication * dpp_controller_get_auth(struct dpp_global *dpp,
+ unsigned int id)
+{
+ struct dpp_controller *ctrl = dpp->controller;
+ struct dpp_connection *conn;
+
+ if (!ctrl)
+ return dpp_tcp_get_auth(dpp, id);
+
+ dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+ if (dpp_tcp_peer_id_match(conn->auth, id))
+ return conn->auth;
+ }
+
+ return dpp_tcp_get_auth(dpp, id);
+}
+
+
+void dpp_controller_new_qr_code(struct dpp_global *dpp,
+ struct dpp_bootstrap_info *bi)
+{
+ struct dpp_controller *ctrl = dpp->controller;
+ struct dpp_connection *conn;
+
+ if (!ctrl)
+ return;
+
+ dl_list_for_each(conn, &ctrl->conn, struct dpp_connection, list) {
+ struct dpp_authentication *auth = conn->auth;
+
+ if (!auth->response_pending ||
+ dpp_notify_new_qr_code(auth, bi) != 1)
+ continue;
+ wpa_printf(MSG_DEBUG,
+ "DPP: Sending out pending authentication response");
+ dpp_tcp_send_msg(conn, conn->auth->resp_msg);
+ }
+}
+
+
+void dpp_tcp_init_flush(struct dpp_global *dpp)
+{
+ struct dpp_connection *conn, *tmp;
+
+ dl_list_for_each_safe(conn, tmp, &dpp->tcp_init, struct dpp_connection,
+ list)
+ dpp_connection_remove(conn);
+}
+
+
+static void dpp_relay_controller_free(struct dpp_relay_controller *ctrl)
+{
+ struct dpp_connection *conn, *tmp;
+
+ dl_list_for_each_safe(conn, tmp, &ctrl->conn, struct dpp_connection,
+ list)
+ dpp_connection_remove(conn);
+ os_free(ctrl);
+}
+
+
+void dpp_relay_flush_controllers(struct dpp_global *dpp)
+{
+ struct dpp_relay_controller *ctrl, *tmp;
+
+ if (!dpp)
+ return;
+
+ dl_list_for_each_safe(ctrl, tmp, &dpp->controllers,
+ struct dpp_relay_controller, list) {
+ dl_list_del(&ctrl->list);
+ dpp_relay_controller_free(ctrl);
+ }
+}
+
+#endif /* CONFIG_DPP2 */
diff --git a/contrib/wpa/src/common/ptksa_cache.c b/contrib/wpa/src/common/ptksa_cache.c
new file mode 100644
index 000000000000..6a053d65019d
--- /dev/null
+++ b/contrib/wpa/src/common/ptksa_cache.c
@@ -0,0 +1,321 @@
+/*
+ * RSN PTKSA cache implementation
+ *
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * 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 "eloop.h"
+#include "common/ptksa_cache.h"
+
+#define PTKSA_CACHE_MAX_ENTRIES 16
+
+struct ptksa_cache {
+ struct dl_list ptksa;
+ unsigned int n_ptksa;
+};
+
+static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa);
+
+
+static void ptksa_cache_free_entry(struct ptksa_cache *ptksa,
+ struct ptksa_cache_entry *entry)
+{
+ ptksa->n_ptksa--;
+
+ dl_list_del(&entry->list);
+ bin_clear_free(entry, sizeof(*entry));
+}
+
+
+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;
+
+ if (!ptksa)
+ return;
+
+ os_get_reltime(&now);
+
+ dl_list_for_each_safe(e, next, &ptksa->ptksa,
+ struct ptksa_cache_entry, list) {
+ if (e->expiration > now.sec)
+ continue;
+
+ wpa_printf(MSG_DEBUG, "Expired PTKSA cache entry for " MACSTR,
+ MAC2STR(e->addr));
+
+ ptksa_cache_free_entry(ptksa, e);
+ }
+
+ ptksa_cache_set_expiration(ptksa);
+}
+
+
+static void ptksa_cache_set_expiration(struct ptksa_cache *ptksa)
+{
+ struct ptksa_cache_entry *e;
+ int sec;
+ struct os_reltime now;
+
+ eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL);
+
+ if (!ptksa || !ptksa->n_ptksa)
+ return;
+
+ e = dl_list_first(&ptksa->ptksa, struct ptksa_cache_entry, list);
+ if (!e)
+ return;
+
+ os_get_reltime(&now);
+ sec = e->expiration - now.sec;
+ if (sec < 0)
+ sec = 0;
+
+ eloop_register_timeout(sec + 1, 0, ptksa_cache_expire, ptksa, NULL);
+}
+
+
+/*
+ * ptksa_cache_init - Initialize PTKSA cache
+ *
+ * Returns: Pointer to PTKSA cache data or %NULL on failure
+ */
+struct ptksa_cache * ptksa_cache_init(void)
+{
+ struct ptksa_cache *ptksa = os_zalloc(sizeof(struct ptksa_cache));
+
+ wpa_printf(MSG_DEBUG, "PTKSA: Initializing");
+
+ if (ptksa)
+ dl_list_init(&ptksa->ptksa);
+
+ return ptksa;
+}
+
+
+/*
+ * ptksa_cache_deinit - Free all entries in PTKSA cache
+ * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
+ */
+void ptksa_cache_deinit(struct ptksa_cache *ptksa)
+{
+ struct ptksa_cache_entry *e, *next;
+
+ if (!ptksa)
+ return;
+
+ wpa_printf(MSG_DEBUG, "PTKSA: Deinit. n_ptksa=%u", ptksa->n_ptksa);
+
+ dl_list_for_each_safe(e, next, &ptksa->ptksa,
+ struct ptksa_cache_entry, list)
+ ptksa_cache_free_entry(ptksa, e);
+
+ eloop_cancel_timeout(ptksa_cache_expire, ptksa, NULL);
+ os_free(ptksa);
+}
+
+
+/*
+ * ptksa_cache_get - Fetch a PTKSA cache entry
+ * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
+ * @addr: Peer address or %NULL to match any
+ * @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any
+ * Returns: Pointer to PTKSA cache entry or %NULL if no match was found
+ */
+struct ptksa_cache_entry * ptksa_cache_get(struct ptksa_cache *ptksa,
+ const u8 *addr, u32 cipher)
+{
+ struct ptksa_cache_entry *e;
+
+ if (!ptksa)
+ return NULL;
+
+ dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) {
+ if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) &&
+ (cipher == WPA_CIPHER_NONE || cipher == e->cipher))
+ return e;
+ }
+
+ return NULL;
+}
+
+
+/*
+ * ptksa_cache_list - Dump text list of entries in PTKSA cache
+ * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
+ * @buf: Buffer for the list
+ * @len: Length of the buffer
+ * Returns: Number of bytes written to buffer
+ *
+ * This function is used to generate a text format representation of the
+ * current PTKSA cache contents for the ctrl_iface PTKSA command.
+ */
+int ptksa_cache_list(struct ptksa_cache *ptksa, char *buf, size_t len)
+{
+ struct ptksa_cache_entry *e;
+ int i = 0, ret;
+ char *pos = buf;
+ struct os_reltime now;
+
+ if (!ptksa)
+ return 0;
+
+ os_get_reltime(&now);
+
+ ret = os_snprintf(pos, buf + len - pos,
+ "Index / ADDR / Cipher / expiration (secs) / TK / KDK\n");
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ dl_list_for_each(e, &ptksa->ptksa, struct ptksa_cache_entry, list) {
+ ret = os_snprintf(pos, buf + len - pos, "%u " MACSTR,
+ i, MAC2STR(e->addr));
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ ret = os_snprintf(pos, buf + len - pos, " %s %lu ",
+ wpa_cipher_txt(e->cipher),
+ e->expiration - now.sec);
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.tk,
+ e->ptk.tk_len);
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ ret = os_snprintf(pos, buf + len - pos, " ");
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ ret = wpa_snprintf_hex(pos, buf + len - pos, e->ptk.kdk,
+ e->ptk.kdk_len);
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ ret = os_snprintf(pos, buf + len - pos, "\n");
+ if (os_snprintf_error(buf + len - pos, ret))
+ return pos - buf;
+ pos += ret;
+
+ i++;
+ }
+
+ return pos - buf;
+}
+
+
+/*
+ * ptksa_cache_flush - Flush PTKSA cache entries
+ *
+ * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
+ * @addr: Peer address or %NULL to match any
+ * @cipher: Specific cipher suite to search for or WPA_CIPHER_NONE for any
+ */
+void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
+{
+ struct ptksa_cache_entry *e, *next;
+ bool removed = false;
+
+ if (!ptksa)
+ return;
+
+ dl_list_for_each_safe(e, next, &ptksa->ptksa, struct ptksa_cache_entry,
+ list) {
+ if ((!addr || os_memcmp(e->addr, addr, ETH_ALEN) == 0) &&
+ (cipher == WPA_CIPHER_NONE || cipher == e->cipher)) {
+ wpa_printf(MSG_DEBUG,
+ "Flush PTKSA cache entry for " MACSTR,
+ MAC2STR(e->addr));
+
+ ptksa_cache_free_entry(ptksa, e);
+ removed = true;
+ }
+ }
+
+ if (removed)
+ ptksa_cache_set_expiration(ptksa);
+}
+
+
+/*
+ * ptksa_cache_add - Add a PTKSA cache entry
+ * @ptksa: Pointer to PTKSA cache data from ptksa_cache_init()
+ * @addr: Peer address
+ * @cipher: The cipher used
+ * @life_time: The PTK life time in seconds
+ * @ptk: 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.
+ * If an old entry is already in the cache for the same peer and cipher
+ * this entry will be replaced with the new entry.
+ */
+struct ptksa_cache_entry * ptksa_cache_add(struct ptksa_cache *ptksa,
+ const u8 *addr, u32 cipher,
+ u32 life_time,
+ const struct wpa_ptk *ptk)
+{
+ struct ptksa_cache_entry *entry, *tmp;
+ struct os_reltime now;
+
+ if (!ptksa || !ptk || !addr || !life_time || cipher == WPA_CIPHER_NONE)
+ return NULL;
+
+ /* remove a previous entry if present */
+ ptksa_cache_flush(ptksa, addr, cipher);
+
+ /* no place to add another entry */
+ if (ptksa->n_ptksa >= PTKSA_CACHE_MAX_ENTRIES)
+ return NULL;
+
+ entry = os_zalloc(sizeof(*entry));
+ if (!entry)
+ return NULL;
+
+ dl_list_init(&entry->list);
+ os_memcpy(entry->addr, addr, ETH_ALEN);
+ entry->cipher = cipher;
+
+ os_memcpy(&entry->ptk, ptk, sizeof(entry->ptk));
+
+ os_get_reltime(&now);
+ entry->expiration = now.sec + life_time;
+
+ dl_list_for_each(tmp, &ptksa->ptksa, struct ptksa_cache_entry, list) {
+ if (tmp->expiration > entry->expiration)
+ break;
+ }
+
+ /*
+ * If the list was empty add to the head; otherwise if the expiration is
+ * later then all other entries, add it to the end of the list;
+ * otherwise add it before the relevant entry.
+ */
+ if (!tmp)
+ dl_list_add(&ptksa->ptksa, &entry->list);
+ else if (tmp->expiration < entry->expiration)
+ dl_list_add(&tmp->list, &entry->list);
+ else
+ dl_list_add_tail(&tmp->list, &entry->list);
+
+ ptksa->n_ptksa++;
+ wpa_printf(MSG_DEBUG,
+ "Added PTKSA cache entry addr=" MACSTR " cipher=%u",
+ MAC2STR(addr), cipher);
+
+ return entry;
+}
diff --git a/contrib/wpa/src/common/ptksa_cache.h b/contrib/wpa/src/common/ptksa_cache.h
new file mode 100644
index 000000000000..28ef29144647
--- /dev/null
+++ b/contrib/wpa/src/common/ptksa_cache.h
@@ -0,0 +1,79 @@
+/*
+ * RSN PTKSA cache interface
+ *
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef PTKSA_CACHE_H
+#define PTKSA_CACHE_H
+
+#include "wpa_common.h"
+#include "defs.h"
+#include "list.h"
+
+/**
+ * struct ptksa_cache_entry - PTKSA cache entry
+ */
+struct ptksa_cache_entry {
+ struct dl_list list;
+ struct wpa_ptk ptk;
+ os_time_t expiration;
+ u32 cipher;
+ u8 addr[ETH_ALEN];
+};
+
+#ifdef CONFIG_PTKSA_CACHE
+
+struct ptksa_cache;
+
+struct ptksa_cache * ptksa_cache_init(void);
+void ptksa_cache_deinit(struct ptksa_cache *ptksa);
+struct ptksa_cache_entry * ptksa_cache_get(struct ptksa_cache *ptksa,
+ const u8 *addr, u32 cipher);
+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 *addr, u32 cipher,
+ u32 life_time,
+ const struct wpa_ptk *ptk);
+void ptksa_cache_flush(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher);
+
+#else /* CONFIG_PTKSA_CACHE */
+
+static inline struct ptksa_cache * ptksa_cache_init(void)
+{
+ return (struct ptksa_cache *) 1;
+}
+
+static inline void ptksa_cache_deinit(struct ptksa_cache *ptksa)
+{
+}
+
+static inline struct ptksa_cache_entry *
+ptksa_cache_get(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher)
+{
+ return NULL;
+}
+
+static inline int ptksa_cache_list(struct ptksa_cache *ptksa,
+ char *buf, size_t len)
+{
+ return -1;
+}
+
+static inline struct ptksa_cache_entry *
+ptksa_cache_add(struct ptksa_cache *ptksa, const u8 *addr, u32 cipher,
+ u32 life_time, const struct wpa_ptk *ptk)
+{
+ return NULL;
+}
+
+static inline void ptksa_cache_flush(struct ptksa_cache *ptksa,
+ const u8 *addr, u32 cipher)
+{
+}
+
+#endif /* CONFIG_PTKSA_CACHE */
+#endif /* PTKSA_CACHE_H */
diff --git a/contrib/wpa/src/common/sae_pk.c b/contrib/wpa/src/common/sae_pk.c
new file mode 100644
index 000000000000..df79e5f2c3b0
--- /dev/null
+++ b/contrib/wpa/src/common/sae_pk.c
@@ -0,0 +1,884 @@
+/*
+ * SAE-PK
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <stdint.h>
+
+#include "utils/common.h"
+#include "utils/base64.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/crypto.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "sae.h"
+
+
+/* RFC 4648 base 32 alphabet with lowercase characters */
+static const char *sae_pk_base32_table = "abcdefghijklmnopqrstuvwxyz234567";
+
+
+static const u8 d_mult_table[] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16,
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17,
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2,
+ 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21,
+ 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6,
+ 23, 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22,
+ 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7,
+ 24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23,
+ 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26,
+ 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
+ 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
+ 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28,
+ 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 30, 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
+ 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 31, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
+ 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17,
+ 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1,
+ 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18,
+ 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2,
+ 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19,
+ 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3,
+ 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20,
+ 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4,
+ 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21,
+ 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5,
+ 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22,
+ 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6,
+ 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 23,
+ 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 7,
+ 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24,
+ 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8,
+ 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25,
+ 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9,
+ 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26,
+ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10,
+ 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27,
+ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11,
+ 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28,
+ 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12,
+ 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29,
+ 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13,
+ 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30,
+ 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15, 14,
+ 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 31,
+ 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 15,
+ 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
+ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
+};
+
+static const u8 d_perm_table[] = {
+ 7, 2, 1, 30, 16, 20, 27, 11, 31, 6, 8, 13, 29, 5, 10, 21,
+ 22, 3, 24, 0, 23, 25, 12, 9, 28, 14, 4, 15, 17, 18, 19, 26
+};
+
+
+static u8 d_permute(u8 val, unsigned int iter)
+{
+ if (iter == 0)
+ return val;
+ return d_permute(d_perm_table[val], iter - 1);
+}
+
+
+static u8 d_invert(u8 val)
+{
+ if (val > 0 && val < 16)
+ return 16 - val;
+ return val;
+}
+
+
+static char d_check_char(const char *str, size_t len)
+{
+ size_t i;
+ u8 val = 0;
+ u8 dtable[256];
+ unsigned int iter = 1;
+ int j;
+
+ os_memset(dtable, 0x80, 256);
+ for (i = 0; sae_pk_base32_table[i]; i++)
+ dtable[(u8) sae_pk_base32_table[i]] = i;
+
+ for (j = len - 1; j >= 0; j--) {
+ u8 c, p;
+
+ c = dtable[(u8) str[j]];
+ if (c == 0x80)
+ continue;
+ p = d_permute(c, iter);
+ iter++;
+ val = d_mult_table[val * 32 + p];
+ }
+
+ return sae_pk_base32_table[d_invert(val)];
+}
+
+
+bool sae_pk_valid_password(const char *pw)
+{
+ int pos;
+ size_t i, pw_len = os_strlen(pw);
+ u8 sec_1b;
+ u8 dtable[256];
+
+ os_memset(dtable, 0x80, 256);
+ for (i = 0; sae_pk_base32_table[i]; i++)
+ dtable[(u8) sae_pk_base32_table[i]] = i;
+
+ /* SAE-PK password has at least three four character components
+ * separated by hyphens. */
+ if (pw_len < 14 || pw_len % 5 != 4) {
+ wpa_printf(MSG_DEBUG, "SAE-PK: Not a valid password (length)");
+ return false;
+ }
+
+ for (pos = 0; pw[pos]; pos++) {
+ if (pos && pos % 5 == 4) {
+ if (pw[pos] != '-') {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: Not a valid password (separator)");
+ return false;
+ }
+ continue;
+ }
+ if (dtable[(u8) pw[pos]] == 0x80) {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: Not a valid password (character)");
+ return false;
+ }
+ }
+
+ /* Verify that the checksum character is valid */
+ if (pw[pw_len - 1] != d_check_char(pw, pw_len - 1)) {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: Not a valid password (checksum)");
+ return false;
+ }
+
+ /* Verify that Sec_1b bits match */
+ sec_1b = dtable[(u8) pw[0]] & BIT(4);
+ for (i = 5; i < pw_len; i += 5) {
+ if (sec_1b != (dtable[(u8) pw[i]] & BIT(4))) {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: Not a valid password (Sec_1b)");
+ return false;
+ }
+ }
+ return true;
+}
+
+
+static char * add_char(const char *start, char *pos, u8 idx, size_t *bits)
+{
+ if (*bits == 0)
+ return pos;
+ if (*bits > 5)
+ *bits -= 5;
+ else
+ *bits = 0;
+
+ if ((pos - start) % 5 == 4)
+ *pos++ = '-';
+ *pos++ = sae_pk_base32_table[idx];
+ return pos;
+}
+
+
+/* Base32 encode a password and add hyper separators and checksum */
+char * sae_pk_base32_encode(const u8 *src, size_t len_bits)
+{
+ char *out, *pos;
+ size_t olen, extra_pad, i;
+ u64 block = 0;
+ u8 val;
+ size_t len = (len_bits + 7) / 8;
+ size_t left = len_bits;
+ int j;
+
+ if (len == 0 || len >= SIZE_MAX / 8)
+ return NULL;
+ olen = len * 8 / 5 + 1;
+ olen += olen / 4; /* hyphen separators */
+ pos = out = os_zalloc(olen + 2); /* include room for ChkSum and nul */
+ if (!out)
+ return NULL;
+
+ extra_pad = (5 - len % 5) % 5;
+ for (i = 0; i < len + extra_pad; i++) {
+ val = i < len ? src[i] : 0;
+ block <<= 8;
+ block |= val;
+ if (i % 5 == 4) {
+ for (j = 7; j >= 0; j--)
+ pos = add_char(out, pos,
+ (block >> j * 5) & 0x1f, &left);
+ block = 0;
+ }
+ }
+
+ *pos = d_check_char(out, os_strlen(out));
+
+ return out;
+}
+
+
+u8 * sae_pk_base32_decode(const char *src, size_t len, size_t *out_len)
+{
+ u8 dtable[256], *out, *pos, tmp;
+ u64 block = 0;
+ size_t i, count, olen;
+ int pad = 0;
+ size_t extra_pad;
+
+ os_memset(dtable, 0x80, 256);
+ for (i = 0; sae_pk_base32_table[i]; i++)
+ dtable[(u8) sae_pk_base32_table[i]] = i;
+ dtable['='] = 0;
+
+ count = 0;
+ for (i = 0; i < len; i++) {
+ if (dtable[(u8) src[i]] != 0x80)
+ count++;
+ }
+
+ if (count == 0)
+ return NULL;
+ extra_pad = (8 - count % 8) % 8;
+
+ olen = (count + extra_pad) / 8 * 5;
+ pos = out = os_malloc(olen);
+ if (!out)
+ return NULL;
+
+ count = 0;
+ for (i = 0; i < len + extra_pad; i++) {
+ u8 val;
+
+ if (i >= len)
+ val = '=';
+ else
+ val = src[i];
+ tmp = dtable[val];
+ if (tmp == 0x80)
+ continue;
+
+ if (val == '=')
+ pad++;
+ block <<= 5;
+ block |= tmp;
+ count++;
+ if (count == 8) {
+ *pos++ = (block >> 32) & 0xff;
+ *pos++ = (block >> 24) & 0xff;
+ *pos++ = (block >> 16) & 0xff;
+ *pos++ = (block >> 8) & 0xff;
+ *pos++ = block & 0xff;
+ count = 0;
+ block = 0;
+ if (pad) {
+ /* Leave in all the available bits with zero
+ * padding to full octets from right. */
+ pos -= pad * 5 / 8;
+ break;
+ }
+ }
+ }
+
+ *out_len = pos - out;
+ return out;
+}
+
+
+u32 sae_pk_get_be19(const u8 *buf)
+{
+ return (buf[0] << 11) | (buf[1] << 3) | (buf[2] >> 5);
+}
+
+
+/* shift left by two octets and three bits; fill in zeros from right;
+ * len must be at least three */
+void sae_pk_buf_shift_left_19(u8 *buf, size_t len)
+{
+ u8 *dst, *src, *end;
+
+ dst = buf;
+ src = buf + 2;
+ end = buf + len;
+
+ while (src + 1 < end) {
+ *dst++ = (src[0] << 3) | (src[1] >> 5);
+ src++;
+ }
+ *dst++ = *src << 3;
+ *dst++ = 0;
+ *dst++ = 0;
+}
+
+
+static void sae_pk_buf_shift_left_1(u8 *buf, size_t len)
+{
+ u8 *dst, *src, *end;
+
+ dst = buf;
+ src = buf;
+ end = buf + len;
+
+ while (src + 1 < end) {
+ *dst++ = (src[0] << 1) | (src[1] >> 7);
+ src++;
+ }
+ *dst++ = *src << 1;
+}
+
+
+int sae_pk_set_password(struct sae_data *sae, const char *password)
+{
+ struct sae_temporary_data *tmp = sae->tmp;
+ size_t len, pw_len;
+ u8 *pw, *pos;
+ int bits;
+ u32 val = 0, val19;
+ unsigned int val_bits = 0;
+
+ if (!tmp)
+ return -1;
+
+ os_memset(tmp->fingerprint, 0, sizeof(tmp->fingerprint));
+ tmp->fingerprint_bytes = tmp->fingerprint_bits = 0;
+
+ len = os_strlen(password);
+ if (len < 1 || !sae_pk_valid_password(password))
+ return -1;
+
+ pw = sae_pk_base32_decode(password, len, &pw_len);
+ if (!pw)
+ return -1;
+
+ tmp->sec = (pw[0] & BIT(7)) ? 3 : 5;
+ tmp->lambda = len - len / 5;
+ 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);
+
+ /* Construct Fingerprint from PasswordBase by prefixing with Sec zero
+ * octets and skipping the Sec_1b bits */
+ pos = &tmp->fingerprint[tmp->sec];
+ bits = tmp->fingerprint_bits - 8 * tmp->sec;
+ wpa_hexdump_key(MSG_DEBUG, "SAE-PK: PasswordBase", pw, pw_len);
+ while (bits > 0) {
+ if (val_bits < 8) {
+ sae_pk_buf_shift_left_1(pw, pw_len); /* Sec_1b */
+ val19 = sae_pk_get_be19(pw);
+ sae_pk_buf_shift_left_19(pw, pw_len);
+ val = (val << 19) | val19;
+ val_bits += 19;
+ }
+ if (val_bits >= 8) {
+ if (bits < 8)
+ break;
+ *pos++ = (val >> (val_bits - 8)) & 0xff;
+ val_bits -= 8;
+ bits -= 8;
+ }
+ }
+ if (bits > 0) {
+ val >>= val_bits - bits;
+ *pos++ = val << (8 - bits);
+ }
+ tmp->fingerprint_bytes = pos - tmp->fingerprint;
+ wpa_hexdump_key(MSG_DEBUG, "SAE-PK: Fingerprint",
+ tmp->fingerprint, tmp->fingerprint_bytes);
+ bin_clear_free(pw, pw_len);
+ return 0;
+}
+
+
+static size_t sae_group_2_hash_len(int group)
+{
+ switch (group) {
+ case 19:
+ return 32;
+ case 20:
+ return 48;
+ case 21:
+ return 64;
+ }
+
+ return 0;
+}
+
+
+void sae_deinit_pk(struct sae_pk *pk)
+{
+ if (pk) {
+ wpabuf_free(pk->m);
+ crypto_ec_key_deinit(pk->key);
+#ifdef CONFIG_TESTING_OPTIONS
+ crypto_ec_key_deinit(pk->sign_key_override);
+#endif /* CONFIG_TESTING_OPTIONS */
+ wpabuf_free(pk->pubkey);
+ os_free(pk);
+ }
+}
+
+
+struct sae_pk * sae_parse_pk(const char *val)
+{
+ struct sae_pk *pk;
+ const char *pos;
+#ifdef CONFIG_TESTING_OPTIONS
+ const char *pos2;
+#endif /* CONFIG_TESTING_OPTIONS */
+ size_t len;
+ unsigned char *der;
+ size_t der_len, b_len;
+
+ /* <m-as-hexdump>:<base64-encoded-DER-encoded-key> */
+
+ pos = os_strchr(val, ':');
+ if (!pos || (pos - val) & 0x01)
+ return NULL;
+ len = (pos - val) / 2;
+ if (len != SAE_PK_M_LEN) {
+ wpa_printf(MSG_INFO, "SAE: Unexpected Modifier M length %zu",
+ len);
+ return NULL;
+ }
+
+ pk = os_zalloc(sizeof(*pk));
+ if (!pk)
+ return NULL;
+ pk->m = wpabuf_alloc(len);
+ if (!pk->m || hexstr2bin(val, wpabuf_put(pk->m, len), len)) {
+ wpa_printf(MSG_INFO, "SAE: Failed to parse m");
+ goto fail;
+ }
+
+ pos++;
+ b_len = os_strlen(pos);
+#ifdef CONFIG_TESTING_OPTIONS
+ pos2 = os_strchr(pos, ':');
+ if (pos2) {
+ b_len = pos2 - pos;
+ pos2++;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+ der = base64_decode(pos, b_len, &der_len);
+ if (!der) {
+ wpa_printf(MSG_INFO, "SAE: Failed to base64 decode PK key");
+ goto fail;
+ }
+
+ pk->key = crypto_ec_key_parse_priv(der, der_len);
+ bin_clear_free(der, der_len);
+ if (!pk->key)
+ goto fail;
+ pk->group = crypto_ec_key_group(pk->key);
+ pk->pubkey = crypto_ec_key_get_subject_public_key(pk->key);
+ if (!pk->pubkey)
+ goto fail;
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (pos2) {
+ der = base64_decode(pos2, os_strlen(pos2), &der_len);
+ if (!der) {
+ wpa_printf(MSG_INFO,
+ "SAE: Failed to base64 decode PK key");
+ goto fail;
+ }
+
+ pk->sign_key_override = crypto_ec_key_parse_priv(der, der_len);
+ bin_clear_free(der, der_len);
+ if (!pk->sign_key_override)
+ goto fail;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ return pk;
+fail:
+ sae_deinit_pk(pk);
+ return NULL;
+}
+
+
+int sae_hash(size_t hash_len, const u8 *data, size_t len, u8 *hash)
+{
+ if (hash_len == 32)
+ return sha256_vector(1, &data, &len, hash);
+#ifdef CONFIG_SHA384
+ if (hash_len == 48)
+ return sha384_vector(1, &data, &len, hash);
+#endif /* CONFIG_SHA384 */
+#ifdef CONFIG_SHA512
+ if (hash_len == 64)
+ return sha512_vector(1, &data, &len, hash);
+#endif /* CONFIG_SHA512 */
+ return -1;
+}
+
+
+static int sae_pk_hash_sig_data(struct sae_data *sae, size_t hash_len,
+ bool ap, const u8 *m, size_t m_len,
+ const u8 *pubkey, size_t pubkey_len, u8 *hash)
+{
+ struct sae_temporary_data *tmp = sae->tmp;
+ struct wpabuf *sig_data;
+ u8 *pos;
+ int ret = -1;
+
+ /* Signed data for KeyAuth: eleAP || eleSTA || scaAP || scaSTA ||
+ * M || K_AP || AP-BSSID || STA-MAC */
+ sig_data = wpabuf_alloc(tmp->prime_len * 6 + m_len + pubkey_len +
+ 2 * ETH_ALEN);
+ if (!sig_data)
+ goto fail;
+ pos = wpabuf_put(sig_data, 2 * tmp->prime_len);
+ if (crypto_ec_point_to_bin(tmp->ec, ap ? tmp->own_commit_element_ecc :
+ tmp->peer_commit_element_ecc,
+ pos, pos + tmp->prime_len) < 0)
+ goto fail;
+ pos = wpabuf_put(sig_data, 2 * tmp->prime_len);
+ if (crypto_ec_point_to_bin(tmp->ec, ap ? tmp->peer_commit_element_ecc :
+ tmp->own_commit_element_ecc,
+ pos, pos + tmp->prime_len) < 0)
+ goto fail;
+ if (crypto_bignum_to_bin(ap ? tmp->own_commit_scalar :
+ sae->peer_commit_scalar,
+ wpabuf_put(sig_data, tmp->prime_len),
+ tmp->prime_len, tmp->prime_len) < 0 ||
+ crypto_bignum_to_bin(ap ? sae->peer_commit_scalar :
+ tmp->own_commit_scalar,
+ wpabuf_put(sig_data, tmp->prime_len),
+ tmp->prime_len, tmp->prime_len) < 0)
+ goto fail;
+ wpabuf_put_data(sig_data, m, m_len);
+ wpabuf_put_data(sig_data, pubkey, pubkey_len);
+ wpabuf_put_data(sig_data, ap ? tmp->own_addr : tmp->peer_addr,
+ ETH_ALEN);
+ wpabuf_put_data(sig_data, ap ? tmp->peer_addr : tmp->own_addr,
+ ETH_ALEN);
+ wpa_hexdump_buf_key(MSG_DEBUG, "SAE-PK: Data to be signed for KeyAuth",
+ sig_data);
+ if (sae_hash(hash_len, wpabuf_head(sig_data), wpabuf_len(sig_data),
+ hash) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: hash(data to be signed)",
+ hash, hash_len);
+ ret = 0;
+fail:
+ wpabuf_free(sig_data);
+ return ret;
+}
+
+
+int sae_write_confirm_pk(struct sae_data *sae, struct wpabuf *buf)
+{
+ struct sae_temporary_data *tmp = sae->tmp;
+ struct wpabuf *sig = NULL;
+ size_t need;
+ int ret = -1;
+ u8 *encr_mod;
+ size_t encr_mod_len;
+ const struct sae_pk *pk;
+ u8 hash[SAE_MAX_HASH_LEN];
+ size_t hash_len;
+ struct crypto_ec_key *key;
+
+ if (!tmp)
+ return -1;
+
+ pk = tmp->ap_pk;
+ if (!sae->pk || !pk)
+ return 0;
+
+ key = pk->key;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (tmp->omit_pk_elem)
+ return 0;
+ if (pk->sign_key_override) {
+ wpa_printf(MSG_INFO, "TESTING: Override SAE-PK signing key");
+ key = pk->sign_key_override;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ if (tmp->kek_len != 32 && tmp->kek_len != 48 && tmp->kek_len != 64) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: No KEK available for writing confirm");
+ return -1;
+ }
+
+ if (!tmp->ec) {
+ /* Only ECC groups are supported for SAE-PK in the current
+ * implementation. */
+ wpa_printf(MSG_INFO,
+ "SAE-PK: SAE commit did not use an ECC group");
+ return -1;
+ }
+
+ hash_len = sae_group_2_hash_len(pk->group);
+ if (sae_pk_hash_sig_data(sae, hash_len, true, wpabuf_head(pk->m),
+ wpabuf_len(pk->m), wpabuf_head(pk->pubkey),
+ wpabuf_len(pk->pubkey), hash) < 0)
+ goto fail;
+ sig = crypto_ec_key_sign(key, hash, hash_len);
+ if (!sig)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "SAE-PK: KeyAuth = Sig_AP()", sig);
+
+ /* TODO: fragmentation if any of the elements needs it for a group
+ * using sufficiently large primes (none of the currently supported
+ * ones do) */
+
+ encr_mod_len = wpabuf_len(pk->m) + AES_BLOCK_SIZE;
+ need = 4 + wpabuf_len(pk->pubkey) + 3 + wpabuf_len(sig) +
+ 6 + encr_mod_len;
+ if (wpabuf_tailroom(buf) < need) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: No room in message buffer for SAE-PK elements (%zu < %zu)",
+ wpabuf_tailroom(buf), need);
+ goto fail;
+ }
+
+ /* FILS Public Key element */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 2 + wpabuf_len(pk->pubkey));
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_PUBLIC_KEY);
+ wpabuf_put_u8(buf, 2); /* Key Type: ECDSA public key */
+ wpabuf_put_buf(buf, pk->pubkey);
+
+ /* FILS Key Confirmation element (KeyAuth) */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + wpabuf_len(sig));
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_KEY_CONFIRM);
+ /* KeyAuth = Sig_AP(eleAP || eleSTA || scaAP || scaSTA || M || K_AP ||
+ * AP-BSSID || STA-MAC) */
+ wpabuf_put_buf(buf, sig);
+
+ /* SAE-PK element */
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 4 + encr_mod_len);
+ wpabuf_put_be32(buf, SAE_PK_IE_VENDOR_TYPE);
+ /* EncryptedModifier = AES-SIV-Q(M); no AAD */
+ encr_mod = wpabuf_put(buf, encr_mod_len);
+ if (aes_siv_encrypt(tmp->kek, tmp->kek_len,
+ wpabuf_head(pk->m), wpabuf_len(pk->m),
+ 0, NULL, NULL, encr_mod) < 0)
+ goto fail;
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: EncryptedModifier",
+ encr_mod, encr_mod_len);
+
+ ret = 0;
+fail:
+ wpabuf_free(sig);
+ return ret;
+
+}
+
+
+static bool sae_pk_valid_fingerprint(struct sae_data *sae,
+ const u8 *m, size_t m_len,
+ const u8 *k_ap, size_t k_ap_len, int group)
+{
+ struct sae_temporary_data *tmp = sae->tmp;
+ u8 *hash_data, *pos;
+ size_t hash_len, hash_data_len;
+ u8 hash[SAE_MAX_HASH_LEN];
+ int res;
+
+ if (!tmp->fingerprint_bytes) {
+ wpa_printf(MSG_DEBUG,
+ "SAE-PK: No PW available for K_AP fingerprint check");
+ return false;
+ }
+
+ /* Fingerprint = L(Hash(SSID || M || K_AP), 0, 8*Sec + 19*Lambda/4 - 5)
+ */
+
+ hash_len = sae_group_2_hash_len(group);
+ hash_data_len = tmp->ssid_len + m_len + k_ap_len;
+ hash_data = os_malloc(hash_data_len);
+ if (!hash_data)
+ return false;
+ pos = hash_data;
+ os_memcpy(pos, tmp->ssid, tmp->ssid_len);
+ pos += tmp->ssid_len;
+ os_memcpy(pos, m, m_len);
+ pos += m_len;
+ os_memcpy(pos, k_ap, k_ap_len);
+
+ wpa_hexdump_key(MSG_DEBUG, "SAE-PK: SSID || M || K_AP",
+ hash_data, hash_data_len);
+ res = sae_hash(hash_len, hash_data, hash_data_len, hash);
+ bin_clear_free(hash_data, hash_data_len);
+ if (res < 0)
+ return false;
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Hash(SSID || M || K_AP)",
+ hash, hash_len);
+
+ if (tmp->fingerprint_bits > hash_len * 8) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: Not enough hash output bits for the fingerprint");
+ return false;
+ }
+ if (tmp->fingerprint_bits % 8) {
+ size_t extra;
+
+ /* Zero out the extra bits in the last octet */
+ extra = 8 - tmp->fingerprint_bits % 8;
+ pos = &hash[tmp->fingerprint_bits / 8];
+ *pos = (*pos >> extra) << extra;
+ }
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Fingerprint", hash,
+ tmp->fingerprint_bytes);
+ res = os_memcmp_const(hash, tmp->fingerprint, tmp->fingerprint_bytes);
+ if (res) {
+ wpa_printf(MSG_DEBUG, "SAE-PK: K_AP fingerprint mismatch");
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Expected fingerprint",
+ tmp->fingerprint, tmp->fingerprint_bytes);
+ return false;
+ }
+
+ wpa_printf(MSG_DEBUG, "SAE-PK: Valid K_AP fingerprint");
+ return true;
+}
+
+
+int sae_check_confirm_pk(struct sae_data *sae, const u8 *ies, size_t ies_len)
+{
+ struct sae_temporary_data *tmp = sae->tmp;
+ const u8 *k_ap;
+ u8 m[SAE_PK_M_LEN];
+ size_t k_ap_len;
+ struct crypto_ec_key *key;
+ int res;
+ u8 hash[SAE_MAX_HASH_LEN];
+ size_t hash_len;
+ int group;
+ struct ieee802_11_elems elems;
+
+ if (!tmp)
+ return -1;
+ if (!sae->pk || tmp->ap_pk)
+ return 0;
+
+ if (tmp->kek_len != 32 && tmp->kek_len != 48 && tmp->kek_len != 64) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: No KEK available for checking confirm");
+ return -1;
+ }
+
+ if (!tmp->ec) {
+ /* Only ECC groups are supported for SAE-PK in the current
+ * implementation. */
+ wpa_printf(MSG_INFO,
+ "SAE-PK: SAE commit did not use an ECC group");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Received confirm IEs", ies, ies_len);
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_INFO, "SAE-PK: Failed to parse confirm IEs");
+ return -1;
+ }
+ if (!elems.fils_pk || !elems.fils_key_confirm || !elems.sae_pk) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: Not all mandatory IEs included in confirm");
+ return -1;
+ }
+
+ /* TODO: Fragment reassembly */
+
+ if (elems.sae_pk_len < SAE_PK_M_LEN + AES_BLOCK_SIZE) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: No room for EncryptedModifier in SAE-PK element");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: EncryptedModifier",
+ elems.sae_pk, SAE_PK_M_LEN + AES_BLOCK_SIZE);
+
+ if (aes_siv_decrypt(tmp->kek, tmp->kek_len,
+ elems.sae_pk, SAE_PK_M_LEN + AES_BLOCK_SIZE,
+ 0, NULL, NULL, m) < 0) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: Failed to decrypt EncryptedModifier");
+ return -1;
+ }
+ wpa_hexdump_key(MSG_DEBUG, "SAE-PK: Modifier M", m, SAE_PK_M_LEN);
+
+ if (elems.fils_pk[0] != 2) {
+ wpa_printf(MSG_INFO, "SAE-PK: Unsupported public key type %u",
+ elems.fils_pk[0]);
+ return -1;
+ }
+ k_ap_len = elems.fils_pk_len - 1;
+ k_ap = elems.fils_pk + 1;
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Received K_AP", k_ap, k_ap_len);
+ /* TODO: Check against the public key, if one is stored in the network
+ * profile */
+
+ key = crypto_ec_key_parse_pub(k_ap, k_ap_len);
+ if (!key) {
+ wpa_printf(MSG_INFO, "SAE-PK: Failed to parse K_AP");
+ return -1;
+ }
+
+ group = crypto_ec_key_group(key);
+ if (!sae_pk_valid_fingerprint(sae, m, SAE_PK_M_LEN, k_ap, k_ap_len,
+ group)) {
+ crypto_ec_key_deinit(key);
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "SAE-PK: Received KeyAuth",
+ elems.fils_key_confirm, elems.fils_key_confirm_len);
+
+ hash_len = sae_group_2_hash_len(group);
+ if (sae_pk_hash_sig_data(sae, hash_len, false, m, SAE_PK_M_LEN,
+ k_ap, k_ap_len, hash) < 0) {
+ crypto_ec_key_deinit(key);
+ return -1;
+ }
+
+ res = crypto_ec_key_verify_signature(key, hash, hash_len,
+ elems.fils_key_confirm,
+ elems.fils_key_confirm_len);
+ crypto_ec_key_deinit(key);
+
+ if (res != 1) {
+ wpa_printf(MSG_INFO,
+ "SAE-PK: Invalid or incorrect signature in KeyAuth");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "SAE-PK: Valid KeyAuth signature received");
+
+ /* TODO: Store validated public key into network profile */
+
+ return 0;
+}
diff --git a/contrib/wpa/src/crypto/sha384-tlsprf.c b/contrib/wpa/src/crypto/sha384-tlsprf.c
new file mode 100644
index 000000000000..9ff96ac2c7b2
--- /dev/null
+++ b/contrib/wpa/src/crypto/sha384-tlsprf.c
@@ -0,0 +1,71 @@
+/*
+ * TLS PRF P_SHA384
+ * Copyright (c) 2011-2019, 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 "common.h"
+#include "sha384.h"
+
+
+/**
+ * tls_prf_sha384 - Pseudo-Random Function for TLS v1.2 (P_SHA384, RFC 5246)
+ * @secret: Key for PRF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the PRF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in TLS. This PRF is defined in RFC 5246, Chapter 5.
+ */
+int tls_prf_sha384(const u8 *secret, size_t secret_len, const char *label,
+ const u8 *seed, size_t seed_len, u8 *out, size_t outlen)
+{
+ size_t clen;
+ u8 A[SHA384_MAC_LEN];
+ u8 P[SHA384_MAC_LEN];
+ size_t pos;
+ const unsigned char *addr[3];
+ size_t len[3];
+
+ addr[0] = A;
+ len[0] = SHA384_MAC_LEN;
+ addr[1] = (unsigned char *) label;
+ len[1] = os_strlen(label);
+ addr[2] = seed;
+ len[2] = seed_len;
+
+ /*
+ * RFC 5246, Chapter 5
+ * A(0) = seed, A(i) = HMAC(secret, A(i-1))
+ * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + ..
+ * PRF(secret, label, seed) = P_SHA384(secret, label + seed)
+ */
+
+ if (hmac_sha384_vector(secret, secret_len, 2, &addr[1], &len[1], A) < 0)
+ return -1;
+
+ pos = 0;
+ while (pos < outlen) {
+ if (hmac_sha384_vector(secret, secret_len, 3, addr, len, P) <
+ 0 ||
+ hmac_sha384(secret, secret_len, A, SHA384_MAC_LEN, A) < 0)
+ return -1;
+
+ clen = outlen - pos;
+ if (clen > SHA384_MAC_LEN)
+ clen = SHA384_MAC_LEN;
+ os_memcpy(out + pos, P, clen);
+ pos += clen;
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa/src/eap_peer/.gitignore b/contrib/wpa/src/eap_peer/.gitignore
new file mode 100644
index 000000000000..140f8cf80f2c
--- /dev/null
+++ b/contrib/wpa/src/eap_peer/.gitignore
@@ -0,0 +1 @@
+*.so
diff --git a/contrib/wpa/src/objs.mk b/contrib/wpa/src/objs.mk
new file mode 100644
index 000000000000..a3040b21bd94
--- /dev/null
+++ b/contrib/wpa/src/objs.mk
@@ -0,0 +1,3 @@
+$(_OBJS_VAR) := $(call BUILDOBJ,$($(_OBJS_VAR)))
+-include $(filter-out %.a,$($(_OBJS_VAR):%.o=%.d))
+_DIRS += $(dir $($(_OBJS_VAR)))
diff --git a/contrib/wpa/src/utils/config.c b/contrib/wpa/src/utils/config.c
new file mode 100644
index 000000000000..22aa2216eb3f
--- /dev/null
+++ b/contrib/wpa/src/utils/config.c
@@ -0,0 +1,97 @@
+/*
+ * Configuration parsing
+ * Copyright (c) 2003-2019, 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 "utils/config.h"
+#include "common.h"
+
+
+static int newline_terminated(const char *buf, size_t buflen)
+{
+ size_t len = os_strlen(buf);
+ if (len == 0)
+ return 0;
+ if (len == buflen - 1 && buf[buflen - 1] != '\r' &&
+ buf[len - 1] != '\n')
+ return 0;
+ return 1;
+}
+
+
+static void skip_line_end(FILE *stream)
+{
+ char buf[100];
+ while (fgets(buf, sizeof(buf), stream)) {
+ buf[sizeof(buf) - 1] = '\0';
+ if (newline_terminated(buf, sizeof(buf)))
+ return;
+ }
+}
+
+
+char * wpa_config_get_line(char *s, int size, FILE *stream, int *line,
+ char **_pos)
+{
+ char *pos, *end, *sstart;
+
+ while (fgets(s, size, stream)) {
+ (*line)++;
+ s[size - 1] = '\0';
+ if (!newline_terminated(s, size)) {
+ /*
+ * The line was truncated - skip rest of it to avoid
+ * confusing error messages.
+ */
+ wpa_printf(MSG_INFO, "Long line in configuration file "
+ "truncated");
+ skip_line_end(stream);
+ }
+ pos = s;
+
+ /* Skip white space from the beginning of line. */
+ while (*pos == ' ' || *pos == '\t' || *pos == '\r')
+ pos++;
+
+ /* Skip comment lines and empty lines */
+ if (*pos == '#' || *pos == '\n' || *pos == '\0')
+ continue;
+
+ /*
+ * Remove # comments unless they are within a double quoted
+ * string.
+ */
+ sstart = os_strchr(pos, '"');
+ if (sstart)
+ sstart = os_strrchr(sstart + 1, '"');
+ if (!sstart)
+ sstart = pos;
+ end = os_strchr(sstart, '#');
+ if (end)
+ *end-- = '\0';
+ else
+ end = pos + os_strlen(pos) - 1;
+
+ /* Remove trailing white space. */
+ while (end > pos &&
+ (*end == '\n' || *end == ' ' || *end == '\t' ||
+ *end == '\r'))
+ *end-- = '\0';
+
+ if (*pos == '\0')
+ continue;
+
+ if (_pos)
+ *_pos = pos;
+ return pos;
+ }
+
+ if (_pos)
+ *_pos = NULL;
+ return NULL;
+}
diff --git a/contrib/wpa/src/utils/config.h b/contrib/wpa/src/utils/config.h
new file mode 100644
index 000000000000..074a88a5da3c
--- /dev/null
+++ b/contrib/wpa/src/utils/config.h
@@ -0,0 +1,29 @@
+/*
+ * Configuration parsing
+ * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef UTILS_CONFIG_H
+#define UTILS_CONFIG_H
+
+/**
+ * wpa_config_get_line - Read the next configuration file line
+ * @s: Buffer for the line
+ * @size: The buffer length
+ * @stream: File stream to read from
+ * @line: Pointer to a variable storing the file line number
+ * @_pos: Buffer for the pointer to the beginning of data on the text line or
+ * %NULL if not needed (returned value used instead)
+ * Returns: Pointer to the beginning of data on the text line or %NULL if no
+ * more text lines are available.
+ *
+ * This function reads the next non-empty line from the configuration file and
+ * removes comments. The returned string is guaranteed to be null-terminated.
+ */
+char * wpa_config_get_line(char *s, int size, FILE *stream, int *line,
+ char **_pos);
+
+#endif /* UTILS_CONFIG_H */
diff --git a/contrib/wpa/src/utils/ext_password_file.c b/contrib/wpa/src/utils/ext_password_file.c
new file mode 100644
index 000000000000..4bb0095f3f28
--- /dev/null
+++ b/contrib/wpa/src/utils/ext_password_file.c
@@ -0,0 +1,136 @@
+/*
+ * External backend for file-backed passwords
+ * Copyright (c) 2021, Patrick Steinhardt <ps@pks.im>
+ *
+ * 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 "utils/config.h"
+#include "ext_password_i.h"
+
+
+/**
+ * Data structure for the file-backed password backend.
+ */
+struct ext_password_file_data {
+ char *path; /* path of the password file */
+};
+
+
+/**
+ * ext_password_file_init - Initialize file-backed password backend
+ * @params: Parameters passed by the user.
+ * Returns: Pointer to the initialized backend.
+ *
+ * This function initializes a new file-backed password backend. The user is
+ * expected to initialize this backend with the parameters being the path of
+ * the file that contains the passwords.
+ */
+static void * ext_password_file_init(const char *params)
+{
+ struct ext_password_file_data *data;
+
+ if (!params) {
+ wpa_printf(MSG_ERROR, "EXT PW FILE: no path given");
+ return NULL;
+ }
+
+ data = os_zalloc(sizeof(*data));
+ if (!data)
+ return NULL;
+
+ data->path = os_strdup(params);
+ if (!data->path) {
+ os_free(data);
+ return NULL;
+ }
+
+ return data;
+}
+
+
+/**
+ * ext_password_file_deinit - Deinitialize file-backed password backend
+ * @ctx: The file-backed password backend
+ *
+ * This function frees all data associated with the file-backed password
+ * backend.
+ */
+static void ext_password_file_deinit(void *ctx)
+{
+ struct ext_password_file_data *data = ctx;
+
+ str_clear_free(data->path);
+ os_free(data);
+}
+
+/**
+ * ext_password_file_get - Retrieve password from the file-backed password backend
+ * @ctx: The file-backed password backend
+ * @name: Name of the password to retrieve
+ * Returns: Buffer containing the password if one was found or %NULL.
+ *
+ * This function tries to find a password identified by name in the password
+ * file. The password is expected to be stored in `NAME=PASSWORD` format.
+ * Comments and empty lines in the file are ignored. Invalid lines will cause
+ * an error message, but will not cause the function to fail.
+ */
+static struct wpabuf * ext_password_file_get(void *ctx, const char *name)
+{
+ struct ext_password_file_data *data = ctx;
+ struct wpabuf *password = NULL;
+ char buf[512], *pos;
+ int line = 0;
+ FILE *f;
+
+ f = fopen(data->path, "r");
+ if (!f) {
+ wpa_printf(MSG_ERROR,
+ "EXT PW FILE: could not open file '%s': %s",
+ data->path, strerror(errno));
+ return NULL;
+ }
+
+ wpa_printf(MSG_DEBUG, "EXT PW FILE: get(%s)", name);
+
+ while (wpa_config_get_line(buf, sizeof(buf), f, &line, &pos)) {
+ char *sep = os_strchr(pos, '=');
+
+ if (!sep) {
+ wpa_printf(MSG_ERROR, "Invalid password line %d.",
+ line);
+ continue;
+ }
+
+ if (!sep[1]) {
+ wpa_printf(MSG_ERROR, "No password for line %d.", line);
+ continue;
+
+ }
+
+ if (os_strncmp(name, pos, sep - pos) != 0)
+ continue;
+
+ password = wpabuf_alloc_copy(sep + 1, os_strlen(sep + 1));
+ goto done;
+ }
+
+ wpa_printf(MSG_ERROR, "Password for '%s' was not found.", name);
+
+done:
+ forced_memzero(buf, sizeof(buf));
+ fclose(f);
+ return password;
+}
+
+
+const struct ext_password_backend ext_password_file = {
+ .name = "file",
+ .init = ext_password_file_init,
+ .deinit = ext_password_file_deinit,
+ .get = ext_password_file_get,
+};
diff --git a/contrib/wpa/tests/.gitignore b/contrib/wpa/tests/.gitignore
new file mode 100644
index 000000000000..f3c8ac941d25
--- /dev/null
+++ b/contrib/wpa/tests/.gitignore
@@ -0,0 +1,3 @@
+test-*
+!test-*.[ch]
+!test-*.sh
diff --git a/contrib/wpa/tests/Makefile b/contrib/wpa/tests/Makefile
new file mode 100644
index 000000000000..2d2343b640a5
--- /dev/null
+++ b/contrib/wpa/tests/Makefile
@@ -0,0 +1,99 @@
+ALL=test-base64 test-md4 test-milenage \
+ test-rsa-sig-ver \
+ test-sha1 \
+ test-https test-https_server \
+ test-sha256 test-aes test-x509v3 test-list test-rc4
+
+include ../src/build.rules
+
+ifdef LIBFUZZER
+CC=clang
+CFLAGS = -MMD -O2 -Wall -g
+CFLAGS += -fsanitize=fuzzer,address,signed-integer-overflow,unsigned-integer-overflow
+CFLAGS += -DTEST_LIBFUZZER
+LDFLAGS += -fsanitize=fuzzer,address,signed-integer-overflow,unsigned-integer-overflow
+TEST_FUZZ=y
+endif
+
+ifdef TEST_FUZZ
+CFLAGS += -DCONFIG_NO_RANDOM_POOL
+CFLAGS += -DTEST_FUZZ
+endif
+
+CFLAGS += -DCONFIG_IEEE80211R_AP
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_TDLS
+
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+SLIBS = ../src/utils/libutils.a
+
+DLIBS = ../src/crypto/libcrypto.a \
+ ../src/tls/libtls.a
+
+_OBJS_VAR := LLIBS
+include ../src/objs.mk
+_OBJS_VAR := SLIBS
+include ../src/objs.mk
+_OBJS_VAR := DLIBS
+include ../src/objs.mk
+
+LIBS = $(SLIBS) $(DLIBS)
+LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
+
+# glibc < 2.17 needs -lrt for clock_gettime()
+LLIBS += -lrt
+
+test-aes: $(call BUILDOBJ,test-aes.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-base64: $(call BUILDOBJ,test-base64.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-https: $(call BUILDOBJ,test-https.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
+
+test-https_server: $(call BUILDOBJ,test-https_server.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
+
+test-list: $(call BUILDOBJ,test-list.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-md4: $(call BUILDOBJ,test-md4.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-milenage: $(call BUILDOBJ,test-milenage.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-rc4: $(call BUILDOBJ,test-rc4.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-rsa-sig-ver: $(call BUILDOBJ,test-rsa-sig-ver.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
+
+test-sha1: $(call BUILDOBJ,test-sha1.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-sha256: $(call BUILDOBJ,test-sha256.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-x509v3: $(call BUILDOBJ,test-x509v3.o) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
+
+
+run-tests: $(ALL)
+ ./test-aes
+ ./test-list
+ ./test-md4
+ ./test-milenage
+ ./test-rsa-sig-ver
+ ./test-sha1
+ ./test-sha256
+ @echo
+ @echo All tests completed successfully.
+
+clean: common-clean
+ rm -f *~
+ rm -f test_x509v3_nist.out.*
+ rm -f test_x509v3_nist2.out.*
diff --git a/contrib/wpa/tests/README b/contrib/wpa/tests/README
new file mode 100644
index 000000000000..0e2dcffcfbd8
--- /dev/null
+++ b/contrib/wpa/tests/README
@@ -0,0 +1,123 @@
+hostap.git test tools
+---------------------
+
+The tests directory with its subdirectories contain number of tools used
+for testing wpa_supplicant and hostapd implementations.
+
+hwsim directory contains the test setup for full system testing of
+wpa_supplicant and hostapd with a simulated radio (mac80211_hwsim). See
+hwsim/READM and hwsim/vm/README for more details.
+
+
+Build testing
+-------------
+
+wpa_supplicant and hostapd support number of build option
+combinations. The test scripts in the build subdirectory can be used to
+verify that various combinations do not break the builds. More
+configuration examples can be added there
+(build-{hostapd,wpa_supplicant}-*.config) to get them included in test
+builds.
+
+# Example
+cd build
+./run-build-tests.h
+
+
+Fuzz testing
+------------
+
+Newer fuzz testing tools are under the fuzzing directory. See
+fuzzing/README for more details on them. The following text describes
+the older fuzz testing tools that are subject to removal once the same
+newer tools have the same coverage available.
+
+Number of the test tools here can be used for fuzz testing with tools
+like American fuzzy lop (afl-fuzz) that are designed to modify an
+external file for program input. ap-mgmt-fuzzer, eapol-fuzzer,
+test-eapol, test-json, test-tls, and test-x509 are examples of such
+tools that expose hostap.git module functionality with input from a file
+specified on the command line.
+
+Here are some examples of how fuzzing can be performed:
+
+##### JSON parser
+make clean
+CC=afl-gcc make test-json
+mkdir json-examples
+cat > json-examples/1.json <<EOF
+{"a":[[]],"b":1,"c":"q","d":{"e":[{}]}}
+EOF
+afl-fuzz -i json-examples -o json-findings -- $PWD/test-json @@
+
+Alternatively, using libFuzzer from LLVM:
+make clean
+make test-json LIBFUZZER=y
+mkdir json-examples
+cat > json-examples/1.json <<EOF
+{"a":[[]],"b":1,"c":"q","d":{"e":[{}]}}
+EOF
+./test-json json-examples
+
+##### EAPOL-Key Supplicant
+make clean
+CC=afl-gcc make test-eapol TEST_FUZZ=y
+mkdir eapol-auth-examples
+./test-eapol auth write eapol-auth-examples/auth.msg
+afl-fuzz -i eapol-auth-examples -o eapol-auth-findings -- $PWD/test-eapol auth read @@
+
+##### EAPOL-Key Authenticator
+make clean
+CC=afl-gcc make test-eapol TEST_FUZZ=y
+mkdir eapol-supp-examples
+./test-eapol supp write eapol-supp-examples/supp.msg
+afl-fuzz -i eapol-supp-examples -o eapol-supp-findings -- $PWD/test-eapol supp read @@
+
+##### TLS client
+make clean
+CC=afl-gcc make test-tls TEST_FUZZ=y
+mkdir tls-server-examples
+./test-tls server write tls-server-examples/server.msg
+afl-fuzz -i tls-server-examples -o tls-server-findings -- $PWD/test-tls server read @@
+
+##### TLS server
+make clean
+CC=afl-gcc make test-tls TEST_FUZZ=y
+mkdir tls-client-examples
+./test-tls client write tls-client-examples/client.msg
+afl-fuzz -i tls-client-examples -o tls-client-findings -- $PWD/test-tls client read @@
+
+##### AP management frame processing
+cd ap-mgmt-fuzzer
+make clean
+CC=afl-gcc make
+mkdir multi-examples
+cp multi.dat multi-examples
+afl-fuzz -i multi-examples -o multi-findings -- $PWD/ap-mgmt-fuzzer -m @@
+
+##### EAPOL-Key Supplicant (separate)
+cd eapol-fuzzer
+make clean
+CC=afl-gcc make
+mkdir eapol-examples
+cp *.dat eapol-examples
+afl-fuzz -i eapol-examples -o eapol-findings -- $PWD/eapol-fuzzer @@
+
+##### P2P
+cd p2p-fuzzer
+make clean
+CC=afl-gcc make
+mkdir p2p-proberesp-examples
+cp proberesp*.dat p2p-proberesp-examples
+afl-fuzz -i p2p-proberesp-examples -o p2p-proberesp-findings -- $PWD/p2p-fuzzer proberesp @@
+mkdir p2p-action-examples
+cp go*.dat inv*.dat p2ps*.dat p2p-action-examples
+afl-fuzz -i p2p-action-examples -o p2p-action-findings -- $PWD/p2p-fuzzer action @@
+
+##### WNM
+cd wnm-fuzzer
+make clean
+CC=afl-gcc make
+mkdir wnm-examples
+cp *.dat wnm-examples
+afl-fuzz -i wnm-examples -o wnm-findings -- $PWD/wnm-fuzzer @@
diff --git a/contrib/wpa/tests/cipher-and-key-mgmt-testing.txt b/contrib/wpa/tests/cipher-and-key-mgmt-testing.txt
new file mode 100644
index 000000000000..1b93b777e556
--- /dev/null
+++ b/contrib/wpa/tests/cipher-and-key-mgmt-testing.txt
@@ -0,0 +1,377 @@
+Cipher suite (CCMP, TKIP, GCMP, ..) and key management testing
+==============================================================
+
+wpa_supplicant and hostapd include number of extensions that allow
+special test builds to be used for testing functionality related to
+correct implementation of IEEE 802.11. These extensions allow behavior
+to be modified and invalid operations to be performed to verify behavior
+of other devices in unexpected situations. While most of the testing
+extensions are focused on the fully automated testing framework with
+mac80211_hwsim (see tests/hwsim subdirectory), many of these can be used
+for over-the-air testing of the protocol as well.
+
+Since some of the testing extensions can result in exposing key
+information or allowing non-compliant behavior, these changes are
+disabled in default wpa_supplicant and hostapd builds for production
+purposes. Testing functionality can be enabled by adding
+CONFIG_TESTING_OPTIONS=y into build configuration (hostapd/.config and
+wpa_supplicant/.config).
+
+
+Testing setup
+-------------
+
+These tests can be run as black-box testing without having to modify the
+tested device at all or without knowing details of its
+functionality. The test commands in wpa_supplicant/hostapd control
+interfaces are used to perform unexpected operations and normal data
+traffic is used to verify reaction of the tested device to such
+operations.
+
+In theory, the test functionality is available with most drivers
+supported by wpa_supplicant/hostapd, but the most reliable results are
+likely available through ath9k-based devices. If you are using something
+else, it is strongly recommended that you'll run the first tests with
+sniffer captures and verify that the test tools are behaving correctly.
+
+wpa_supplicant is used to control a test device in station mode to test
+an AP and hostapd is similarly used to control a test device in AP mode
+to test a station.
+
+Various data traffic generators could be used to test the behavior, but
+this document focuses on using ping to test unicast traffic and arping
+to test broadcast traffic. To keep things simple and to reduce
+interference from unrelated traffic, the steps here assume static IPv4
+addresses are used and IPv6 is disabled.
+
+The tests here use WPA2-Personal for simplicity. WPA2-Enterprise and
+other cipher suites can also be tested for more complete coverage.
+
+Example hostapd.conf for the test tool in AP mode:
+
+driver=nl80211
+hw_mode=g
+channel=1
+ieee80211n=1
+interface=wlan0
+ctrl_interface=/var/run/hostapd
+ctrl_interface_group=adm
+ssid=test-psk
+wpa=2
+wpa_key_mgmt=WPA-PSK
+wpa_pairwise=CCMP
+wpa_passphrase=12345678
+
+Example wpa_supplicant.conf for the test tool in station mode:
+
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=adm
+
+network={
+ ssid="test-psk"
+ key_mgmt=WPA-PSK
+ psk="12345678"
+}
+
+The examples in this document assume following IPv4 address
+configuration:
+
+Test tool (either AP or station mode): 192.168.1.1/24
+Device under test: 192.168.1.2/24
+
+
+Data traffic tests
+------------------
+
+ping is used to test whether unicast frames go through on the data
+link. It should be noted that ping may need to use broadcast ARP at the
+beginning if the other device is not yet in the ARP table, so working
+broadcast and unicast connectivity may be needed to get this started.
+
+Example:
+
+$ ping -n -c 5 192.168.1.2
+PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
+64 bytes from 192.168.1.2: icmp_seq=1 ttl=64 time=43.7 ms
+64 bytes from 192.168.1.2: icmp_seq=2 ttl=64 time=67.9 ms
+64 bytes from 192.168.1.2: icmp_seq=3 ttl=64 time=900 ms
+64 bytes from 192.168.1.2: icmp_seq=4 ttl=64 time=5.81 ms
+64 bytes from 192.168.1.2: icmp_seq=5 ttl=64 time=135 ms
+
+--- 192.168.1.2 ping statistics ---
+5 packets transmitted, 5 received, 0% packet loss, time 4004ms
+rtt min/avg/max/mdev = 5.811/230.605/900.223/337.451 ms
+
+This shows working unicast data connectivity.
+
+$ ping -n -c 5 192.168.1.2
+PING 192.168.1.2 (192.168.1.2) 56(84) bytes of data.
+
+--- 192.168.1.2 ping statistics ---
+5 packets transmitted, 0 received, 100% packet loss, time 4033ms
+
+This shows not working unicast data connectivity.
+
+
+arping is used to test broadcast connectivity.
+
+Example:
+
+$ arping -b -I wlan0 192.168.1.2 -c 5
+ARPING 192.168.1.2 from 192.168.1.1 wlan0
+Unicast reply from 192.168.1.2 [<DUT MAC address>] 119.695ms
+Unicast reply from 192.168.1.2 [<DUT MAC address>] 144.496ms
+Unicast reply from 192.168.1.2 [<DUT MAC address>] 166.788ms
+Unicast reply from 192.168.1.2 [<DUT MAC address>] 2.283ms
+Unicast reply from 192.168.1.2 [<DUT MAC address>] 2.234ms
+Sent 5 probes (5 broadcast(s))
+Received 5 response(s)
+
+This shows working broadcast data connectivity.
+
+$ arping -b -I wlan0 192.168.1.2 -c 5
+ARPING 192.168.1.2 from 192.168.1.1 wlan0
+Sent 5 probes (5 broadcast(s))
+Received 0 response(s)
+
+This shows not working broadcast data connectivity.
+
+If testing results do not look consistent, the testing state can be
+cleared by disconnection and reconnecting the station (the test tool or
+the DUT) to the network.
+
+
+Sniffer and wlantest
+--------------------
+
+It is useful to get a wireless sniffer capture from the operating
+channel of the AP to be able to confirm DUT behavior if any of the data
+tests indicate reason to believe something is not working as expected.
+
+wlantest (from the wlantest directory of hostap.git) can be used to
+decrypt and analyze a sniffer capture. For example:
+
+wlantest -r wlan0.pcap -n decrypted.pcap -p 12345678
+
+The debug prints and comments in the generated file indicate where
+unexpected behavior has been detected, e.g., when the test tool ends up
+clearing its packet number to test replay protection. That can help in
+checking whether the DUT actually replies to a frame that it was
+supposed to drop due replay.
+
+
+Testing replay protection on a station device
+---------------------------------------------
+
+Start hostapd and use hostapd_cli on the test device to control testing
+operations. Connect the DUT to the network.
+
+<3>AP-STA-CONNECTED <DUT MAC address>
+
+This indicates that the connection was completed successfully.
+
+Verify that broadcast and unicast traffic works correctly (if not,
+something is wrong in the test setup and that needs to be resolved
+before being able to run any tests).
+
+Verify that unicast traffic works and issue the following command in
+hostapd_cli:
+
+> raw RESET_PN <DUT MAC address>
+OK
+
+Verify that unicast traffic does not work anymore. If it does, the DUT
+does not implement replay protection correctly for unicast frames. Note
+that unicast traffic can recover once the packet number from the test
+device increases beyond the value used prior to that RESET_PN command.
+
+
+Verify that broadcast traffic works and issue the following command in
+hostapd_cli:
+
+> raw RESET_PN ff:ff:ff:ff:ff:ff
+OK
+
+Verify that broadcast traffic does not work anymore. If it does, the DUT
+does not implement replay protection correctly for broadcast
+frames. Note that broadcast traffic can recover once the packet number
+from the test device increases beyond the value used prior to that
+RESET_PN command.
+
+
+Testing replay protection on an AP device
+-----------------------------------------
+
+Start the AP (DUT) and start wpa_supplicant on the test device to
+connect to the network. Use wpa_cli to control the test device.
+
+<3>SME: Trying to authenticate with <DUT MAC address> (SSID='test-psk' freq=5240 MHz)
+<3>CTRL-EVENT-REGDOM-CHANGE init=CORE type=WORLD
+<3>Trying to associate with <DUT MAC address> (SSID='test-psk' freq=5240 MHz)
+<3>Associated with <DUT MAC address>
+<3>WPA: Key negotiation completed with <DUT MAC address> [PTK=CCMP GTK=CCMP]
+<3>CTRL-EVENT-CONNECTED - Connection to <DUT MAC address> completed [id=0 id_str=]
+
+Verify that unicast traffic works and issue the following command in
+wpa_cli:
+
+> raw RESET_PN
+OK
+
+Verify that unicast traffic does not work anymore. If it does, the DUT
+does not implement replay protection correctly. Note that unicast
+traffic can recover once the packet number from the test device
+increases beyond the value used prior to that RESET_PN command.
+
+IEEE 802.11 protocol uses unicast frames in station-to-AP direction, so
+there is no need to test AP replay protection behavior separately with
+the broadcast IPv4 traffic (which would be converted to unicast frames
+on the link layer).
+
+
+Testing GTK reinstallation protection on a station device (group handshake)
+---------------------------------------------------------------------------
+
+Use the procedure describe above for testing replay protection, but with
+the following hostapd_cli commands:
+
+Test broadcast connectivity; should work
+
+> raw RESEND_GROUP_M1 <DUT MAC address>
+OK
+> raw RESET_PN ff:ff:ff:ff:ff:ff
+OK
+
+Test broadcast connectivity; should not work; if it does, the device
+does not implement protection for delayed retransmission of Group Key
+Message 1/2.
+
+
+Testing GTK reinstallation protection on a station device (4-way handshake)
+---------------------------------------------------------------------------
+
+Use the procedure described above for testing replay protection for
+broadcast traffic, but with the following hostapd_cli commands:
+
+Test broadcast connectivity; should work
+
+> raw RESEND_M3 <DUT MAC address>
+OK
+> raw RESET_PN ff:ff:ff:ff:ff:ff
+OK
+
+Test broadcast connectivity; should not work; if it does, the device
+does not implement protection for delayed retransmission of 4-way
+handshake EAPOL-Key Message 3/4.
+
+Variant 1: Include extra Message 1/4
+
+Otherwise same as above, but replace RESEND_M3 command with:
+
+> raw RESEND_M1 <DUT MAC address>
+OK
+> raw RESEND_M3 <DUT MAC address>
+OK
+
+Variant 2: Include two extra Message 1/4
+
+Otherwise same as above, but replace RESEND_M3 command with:
+
+> raw RESEND_M1 <DUT MAC address> change-anonce
+OK
+> raw RESEND_M1 <DUT MAC address>
+OK
+> raw RESEND_M3 <DUT MAC address>
+OK
+
+
+Testing TK reinstallation protection on a station device (4-way handshake)
+--------------------------------------------------------------------------
+
+Use the procedure described above for testing replay protection for
+unicast traffic, but with the following hostapd_cli commands:
+
+Test unicast connectivity; should work
+
+> raw RESEND_M3 <DUT MAC address>
+OK
+> raw RESET_PN <DUT MAC address>
+OK
+
+Test unicast connectivity; should not work; if it does, the device
+does not implement protection for delayed retransmission of 4-way
+handshake EAPOL-Key Message 3/4.
+
+Variant 1: Include extra Message 1/4
+
+Otherwise same as above, but replace RESEND_M3 command with:
+
+> raw RESEND_M1 <DUT MAC address>
+OK
+> raw RESEND_M3 <DUT MAC address>
+OK
+
+Variant 2: Include two extra Message 1/4
+
+Otherwise same as above, but replace RESEND_M3 command with:
+
+> raw RESEND_M1 <DUT MAC address> change-anonce
+OK
+> raw RESEND_M1 <DUT MAC address>
+OK
+> raw RESEND_M3 <DUT MAC address>
+OK
+
+
+Testing ANonce generation on an AP device
+-----------------------------------------
+
+Start the AP (DUT) and start wpa_supplicant on the test device to
+connect to the network. Use wpa_cli to control the test device.
+
+<3>SME: Trying to authenticate with <DUT MAC address> (SSID='test-psk' freq=5240 MHz)
+<3>CTRL-EVENT-REGDOM-CHANGE init=CORE type=WORLD
+<3>Trying to associate with <DUT MAC address> (SSID='test-psk' freq=5240 MHz)
+<3>Associated with <DUT MAC address>
+<3>WPA: Key negotiation completed with <DUT MAC address> [PTK=CCMP GTK=CCMP]
+<3>CTRL-EVENT-CONNECTED - Connection to <DUT MAC address> completed [id=0 id_str=]
+
+Show the ANonce from the first 4-way handshake, request PTK rekeying,
+and show the ANonce from the second 4-way handshake:
+
+> GET anonce
+df8c61d1f1f7aca9f1739dd888199547f4af2b8b07f8bf15b45ea271da0072b2
+> raw KEY_REQUEST 0 1
+OK
+> GET anonce
+d8ddcb716f28abfdf1352a05d51e7a70f58802122e99d13c730c3c0f09594aac
+
+If the ANonce values are same, the AP did not update the ANonce for
+rekeying (it should have as shown in the example above).
+
+
+Testing FT Reassociation Request frame retransmission on an AP device
+---------------------------------------------------------------------
+
+This test case requires a sniffer to be used and manually analyzed.
+
+Enable FT on the DUT AP (likely two AP devices needed), connect test
+tool to the AP using FT protocol (e.g., connect to another AP first and
+then use the "ROAM <BSSID>" command), and do the following steps:
+
+- verify unicast traffic from the AP to test station (either ping from
+ the AP or from a device behind the AP); this needs to work
+- wpa_cli "raw RESEND_ASSOC"
+- verify unicast traffic from the AP to test station (either ping from
+ the AP or from a device behind the AP); this is likely to fail, but
+ the real analysis is done based on the sniffer capture
+
+In the sniffer capture, find the last Reassociation Request frame from
+the test station (this is more or less identical to the previous one and
+the only one that should not have Authentication frame exchange before
+it). Look at the last used PN in a unicast Data frame from the AP to the
+test station before the last Reassociation Request frame and the PN in
+the following unicast Data frame after the last Reassociation Request
+frame. If the PN goes down (e.g., is reset to 1), this would be a sign
+of a likely security vulnerability. The AP's TK configuration should be
+verified (i.e., whether it is configuring the same TK again and then
+allowing it to be used with reused PN values).
diff --git a/contrib/wpa/tests/fuzzing/README b/contrib/wpa/tests/fuzzing/README
new file mode 100644
index 000000000000..9ba0f176f2cd
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/README
@@ -0,0 +1,23 @@
+hostap.git fuzz testing
+-----------------------
+
+These tools can be used for fuzz testing of various components used
+within wpa_supplicant and hostapd. Each directory contains a fuzzing
+tool that focuses on one input handler. Each tool can be compiled either
+to work with the libFuzzer or as a separate tool that reads the input
+from a file specified on the command line, e.g., for American fuzzy lop
+(afl-fuzz). Example test corpus is included in */corpus directory.
+
+Example fuzzing with libFuzzer
+
+cd @TOOL@
+make clean
+make LIBFUZZER=y
+./@TOOL@ corpus
+
+Example fuzzing with afl-fuzz
+
+cd @TOOL@
+make clean
+CC=afl-gcc make
+afl-fuzz -i corpus -o findings -- $PWD/@TOOL@ @@
diff --git a/contrib/wpa/tests/fuzzing/ap-mgmt/.gitignore b/contrib/wpa/tests/fuzzing/ap-mgmt/.gitignore
new file mode 100644
index 000000000000..8d79d3c8062d
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/ap-mgmt/.gitignore
@@ -0,0 +1 @@
+ap-mgmt
diff --git a/contrib/wpa/tests/fuzzing/ap-mgmt/Makefile b/contrib/wpa/tests/fuzzing/ap-mgmt/Makefile
new file mode 100644
index 000000000000..74b6a02da3fd
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/ap-mgmt/Makefile
@@ -0,0 +1,44 @@
+ALL=ap-mgmt
+include ../rules.include
+
+CFLAGS += -DCONFIG_WNM
+CFLAGS += -DCONFIG_INTERWORKING
+CFLAGS += -DCONFIG_GAS
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DIEEE8021X_EAPOL
+CFLAGS += -DNEED_AP_MLME
+CFLAGS += -DCONFIG_AIRTIME_POLICY
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/wps/libwps.a
+LIBS += $(SRC)/eap_server/libeap_server.a
+LIBS += $(SRC)/eap_common/libeap_common.a
+LIBS += $(SRC)/l2_packet/libl2_packet.a
+LIBS += $(SRC)/ap/libap.a
+LIBS += $(SRC)/eapol_auth/libeapol_auth.a
+LIBS += $(SRC)/radius/libradius.a
+LIBS += $(SRC)/utils/libutils.a
+
+ELIBS += $(SRC)/crypto/libcrypto.a
+ELIBS += $(SRC)/tls/libtls.a
+
+OBJS += $(SRC)/drivers/driver_common.o
+
+OBJS += ap-mgmt.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+_OBJS_VAR := ELIBS
+include ../../../src/objs.mk
+
+ap-mgmt: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+
+clean: common-clean
+ rm -f ap-mgmt *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/ap-mgmt/ap-mgmt.c b/contrib/wpa/tests/fuzzing/ap-mgmt/ap-mgmt.c
new file mode 100644
index 000000000000..d49ac5f08aea
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/ap-mgmt/ap-mgmt.c
@@ -0,0 +1,167 @@
+/*
+ * hostapd - Management frame fuzzer
+ * Copyright (c) 2015-2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/eloop.h"
+#include "ap/hostapd.h"
+#include "ap/hw_features.h"
+#include "ap/ieee802_11.h"
+#include "ap/sta_info.h"
+#include "ap/ap_list.h"
+#include "../fuzzer-common.h"
+
+
+const struct wpa_driver_ops *const wpa_drivers[] =
+{
+ NULL
+};
+
+
+struct arg_ctx {
+ const u8 *data;
+ size_t data_len;
+ struct hostapd_iface iface;
+ struct hostapd_data hapd;
+ struct wpa_driver_ops driver;
+ struct hostapd_config iconf;
+ struct hostapd_bss_config conf;
+};
+
+
+static void test_send_mgmt(void *eloop_data, void *user_ctx)
+{
+ struct arg_ctx *ctx = eloop_data;
+ struct hostapd_frame_info fi;
+ const u8 *pos, *end;
+
+ os_memset(&fi, 0, sizeof(fi));
+
+ pos = ctx->data;
+ end = pos + ctx->data_len;
+
+ while (end - pos > 2) {
+ u16 flen;
+
+ flen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < flen)
+ break;
+ wpa_hexdump(MSG_MSGDUMP, "fuzzer - frame", pos, flen);
+ ieee802_11_mgmt(&ctx->hapd, pos, flen, &fi);
+ pos += flen;
+ }
+
+ eloop_terminate();
+}
+
+
+static struct hostapd_hw_modes * gen_modes(void)
+{
+ struct hostapd_hw_modes *mode;
+ struct hostapd_channel_data *chan;
+
+ mode = os_zalloc(sizeof(struct hostapd_hw_modes));
+ if (!mode)
+ return NULL;
+
+ mode->mode = HOSTAPD_MODE_IEEE80211G;
+ chan = os_zalloc(sizeof(struct hostapd_channel_data));
+ if (!chan) {
+ os_free(mode);
+ return NULL;
+ }
+ chan->chan = 1;
+ chan->freq = 2412;
+ mode->channels = chan;
+ mode->num_channels = 1;
+
+ mode->rates = os_zalloc(sizeof(int));
+ if (!mode->rates) {
+ os_free(chan);
+ os_free(mode);
+ return NULL;
+ }
+ mode->rates[0] = 10;
+ mode->num_rates = 1;
+
+ return mode;
+}
+
+
+static int init_hapd(struct arg_ctx *ctx)
+{
+ struct hostapd_data *hapd = &ctx->hapd;
+ struct sta_info *sta;
+ struct hostapd_bss_config *bss;
+
+ hapd->driver = &ctx->driver;
+ os_memcpy(hapd->own_addr, "\x02\x00\x00\x00\x03\x00", ETH_ALEN);
+ hapd->iface = &ctx->iface;
+ hapd->iface->conf = hostapd_config_defaults();
+ if (!hapd->iface->conf)
+ return -1;
+ hapd->iface->hw_features = gen_modes();
+ hapd->iface->num_hw_features = 1;
+ hapd->iface->current_mode = hapd->iface->hw_features;
+ hapd->iconf = hapd->iface->conf;
+ hapd->iconf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+ hapd->iconf->channel = 1;
+ bss = hapd->conf = hapd->iconf->bss[0];
+ hostapd_config_defaults_bss(hapd->conf);
+ os_memcpy(bss->ssid.ssid, "test", 4);
+ bss->ssid.ssid_len = 4;
+ bss->ssid.ssid_set = 1;
+
+ sta = ap_sta_add(hapd, (u8 *) "\x02\x00\x00\x00\x00\x00");
+ if (sta)
+ sta->flags |= WLAN_STA_ASSOC | WLAN_STA_WMM;
+
+ return 0;
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct arg_ctx ctx;
+
+ wpa_fuzzer_set_debug_level();
+
+ if (os_program_init())
+ return 0;
+
+ if (eloop_init()) {
+ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+ return 0;
+ }
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.data = data;
+ ctx.data_len = size;
+
+ if (init_hapd(&ctx))
+ goto fail;
+
+ eloop_register_timeout(0, 0, test_send_mgmt, &ctx, NULL);
+
+ wpa_printf(MSG_DEBUG, "Starting eloop");
+ eloop_run();
+ wpa_printf(MSG_DEBUG, "eloop done");
+ hostapd_free_stas(&ctx.hapd);
+ hostapd_free_hw_features(ctx.hapd.iface->hw_features,
+ ctx.hapd.iface->num_hw_features);
+
+fail:
+ hostapd_config_free(ctx.hapd.iconf);
+ ap_list_deinit(&ctx.iface);
+ eloop_destroy();
+ os_program_deinit();
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/ap-mgmt/corpus/multi-sae-ffc.dat b/contrib/wpa/tests/fuzzing/ap-mgmt/corpus/multi-sae-ffc.dat
new file mode 100644
index 000000000000..8c0059d5f322
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/ap-mgmt/corpus/multi-sae-ffc.dat differ
diff --git a/contrib/wpa/tests/fuzzing/ap-mgmt/corpus/multi-sae.dat b/contrib/wpa/tests/fuzzing/ap-mgmt/corpus/multi-sae.dat
new file mode 100644
index 000000000000..31a0d091dba9
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/ap-mgmt/corpus/multi-sae.dat differ
diff --git a/contrib/wpa/tests/fuzzing/ap-mgmt/corpus/multi.dat b/contrib/wpa/tests/fuzzing/ap-mgmt/corpus/multi.dat
new file mode 100644
index 000000000000..29d074e0c414
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/ap-mgmt/corpus/multi.dat differ
diff --git a/contrib/wpa/tests/fuzzing/asn1/.gitignore b/contrib/wpa/tests/fuzzing/asn1/.gitignore
new file mode 100644
index 000000000000..69d0e5c726be
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/asn1/.gitignore
@@ -0,0 +1 @@
+asn1
diff --git a/contrib/wpa/tests/fuzzing/asn1/Makefile b/contrib/wpa/tests/fuzzing/asn1/Makefile
new file mode 100644
index 000000000000..274641a406bb
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/asn1/Makefile
@@ -0,0 +1,23 @@
+ALL=asn1
+include ../rules.include
+
+OBJS += $(SRC)/utils/common.o
+OBJS += $(SRC)/utils/os_unix.o
+OBJS += $(SRC)/utils/wpa_debug.o
+OBJS += $(SRC)/utils/wpabuf.o
+OBJS += $(SRC)/tls/asn1.o
+
+OBJS += asn1.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+asn1: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+
+clean: common-clean
+ $(MAKE) -C $(SRC) clean
+ rm -f asn1 *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/asn1/asn1.c b/contrib/wpa/tests/fuzzing/asn1/asn1.c
new file mode 100644
index 000000000000..2cd18fe994ef
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/asn1/asn1.c
@@ -0,0 +1,184 @@
+/*
+ * Fuzzing tool for ASN.1 routines
+ * Copyright (c) 2006-2019, 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 "common.h"
+#include "tls/asn1.h"
+#include "../fuzzer-common.h"
+
+
+static const char * asn1_class_str(int class)
+{
+ switch (class) {
+ case ASN1_CLASS_UNIVERSAL:
+ return "Universal";
+ case ASN1_CLASS_APPLICATION:
+ return "Application";
+ case ASN1_CLASS_CONTEXT_SPECIFIC:
+ return "Context-specific";
+ case ASN1_CLASS_PRIVATE:
+ return "Private";
+ default:
+ return "?";
+ }
+}
+
+
+static int asn1_parse(const u8 *buf, size_t len, int level)
+{
+ const u8 *pos, *prev, *end;
+ char prefix[10], str[100];
+ int _level;
+ struct asn1_hdr hdr;
+ struct asn1_oid oid;
+ u8 tmp;
+
+ _level = level;
+ if ((size_t) _level > sizeof(prefix) - 1)
+ _level = sizeof(prefix) - 1;
+ memset(prefix, ' ', _level);
+ prefix[_level] = '\0';
+
+ pos = buf;
+ end = buf + len;
+
+ while (pos < end) {
+ if (asn1_get_next(pos, end - pos, &hdr) < 0)
+ return -1;
+
+ prev = pos;
+ pos = hdr.payload;
+
+ wpa_printf(MSG_MSGDUMP, "ASN.1:%s Class %d(%s) P/C %d(%s) "
+ "Tag %u Length %u",
+ prefix, hdr.class, asn1_class_str(hdr.class),
+ hdr.constructed,
+ hdr.constructed ? "Constructed" : "Primitive",
+ hdr.tag, hdr.length);
+
+ if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC &&
+ hdr.constructed) {
+ if (asn1_parse(pos, hdr.length, level + 1) < 0)
+ return -1;
+ pos += hdr.length;
+ }
+
+ if (hdr.class != ASN1_CLASS_UNIVERSAL)
+ continue;
+
+ switch (hdr.tag) {
+ case ASN1_TAG_EOC:
+ if (hdr.length) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Non-zero "
+ "end-of-contents length (%u)",
+ hdr.length);
+ return -1;
+ }
+ wpa_printf(MSG_MSGDUMP, "ASN.1:%s EOC", prefix);
+ break;
+ case ASN1_TAG_BOOLEAN:
+ if (hdr.length != 1) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Unexpected "
+ "Boolean length (%u)", hdr.length);
+ return -1;
+ }
+ tmp = *pos++;
+ wpa_printf(MSG_MSGDUMP, "ASN.1:%s Boolean %s",
+ prefix, tmp ? "TRUE" : "FALSE");
+ break;
+ case ASN1_TAG_INTEGER:
+ wpa_hexdump(MSG_MSGDUMP, "ASN.1: INTEGER",
+ pos, hdr.length);
+ pos += hdr.length;
+ break;
+ case ASN1_TAG_BITSTRING:
+ wpa_hexdump(MSG_MSGDUMP, "ASN.1: BitString",
+ pos, hdr.length);
+ pos += hdr.length;
+ break;
+ case ASN1_TAG_OCTETSTRING:
+ wpa_hexdump(MSG_MSGDUMP, "ASN.1: OctetString",
+ pos, hdr.length);
+ pos += hdr.length;
+ break;
+ case ASN1_TAG_NULL:
+ if (hdr.length) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Non-zero Null "
+ "length (%u)", hdr.length);
+ return -1;
+ }
+ wpa_printf(MSG_MSGDUMP, "ASN.1:%s Null", prefix);
+ break;
+ case ASN1_TAG_OID:
+ if (asn1_get_oid(prev, end - prev, &oid, &prev) < 0) {
+ wpa_printf(MSG_DEBUG, "ASN.1: Invalid OID");
+ return -1;
+ }
+ asn1_oid_to_str(&oid, str, sizeof(str));
+ wpa_printf(MSG_DEBUG, "ASN.1:%s OID %s", prefix, str);
+ pos += hdr.length;
+ break;
+ case ANS1_TAG_RELATIVE_OID:
+ wpa_hexdump(MSG_MSGDUMP, "ASN.1: Relative OID",
+ pos, hdr.length);
+ pos += hdr.length;
+ break;
+ case ASN1_TAG_SEQUENCE:
+ wpa_printf(MSG_MSGDUMP, "ASN.1:%s SEQUENCE", prefix);
+ if (asn1_parse(pos, hdr.length, level + 1) < 0)
+ return -1;
+ pos += hdr.length;
+ break;
+ case ASN1_TAG_SET:
+ wpa_printf(MSG_MSGDUMP, "ASN.1:%s SET", prefix);
+ if (asn1_parse(pos, hdr.length, level + 1) < 0)
+ return -1;
+ pos += hdr.length;
+ break;
+ case ASN1_TAG_PRINTABLESTRING:
+ wpa_hexdump_ascii(MSG_MSGDUMP,
+ "ASN.1: PrintableString",
+ pos, hdr.length);
+ pos += hdr.length;
+ break;
+ case ASN1_TAG_IA5STRING:
+ wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: IA5String",
+ pos, hdr.length);
+ pos += hdr.length;
+ break;
+ case ASN1_TAG_UTCTIME:
+ wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: UTCTIME",
+ pos, hdr.length);
+ pos += hdr.length;
+ break;
+ case ASN1_TAG_VISIBLESTRING:
+ wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: VisibleString",
+ pos, hdr.length);
+ pos += hdr.length;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "ASN.1: Unknown tag %d",
+ hdr.tag);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ wpa_fuzzer_set_debug_level();
+
+ if (asn1_parse(data, size, 0) < 0)
+ wpa_printf(MSG_DEBUG, "Failed to parse DER ASN.1");
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/asn1/corpus/ca.der b/contrib/wpa/tests/fuzzing/asn1/corpus/ca.der
new file mode 100644
index 000000000000..09d5fa051bf3
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/asn1/corpus/ca.der differ
diff --git a/contrib/wpa/tests/fuzzing/asn1/corpus/ocsp-multi-server-cache.der b/contrib/wpa/tests/fuzzing/asn1/corpus/ocsp-multi-server-cache.der
new file mode 100644
index 000000000000..36be8118aa1d
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/asn1/corpus/ocsp-multi-server-cache.der differ
diff --git a/contrib/wpa/tests/fuzzing/asn1/corpus/ocsp-req.der b/contrib/wpa/tests/fuzzing/asn1/corpus/ocsp-req.der
new file mode 100644
index 000000000000..3a70e3872d4c
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/asn1/corpus/ocsp-req.der differ
diff --git a/contrib/wpa/tests/fuzzing/build-test.sh b/contrib/wpa/tests/fuzzing/build-test.sh
new file mode 100755
index 000000000000..26c94cca833d
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/build-test.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+error()
+{
+ echo "Build test failed"
+ exit 1
+}
+
+for i in *; do
+ if [ -d $i ]; then
+ cd $i
+ make clean
+ make -j8 || error
+ make clean
+ cd ..
+ fi
+done
+
+echo "Build test succeeded"
diff --git a/contrib/wpa/tests/fuzzing/dpp-uri/.gitignore b/contrib/wpa/tests/fuzzing/dpp-uri/.gitignore
new file mode 100644
index 000000000000..6dd276499bef
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/dpp-uri/.gitignore
@@ -0,0 +1 @@
+dpp-uri
diff --git a/contrib/wpa/tests/fuzzing/dpp-uri/Makefile b/contrib/wpa/tests/fuzzing/dpp-uri/Makefile
new file mode 100644
index 000000000000..1c1bab631a04
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/dpp-uri/Makefile
@@ -0,0 +1,43 @@
+all: dpp-uri
+include ../rules.include
+
+CFLAGS += -DCONFIG_DPP
+CFLAGS += -DCONFIG_DPP2
+CFLAGS += -DCONFIG_SHA256
+CFLAGS += -DCONFIG_SHA384
+CFLAGS += -DCONFIG_SHA512
+CFLAGS += -DCONFIG_ECC
+CFLAGS += -DCONFIG_OPENSSL_CMAC
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/utils/libutils.a
+
+OBJS += $(SRC)/crypto/crypto_openssl.o
+
+OBJS += $(SRC)/crypto/aes-ctr.o
+OBJS += $(SRC)/crypto/aes-siv.o
+OBJS += $(SRC)/crypto/sha256-kdf.o
+OBJS += $(SRC)/crypto/sha384-kdf.o
+OBJS += $(SRC)/crypto/sha512-kdf.o
+OBJS += $(SRC)/tls/asn1.o
+OBJS += $(SRC)/common/dpp.o
+OBJS += $(SRC)/common/dpp_auth.o
+OBJS += $(SRC)/common/dpp_backup.o
+OBJS += $(SRC)/common/dpp_crypto.o
+OBJS += $(SRC)/common/dpp_pkex.o
+OBJS += $(SRC)/common/dpp_reconfig.o
+OBJS += $(SRC)/common/dpp_tcp.o
+
+OBJS += dpp-uri.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+dpp-uri: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ -lcrypto
+
+clean: common-clean
+ rm -f dpp-uri *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/dpp-uri/corpus/1.dat b/contrib/wpa/tests/fuzzing/dpp-uri/corpus/1.dat
new file mode 100644
index 000000000000..b2387e09ad07
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/dpp-uri/corpus/1.dat
@@ -0,0 +1 @@
+DPP:K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;
\ No newline at end of file
diff --git a/contrib/wpa/tests/fuzzing/dpp-uri/corpus/2.dat b/contrib/wpa/tests/fuzzing/dpp-uri/corpus/2.dat
new file mode 100644
index 000000000000..ee2ff90dd360
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/dpp-uri/corpus/2.dat
@@ -0,0 +1 @@
+DPP:C:81/1,115/36;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;
\ No newline at end of file
diff --git a/contrib/wpa/tests/fuzzing/dpp-uri/corpus/3.dat b/contrib/wpa/tests/fuzzing/dpp-uri/corpus/3.dat
new file mode 100644
index 000000000000..ce7ad16f3329
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/dpp-uri/corpus/3.dat
@@ -0,0 +1 @@
+DPP:I:SN=4774LH2b4044;M:010203040506;C:81/1,115/36;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;
\ No newline at end of file
diff --git a/contrib/wpa/tests/fuzzing/dpp-uri/dpp-uri.c b/contrib/wpa/tests/fuzzing/dpp-uri/dpp-uri.c
new file mode 100644
index 000000000000..77db5b8bbf40
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/dpp-uri/dpp-uri.c
@@ -0,0 +1,51 @@
+/*
+ * DPP URI fuzzer
+ * Copyright (c) 2020, Jouni Malinen <j@w1.fi>
+ *
+ * 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/dpp.h"
+#include "../fuzzer-common.h"
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct dpp_global *dpp;
+ struct dpp_global_config config;
+ struct dpp_bootstrap_info *bi;
+ char *uri;
+ char buf[1000];
+ int ret = -1;
+
+ wpa_fuzzer_set_debug_level();
+
+ if (os_program_init())
+ return 0;
+
+ uri = os_malloc(size + 1);
+ if (!uri)
+ goto out;
+ os_memcpy(uri, data, size);
+ uri[size] = '\0';
+ os_memset(&config, 0, sizeof(config));
+ dpp = dpp_global_init(&config);
+ if (!dpp)
+ goto out;
+
+ bi = dpp_add_qr_code(dpp, uri);
+ if (bi && dpp_bootstrap_info(dpp, bi->id, buf, sizeof(buf)) > 0)
+ wpa_printf(MSG_DEBUG, "DPP: %s", buf);
+ dpp_global_deinit(dpp);
+
+ ret = 0;
+out:
+ os_free(uri);
+ os_program_deinit();
+
+ return ret;
+}
diff --git a/contrib/wpa/tests/fuzzing/eap-aka-peer/.gitignore b/contrib/wpa/tests/fuzzing/eap-aka-peer/.gitignore
new file mode 100644
index 000000000000..d11f75fe3bd9
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eap-aka-peer/.gitignore
@@ -0,0 +1 @@
+eap-aka-peer
diff --git a/contrib/wpa/tests/fuzzing/eap-aka-peer/Makefile b/contrib/wpa/tests/fuzzing/eap-aka-peer/Makefile
new file mode 100644
index 000000000000..c964f186d146
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eap-aka-peer/Makefile
@@ -0,0 +1,26 @@
+ALL=eap-aka-peer
+include ../rules.include
+
+CFLAGS += -DIEEE8021X_EAPOL
+CFLAGS += -DCONFIG_USIM_SIMULATOR
+
+OBJS += $(SRC)/eap_peer/eap_aka.o
+OBJS += $(SRC)/eap_common/eap_sim_common.o
+OBJS += $(SRC)/eap_common/eap_common.o
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/utils/libutils.a
+
+OBJS += eap-aka-peer.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+eap-aka-peer: $(OBJS) $(LIBS)
+ $(Q)$(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+ @$(E) " LD " $@
+
+clean: common-clean
+ rm -f eap-aka-peer *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/eap-aka-peer/corpus/server.msg b/contrib/wpa/tests/fuzzing/eap-aka-peer/corpus/server.msg
new file mode 100644
index 000000000000..64843912d838
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/eap-aka-peer/corpus/server.msg differ
diff --git a/contrib/wpa/tests/fuzzing/eap-aka-peer/eap-aka-peer.c b/contrib/wpa/tests/fuzzing/eap-aka-peer/eap-aka-peer.c
new file mode 100644
index 000000000000..db06ed52b1de
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eap-aka-peer/eap-aka-peer.c
@@ -0,0 +1,131 @@
+/*
+ * EAP-AKA peer fuzzer
+ * Copyright (c) 2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "eap_peer/eap_methods.h"
+#include "eap_peer/eap_config.h"
+#include "eap_peer/eap_i.h"
+#include "../fuzzer-common.h"
+
+int eap_peer_sim_register(void);
+
+struct eap_method * registered_eap_method = NULL;
+
+
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+ enum eap_type method,
+ const char *name)
+{
+ struct eap_method *eap;
+ eap = os_zalloc(sizeof(*eap));
+ if (!eap)
+ return NULL;
+ eap->version = version;
+ eap->vendor = vendor;
+ eap->method = method;
+ eap->name = name;
+ return eap;
+}
+
+
+int eap_peer_method_register(struct eap_method *method)
+{
+ registered_eap_method = method;
+ return 0;
+}
+
+
+static struct eap_peer_config eap_aka_config = {
+ .identity = (u8 *) "0232010000000000",
+ .identity_len = 16,
+ .password = (u8 *) "90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ .password_len = 78,
+};
+
+struct eap_peer_config * eap_get_config(struct eap_sm *sm)
+{
+ return &eap_aka_config;
+}
+
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
+{
+ static const char *id = "0232010000000000";
+
+ *len = os_strlen(id);
+ return (const u8 *) id;
+}
+
+
+const char * eap_get_config_phase1(struct eap_sm *sm)
+{
+ return NULL;
+}
+
+
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len)
+{
+}
+
+
+void eap_sm_request_identity(struct eap_sm *sm)
+{
+}
+
+
+void eap_sm_request_sim(struct eap_sm *sm, const char *req)
+{
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ const u8 *pos, *end;
+ struct eap_sm *sm;
+ void *priv;
+ struct eap_method_ret ret;
+
+ wpa_fuzzer_set_debug_level();
+
+ eap_peer_aka_register();
+ sm = os_zalloc(sizeof(*sm));
+ if (!sm)
+ return 0;
+ priv = registered_eap_method->init(sm);
+ os_memset(&ret, 0, sizeof(ret));
+
+ pos = data;
+ end = pos + size;
+
+ while (end - pos > 2) {
+ u16 flen;
+ struct wpabuf *buf, *req;
+
+ flen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < flen)
+ break;
+ req = wpabuf_alloc_copy(pos, flen);
+ if (!req)
+ break;
+ wpa_hexdump_buf(MSG_MSGDUMP, "fuzzer - request", req);
+ buf = registered_eap_method->process(sm, priv, &ret, req);
+ wpa_hexdump_buf(MSG_MSGDUMP, "fuzzer - local response", buf);
+ wpabuf_free(req);
+ wpabuf_free(buf);
+ pos += flen;
+ }
+
+ registered_eap_method->deinit(sm, priv);
+ os_free(registered_eap_method);
+ os_free(sm);
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/.gitignore b/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/.gitignore
new file mode 100644
index 000000000000..3368b213cf0c
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/.gitignore
@@ -0,0 +1 @@
+eap-mschapv2-peer
diff --git a/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/Makefile b/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/Makefile
new file mode 100644
index 000000000000..7290e90c62b7
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/Makefile
@@ -0,0 +1,25 @@
+ALL=eap-mschapv2-peer
+include ../rules.include
+
+CFLAGS += -DIEEE8021X_EAPOL
+
+OBJS += $(SRC)/eap_peer/eap_mschapv2.o
+OBJS += $(SRC)/eap_peer/mschapv2.o
+OBJS += $(SRC)/eap_common/eap_common.o
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/utils/libutils.a
+
+OBJS += eap-mschapv2-peer.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+eap-mschapv2-peer: $(OBJS) $(LIBS)
+ $(Q)$(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+ @$(E) " LD " $@
+
+clean: common-clean
+ rm -f eap-mschapv2-peer *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/corpus/server.msg b/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/corpus/server.msg
new file mode 100644
index 000000000000..50ff9d19e3a5
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/corpus/server.msg differ
diff --git a/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/eap-mschapv2-peer.c b/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/eap-mschapv2-peer.c
new file mode 100644
index 000000000000..8dc794cf867d
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eap-mschapv2-peer/eap-mschapv2-peer.c
@@ -0,0 +1,152 @@
+/*
+ * EAP-SIM peer fuzzer
+ * Copyright (c) 2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "eap_peer/eap_methods.h"
+#include "eap_peer/eap_config.h"
+#include "eap_peer/eap_i.h"
+#include "../fuzzer-common.h"
+
+int eap_peer_sim_register(void);
+
+struct eap_method * registered_eap_method = NULL;
+
+
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+ enum eap_type method,
+ const char *name)
+{
+ struct eap_method *eap;
+ eap = os_zalloc(sizeof(*eap));
+ if (!eap)
+ return NULL;
+ eap->version = version;
+ eap->vendor = vendor;
+ eap->method = method;
+ eap->name = name;
+ return eap;
+}
+
+
+int eap_peer_method_register(struct eap_method *method)
+{
+ registered_eap_method = method;
+ return 0;
+}
+
+
+static struct eap_peer_config eap_mschapv2_config = {
+ .identity = (u8 *) "user",
+ .identity_len = 4,
+ .password = (u8 *) "password",
+ .password_len = 8,
+};
+
+struct eap_peer_config * eap_get_config(struct eap_sm *sm)
+{
+ return &eap_mschapv2_config;
+}
+
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
+{
+ static const char *id = "user";
+
+ *len = os_strlen(id);
+ return (const u8 *) id;
+}
+
+
+const u8 * eap_get_config_password(struct eap_sm *sm, size_t *len)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ *len = config->password_len;
+ return config->password;
+}
+
+
+const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
+{
+ struct eap_peer_config *config = eap_get_config(sm);
+
+ *len = config->password_len;
+ if (hash)
+ *hash = !!(config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH);
+ return config->password;
+}
+
+
+const u8 * eap_get_config_new_password(struct eap_sm *sm, size_t *len)
+{
+ *len = 3;
+ return (const u8 *) "new";
+}
+
+
+void eap_sm_request_identity(struct eap_sm *sm)
+{
+}
+
+
+void eap_sm_request_password(struct eap_sm *sm)
+{
+}
+
+
+void eap_sm_request_new_password(struct eap_sm *sm)
+{
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ const u8 *pos, *end;
+ struct eap_sm *sm;
+ void *priv;
+ struct eap_method_ret ret;
+
+ wpa_fuzzer_set_debug_level();
+
+ eap_peer_mschapv2_register();
+ sm = os_zalloc(sizeof(*sm));
+ if (!sm)
+ return 0;
+ priv = registered_eap_method->init(sm);
+ os_memset(&ret, 0, sizeof(ret));
+
+ pos = data;
+ end = pos + size;
+
+ while (end - pos > 2) {
+ u16 flen;
+ struct wpabuf *buf, *req;
+
+ flen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < flen)
+ break;
+ req = wpabuf_alloc_copy(pos, flen);
+ if (!req)
+ break;
+ wpa_hexdump_buf(MSG_MSGDUMP, "fuzzer - request", req);
+ buf = registered_eap_method->process(sm, priv, &ret, req);
+ wpa_hexdump_buf(MSG_MSGDUMP, "fuzzer - local response", buf);
+ wpabuf_free(req);
+ wpabuf_free(buf);
+ pos += flen;
+ }
+
+ registered_eap_method->deinit(sm, priv);
+ os_free(registered_eap_method);
+ os_free(sm);
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/eap-sim-peer/.gitignore b/contrib/wpa/tests/fuzzing/eap-sim-peer/.gitignore
new file mode 100644
index 000000000000..ea94e26e2eb8
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eap-sim-peer/.gitignore
@@ -0,0 +1 @@
+eap-sim-peer
diff --git a/contrib/wpa/tests/fuzzing/eap-sim-peer/Makefile b/contrib/wpa/tests/fuzzing/eap-sim-peer/Makefile
new file mode 100644
index 000000000000..9e728e4f476b
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eap-sim-peer/Makefile
@@ -0,0 +1,26 @@
+ALL=eap-sim-peer
+include ../rules.include
+
+CFLAGS += -DIEEE8021X_EAPOL
+CFLAGS += -DCONFIG_SIM_SIMULATOR
+
+OBJS += $(SRC)/eap_peer/eap_sim.o
+OBJS += $(SRC)/eap_common/eap_sim_common.o
+OBJS += $(SRC)/eap_common/eap_common.o
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/utils/libutils.a
+
+OBJS += eap-sim-peer.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+eap-sim-peer: $(OBJS) $(LIBS)
+ $(Q)$(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+ @$(E) " LD " $@
+
+clean: common-clean
+ rm -f eap-sim-peer *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/eap-sim-peer/corpus/server.msg b/contrib/wpa/tests/fuzzing/eap-sim-peer/corpus/server.msg
new file mode 100644
index 000000000000..adb9f6c6fea9
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/eap-sim-peer/corpus/server.msg differ
diff --git a/contrib/wpa/tests/fuzzing/eap-sim-peer/eap-sim-peer.c b/contrib/wpa/tests/fuzzing/eap-sim-peer/eap-sim-peer.c
new file mode 100644
index 000000000000..b6798ee47869
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eap-sim-peer/eap-sim-peer.c
@@ -0,0 +1,125 @@
+/*
+ * EAP-SIM peer fuzzer
+ * Copyright (c) 2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "eap_peer/eap_methods.h"
+#include "eap_peer/eap_config.h"
+#include "eap_peer/eap_i.h"
+#include "../fuzzer-common.h"
+
+int eap_peer_sim_register(void);
+
+struct eap_method * registered_eap_method = NULL;
+
+
+struct eap_method * eap_peer_method_alloc(int version, int vendor,
+ enum eap_type method,
+ const char *name)
+{
+ struct eap_method *eap;
+ eap = os_zalloc(sizeof(*eap));
+ if (!eap)
+ return NULL;
+ eap->version = version;
+ eap->vendor = vendor;
+ eap->method = method;
+ eap->name = name;
+ return eap;
+}
+
+
+int eap_peer_method_register(struct eap_method *method)
+{
+ registered_eap_method = method;
+ return 0;
+}
+
+
+static struct eap_peer_config eap_sim_config = {
+ .identity = (u8 *) "1232010000000000",
+ .identity_len = 16,
+ .password = (u8 *) "90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ .password_len = 65,
+};
+
+struct eap_peer_config * eap_get_config(struct eap_sm *sm)
+{
+ return &eap_sim_config;
+}
+
+
+const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len)
+{
+ static const char *id = "1232010000000000";
+
+ *len = os_strlen(id);
+ return (const u8 *) id;
+}
+
+
+void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len)
+{
+}
+
+
+void eap_sm_request_identity(struct eap_sm *sm)
+{
+}
+
+
+void eap_sm_request_sim(struct eap_sm *sm, const char *req)
+{
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ const u8 *pos, *end;
+ struct eap_sm *sm;
+ void *priv;
+ struct eap_method_ret ret;
+
+ wpa_fuzzer_set_debug_level();
+
+ eap_peer_sim_register();
+ sm = os_zalloc(sizeof(*sm));
+ if (!sm)
+ return 0;
+ priv = registered_eap_method->init(sm);
+ os_memset(&ret, 0, sizeof(ret));
+
+ pos = data;
+ end = pos + size;
+
+ while (end - pos > 2) {
+ u16 flen;
+ struct wpabuf *buf, *req;
+
+ flen = WPA_GET_BE16(pos);
+ pos += 2;
+ if (end - pos < flen)
+ break;
+ req = wpabuf_alloc_copy(pos, flen);
+ if (!req)
+ break;
+ wpa_hexdump_buf(MSG_MSGDUMP, "fuzzer - request", req);
+ buf = registered_eap_method->process(sm, priv, &ret, req);
+ wpa_hexdump_buf(MSG_MSGDUMP, "fuzzer - local response", buf);
+ wpabuf_free(req);
+ wpabuf_free(buf);
+ pos += flen;
+ }
+
+ registered_eap_method->deinit(sm, priv);
+ os_free(registered_eap_method);
+ os_free(sm);
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/eapol-key-auth/.gitignore b/contrib/wpa/tests/fuzzing/eapol-key-auth/.gitignore
new file mode 100644
index 000000000000..f693f2cd05c0
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eapol-key-auth/.gitignore
@@ -0,0 +1 @@
+eapol-key-auth
diff --git a/contrib/wpa/tests/fuzzing/eapol-key-auth/Makefile b/contrib/wpa/tests/fuzzing/eapol-key-auth/Makefile
new file mode 100644
index 000000000000..bd15b91f2394
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eapol-key-auth/Makefile
@@ -0,0 +1,34 @@
+ALL=eapol-key-auth
+include ../rules.include
+
+CFLAGS += -DCONFIG_IEEE80211R_AP
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_TDLS
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/eap_common/libeap_common.a
+LIBS += $(SRC)/l2_packet/libl2_packet.a
+LIBS += $(SRC)/utils/libutils.a
+LIBS += $(SRC)/wps/libwps.a
+LIBS += $(SRC)/eapol_auth/libeapol_auth.a
+LIBS += $(SRC)/eap_server/libeap_server.a
+LIBS += $(SRC)/ap/libap.a
+LIBS += $(SRC)/radius/libradius.a
+
+OBJS += $(SRC)/drivers/driver_common.o
+
+OBJS += eapol-key-auth.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+eapol-key-auth: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ -Wl,--start-group $(LIBS) -Wl,--end-group
+
+clean: common-clean
+ rm -f eapol-key-auth *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/eapol-key-auth/corpus/supp.msg b/contrib/wpa/tests/fuzzing/eapol-key-auth/corpus/supp.msg
new file mode 100644
index 000000000000..437d45175448
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/eapol-key-auth/corpus/supp.msg differ
diff --git a/contrib/wpa/tests/fuzzing/eapol-key-auth/eapol-key-auth.c b/contrib/wpa/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
new file mode 100644
index 000000000000..bb46422c6dbc
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eapol-key-auth/eapol-key-auth.c
@@ -0,0 +1,328 @@
+/*
+ * Testing tool for EAPOL-Key Authenticator routines
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/eloop.h"
+#include "ap/wpa_auth.h"
+#include "../fuzzer-common.h"
+
+
+struct wpa {
+ const u8 *data;
+ size_t data_len;
+ size_t data_offset;
+ int wpa1;
+
+ u8 auth_addr[ETH_ALEN];
+ u8 supp_addr[ETH_ALEN];
+ u8 psk[PMK_LEN];
+
+ /* from supplicant */
+ u8 *supp_eapol;
+ size_t supp_eapol_len;
+
+ struct wpa_auth_callbacks auth_cb;
+ struct wpa_authenticator *auth_group;
+ struct wpa_state_machine *auth;
+
+ u8 supp_ie[80];
+ size_t supp_ie_len;
+
+ int key_request_done;
+ int key_request_done1;
+ int auth_sent;
+};
+
+
+const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
+
+
+static int auth_read_msg(struct wpa *wpa);
+static void supp_eapol_key_request(void *eloop_data, void *user_ctx);
+
+
+static u8 * read_msg(struct wpa *wpa, size_t *ret_len)
+{
+ u16 msg_len;
+ u8 *msg;
+
+ if (wpa->data_len - wpa->data_offset < 2) {
+ wpa_printf(MSG_ERROR, "TEST-ERROR: Could not read msg len");
+ eloop_terminate();
+ return NULL;
+ }
+ msg_len = WPA_GET_BE16(&wpa->data[wpa->data_offset]);
+ wpa->data_offset += 2;
+
+ msg = os_malloc(msg_len);
+ if (!msg)
+ return NULL;
+ if (msg_len > 0 && wpa->data_len - wpa->data_offset < msg_len) {
+ wpa_printf(MSG_ERROR, "TEST-ERROR: Truncated msg (msg_len=%u)",
+ msg_len);
+ os_free(msg);
+ eloop_terminate();
+ return NULL;
+ }
+ os_memcpy(msg, &wpa->data[wpa->data_offset], msg_len);
+ wpa->data_offset += msg_len;
+ wpa_hexdump(MSG_DEBUG, "TEST: Read message from file", msg, msg_len);
+
+ *ret_len = msg_len;
+ return msg;
+}
+
+
+static void auth_eapol_rx(void *eloop_data, void *user_ctx)
+{
+ struct wpa *wpa = eloop_data;
+
+ wpa_printf(MSG_DEBUG, "AUTH: RX EAPOL frame");
+ wpa->auth_sent = 0;
+ wpa_receive(wpa->auth_group, wpa->auth, wpa->supp_eapol,
+ wpa->supp_eapol_len);
+ if (!wpa->auth_sent) {
+ /* Speed up process by not going through retransmit timeout */
+ wpa_printf(MSG_DEBUG,
+ "AUTH: No response was sent - process next message");
+ auth_read_msg(wpa);
+ }
+ if (wpa->wpa1 && wpa->key_request_done && !wpa->key_request_done1) {
+ wpa->key_request_done1 = 1;
+ eloop_register_timeout(0, 0, supp_eapol_key_request,
+ wpa, NULL);
+ }
+
+}
+
+
+static void auth_logger(void *ctx, const u8 *addr, logger_level level,
+ const char *txt)
+{
+ if (addr)
+ wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
+ MAC2STR(addr), txt);
+ else
+ wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
+}
+
+
+static int auth_read_msg(struct wpa *wpa)
+{
+ os_free(wpa->supp_eapol);
+ wpa->supp_eapol = read_msg(wpa, &wpa->supp_eapol_len);
+ if (!wpa->supp_eapol)
+ return -1;
+ eloop_register_timeout(0, 0, auth_eapol_rx, wpa, NULL);
+ return 0;
+}
+
+
+static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
+ size_t data_len, int encrypt)
+{
+ struct wpa *wpa = ctx;
+
+ wpa_printf(MSG_DEBUG, "AUTH: %s(addr=" MACSTR " data_len=%lu "
+ "encrypt=%d)",
+ __func__, MAC2STR(addr), (unsigned long) data_len, encrypt);
+ wpa->auth_sent = 1;
+
+ return auth_read_msg(wpa);
+}
+
+
+static const u8 * auth_get_psk(void *ctx, const u8 *addr,
+ const u8 *p2p_dev_addr, const u8 *prev_psk,
+ size_t *psk_len, int *vlan_id)
+{
+ struct wpa *wpa = ctx;
+
+ wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
+ __func__, MAC2STR(addr), prev_psk);
+ if (vlan_id)
+ *vlan_id = 0;
+ if (psk_len)
+ *psk_len = PMK_LEN;
+ if (prev_psk)
+ return NULL;
+ return wpa->psk;
+}
+
+
+static void supp_eapol_key_request(void *eloop_data, void *user_ctx)
+{
+ struct wpa *wpa = eloop_data;
+
+ wpa_printf(MSG_DEBUG, "SUPP: EAPOL-Key Request trigger");
+ if (!eloop_is_timeout_registered(auth_eapol_rx, wpa, NULL))
+ auth_read_msg(wpa);
+}
+
+
+static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+ const u8 *addr, int idx, u8 *key,
+ size_t key_len, enum key_flag key_flag)
+{
+ struct wpa *wpa = ctx;
+
+ wpa_printf(MSG_DEBUG,
+ "AUTH: %s (vlan_id=%d alg=%d idx=%d key_len=%d key_flag=0x%x)",
+ __func__, vlan_id, alg, idx, (int) key_len, key_flag);
+ if (addr)
+ wpa_printf(MSG_DEBUG, "AUTH: addr=" MACSTR, MAC2STR(addr));
+
+ if (alg != WPA_ALG_NONE && idx == 0 && key_len > 0 &&
+ !wpa->key_request_done) {
+ wpa_printf(MSG_DEBUG, "Test EAPOL-Key Request");
+ wpa->key_request_done = 1;
+ if (!wpa->wpa1)
+ eloop_register_timeout(0, 0, supp_eapol_key_request,
+ wpa, NULL);
+ }
+
+ return 0;
+}
+
+
+static int auth_init_group(struct wpa *wpa)
+{
+ struct wpa_auth_config conf;
+
+ wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
+
+ os_memset(&conf, 0, sizeof(conf));
+ if (wpa->wpa1) {
+ conf.wpa = 1;
+ conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+ conf.wpa_pairwise = WPA_CIPHER_TKIP;
+ conf.wpa_group = WPA_CIPHER_TKIP;
+ } else {
+ conf.wpa = 2;
+ conf.wpa_key_mgmt = WPA_KEY_MGMT_PSK;
+ conf.wpa_pairwise = WPA_CIPHER_CCMP;
+ conf.rsn_pairwise = WPA_CIPHER_CCMP;
+ conf.wpa_group = WPA_CIPHER_CCMP;
+ conf.ieee80211w = 2;
+ conf.group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
+ }
+ conf.eapol_version = 2;
+ conf.wpa_group_update_count = 4;
+ conf.wpa_pairwise_update_count = 4;
+
+ wpa->auth_cb.logger = auth_logger;
+ wpa->auth_cb.send_eapol = auth_send_eapol;
+ wpa->auth_cb.get_psk = auth_get_psk;
+ wpa->auth_cb.set_key = auth_set_key;
+
+ wpa->auth_group = wpa_init(wpa->auth_addr, &conf, &wpa->auth_cb, wpa);
+ if (!wpa->auth_group) {
+ wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int auth_init(struct wpa *wpa)
+{
+ const u8 *supp_ie;
+ size_t supp_ie_len;
+ static const u8 ie_rsn[] = {
+ 0x30, 0x14, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04,
+ 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00,
+ 0x00, 0x0f, 0xac, 0x02, 0x80, 0x00
+ };
+ static const u8 ie_wpa[] = {
+ 0xdd, 0x16, 0x00, 0x50, 0xf2, 0x01, 0x01, 0x00,
+ 0x00, 0x50, 0xf2, 0x02, 0x01, 0x00, 0x00, 0x50,
+ 0xf2, 0x02, 0x01, 0x00, 0x00, 0x50, 0xf2, 0x02
+ };
+
+ if (wpa->wpa1) {
+ supp_ie = ie_wpa;
+ supp_ie_len = sizeof(ie_wpa);
+ } else {
+ supp_ie = ie_rsn;
+ supp_ie_len = sizeof(ie_rsn);
+ }
+
+ wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL);
+ if (!wpa->auth) {
+ wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
+ return -1;
+ }
+
+ if (wpa_validate_wpa_ie(wpa->auth_group, wpa->auth, 2412, supp_ie,
+ supp_ie_len, NULL, 0, NULL, 0, NULL, 0) !=
+ WPA_IE_OK) {
+ wpa_printf(MSG_DEBUG, "AUTH: wpa_validate_wpa_ie() failed");
+ return -1;
+ }
+
+ wpa_auth_sm_event(wpa->auth, WPA_ASSOC);
+
+ wpa_auth_sta_associated(wpa->auth_group, wpa->auth);
+
+ return 0;
+}
+
+
+static void deinit(struct wpa *wpa)
+{
+ wpa_auth_sta_deinit(wpa->auth);
+ wpa_deinit(wpa->auth_group);
+ os_free(wpa->supp_eapol);
+ wpa->supp_eapol = NULL;
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct wpa wpa;
+
+ wpa_fuzzer_set_debug_level();
+
+ if (os_program_init())
+ return -1;
+
+ os_memset(&wpa, 0, sizeof(wpa));
+ wpa.data = data;
+ wpa.data_len = size;
+
+ os_memset(wpa.auth_addr, 0x12, ETH_ALEN);
+ os_memset(wpa.supp_addr, 0x32, ETH_ALEN);
+ os_memset(wpa.psk, 0x44, PMK_LEN);
+
+ if (eloop_init()) {
+ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+ goto fail;
+ }
+
+ if (auth_init_group(&wpa) < 0)
+ goto fail;
+
+ if (auth_init(&wpa) < 0)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "Starting eloop");
+ eloop_run();
+ wpa_printf(MSG_DEBUG, "eloop done");
+
+fail:
+ deinit(&wpa);
+
+ eloop_destroy();
+
+ os_program_deinit();
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/eapol-key-supp/.gitignore b/contrib/wpa/tests/fuzzing/eapol-key-supp/.gitignore
new file mode 100644
index 000000000000..dcbbbdb02651
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eapol-key-supp/.gitignore
@@ -0,0 +1 @@
+eapol-key-supp
diff --git a/contrib/wpa/tests/fuzzing/eapol-key-supp/Makefile b/contrib/wpa/tests/fuzzing/eapol-key-supp/Makefile
new file mode 100644
index 000000000000..949e2efe991c
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eapol-key-supp/Makefile
@@ -0,0 +1,30 @@
+ALL=eapol-key-supp
+include ../rules.include
+
+CFLAGS += -DCONFIG_IEEE80211R_AP
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_TDLS
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/rsn_supp/librsn_supp.a
+LIBS += $(SRC)/eapol_supp/libeapol_supp.a
+LIBS += $(SRC)/eap_peer/libeap_peer.a
+LIBS += $(SRC)/eap_common/libeap_common.a
+LIBS += $(SRC)/l2_packet/libl2_packet.a
+LIBS += $(SRC)/utils/libutils.a
+
+OBJS += eapol-key-supp.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+eapol-key-supp: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ -Wl,--start-group $(LIBS) -Wl,--end-group
+
+clean: common-clean
+ rm -f eapol-key-supp *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/eapol-key-supp/corpus/auth.msg b/contrib/wpa/tests/fuzzing/eapol-key-supp/corpus/auth.msg
new file mode 100644
index 000000000000..00ae53dff88e
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/eapol-key-supp/corpus/auth.msg differ
diff --git a/contrib/wpa/tests/fuzzing/eapol-key-supp/eapol-key-supp.c b/contrib/wpa/tests/fuzzing/eapol-key-supp/eapol-key-supp.c
new file mode 100644
index 000000000000..487c889d7a6d
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eapol-key-supp/eapol-key-supp.c
@@ -0,0 +1,331 @@
+/*
+ * Testing tool for EAPOL-Key Supplicant routines
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/eloop.h"
+#include "rsn_supp/wpa.h"
+#include "../fuzzer-common.h"
+
+
+struct wpa {
+ const u8 *data;
+ size_t data_len;
+ size_t data_offset;
+ int wpa1;
+
+ u8 auth_addr[ETH_ALEN];
+ u8 supp_addr[ETH_ALEN];
+ u8 psk[PMK_LEN];
+
+ /* from authenticator */
+ u8 *auth_eapol;
+ size_t auth_eapol_len;
+
+ struct wpa_sm *supp;
+
+ u8 supp_ie[80];
+ size_t supp_ie_len;
+};
+
+
+const struct wpa_driver_ops *const wpa_drivers[] = { NULL };
+
+
+static u8 * read_msg(struct wpa *wpa, size_t *ret_len)
+{
+ u16 msg_len;
+ u8 *msg;
+
+ if (wpa->data_len - wpa->data_offset < 2) {
+ wpa_printf(MSG_ERROR, "TEST-ERROR: Could not read msg len");
+ eloop_terminate();
+ return NULL;
+ }
+ msg_len = WPA_GET_BE16(&wpa->data[wpa->data_offset]);
+ wpa->data_offset += 2;
+
+ msg = os_malloc(msg_len);
+ if (!msg)
+ return NULL;
+ if (msg_len > 0 && wpa->data_len - wpa->data_offset < msg_len) {
+ wpa_printf(MSG_ERROR, "TEST-ERROR: Truncated msg (msg_len=%u)",
+ msg_len);
+ os_free(msg);
+ eloop_terminate();
+ return NULL;
+ }
+ os_memcpy(msg, &wpa->data[wpa->data_offset], msg_len);
+ wpa->data_offset += msg_len;
+ wpa_hexdump(MSG_DEBUG, "TEST: Read message from file", msg, msg_len);
+
+ *ret_len = msg_len;
+ return msg;
+}
+
+
+static int supp_get_bssid(void *ctx, u8 *bssid)
+{
+ struct wpa *wpa = ctx;
+ wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+ os_memcpy(bssid, wpa->auth_addr, ETH_ALEN);
+ return 0;
+}
+
+
+static void supp_set_state(void *ctx, enum wpa_states state)
+{
+ wpa_printf(MSG_DEBUG, "SUPP: %s(state=%d)", __func__, state);
+}
+
+
+static void supp_eapol_rx(void *eloop_data, void *user_ctx)
+{
+ struct wpa *wpa = eloop_data;
+
+ wpa_printf(MSG_DEBUG, "SUPP: RX EAPOL frame");
+ wpa_sm_rx_eapol(wpa->supp, wpa->auth_addr, wpa->auth_eapol,
+ wpa->auth_eapol_len);
+}
+
+
+static int supp_read_msg(struct wpa *wpa)
+{
+ os_free(wpa->auth_eapol);
+ wpa->auth_eapol = read_msg(wpa, &wpa->auth_eapol_len);
+ if (!wpa->auth_eapol)
+ return -1;
+ eloop_register_timeout(0, 0, supp_eapol_rx, wpa, NULL);
+ return 0;
+}
+
+
+static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
+ size_t len)
+{
+ struct wpa *wpa = ctx;
+
+ wpa_printf(MSG_DEBUG, "SUPP: %s(dest=" MACSTR " proto=0x%04x "
+ "len=%lu)",
+ __func__, MAC2STR(dest), proto, (unsigned long) len);
+
+ return supp_read_msg(wpa);
+}
+
+
+static u8 * supp_alloc_eapol(void *ctx, u8 type, const void *data,
+ u16 data_len, size_t *msg_len, void **data_pos)
+{
+ struct ieee802_1x_hdr *hdr;
+
+ wpa_printf(MSG_DEBUG, "SUPP: %s(type=%d data_len=%d)",
+ __func__, type, data_len);
+
+ *msg_len = sizeof(*hdr) + data_len;
+ hdr = os_malloc(*msg_len);
+ if (hdr == NULL)
+ return NULL;
+
+ hdr->version = 2;
+ hdr->type = type;
+ hdr->length = host_to_be16(data_len);
+
+ if (data)
+ os_memcpy(hdr + 1, data, data_len);
+ else
+ os_memset(hdr + 1, 0, data_len);
+
+ if (data_pos)
+ *data_pos = hdr + 1;
+
+ return (u8 *) hdr;
+}
+
+
+static int supp_get_beacon_ie(void *ctx)
+{
+ struct wpa *wpa = ctx;
+ const u8 *ie;
+ static const u8 wpaie[] = {
+ 0xdd, 0x16, 0x00, 0x50, 0xf2, 0x01, 0x01, 0x00,
+ 0x00, 0x50, 0xf2, 0x02, 0x01, 0x00, 0x00, 0x50,
+ 0xf2, 0x02, 0x01, 0x00, 0x00, 0x50, 0xf2, 0x02
+ };
+ static const u8 rsne[] = {
+ 0x30, 0x14, 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04,
+ 0x01, 0x00, 0x00, 0x0f, 0xac, 0x04, 0x01, 0x00,
+ 0x00, 0x0f, 0xac, 0x02, 0xc0, 0x00
+ };
+
+ wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+
+ ie = wpa->wpa1 ? wpaie : rsne;
+ if (ie[0] == WLAN_EID_RSN)
+ return wpa_sm_set_ap_rsn_ie(wpa->supp, ie, 2 + ie[1]);
+ return wpa_sm_set_ap_wpa_ie(wpa->supp, ie, 2 + ie[1]);
+}
+
+
+static int supp_set_key(void *ctx, enum wpa_alg alg,
+ const u8 *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len, enum key_flag key_flag)
+{
+ wpa_printf(MSG_DEBUG, "SUPP: %s(alg=%d addr=" MACSTR " key_idx=%d "
+ "set_tx=%d key_flag=0x%x)",
+ __func__, alg, MAC2STR(addr), key_idx, set_tx, key_flag);
+ wpa_hexdump(MSG_DEBUG, "SUPP: set_key - seq", seq, seq_len);
+ wpa_hexdump(MSG_DEBUG, "SUPP: set_key - key", key, key_len);
+ return 0;
+}
+
+
+static int supp_mlme_setprotection(void *ctx, const u8 *addr,
+ int protection_type, int key_type)
+{
+ wpa_printf(MSG_DEBUG, "SUPP: %s(addr=" MACSTR " protection_type=%d "
+ "key_type=%d)",
+ __func__, MAC2STR(addr), protection_type, key_type);
+ return 0;
+}
+
+
+static void supp_cancel_auth_timeout(void *ctx)
+{
+ wpa_printf(MSG_DEBUG, "SUPP: %s", __func__);
+}
+
+
+static void * supp_get_network_ctx(void *ctx)
+{
+ return (void *) 1;
+}
+
+
+static void supp_deauthenticate(void *ctx, u16 reason_code)
+{
+ wpa_printf(MSG_DEBUG, "SUPP: %s(%d)", __func__, reason_code);
+}
+
+
+static enum wpa_states supp_get_state(void *ctx)
+{
+ return WPA_COMPLETED;
+}
+
+
+static int supp_init(struct wpa *wpa)
+{
+ struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
+
+ if (!ctx)
+ return -1;
+
+ ctx->ctx = wpa;
+ ctx->msg_ctx = wpa;
+ ctx->set_state = supp_set_state;
+ ctx->get_bssid = supp_get_bssid;
+ ctx->ether_send = supp_ether_send;
+ ctx->get_beacon_ie = supp_get_beacon_ie;
+ ctx->alloc_eapol = supp_alloc_eapol;
+ ctx->set_key = supp_set_key;
+ ctx->mlme_setprotection = supp_mlme_setprotection;
+ ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
+ ctx->get_network_ctx = supp_get_network_ctx;
+ ctx->deauthenticate = supp_deauthenticate;
+ ctx->get_state = supp_get_state;
+ wpa->supp = wpa_sm_init(ctx);
+ if (!wpa->supp) {
+ wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
+ return -1;
+ }
+
+ wpa_sm_set_own_addr(wpa->supp, wpa->supp_addr);
+ if (wpa->wpa1) {
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 0);
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_WPA);
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE,
+ WPA_CIPHER_TKIP);
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_TKIP);
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT,
+ WPA_KEY_MGMT_PSK);
+ } else {
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_RSN_ENABLED, 1);
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_PROTO, WPA_PROTO_RSN);
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_PAIRWISE,
+ WPA_CIPHER_CCMP);
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_KEY_MGMT,
+ WPA_KEY_MGMT_PSK);
+ wpa_sm_set_param(wpa->supp, WPA_PARAM_MFP,
+ MGMT_FRAME_PROTECTION_OPTIONAL);
+ }
+ wpa_sm_set_pmk(wpa->supp, wpa->psk, PMK_LEN, NULL, NULL);
+
+ wpa->supp_ie_len = sizeof(wpa->supp_ie);
+ if (wpa_sm_set_assoc_wpa_ie_default(wpa->supp, wpa->supp_ie,
+ &wpa->supp_ie_len) < 0) {
+ wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_set_assoc_wpa_ie_default()"
+ " failed");
+ return -1;
+ }
+
+ wpa_sm_notify_assoc(wpa->supp, wpa->auth_addr);
+ supp_read_msg(wpa);
+
+ return 0;
+}
+
+
+static void deinit(struct wpa *wpa)
+{
+ wpa_sm_deinit(wpa->supp);
+ os_free(wpa->auth_eapol);
+ wpa->auth_eapol = NULL;
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct wpa wpa;
+
+ wpa_fuzzer_set_debug_level();
+
+ if (os_program_init())
+ return -1;
+
+ os_memset(&wpa, 0, sizeof(wpa));
+ wpa.data = data;
+ wpa.data_len = size;
+
+ os_memset(wpa.auth_addr, 0x12, ETH_ALEN);
+ os_memset(wpa.supp_addr, 0x32, ETH_ALEN);
+ os_memset(wpa.psk, 0x44, PMK_LEN);
+
+ if (eloop_init()) {
+ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+ goto fail;
+ }
+
+ if (supp_init(&wpa) < 0)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "Starting eloop");
+ eloop_run();
+ wpa_printf(MSG_DEBUG, "eloop done");
+
+fail:
+ deinit(&wpa);
+
+ eloop_destroy();
+
+ os_program_deinit();
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/eapol-supp/.gitignore b/contrib/wpa/tests/fuzzing/eapol-supp/.gitignore
new file mode 100644
index 000000000000..e370093d82a3
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eapol-supp/.gitignore
@@ -0,0 +1 @@
+eapol-supp
diff --git a/contrib/wpa/tests/fuzzing/eapol-supp/Makefile b/contrib/wpa/tests/fuzzing/eapol-supp/Makefile
new file mode 100644
index 000000000000..ea32346b2df1
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eapol-supp/Makefile
@@ -0,0 +1,28 @@
+ALL=eapol-supp
+include ../rules.include
+
+CFLAGS += -DIEEE8021X_EAPOL
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/rsn_supp/librsn_supp.a
+LIBS += $(SRC)/eapol_supp/libeapol_supp.a
+LIBS += $(SRC)/eap_peer/libeap_peer.a
+LIBS += $(SRC)/eap_common/libeap_common.a
+LIBS += $(SRC)/l2_packet/libl2_packet.a
+LIBS += $(SRC)/utils/libutils.a
+
+OBJS += eapol-supp.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+eapol-supp: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ -Wl,--start-group $(LIBS) -Wl,--end-group
+
+clean: common-clean
+ rm -f eapol-supp *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/eapol-supp/corpus/eap-req-identity.dat b/contrib/wpa/tests/fuzzing/eapol-supp/corpus/eap-req-identity.dat
new file mode 100644
index 000000000000..768b27754167
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/eapol-supp/corpus/eap-req-identity.dat differ
diff --git a/contrib/wpa/tests/fuzzing/eapol-supp/corpus/eap-req-sim.dat b/contrib/wpa/tests/fuzzing/eapol-supp/corpus/eap-req-sim.dat
new file mode 100644
index 000000000000..eb854aae01fd
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/eapol-supp/corpus/eap-req-sim.dat differ
diff --git a/contrib/wpa/tests/fuzzing/eapol-supp/corpus/eapol-key-m1.dat b/contrib/wpa/tests/fuzzing/eapol-supp/corpus/eapol-key-m1.dat
new file mode 100644
index 000000000000..937721c5013d
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/eapol-supp/corpus/eapol-key-m1.dat differ
diff --git a/contrib/wpa/tests/fuzzing/eapol-supp/eapol-supp.c b/contrib/wpa/tests/fuzzing/eapol-supp/eapol-supp.c
new file mode 100644
index 000000000000..94e0147adf15
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/eapol-supp/eapol-supp.c
@@ -0,0 +1,198 @@
+/*
+ * wpa_supplicant - EAPOL fuzzer
+ * Copyright (c) 2015-2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/eloop.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_i.h"
+#include "../fuzzer-common.h"
+
+
+struct arg_ctx {
+ const u8 *data;
+ size_t data_len;
+ struct wpa_sm *wpa;
+ struct eapol_sm *eapol;
+};
+
+
+static void test_send_eapol(void *eloop_data, void *user_ctx)
+{
+ struct arg_ctx *ctx = eloop_data;
+ u8 src[ETH_ALEN] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x01 };
+ u8 wpa_ie[200];
+ size_t wpa_ie_len;
+
+ wpa_hexdump(MSG_MSGDUMP, "fuzzer - EAPOL", ctx->data, ctx->data_len);
+
+ eapol_sm_notify_portEnabled(ctx->eapol, true);
+
+ wpa_sm_set_param(ctx->wpa, WPA_PARAM_PROTO, WPA_PROTO_RSN);
+ wpa_sm_set_param(ctx->wpa, WPA_PARAM_RSN_ENABLED, 1);
+ wpa_sm_set_param(ctx->wpa, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
+ wpa_sm_set_param(ctx->wpa, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
+ wpa_sm_set_param(ctx->wpa, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
+
+ wpa_ie_len = sizeof(wpa_ie);
+ wpa_sm_set_assoc_wpa_ie_default(ctx->wpa, wpa_ie, &wpa_ie_len);
+
+ if (eapol_sm_rx_eapol(ctx->eapol, src, ctx->data, ctx->data_len) <= 0)
+ wpa_sm_rx_eapol(ctx->wpa, src, ctx->data, ctx->data_len);
+
+ eloop_terminate();
+}
+
+
+static void * get_network_ctx(void *arg)
+{
+ return (void *) 1;
+}
+
+
+static void set_state(void *arg, enum wpa_states state)
+{
+}
+
+
+static void deauthenticate(void *arg, u16 reason_code)
+{
+}
+
+
+static u8 * alloc_eapol(void *arg, u8 type,
+ const void *data, u16 data_len,
+ size_t *msg_len, void **data_pos)
+{
+ struct ieee802_1x_hdr *hdr;
+
+ *msg_len = sizeof(*hdr) + data_len;
+ hdr = os_malloc(*msg_len);
+ if (hdr == NULL)
+ return NULL;
+
+ hdr->version = 2;
+ hdr->type = type;
+ hdr->length = host_to_be16(data_len);
+
+ if (data)
+ os_memcpy(hdr + 1, data, data_len);
+ else
+ os_memset(hdr + 1, 0, data_len);
+
+ if (data_pos)
+ *data_pos = hdr + 1;
+
+ return (u8 *) hdr;
+}
+
+
+static int ether_send(void *arg, const u8 *dest, u16 proto,
+ const u8 *buf, size_t len)
+{
+ return 0;
+}
+
+
+static int get_bssid(void *ctx, u8 *bssid)
+{
+ return -1;
+}
+
+
+static int eapol_send(void *ctx, int type, const u8 *buf, size_t len)
+{
+ return 0;
+}
+
+
+static int init_wpa(struct arg_ctx *arg)
+{
+ struct wpa_sm_ctx *ctx;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate WPA context.");
+ return -1;
+ }
+
+ ctx->ctx = arg;
+ ctx->msg_ctx = arg;
+ ctx->get_network_ctx = get_network_ctx;
+ ctx->set_state = set_state;
+ ctx->deauthenticate = deauthenticate;
+ ctx->alloc_eapol = alloc_eapol;
+ ctx->ether_send = ether_send;
+ ctx->get_bssid = get_bssid;
+
+ arg->wpa = wpa_sm_init(ctx);
+ if (!arg->wpa)
+ return -1;
+ arg->wpa->pmk_len = PMK_LEN;
+ return 0;
+}
+
+
+static int init_eapol(struct arg_ctx *arg)
+{
+ struct eapol_ctx *ctx;
+
+ ctx = os_zalloc(sizeof(*ctx));
+ if (ctx == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to allocate EAPOL context.");
+ return -1;
+ }
+
+ ctx->ctx = arg;
+ ctx->msg_ctx = arg;
+ ctx->eapol_send = eapol_send;
+
+ arg->eapol = eapol_sm_init(ctx);
+ return arg->eapol ? 0 : -1;
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct arg_ctx ctx;
+
+ wpa_fuzzer_set_debug_level();
+
+ if (os_program_init())
+ return 0;
+
+ if (eloop_init()) {
+ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+ return 0;
+ }
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.data = data;
+ ctx.data_len = size;
+ if (init_wpa(&ctx) || init_eapol(&ctx))
+ goto fail;
+
+ eloop_register_timeout(0, 0, test_send_eapol, &ctx, NULL);
+
+ wpa_printf(MSG_DEBUG, "Starting eloop");
+ eloop_run();
+ wpa_printf(MSG_DEBUG, "eloop done");
+
+fail:
+ if (ctx.wpa)
+ wpa_sm_deinit(ctx.wpa);
+ if (ctx.eapol)
+ eapol_sm_deinit(ctx.eapol);
+
+ eloop_destroy();
+ os_program_deinit();
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/fuzzer-common.c b/contrib/wpa/tests/fuzzing/fuzzer-common.c
new file mode 100644
index 000000000000..43b91e19a512
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/fuzzer-common.c
@@ -0,0 +1,56 @@
+/*
+ * Common helper functions for fuzzing tools
+ * Copyright (c) 2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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"
+
+
+void wpa_fuzzer_set_debug_level(void)
+{
+ static int first = 1;
+
+ if (first) {
+ char *env;
+
+ first = 0;
+ env = getenv("WPADEBUG");
+ if (env)
+ wpa_debug_level = atoi(env);
+ else
+ wpa_debug_level = MSG_ERROR + 1;
+
+ wpa_debug_show_keys = 1;
+ }
+}
+
+
+#ifndef TEST_LIBFUZZER
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int main(int argc, char *argv[])
+{
+ char *data;
+ size_t len;
+
+ if (argc < 2) {
+ printf("usage: %s <file>\n", argv[0]);
+ return -1;
+ }
+
+ data = os_readfile(argv[1], &len);
+ if (!data) {
+ printf("Could not read '%s'\n", argv[1]);
+ return -1;
+ }
+
+ LLVMFuzzerTestOneInput((const uint8_t *) data, len);
+ os_free(data);
+ return 0;
+}
+#endif /* !TEST_LIBFUZZER */
diff --git a/contrib/wpa/tests/fuzzing/fuzzer-common.h b/contrib/wpa/tests/fuzzing/fuzzer-common.h
new file mode 100644
index 000000000000..80ebfd28ba76
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/fuzzer-common.h
@@ -0,0 +1,14 @@
+/*
+ * Common helper functions for fuzzing tools
+ * Copyright (c) 2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef FUZZER_COMMON_H
+#define FUZZER_COMMON_H
+
+void wpa_fuzzer_set_debug_level(void);
+
+#endif /* FUZZER_COMMON_H */
diff --git a/contrib/wpa/tests/fuzzing/json/.gitignore b/contrib/wpa/tests/fuzzing/json/.gitignore
new file mode 100644
index 000000000000..3c840093b758
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/json/.gitignore
@@ -0,0 +1 @@
+json
diff --git a/contrib/wpa/tests/fuzzing/json/Makefile b/contrib/wpa/tests/fuzzing/json/Makefile
new file mode 100644
index 000000000000..9dd51a5f22ef
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/json/Makefile
@@ -0,0 +1,23 @@
+ALL=json
+include ../rules.include
+
+OBJS += $(SRC)/utils/base64.o
+OBJS += $(SRC)/utils/common.o
+OBJS += $(SRC)/utils/json.o
+OBJS += $(SRC)/utils/os_unix.o
+OBJS += $(SRC)/utils/wpa_debug.o
+OBJS += $(SRC)/utils/wpabuf.o
+
+OBJS += json.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+json: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+
+clean: common-clean
+ rm -f json *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/json/corpus/1.json b/contrib/wpa/tests/fuzzing/json/corpus/1.json
new file mode 100644
index 000000000000..16c8b963cc14
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/json/corpus/1.json
@@ -0,0 +1 @@
+{"a":[[]],"b":1,"c":"q","d":{"e":[{}]}}
diff --git a/contrib/wpa/tests/fuzzing/json/corpus/2.json b/contrib/wpa/tests/fuzzing/json/corpus/2.json
new file mode 100644
index 000000000000..0967ef424bce
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/json/corpus/2.json
@@ -0,0 +1 @@
+{}
diff --git a/contrib/wpa/tests/fuzzing/json/corpus/3.json b/contrib/wpa/tests/fuzzing/json/corpus/3.json
new file mode 100644
index 000000000000..573541ac9702
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/json/corpus/3.json
@@ -0,0 +1 @@
+0
diff --git a/contrib/wpa/tests/fuzzing/json/json.c b/contrib/wpa/tests/fuzzing/json/json.c
new file mode 100644
index 000000000000..af6c5e74cd54
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/json/json.c
@@ -0,0 +1,38 @@
+/*
+ * JSON parser - test program
+ * Copyright (c) 2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/json.h"
+#include "../fuzzer-common.h"
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct json_token *root;
+ char *txt;
+ size_t buflen = 10000;
+
+ wpa_fuzzer_set_debug_level();
+
+ root = json_parse((const char *) data, size);
+ if (!root) {
+ wpa_printf(MSG_DEBUG, "JSON parsing failed");
+ return 0;
+ }
+
+ txt = os_zalloc(buflen);
+ if (txt) {
+ json_print_tree(root, txt, buflen);
+ wpa_printf(MSG_DEBUG, "%s", txt);
+ os_free(txt);
+ }
+ json_free(root);
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/p2p/.gitignore b/contrib/wpa/tests/fuzzing/p2p/.gitignore
new file mode 100644
index 000000000000..8bea15da48ef
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/p2p/.gitignore
@@ -0,0 +1 @@
+p2p
diff --git a/contrib/wpa/tests/fuzzing/p2p/Makefile b/contrib/wpa/tests/fuzzing/p2p/Makefile
new file mode 100644
index 000000000000..acac9d38f1b1
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/p2p/Makefile
@@ -0,0 +1,23 @@
+ALL=p2p
+include ../rules.include
+
+LIBS += $(SRC)/utils/libutils.a
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/p2p/libp2p.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/wps/libwps.a
+
+OBJS += p2p.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+p2p: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LIBS)
+
+clean: common-clean
+ rm -f p2p *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/p2p/corpus/go-neg-req.dat b/contrib/wpa/tests/fuzzing/p2p/corpus/go-neg-req.dat
new file mode 100644
index 000000000000..ed06834d71a1
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/p2p/corpus/go-neg-req.dat differ
diff --git a/contrib/wpa/tests/fuzzing/p2p/corpus/invitation-req.dat b/contrib/wpa/tests/fuzzing/p2p/corpus/invitation-req.dat
new file mode 100644
index 000000000000..5991f3e6e3f9
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/p2p/corpus/invitation-req.dat differ
diff --git a/contrib/wpa/tests/fuzzing/p2p/corpus/p2ps-pd-req.dat b/contrib/wpa/tests/fuzzing/p2p/corpus/p2ps-pd-req.dat
new file mode 100644
index 000000000000..7e1b6d91dead
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/p2p/corpus/p2ps-pd-req.dat differ
diff --git a/contrib/wpa/tests/fuzzing/p2p/corpus/proberesp-go.dat b/contrib/wpa/tests/fuzzing/p2p/corpus/proberesp-go.dat
new file mode 100644
index 000000000000..8541652ff955
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/p2p/corpus/proberesp-go.dat differ
diff --git a/contrib/wpa/tests/fuzzing/p2p/corpus/proberesp.dat b/contrib/wpa/tests/fuzzing/p2p/corpus/proberesp.dat
new file mode 100644
index 000000000000..8d997d1c5e13
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/p2p/corpus/proberesp.dat differ
diff --git a/contrib/wpa/tests/fuzzing/p2p/p2p.c b/contrib/wpa/tests/fuzzing/p2p/p2p.c
new file mode 100644
index 000000000000..fc83c3561c1a
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/p2p/p2p.c
@@ -0,0 +1,178 @@
+/*
+ * wpa_supplicant - P2P fuzzer
+ * Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "p2p/p2p.h"
+#include "../fuzzer-common.h"
+
+
+static void debug_print(void *ctx, int level, const char *msg)
+{
+ wpa_printf(level, "P2P: %s", msg);
+}
+
+
+static void find_stopped(void *ctx)
+{
+}
+
+
+static int start_listen(void *ctx, unsigned int freq,
+ unsigned int duration,
+ const struct wpabuf *probe_resp_ie)
+{
+ return 0;
+}
+
+
+static void stop_listen(void *ctx)
+{
+}
+
+
+static void dev_found(void *ctx, const u8 *addr,
+ const struct p2p_peer_info *info,
+ int new_device)
+{
+}
+
+
+static void dev_lost(void *ctx, const u8 *dev_addr)
+{
+}
+
+
+static int send_action(void *ctx, unsigned int freq, const u8 *dst,
+ const u8 *src, const u8 *bssid, const u8 *buf,
+ size_t len, unsigned int wait_time, int *scheduled)
+{
+ *scheduled = 0;
+ return 0;
+}
+
+
+static void send_action_done(void *ctx)
+{
+}
+
+
+static void go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id,
+ u8 go_intent)
+{
+}
+
+
+static struct p2p_data * init_p2p(void)
+{
+ struct p2p_config p2p;
+
+ os_memset(&p2p, 0, sizeof(p2p));
+ p2p.max_peers = 100;
+ p2p.passphrase_len = 8;
+ p2p.channels.reg_classes = 1;
+ p2p.channels.reg_class[0].reg_class = 81;
+ p2p.channels.reg_class[0].channel[0] = 1;
+ p2p.channels.reg_class[0].channel[1] = 2;
+ p2p.channels.reg_class[0].channels = 2;
+ p2p.debug_print = debug_print;
+ p2p.find_stopped = find_stopped;
+ p2p.start_listen = start_listen;
+ p2p.stop_listen = stop_listen;
+ p2p.dev_found = dev_found;
+ p2p.dev_lost = dev_lost;
+ p2p.send_action = send_action;
+ p2p.send_action_done = send_action_done;
+ p2p.go_neg_req_rx = go_neg_req_rx;
+
+ return p2p_init(&p2p);
+}
+
+
+struct arg_ctx {
+ const u8 *data;
+ size_t data_len;
+ struct p2p_data *p2p;
+ int count;
+};
+
+
+static void test_send(void *eloop_data, void *user_ctx)
+{
+ struct arg_ctx *ctx = eloop_data;
+ struct os_reltime rx_time;
+
+ wpa_hexdump(MSG_MSGDUMP, "fuzzer - IEs", ctx->data, ctx->data_len);
+
+ os_memset(&rx_time, 0, sizeof(rx_time));
+ p2p_scan_res_handler(ctx->p2p, (u8 *) "\x02\x00\x00\x00\x01\x00", 2412,
+ &rx_time, 0, ctx->data, ctx->data_len);
+ p2p_scan_res_handled(ctx->p2p, 0);
+
+ p2p_probe_req_rx(ctx->p2p, (u8 *) "\x02\x00\x00\x00\x01\x00",
+ (u8 *) "\x02\x00\x00\x00\x00\x00",
+ (u8 *) "\x02\x00\x00\x00\x00\x00",
+ ctx->data, ctx->data_len, 2412, 0);
+
+ if (ctx->data_len >= IEEE80211_HDRLEN + 1) {
+ struct os_reltime rx_time;
+ const struct ieee80211_mgmt *mgmt;
+
+ mgmt = (const struct ieee80211_mgmt *) ctx->data;
+ os_memset(&rx_time, 0, sizeof(rx_time));
+ p2p_rx_action(ctx->p2p, mgmt->da, mgmt->sa, mgmt->bssid,
+ mgmt->u.action.category,
+ (const u8 *) ctx->data + IEEE80211_HDRLEN + 1,
+ ctx->data_len - IEEE80211_HDRLEN - 1, 2412);
+ }
+
+ eloop_terminate();
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct p2p_data *p2p;
+ struct arg_ctx ctx;
+
+ wpa_fuzzer_set_debug_level();
+
+ if (os_program_init())
+ return -1;
+
+ if (eloop_init()) {
+ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+ return 0;
+ }
+
+ p2p = init_p2p();
+ if (!p2p) {
+ wpa_printf(MSG_ERROR, "P2P init failed");
+ return 0;
+ }
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.p2p = p2p;
+ ctx.data = data;
+ ctx.data_len = size;
+
+ eloop_register_timeout(0, 0, test_send, &ctx, NULL);
+
+ wpa_printf(MSG_DEBUG, "Starting eloop");
+ eloop_run();
+ wpa_printf(MSG_DEBUG, "eloop done");
+
+ p2p_deinit(p2p);
+ eloop_destroy();
+ os_program_deinit();
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/rules.include b/contrib/wpa/tests/fuzzing/rules.include
new file mode 100644
index 000000000000..e2cf577af811
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/rules.include
@@ -0,0 +1,31 @@
+FUZZ_RULES := $(lastword $(MAKEFILE_LIST))
+include $(dir $(FUZZ_RULES))../../src/build.rules
+
+FUZZ_CFLAGS =
+
+ifdef LIBFUZZER
+CC ?= clang
+#FUZZ_FLAGS ?= -fsanitize=fuzzer,address,signed-integer-overflow,unsigned-integer-overflow
+FUZZ_FLAGS ?= -fsanitize=fuzzer,address
+ifndef CFLAGS
+FUZZ_CFLAGS += $(FUZZ_FLAGS)
+endif
+endif
+
+FUZZ_CFLAGS += -DCONFIG_NO_RANDOM_POOL -DTEST_FUZZ
+export FUZZ_CFLAGS
+CFLAGS ?= -MMD -O2 -Wall -g
+CFLAGS += $(FUZZ_CFLAGS)
+ifdef LIBFUZZER
+CFLAGS += -DTEST_LIBFUZZER
+LDFLAGS += $(FUZZ_FLAGS)
+endif
+
+WPAS_SRC=../../../wpa_supplicant
+SRC=../../../src
+
+CFLAGS += -I$(SRC) -I$(SRC)/utils -I$(WPAS_SRC)
+OBJS += ../fuzzer-common.o
+
+# for the lib builds
+export TEST_FUZZ=y
diff --git a/contrib/wpa/tests/fuzzing/sae/.gitignore b/contrib/wpa/tests/fuzzing/sae/.gitignore
new file mode 100644
index 000000000000..1bb959165a21
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/sae/.gitignore
@@ -0,0 +1 @@
+sae
diff --git a/contrib/wpa/tests/fuzzing/sae/Makefile b/contrib/wpa/tests/fuzzing/sae/Makefile
new file mode 100644
index 000000000000..ee4b0c0b6a22
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/sae/Makefile
@@ -0,0 +1,28 @@
+ALL=sae
+include ../rules.include
+
+CFLAGS += -DCONFIG_SHA256
+CFLAGS += -DCONFIG_ECC
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/utils/libutils.a
+
+OBJS += $(SRC)/crypto/crypto_openssl.o
+OBJS += $(SRC)/crypto/dh_groups.o
+OBJS += $(SRC)/crypto/sha256-prf.o
+OBJS += $(SRC)/crypto/sha256-kdf.o
+OBJS += $(SRC)/common/dragonfly.o
+
+OBJS += sae.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+sae: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ -lcrypto
+
+clean: common-clean
+ rm -f sae *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-h2e-rejected-groups.dat b/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-h2e-rejected-groups.dat
new file mode 100644
index 000000000000..cd129a474a72
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-h2e-rejected-groups.dat differ
diff --git a/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-h2e-token.dat b/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-h2e-token.dat
new file mode 100644
index 000000000000..b2886c70d209
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-h2e-token.dat differ
diff --git a/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-pw-id.dat b/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-pw-id.dat
new file mode 100644
index 000000000000..5ca903ed2016
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-pw-id.dat differ
diff --git a/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-token.dat b/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-token.dat
new file mode 100644
index 000000000000..b25cc49f8e10
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-token.dat differ
diff --git a/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-valid.dat b/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-valid.dat
new file mode 100644
index 000000000000..eadfa4993b89
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/sae/corpus/sae-commit-valid.dat differ
diff --git a/contrib/wpa/tests/fuzzing/sae/sae.c b/contrib/wpa/tests/fuzzing/sae/sae.c
new file mode 100644
index 000000000000..8819a4abbc8f
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/sae/sae.c
@@ -0,0 +1,39 @@
+/*
+ * SAE fuzzer
+ * Copyright (c) 2020, Jouni Malinen <j@w1.fi>
+ *
+ * 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/sae.h"
+#include "../fuzzer-common.h"
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct sae_data sae;
+ u16 res;
+ const u8 *token = NULL;
+ size_t token_len = 0;
+ int groups[] = { 19, 0 };
+
+ wpa_fuzzer_set_debug_level();
+
+ if (os_program_init())
+ return 0;
+
+ os_memset(&sae, 0, sizeof(sae));
+ res = sae_parse_commit(&sae, data, size, &token, &token_len, groups, 0);
+ wpa_printf(MSG_DEBUG, "sae_parse_commit(0): %u", res);
+ sae_clear_data(&sae);
+ res = sae_parse_commit(&sae, data, size, &token, &token_len, groups, 1);
+ wpa_printf(MSG_DEBUG, "sae_parse_commit(1): %u", res);
+ sae_clear_data(&sae);
+ os_program_deinit();
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/tls-client/.gitignore b/contrib/wpa/tests/fuzzing/tls-client/.gitignore
new file mode 100644
index 000000000000..b41f93681f4d
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/tls-client/.gitignore
@@ -0,0 +1 @@
+tls-client
diff --git a/contrib/wpa/tests/fuzzing/tls-client/Makefile b/contrib/wpa/tests/fuzzing/tls-client/Makefile
new file mode 100644
index 000000000000..84cfa0431566
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/tls-client/Makefile
@@ -0,0 +1,32 @@
+ALL=tls-client
+include ../rules.include
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/rsn_supp/librsn_supp.a
+LIBS += $(SRC)/eapol_supp/libeapol_supp.a
+LIBS += $(SRC)/eap_peer/libeap_peer.a
+LIBS += $(SRC)/eap_common/libeap_common.a
+LIBS += $(SRC)/l2_packet/libl2_packet.a
+LIBS += $(SRC)/utils/libutils.a
+
+ELIBS += $(SRC)/crypto/libcrypto.a
+ELIBS += $(SRC)/tls/libtls.a
+
+OBJS += tls-client.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+_OBJS_VAR := ELIBS
+include ../../../src/objs.mk
+
+tls-client: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+
+clean: common-clean
+ rm -f tls-client *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/tls-client/corpus/server.msg b/contrib/wpa/tests/fuzzing/tls-client/corpus/server.msg
new file mode 100644
index 000000000000..0f842fdf75e1
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/tls-client/corpus/server.msg differ
diff --git a/contrib/wpa/tests/fuzzing/tls-client/tls-client.c b/contrib/wpa/tests/fuzzing/tls-client/tls-client.c
new file mode 100644
index 000000000000..b15b71984883
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/tls-client/tls-client.c
@@ -0,0 +1,154 @@
+/*
+ * Testing tool for TLSv1 client routines
+ * Copyright (c) 2019, 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 "common.h"
+#include "crypto/tls.h"
+#include "../fuzzer-common.h"
+
+#ifndef CERTDIR
+#define CERTDIR "../../hwsim/auth_serv/"
+#endif
+
+struct context {
+ const u8 *data;
+ size_t data_len;
+ size_t data_offset;
+};
+
+
+static struct wpabuf * read_msg(struct context *ctx)
+{
+ u16 msg_len;
+ struct wpabuf *msg;
+
+ if (ctx->data_len - ctx->data_offset < 2) {
+ wpa_printf(MSG_ERROR, "TEST-ERROR: Could not read msg len");
+ return NULL;
+ }
+ msg_len = WPA_GET_BE16(&ctx->data[ctx->data_offset]);
+ ctx->data_offset += 2;
+
+ msg = wpabuf_alloc(msg_len);
+ if (!msg)
+ return NULL;
+ if (msg_len > 0 && ctx->data_len - ctx->data_offset < msg_len) {
+ wpa_printf(MSG_ERROR, "TEST-ERROR: Truncated msg (msg_len=%u)",
+ msg_len);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_put_data(msg, &ctx->data[ctx->data_offset], msg_len);
+ ctx->data_offset += msg_len;
+ wpa_hexdump_buf(MSG_DEBUG, "TEST: Read message from file", msg);
+
+ return msg;
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct context ctx;
+ struct tls_config conf;
+ void *tls_client;
+ struct tls_connection_params params;
+ struct tls_connection *conn_client = NULL;
+ int ret = -1;
+ struct wpabuf *in = NULL, *out = NULL, *appl;
+
+ wpa_fuzzer_set_debug_level();
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.data = data;
+ ctx.data_len = size;
+
+ os_memset(&conf, 0, sizeof(conf));
+ tls_client = tls_init(&conf);
+ if (!tls_client)
+ goto fail;
+
+ os_memset(&params, 0, sizeof(params));
+ params.ca_cert = CERTDIR "ca.pem";
+ params.client_cert = CERTDIR "server.pem";
+ params.private_key = CERTDIR "server.key";
+ params.dh_file = CERTDIR "dh.conf";
+
+ conn_client = tls_connection_init(tls_client);
+ if (!conn_client)
+ goto fail;
+
+ in = NULL;
+ for (;;) {
+ appl = NULL;
+ out = tls_connection_handshake(tls_client, conn_client, in,
+ &appl);
+ wpabuf_free(in);
+ in = NULL;
+ if (!out)
+ goto fail;
+ if (tls_connection_get_failed(tls_client, conn_client)) {
+ wpa_printf(MSG_ERROR, "TLS handshake failed");
+ goto fail;
+ }
+ if (tls_connection_established(tls_client, conn_client))
+ break;
+
+ appl = NULL;
+ in = read_msg(&ctx);
+ wpabuf_free(out);
+ out = NULL;
+ if (!in)
+ goto fail;
+ if (tls_connection_established(tls_client, conn_client))
+ break;
+ }
+
+ wpabuf_free(in);
+ in = wpabuf_alloc(100);
+ if (!in)
+ goto fail;
+ wpabuf_put_str(in, "PING");
+ wpabuf_free(out);
+ out = tls_connection_encrypt(tls_client, conn_client, in);
+ wpabuf_free(in);
+ in = NULL;
+ if (!out)
+ goto fail;
+
+ wpabuf_free(in);
+ in = wpabuf_alloc(100);
+ if (!in)
+ goto fail;
+ wpabuf_put_str(in, "PONG");
+ wpabuf_free(out);
+ out = read_msg(&ctx);
+ wpabuf_free(in);
+ in = NULL;
+ if (!out)
+ goto fail;
+
+ in = tls_connection_decrypt(tls_client, conn_client, out);
+ wpabuf_free(out);
+ out = NULL;
+ if (!in)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "Client decrypted ApplData", in);
+
+ ret = 0;
+fail:
+ if (tls_client) {
+ if (conn_client)
+ tls_connection_deinit(tls_client, conn_client);
+ tls_deinit(tls_client);
+ }
+ wpabuf_free(in);
+ wpabuf_free(out);
+
+ return ret;
+}
diff --git a/contrib/wpa/tests/fuzzing/tls-server/.gitignore b/contrib/wpa/tests/fuzzing/tls-server/.gitignore
new file mode 100644
index 000000000000..341256f57f3f
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/tls-server/.gitignore
@@ -0,0 +1 @@
+tls-server
diff --git a/contrib/wpa/tests/fuzzing/tls-server/Makefile b/contrib/wpa/tests/fuzzing/tls-server/Makefile
new file mode 100644
index 000000000000..8e2400310397
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/tls-server/Makefile
@@ -0,0 +1,32 @@
+ALL=tls-server
+include ../rules.include
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/rsn_supp/librsn_supp.a
+LIBS += $(SRC)/eapol_supp/libeapol_supp.a
+LIBS += $(SRC)/eap_peer/libeap_peer.a
+LIBS += $(SRC)/eap_common/libeap_common.a
+LIBS += $(SRC)/l2_packet/libl2_packet.a
+LIBS += $(SRC)/utils/libutils.a
+
+ELIBS += $(SRC)/crypto/libcrypto.a
+ELIBS += $(SRC)/tls/libtls.a
+
+OBJS += tls-server.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+_OBJS_VAR := ELIBS
+include ../../../src/objs.mk
+
+tls-server: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+
+clean: common-clean
+ rm -f tls-server *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/tls-server/corpus/client.msg b/contrib/wpa/tests/fuzzing/tls-server/corpus/client.msg
new file mode 100644
index 000000000000..cb390143b0ea
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/tls-server/corpus/client.msg differ
diff --git a/contrib/wpa/tests/fuzzing/tls-server/tls-server.c b/contrib/wpa/tests/fuzzing/tls-server/tls-server.c
new file mode 100644
index 000000000000..d64cd7ad9756
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/tls-server/tls-server.c
@@ -0,0 +1,157 @@
+/*
+ * Testing tool for TLSv1 server routines
+ * Copyright (c) 2019, 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 "common.h"
+#include "crypto/tls.h"
+#include "../fuzzer-common.h"
+
+#ifndef CERTDIR
+#define CERTDIR "../../hwsim/auth_serv/"
+#endif
+
+struct context {
+ const u8 *data;
+ size_t data_len;
+ size_t data_offset;
+};
+
+
+static struct wpabuf * read_msg(struct context *ctx)
+{
+ u16 msg_len;
+ struct wpabuf *msg;
+
+ if (ctx->data_len - ctx->data_offset < 2) {
+ wpa_printf(MSG_ERROR, "TEST-ERROR: Could not read msg len");
+ return NULL;
+ }
+ msg_len = WPA_GET_BE16(&ctx->data[ctx->data_offset]);
+ ctx->data_offset += 2;
+
+ msg = wpabuf_alloc(msg_len);
+ if (!msg)
+ return NULL;
+ if (msg_len > 0 && ctx->data_len - ctx->data_offset < msg_len) {
+ wpa_printf(MSG_ERROR, "TEST-ERROR: Truncated msg (msg_len=%u)",
+ msg_len);
+ wpabuf_free(msg);
+ return NULL;
+ }
+ wpabuf_put_data(msg, &ctx->data[ctx->data_offset], msg_len);
+ ctx->data_offset += msg_len;
+ wpa_hexdump_buf(MSG_DEBUG, "TEST: Read message from file", msg);
+
+ return msg;
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct context ctx;
+ struct tls_config conf;
+ void *tls_server;
+ struct tls_connection_params params;
+ struct tls_connection *conn_server = NULL;
+ int ret = -1;
+ struct wpabuf *in = NULL, *out = NULL, *appl;
+
+ wpa_fuzzer_set_debug_level();
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.data = data;
+ ctx.data_len = size;
+
+ os_memset(&conf, 0, sizeof(conf));
+ tls_server = tls_init(&conf);
+ if (!tls_server)
+ goto fail;
+
+ os_memset(&params, 0, sizeof(params));
+ params.ca_cert = CERTDIR "ca.pem";
+ params.client_cert = CERTDIR "server.pem";
+ params.private_key = CERTDIR "server.key";
+ params.dh_file = CERTDIR "dh.conf";
+
+ if (tls_global_set_params(tls_server, &params)) {
+ wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
+ goto fail;
+ }
+
+ conn_server = tls_connection_init(tls_server);
+ if (!conn_server)
+ goto fail;
+
+ in = NULL;
+ for (;;) {
+ appl = NULL;
+ out = read_msg(&ctx);
+ wpabuf_free(in);
+ in = NULL;
+ if (!out)
+ goto fail;
+
+ appl = NULL;
+ in = tls_connection_server_handshake(tls_server, conn_server,
+ out, &appl);
+ wpabuf_free(out);
+ out = NULL;
+ if (!in)
+ goto fail;
+ if (tls_connection_get_failed(tls_server, conn_server)) {
+ wpa_printf(MSG_ERROR, "TLS handshake failed");
+ goto fail;
+ }
+ if (tls_connection_established(tls_server, conn_server))
+ break;
+ }
+
+ wpabuf_free(in);
+ in = wpabuf_alloc(100);
+ if (!in)
+ goto fail;
+ wpabuf_put_str(in, "PING");
+ wpabuf_free(out);
+ out = read_msg(&ctx);
+ wpabuf_free(in);
+ in = NULL;
+ if (!out)
+ goto fail;
+
+ in = tls_connection_decrypt(tls_server, conn_server, out);
+ wpabuf_free(out);
+ out = NULL;
+ if (!in)
+ goto fail;
+ wpa_hexdump_buf(MSG_DEBUG, "Server decrypted ApplData", in);
+
+ wpabuf_free(in);
+ in = wpabuf_alloc(100);
+ if (!in)
+ goto fail;
+ wpabuf_put_str(in, "PONG");
+ wpabuf_free(out);
+ out = tls_connection_encrypt(tls_server, conn_server, in);
+ wpabuf_free(in);
+ in = NULL;
+ if (!out)
+ goto fail;
+
+ ret = 0;
+fail:
+ if (tls_server) {
+ if (conn_server)
+ tls_connection_deinit(tls_server, conn_server);
+ tls_deinit(tls_server);
+ }
+ wpabuf_free(in);
+ wpabuf_free(out);
+
+ return ret;
+}
diff --git a/contrib/wpa/tests/fuzzing/wnm/.gitignore b/contrib/wpa/tests/fuzzing/wnm/.gitignore
new file mode 100644
index 000000000000..0e1d383dc444
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/wnm/.gitignore
@@ -0,0 +1 @@
+wnm
diff --git a/contrib/wpa/tests/fuzzing/wnm/Makefile b/contrib/wpa/tests/fuzzing/wnm/Makefile
new file mode 100644
index 000000000000..60d27b3a1535
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/wnm/Makefile
@@ -0,0 +1,60 @@
+ALL=wnm
+include ../rules.include
+
+CFLAGS += -DCONFIG_WNM
+CFLAGS += -DCONFIG_INTERWORKING
+CFLAGS += -DCONFIG_GAS
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DIEEE8021X_EAPOL
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/rsn_supp/librsn_supp.a
+LIBS += $(SRC)/eapol_supp/libeapol_supp.a
+LIBS += $(SRC)/eap_peer/libeap_peer.a
+LIBS += $(SRC)/eap_common/libeap_common.a
+LIBS += $(SRC)/l2_packet/libl2_packet.a
+LIBS += $(SRC)/utils/libutils.a
+
+ELIBS += $(SRC)/crypto/libcrypto.a
+ELIBS += $(SRC)/tls/libtls.a
+
+OBJS += $(WPAS_SRC)/bssid_ignore.o
+OBJS += $(WPAS_SRC)/bss.o
+OBJS += $(WPAS_SRC)/config.o
+OBJS += $(WPAS_SRC)/config_file.o
+OBJS += $(WPAS_SRC)/eap_register.o
+OBJS += $(WPAS_SRC)/events.o
+OBJS += $(WPAS_SRC)/gas_query.o
+OBJS += $(WPAS_SRC)/hs20_supplicant.o
+OBJS += $(WPAS_SRC)/interworking.o
+OBJS += $(WPAS_SRC)/notify.o
+OBJS += $(WPAS_SRC)/offchannel.o
+OBJS += $(WPAS_SRC)/op_classes.o
+OBJS += $(WPAS_SRC)/robust_av.o
+OBJS += $(WPAS_SRC)/rrm.o
+OBJS += $(WPAS_SRC)/scan.o
+OBJS += $(WPAS_SRC)/wmm_ac.o
+OBJS += $(WPAS_SRC)/wnm_sta.o
+OBJS += $(WPAS_SRC)/wpa_supplicant.o
+OBJS += $(WPAS_SRC)/wpas_glue.o
+OBJS += $(SRC)/drivers/driver_common.o
+OBJS += $(SRC)/drivers/drivers.o
+
+OBJS += wnm.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+_OBJS_VAR := ELIBS
+include ../../../src/objs.mk
+
+wnm: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+
+clean: common-clean
+ rm -f wnm *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/wnm/corpus/bss-tm-req.dat b/contrib/wpa/tests/fuzzing/wnm/corpus/bss-tm-req.dat
new file mode 100644
index 000000000000..14510bb3ad11
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/wnm/corpus/bss-tm-req.dat differ
diff --git a/contrib/wpa/tests/fuzzing/wnm/corpus/oss-fuzz-0001.dat b/contrib/wpa/tests/fuzzing/wnm/corpus/oss-fuzz-0001.dat
new file mode 100644
index 000000000000..53fdf659c438
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/wnm/corpus/oss-fuzz-0001.dat differ
diff --git a/contrib/wpa/tests/fuzzing/wnm/corpus/oss-fuzz-0002.dat b/contrib/wpa/tests/fuzzing/wnm/corpus/oss-fuzz-0002.dat
new file mode 100644
index 000000000000..cb700936fad5
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/wnm/corpus/oss-fuzz-0002.dat differ
diff --git a/contrib/wpa/tests/fuzzing/wnm/corpus/wnm-notif.dat b/contrib/wpa/tests/fuzzing/wnm/corpus/wnm-notif.dat
new file mode 100644
index 000000000000..c234d3ad5b69
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/wnm/corpus/wnm-notif.dat differ
diff --git a/contrib/wpa/tests/fuzzing/wnm/wnm.c b/contrib/wpa/tests/fuzzing/wnm/wnm.c
new file mode 100644
index 000000000000..7afc648e442a
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/wnm/wnm.c
@@ -0,0 +1,99 @@
+/*
+ * wpa_supplicant - WNM fuzzer
+ * Copyright (c) 2015-2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/wpa_i.h"
+#include "wpa_supplicant_i.h"
+#include "bss.h"
+#include "wnm_sta.h"
+#include "../../../wpa_supplicant/config.h"
+#include "../fuzzer-common.h"
+
+
+struct arg_ctx {
+ const u8 *data;
+ size_t data_len;
+ struct wpa_supplicant wpa_s;
+ struct wpa_bss bss;
+ struct wpa_driver_ops driver;
+ struct wpa_sm wpa;
+ struct wpa_config conf;
+};
+
+
+static void test_send_wnm(void *eloop_data, void *user_ctx)
+{
+ struct arg_ctx *ctx = eloop_data;
+ const struct ieee80211_mgmt *mgmt;
+
+ wpa_hexdump(MSG_MSGDUMP, "fuzzer - WNM", ctx->data, ctx->data_len);
+
+ mgmt = (const struct ieee80211_mgmt *) ctx->data;
+ ieee802_11_rx_wnm_action(&ctx->wpa_s, mgmt, ctx->data_len);
+
+ eloop_terminate();
+}
+
+
+static int init_wpa(struct arg_ctx *ctx)
+{
+ ctx->wpa_s.wpa_state = WPA_COMPLETED;
+ os_memcpy(ctx->wpa_s.bssid, "\x02\x00\x00\x00\x03\x00", ETH_ALEN);
+ ctx->wpa_s.current_bss = &ctx->bss;
+ ctx->wpa_s.driver = &ctx->driver;
+ ctx->wpa_s.wpa = &ctx->wpa;
+ ctx->wpa_s.conf = &ctx->conf;
+
+ return 0;
+}
+
+
+static void deinit_wpa(struct arg_ctx *ctx)
+{
+ wnm_deallocate_memory(&ctx->wpa_s);
+}
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct arg_ctx ctx;
+
+ wpa_fuzzer_set_debug_level();
+
+ if (os_program_init())
+ return 0;
+
+ if (eloop_init()) {
+ wpa_printf(MSG_ERROR, "Failed to initialize event loop");
+ return 0;
+ }
+
+ os_memset(&ctx, 0, sizeof(ctx));
+ ctx.data = data;
+ ctx.data_len = size;
+ if (init_wpa(&ctx))
+ goto fail;
+
+ eloop_register_timeout(0, 0, test_send_wnm, &ctx, NULL);
+
+ wpa_printf(MSG_DEBUG, "Starting eloop");
+ eloop_run();
+ wpa_printf(MSG_DEBUG, "eloop done");
+ deinit_wpa(&ctx);
+
+fail:
+ eloop_destroy();
+ os_program_deinit();
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/fuzzing/x509/.gitignore b/contrib/wpa/tests/fuzzing/x509/.gitignore
new file mode 100644
index 000000000000..490a5d71f3f2
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/x509/.gitignore
@@ -0,0 +1 @@
+x509
diff --git a/contrib/wpa/tests/fuzzing/x509/Makefile b/contrib/wpa/tests/fuzzing/x509/Makefile
new file mode 100644
index 000000000000..306473c3dba3
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/x509/Makefile
@@ -0,0 +1,27 @@
+ALL=x509
+include ../rules.include
+
+LIBS += $(SRC)/common/libcommon.a
+LIBS += $(SRC)/crypto/libcrypto.a
+LIBS += $(SRC)/tls/libtls.a
+LIBS += $(SRC)/utils/libutils.a
+
+ELIBS += $(SRC)/crypto/libcrypto.a
+ELIBS += $(SRC)/tls/libtls.a
+
+OBJS += x509.o
+
+_OBJS_VAR := OBJS
+include ../../../src/objs.mk
+
+_OBJS_VAR := LIBS
+include ../../../src/objs.mk
+
+_OBJS_VAR := ELIBS
+include ../../../src/objs.mk
+
+x509: $(OBJS) $(LIBS)
+ $(LDO) $(LDFLAGS) -o $@ $^ $(LIBS) $(ELIBS)
+
+clean: common-clean
+ rm -f x509 *~ *.o *.d ../*~ ../*.o ../*.d
diff --git a/contrib/wpa/tests/fuzzing/x509/corpus/ca.der b/contrib/wpa/tests/fuzzing/x509/corpus/ca.der
new file mode 100644
index 000000000000..09d5fa051bf3
Binary files /dev/null and b/contrib/wpa/tests/fuzzing/x509/corpus/ca.der differ
diff --git a/contrib/wpa/tests/fuzzing/x509/corpus/oss-fuzz-15408 b/contrib/wpa/tests/fuzzing/x509/corpus/oss-fuzz-15408
new file mode 100644
index 000000000000..a6f74c54623f
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/x509/corpus/oss-fuzz-15408
@@ -0,0 +1 @@
+0‚™0‚•   0  0/1 0  € 1 0   0 3 6 91 1 2Z 3 6 7 6 1 2Z0/1 0   1 0   0Ÿ0   ÿ ÿ ÿ £P0N0  0  0U0! ÿ
\ No newline at end of file
diff --git a/contrib/wpa/tests/fuzzing/x509/x509.c b/contrib/wpa/tests/fuzzing/x509/x509.c
new file mode 100644
index 000000000000..2969fea3e287
--- /dev/null
+++ b/contrib/wpa/tests/fuzzing/x509/x509.c
@@ -0,0 +1,25 @@
+/*
+ * Testing tool for X.509v3 routines
+ * Copyright (c) 2006-2019, 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 "common.h"
+#include "tls/x509v3.h"
+#include "../fuzzer-common.h"
+
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct x509_certificate *cert;
+
+ wpa_fuzzer_set_debug_level();
+
+ cert = x509_certificate_parse(data, size);
+ x509_certificate_free(cert);
+ return 0;
+}
diff --git a/contrib/wpa/tests/hwsim/.gitignore b/contrib/wpa/tests/hwsim/.gitignore
new file mode 100644
index 000000000000..25f0f66cf900
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/.gitignore
@@ -0,0 +1 @@
+sigma_dut
diff --git a/contrib/wpa/tests/hwsim/README b/contrib/wpa/tests/hwsim/README
new file mode 100644
index 000000000000..f0d8b18e3479
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/README
@@ -0,0 +1,220 @@
+Automated hostapd/wpa_supplicant testing with mac80211_hwsim
+------------------------------------------------------------
+
+This directory contains testing infrastructure and test cases to run
+automated tests of full hostapd and wpa_supplicant functionality. This
+testing is done with the help of mac80211_hwsim which is Linux kernel
+driver that simulates IEEE 802.11 radios without requiring any
+additional hardware. This setup most of the hostapd and wpa_supplicant
+functionality (and large parts of the Linux cfg80211 and mac80211
+functionality for that matter) to be tested.
+
+mac80211_hwsim is loaded with five simulated radios to allow different
+device combinations to be tested. wlantest is used analyze raw packets
+captured through the hwsim0 monitor interface that capture all frames
+sent on all channels. wlantest is used to store the frames for
+analysis. Three wpa_supplicant processes are used to control three
+virtual radios and one hostapd process is used to dynamically control
+the other two virtual radios. wpa_supplicant/hostapd test functionality
+is used to verify that data connection (both unicast and broadcast)
+works between two netdevs.
+
+The python scripts and tools in this directory control test case
+execution. They interact wpa_supplicant and hostapd through control
+interfaces to perform the operations. In addition, wlantest_cli is used
+to verify that operations have been performed correctly and that the
+network connection works in the expected way.
+
+These test cases are run automatically against the hostap.git commits
+for regression testing and to help in keeping the hostap.git master
+branch in stable state. Results from these tests are available here:
+http://buildbot.w1.fi/hwsim/
+
+
+Building binaries for testing
+-----------------------------
+
+You will need to build (or use already built) components to be
+tested. These are available in the hostap.git repository and can be
+built for example as follows:
+
+cd ../../wpa_supplicant
+cp ../tests/hwsim/example-wpa_supplicant.config .config
+make clean
+make
+cd ../hostapd
+cp ../tests/hwsim/example-hostapd.config .config
+make clean
+make hostapd hostapd_cli hlr_auc_gw
+cd ../wlantest
+make clean
+make
+
+Alternatively, the build.sh script here can be used to run these steps
+with conditional creation of .config files only if they do not exist.
+
+The test scripts can find the binaries in the locations where they were
+built. It is also possible to install wlantest_cli somewhere on the path
+to use pre-built tools.
+
+Please note that some of the configuration parameters used to enable
+more testing coverage may require development packages that may not be
+installed by default in many distributions. For example, following
+Debian/Ubuntu packages are likely to be needed:
+- binutils-dev
+- libsqlite3-dev
+- libpcap-dev
+
+example-setup.txt provides more complete step-by-step example on how a
+test setup can be built.
+
+
+wpaspy
+------
+
+The python scripts use wpaspy.py to interact with the wpa_supplicant
+control interface, but the run-tests.py script adds the (relative)
+path into the environment so it doesn't need to be installed.
+
+
+mac80211_hwsim
+--------------
+
+mac80211_hwsim kernel module is available from the upstream Linux
+kernel. Some Linux distributions enable it by default. If that's not the
+case, you can either enable it in the kernel configuration
+(CONFIG_MAC80211_HWSIM=m) and rebuild your kernel or use Backports with
+CPTCFG_MAC80211_HWSIM=m to replace the wireless LAN components in the
+base kernel.
+
+
+sudo
+----
+
+Some parts of the testing process requires root privileges. The test
+scripts are currently using sudo to achieve this. To be able to run the
+tests, you'll probably want to enable sudo with a timeout to not expire
+password entry very quickly. For example, use this in the sudoers file:
+
+Defaults env_reset,timestamp_timeout=180
+
+Or on a dedicated test system, you could even disable password prompting
+with this in sudoers:
+
+%sudo ALL=NOPASSWD: ALL
+
+
+Other network interfaces
+------------------------
+
+Some of the test scripts are still using hardcoded interface names, so
+the easiest way of making things work is to avoid using other network
+devices that may use conflicting interface names. For example, unload
+any wireless LAN driver before running the tests and make sure that
+wlan0..4 gets assigned as the interface names for the mac80211_hwsim
+radios. It may also be possible to rename the interface expectations in
+run-tests.py to allow other names to be used.
+
+Please also note that some commonly enabled tools, like NetworkManager,
+may end up trying to control new network interfaces automatically. This
+can result in conflicts with the test scripts and you may need to
+disable such network services or at least mark the mac80211_hwsim wlan#
+interfaces as umanaged. As an example, this can be done in
+/etc/NetworkManager/NetworkManager.conf with following addition:
+
+[keyfile]
+unmanaged-devices=mac:02:00:00:00:00:00;mac:02:00:00:00:01:00;mac:02:00:00:00:02:00;mac:02:00:00:00:03:00;mac:02:00:00:00:04:00
+
+
+Running tests
+-------------
+
+Simplest way to run a full set of the test cases is by running
+run-all.sh in tests/hwsim directory. This will use start.sh to load the
+mac80211_hwsim module and start wpa_supplicant, hostapd, and various
+test tools. run-tests.sh is then used to run through all the defined
+test cases and stop.sh to stop the programs and unload the kernel
+module.
+
+run-all.sh can be used to run the same test cases under different
+conditions:
+
+# run normal test cases
+./run-all.sh
+
+# run normal test cases under valgrind
+./run-all.sh valgrind
+
+# run normal test cases with Linux tracing
+./run-all.sh trace
+
+# run normal test cases with multi channel support (see details below)
+./run-all.sh channels=<num of channels>
+
+run-all.sh directs debug logs into the logs subdirectory (or $LOGDIR if
+present in the environment). Log file names include the current UNIX
+timestamp and a postfix to identify the specific log:
+- *.log0 = wpa_supplicant debug log for the first radio
+- *.log1 = wpa_supplicant debug log for the second radio
+- *.log2 = wpa_supplicant debug log for the third radio
+- *.hostapd = hostapd debug log
+- hwsim0 = wlantest debug log
+- hwsim0.pcapng = capture with all frames exchanged during the tests
+- *.log = debug prints from the test scripts
+- trace.dat = Linux tracing record (if enabled)
+- hlr_auc_gw - hlr_auc_gw (EAP-SIM/AKA/AKA' authentication) log
+- auth_serv - hostapd as RADIUS authentication server log
+
+
+For manual testing, ./start.sh can be used to initialize interfaces and
+programs and run-tests.py to execute one or more test
+cases. run-tests.py output verbosity can be controlled with -d (more
+verbose debug output) and -q (less verbose output) on the command
+line. "-f <module name>" (pointing to file test_<module name>.py) can be
+used to specify that all test cases from a single file are to be
+run. Test name as the last command line argument can be specified that a
+single test case is to be run (e.g., "./run-tests.py ap_pmf_required").
+
+Notice that some tests require the driver to support concurrent
+operation on multi channels in order to run. These tests will be skipped
+in case the driver does not support multi channels. To enable support
+for multi channel, the number of supported channel is passed as an
+argument to run-all.sh or start.sh
+
+
+Adding/modifying test cases
+---------------------------
+
+All the test cases are defined in the test_*.py files. These are python
+scripts that can use the local helper classes to interact with the test
+components. While various python constructs can be used in the scripts,
+only a minimal level of python knowledge should really be needed to
+modify and add new test cases. The easiest starting point for this is
+likely to take a look at some of the example scripts. When working on a
+new test, run-tests.py with -d and the test case name on the command
+line is a convenient way of verifying functionality.
+
+run-tests.py will automatically import all test cases from the test_*.py
+files in this directory. All functions starting with the "test_" prefix
+in these files are assumed to be test cases. Each test case is named by
+the function name following the "test_" prefix.
+
+
+Results database
+----------------
+
+run-tests.py can be requested to write results from the execution of
+each test case into an sqlite database. The "-S <path to database>" and
+"-b <build id>" command line arguments can be used to do that. The
+database must have been prepared before this, e.g., with following:
+
+cat | sqlite3 /tmp/example.db <<EOF
+CREATE TABLE results (test,result,run,time,duration,build,commitid);
+CREATE INDEX results_idx ON results (test);
+CREATE INDEX results_idx2 ON results (run);
+CREATE TABLE tests (test,description);
+CREATE UNIQUE INDEX tests_idx ON tests (test);
+CREATE TABLE logs (test,run,type,contents);
+CREATE INDEX logs_idx ON logs (test);
+CREATE INDEX logs_idx2 ON logs (run);
+EOF
diff --git a/contrib/wpa/tests/hwsim/auth_serv/as.conf b/contrib/wpa/tests/hwsim/auth_serv/as.conf
new file mode 100644
index 000000000000..3c0eda22f739
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/as.conf
@@ -0,0 +1,27 @@
+driver=none
+radius_server_clients=auth_serv/radius_clients.conf
+radius_server_acct_port=1813
+eap_server=1
+eap_user_file=auth_serv/eap_user.conf
+
+interface=as
+ctrl_interface=/var/run/hostapd
+ctrl_interface_group=admin
+
+ca_cert=auth_serv/ca.pem
+server_cert=auth_serv/server.pem
+private_key=auth_serv/server.key
+ocsp_stapling_response=LOGDIR/ocsp-server-cache.der
+ocsp_stapling_response_multi=auth_serv/ocsp-multi-server-cache.der
+server_id=server.w1.fi
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock
+dh_file=auth_serv/dh.conf
+pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f
+eap_fast_a_id=101112131415161718191a1b1c1d1e1f
+eap_fast_a_id_info=test server
+eap_sim_aka_result_ind=1
+tls_flags=[ENABLE-TLSv1.3]
+
+dump_msk_file=LOGDIR/as-msk.lst
+
+hs20_t_c_server_url=https://example.com/t_and_c?addr=@1@&ap=123
diff --git a/contrib/wpa/tests/hwsim/auth_serv/as2.conf b/contrib/wpa/tests/hwsim/auth_serv/as2.conf
new file mode 100644
index 000000000000..963db7aea568
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/as2.conf
@@ -0,0 +1,24 @@
+driver=none
+radius_server_clients=auth_serv/radius_clients.conf
+radius_server_auth_port=1814
+eap_server=1
+eap_user_file=auth_serv/eap_user.conf
+
+interface=as2
+ctrl_interface=/var/run/hostapd
+ctrl_interface_group=admin
+
+ca_cert=auth_serv/ca.pem
+server_cert=auth_serv/server.pem
+private_key=auth_serv/server.key
+ocsp_stapling_response=LOGDIR/ocsp-server-cache.der
+ocsp_stapling_response_multi=auth_serv/ocsp-multi-server-cache.der
+server_id=server2.w1.fi
+eap_sim_db=unix:/tmp/hlr_auc_gw.sock db=LOGDIR/hostapd.db
+dh_file=auth_serv/dh.conf
+pac_opaque_encr_key=000102030405060708090a0b0c0d0e0f
+eap_fast_a_id=101112131415161718191a1b1c1d1e1f
+eap_fast_a_id_info=test server2
+eap_sim_aka_result_ind=1
+
+dump_msk_file=LOGDIR/as2-msk.lst
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ca-and-crl-expired.pem b/contrib/wpa/tests/hwsim/auth_serv/ca-and-crl-expired.pem
new file mode 100644
index 000000000000..dc7bf98c1546
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ca-and-crl-expired.pem
@@ -0,0 +1,90 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 42:97:6c:30:8e:79:fc:7b:6a:e3:ef:9d:18:a4:74:9d:8b:5f:57:53
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C = FI, L = Tuusula, O = w1.fi, CN = Root CA
+ Validity
+ Not Before: May 2 19:49:48 2020 GMT
+ Not After : Apr 30 19:49:48 2030 GMT
+ Subject: C = FI, L = Tuusula, O = w1.fi, CN = Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:bc:f4:ee:44:62:7f:62:4f:a1:81:46:ba:c4:aa:
+ 1e:fd:4e:d0:ed:f1:47:cb:25:5b:66:7a:86:39:91:
+ ca:b5:61:a7:7e:2f:3c:63:7d:39:b8:1a:9e:cb:6d:
+ 32:32:91:de:49:49:84:da:15:be:2b:dd:c6:bc:1f:
+ dc:6e:c0:2d:77:f2:d0:7b:2c:40:19:07:60:55:b0:
+ ff:7c:51:ef:38:d1:f0:2a:da:a8:cc:ea:d6:54:a4:
+ ef:be:17:44:1a:9e:33:70:57:a4:f3:06:ac:3d:ee:
+ 4b:2d:e5:46:25:2d:33:09:f6:49:a8:02:31:a4:65:
+ 9b:32:0a:67:f5:02:e1:3b:47:a6:ae:e4:f6:85:eb:
+ 5d:3e:02:66:dd:11:98:ac:34:72:c2:8f:25:55:4a:
+ 6a:ea:e8:82:2f:bd:7f:78:31:a4:5a:d7:32:bb:64:
+ 48:46:23:ef:c8:c9:e2:84:00:56:72:e8:4b:54:95:
+ 62:3a:5a:11:79:ee:40:43:9e:16:2c:cc:e6:45:f4:
+ bb:82:28:c2:83:35:2c:55:36:99:59:11:b1:15:d0:
+ 03:c1:a5:37:e1:1f:bb:43:c7:b4:b9:33:de:14:d7:
+ 7c:99:45:0f:c1:06:fe:b6:25:10:59:b7:72:76:7f:
+ 91:4b:ea:d1:b9:6a:6a:ed:dd:1b:a9:0e:a7:29:48:
+ b7:4d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha256WithRSAEncryption
+ 41:f9:c3:a3:77:11:92:55:e7:4b:4a:32:6a:31:d9:51:cf:06:
+ a5:39:ea:30:98:b8:8d:4f:24:c5:34:fd:c6:98:10:59:32:7e:
+ 57:f5:8f:ba:67:c9:fc:44:68:b3:7c:f1:af:3a:5f:0d:8f:a1:
+ fe:41:21:0e:e9:08:a3:63:49:66:34:4a:cd:ce:66:74:47:30:
+ f7:dc:82:99:21:56:82:ff:2d:12:90:7d:7a:64:22:a0:ed:fa:
+ 62:d9:5a:d3:97:96:0c:04:a7:47:88:da:53:b6:33:15:15:f9:
+ da:ee:ac:25:e9:07:02:89:bc:73:a2:c6:27:6f:1f:bd:73:b8:
+ 8e:f7:94:54:57:a7:8b:5b:9a:24:aa:86:d4:04:5c:8c:cb:28:
+ a2:45:f9:34:f0:01:20:bb:06:e8:41:14:d2:d7:ca:e8:bf:4e:
+ 16:72:22:a0:0c:86:ca:73:23:09:ae:71:f1:52:0c:db:b2:8a:
+ 4d:94:a5:fa:15:81:5b:a2:95:62:50:a1:d6:64:fe:4c:0c:60:
+ 8d:9b:0f:b8:41:ac:cb:31:c2:17:6c:7b:61:13:16:9a:db:64:
+ fc:5f:47:84:3d:d2:2e:db:0b:9e:b6:1e:85:04:c1:e5:c0:b2:
+ 6d:8f:f2:99:00:3a:1a:ab:02:cf:45:7a:26:c1:b0:1f:c6:b0:
+ d0:4d:f7:52
+-----BEGIN CERTIFICATE-----
+MIIDYDCCAkigAwIBAgIUQpdsMI55/Htq4++dGKR0nYtfV1MwDQYJKoZIhvcNAQEL
+BQAwQTELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoMBXcx
+LmZpMRAwDgYDVQQDDAdSb290IENBMB4XDTIwMDUwMjE5NDk0OFoXDTMwMDQzMDE5
+NDk0OFowQTELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoM
+BXcxLmZpMRAwDgYDVQQDDAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAvPTuRGJ/Yk+hgUa6xKoe/U7Q7fFHyyVbZnqGOZHKtWGnfi88Y305
+uBqey20yMpHeSUmE2hW+K93GvB/cbsAtd/LQeyxAGQdgVbD/fFHvONHwKtqozOrW
+VKTvvhdEGp4zcFek8wasPe5LLeVGJS0zCfZJqAIxpGWbMgpn9QLhO0emruT2hetd
+PgJm3RGYrDRywo8lVUpq6uiCL71/eDGkWtcyu2RIRiPvyMnihABWcuhLVJViOloR
+ee5AQ54WLMzmRfS7gijCgzUsVTaZWRGxFdADwaU34R+7Q8e0uTPeFNd8mUUPwQb+
+tiUQWbdydn+RS+rRuWpq7d0bqQ6nKUi3TQIDAQABo1AwTjAdBgNVHQ4EFgQUpP25
+ORuBs6rriB3Ugam1EXDMp+EwHwYDVR0jBBgwFoAUpP25ORuBs6rriB3Ugam1EXDM
+p+EwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAQfnDo3cRklXnS0oy
+ajHZUc8GpTnqMJi4jU8kxTT9xpgQWTJ+V/WPumfJ/ERos3zxrzpfDY+h/kEhDukI
+o2NJZjRKzc5mdEcw99yCmSFWgv8tEpB9emQioO36Ytla05eWDASnR4jaU7YzFRX5
+2u6sJekHAom8c6LGJ28fvXO4jveUVFeni1uaJKqG1ARcjMsookX5NPABILsG6EEU
+0tfK6L9OFnIioAyGynMjCa5x8VIM27KKTZSl+hWBW6KVYlCh1mT+TAxgjZsPuEGs
+yzHCF2x7YRMWmttk/F9HhD3SLtsLnrYehQTB5cCybY/ymQA6GqsCz0V6JsGwH8aw
+0E33Ug==
+-----END CERTIFICATE-----
+-----BEGIN X509 CRL-----
+MIIBmjCBgwIBATANBgkqhkiG9w0BAQsFADBBMQswCQYDVQQGEwJGSTEQMA4GA1UE
+BwwHVHV1c3VsYTEOMAwGA1UECgwFdzEuZmkxEDAOBgNVBAMMB1Jvb3QgQ0EXDTIw
+MDUwMjE1MDYwN1oXDTIwMDUwMjE2MDYwN1qgDjAMMAoGA1UdFAQDAgEHMA0GCSqG
+SIb3DQEBCwUAA4IBAQBpgpd1hBcONRssjbezGJDE4WC4gSpW9ufS7OgzWXky9AIq
+ea5engK/LCTn0GZVwRvuDkHn0H/dS68pFoQSnrbyS7Alz8oJf/T41vKgG8sxkfra
+tvezWu7x8Kaz6QQuoxoGERZhudyNoPTUYKQpqnUjlz0088j+HqBuy6uSQsDlOXI7
+dxbXU25JvJlebJEeMxd/R+8SkVmXN6OR9RO+kkm0BIjhuUc2BOToxZhPj4PS7If0
+RO5S7WSgZOyg1d0yq/EMNvfm8gT5RioC0rceBlt5FIbjg+xn4VExyg73CbeMjC8O
+CRblHL1o5GK7zHTyKFZ/KUdKIc9sdB/Eehcyvo+Z
+-----END X509 CRL-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ca-and-crl.pem b/contrib/wpa/tests/hwsim/auth_serv/ca-and-crl.pem
new file mode 100644
index 000000000000..4e8367884a78
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ca-and-crl.pem
@@ -0,0 +1,90 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 42:97:6c:30:8e:79:fc:7b:6a:e3:ef:9d:18:a4:74:9d:8b:5f:57:53
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C = FI, L = Tuusula, O = w1.fi, CN = Root CA
+ Validity
+ Not Before: May 2 19:49:48 2020 GMT
+ Not After : Apr 30 19:49:48 2030 GMT
+ Subject: C = FI, L = Tuusula, O = w1.fi, CN = Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:bc:f4:ee:44:62:7f:62:4f:a1:81:46:ba:c4:aa:
+ 1e:fd:4e:d0:ed:f1:47:cb:25:5b:66:7a:86:39:91:
+ ca:b5:61:a7:7e:2f:3c:63:7d:39:b8:1a:9e:cb:6d:
+ 32:32:91:de:49:49:84:da:15:be:2b:dd:c6:bc:1f:
+ dc:6e:c0:2d:77:f2:d0:7b:2c:40:19:07:60:55:b0:
+ ff:7c:51:ef:38:d1:f0:2a:da:a8:cc:ea:d6:54:a4:
+ ef:be:17:44:1a:9e:33:70:57:a4:f3:06:ac:3d:ee:
+ 4b:2d:e5:46:25:2d:33:09:f6:49:a8:02:31:a4:65:
+ 9b:32:0a:67:f5:02:e1:3b:47:a6:ae:e4:f6:85:eb:
+ 5d:3e:02:66:dd:11:98:ac:34:72:c2:8f:25:55:4a:
+ 6a:ea:e8:82:2f:bd:7f:78:31:a4:5a:d7:32:bb:64:
+ 48:46:23:ef:c8:c9:e2:84:00:56:72:e8:4b:54:95:
+ 62:3a:5a:11:79:ee:40:43:9e:16:2c:cc:e6:45:f4:
+ bb:82:28:c2:83:35:2c:55:36:99:59:11:b1:15:d0:
+ 03:c1:a5:37:e1:1f:bb:43:c7:b4:b9:33:de:14:d7:
+ 7c:99:45:0f:c1:06:fe:b6:25:10:59:b7:72:76:7f:
+ 91:4b:ea:d1:b9:6a:6a:ed:dd:1b:a9:0e:a7:29:48:
+ b7:4d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha256WithRSAEncryption
+ 41:f9:c3:a3:77:11:92:55:e7:4b:4a:32:6a:31:d9:51:cf:06:
+ a5:39:ea:30:98:b8:8d:4f:24:c5:34:fd:c6:98:10:59:32:7e:
+ 57:f5:8f:ba:67:c9:fc:44:68:b3:7c:f1:af:3a:5f:0d:8f:a1:
+ fe:41:21:0e:e9:08:a3:63:49:66:34:4a:cd:ce:66:74:47:30:
+ f7:dc:82:99:21:56:82:ff:2d:12:90:7d:7a:64:22:a0:ed:fa:
+ 62:d9:5a:d3:97:96:0c:04:a7:47:88:da:53:b6:33:15:15:f9:
+ da:ee:ac:25:e9:07:02:89:bc:73:a2:c6:27:6f:1f:bd:73:b8:
+ 8e:f7:94:54:57:a7:8b:5b:9a:24:aa:86:d4:04:5c:8c:cb:28:
+ a2:45:f9:34:f0:01:20:bb:06:e8:41:14:d2:d7:ca:e8:bf:4e:
+ 16:72:22:a0:0c:86:ca:73:23:09:ae:71:f1:52:0c:db:b2:8a:
+ 4d:94:a5:fa:15:81:5b:a2:95:62:50:a1:d6:64:fe:4c:0c:60:
+ 8d:9b:0f:b8:41:ac:cb:31:c2:17:6c:7b:61:13:16:9a:db:64:
+ fc:5f:47:84:3d:d2:2e:db:0b:9e:b6:1e:85:04:c1:e5:c0:b2:
+ 6d:8f:f2:99:00:3a:1a:ab:02:cf:45:7a:26:c1:b0:1f:c6:b0:
+ d0:4d:f7:52
+-----BEGIN CERTIFICATE-----
+MIIDYDCCAkigAwIBAgIUQpdsMI55/Htq4++dGKR0nYtfV1MwDQYJKoZIhvcNAQEL
+BQAwQTELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoMBXcx
+LmZpMRAwDgYDVQQDDAdSb290IENBMB4XDTIwMDUwMjE5NDk0OFoXDTMwMDQzMDE5
+NDk0OFowQTELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoM
+BXcxLmZpMRAwDgYDVQQDDAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAvPTuRGJ/Yk+hgUa6xKoe/U7Q7fFHyyVbZnqGOZHKtWGnfi88Y305
+uBqey20yMpHeSUmE2hW+K93GvB/cbsAtd/LQeyxAGQdgVbD/fFHvONHwKtqozOrW
+VKTvvhdEGp4zcFek8wasPe5LLeVGJS0zCfZJqAIxpGWbMgpn9QLhO0emruT2hetd
+PgJm3RGYrDRywo8lVUpq6uiCL71/eDGkWtcyu2RIRiPvyMnihABWcuhLVJViOloR
+ee5AQ54WLMzmRfS7gijCgzUsVTaZWRGxFdADwaU34R+7Q8e0uTPeFNd8mUUPwQb+
+tiUQWbdydn+RS+rRuWpq7d0bqQ6nKUi3TQIDAQABo1AwTjAdBgNVHQ4EFgQUpP25
+ORuBs6rriB3Ugam1EXDMp+EwHwYDVR0jBBgwFoAUpP25ORuBs6rriB3Ugam1EXDM
+p+EwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAQfnDo3cRklXnS0oy
+ajHZUc8GpTnqMJi4jU8kxTT9xpgQWTJ+V/WPumfJ/ERos3zxrzpfDY+h/kEhDukI
+o2NJZjRKzc5mdEcw99yCmSFWgv8tEpB9emQioO36Ytla05eWDASnR4jaU7YzFRX5
+2u6sJekHAom8c6LGJ28fvXO4jveUVFeni1uaJKqG1ARcjMsookX5NPABILsG6EEU
+0tfK6L9OFnIioAyGynMjCa5x8VIM27KKTZSl+hWBW6KVYlCh1mT+TAxgjZsPuEGs
+yzHCF2x7YRMWmttk/F9HhD3SLtsLnrYehQTB5cCybY/ymQA6GqsCz0V6JsGwH8aw
+0E33Ug==
+-----END CERTIFICATE-----
+-----BEGIN X509 CRL-----
+MIIBmjCBgwIBATANBgkqhkiG9w0BAQsFADBBMQswCQYDVQQGEwJGSTEQMA4GA1UE
+BwwHVHV1c3VsYTEOMAwGA1UECgwFdzEuZmkxEDAOBgNVBAMMB1Jvb3QgQ0EXDTIw
+MDUwMzE0NTY1M1oXDTI4MDUwMzE0NTY1M1qgDjAMMAoGA1UdFAQDAgECMA0GCSqG
+SIb3DQEBCwUAA4IBAQCaoYj8yLx4eF+bupRl0YQ7h8MlZ3nFmEJFCXlRrPurWLC0
+tUC/8mMA4GJR6CUGUTZ70pfxKoC1Uca5uMJjNkfOJu0UAnMoiGk7W3Fqbbihigku
+KU48HHieHoKBFc1+95I1TDVHnaDUkoDpT5W9J9yk5XHzJC7xZC411CM2tRZrKo/h
+DRyooWZ5KPT+fthgzDvGSngbMXWumWYMv33PhiMrRlwQgxdt5ECXMbsIN9nY6Sz2
+RFbR9gVA3DwQ5TCMC3UFvHOEn5WcsEeMlNGdoTEb0LbGLnAIxnvHN626HeAgfruj
+6Zec54XKEBnpwBlpfENL6eWJZ+NNVkedrSYdcVM0
+-----END X509 CRL-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ca-incorrect-key.pem b/contrib/wpa/tests/hwsim/auth_serv/ca-incorrect-key.pem
new file mode 100644
index 000000000000..ae28d447c435
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ca-incorrect-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCorkTzjidMmpUN
+G78W6HDErNBhAgZVX6grzX+v1l9YxzUitpwgQP319PlMdHGDtY7E1h0kwlrmifsA
+6p6Ejk1o4BYzLnZtcC/nhu8zjyCakDhgvpwbzL5m6TwVsJHyAhUsmdpOZOFH/yVY
+zgmN1jnKi7MTnEUgXafts5ZJ9+yf1ju1G/pMynZpcayln2ffMaiAz7YGbefjGLKe
+kjgOX82UfZqqvkMu6b5u+hSRRRexDEqsQZxOT/X8iO4VL6tWu8LVU+PJ+bKSiIfW
+oNgqpzAddrv0kAW59u8RCjI/eyXceywsCcEQ2V2JwjL7p4Oil4yyyxHHAi5nmQ7Z
+225UszcJAgMBAAECggEAEaLGysAeE8BFvS0deYOr5qQ61SmlB+AMcSf3JadAKMQL
+Jin5gNXKt6B5QCkchSzCVIoeWe2IG3ppp9rf3/QQ29ox9//vmdmU6JwO/lEEk6Ro
+gJTtNWrerVvNUGc5mxfkptkfHfsmIqTmfrZsAUxYlnisrGw2PgAMwql4GRu2va84
+8ZVUVG6+WNizMetsDU1ZuGLVYL7e7YHQG34xW898mdjojv0pJ/XO0mkJvjWMnVCA
++gQrct7k3G+59ap5p9hd/1kCtFqV4E6XkdqW4bP8W+jXPwyReAxLSibU6VYZVoAV
+VYpkH0f6vDxn8mfEX4llSnOADeYlahO1QkCJE7xxUQKBgQDTCMCIfrmzrfMZo/s7
+68EjGjmBYipaXIZRtIptk//2FmWkm6VHKi5rWO01BUFXFoTEP3syc6BbWhdgRawi
+iM4yS/83sogE8Zl8UKKAv1kcm+HRqrJ+o02b2glcqRmLQPOKcHjNgyWV/yZYbHX0
+BE8yMXlJDBVRcNkGWBtcDuEQ7wKBgQDMn0VBUZyv6ud7vpeLa2RNl7vMyvYAu+Vo
+73lWbvwldAY8md4/Oh9ZWsznpSXer0Kx0cHgGfaZw0yzazg9P25RVOmXt01t21l0
+atz82CTAkWDKT7NdXscW5aAtmsCNIpNLcScU94F9jtNKidMB+FUGhcX1gvNQoFQo
+kHp0cr9HhwKBgDYGxZOsLcqMO/JSgt0iS/26lwJCqWkcyt5cBBxtiVfs//SWTEfd
+yqh8ya2LPOEYyMCdJ+MQqvr4I4foDluA/pjtz9bog94QJCUpV5Dya9PhLHzK4It/
+Lz05IrBwMjPuWusURDkI3DR8b8qvabsg502IUO6cg1CoPUdcgxScUo5NAoGAbExH
+nUCSi1DqX0YKcxHNrnuGO+eXt9+6lYVZVPO8pB36Rwyw4gnjGanDFT8FAg0EYZTA
+5dkX+V2yNKukwlXWD1m/oDq10lTrzX/ZokDLgtfuwGTaa6qD+Ixj8H8dNhV8m8sx
+ghlVAZ0cGzFC6qICbkdS9JPwL1YL9MQy63rn3fUCgYEAj3kgp+ByZJjA4fEk+CkB
+V6VL+3GKEq0oc910O4flUfIRO7bOh9XOcrPyhES9Kxj5hh8UP4OTiTmnQmtqEMq0
+K5/8FojDEnh3DmCC6ZDe79vYXQB4c2MRJg1DApZiklpsLJSIjO4ZQ4H3aQQEKCQe
+DOd7egyPAkD7rmwgWBvF3Nk=
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ca-incorrect.der b/contrib/wpa/tests/hwsim/auth_serv/ca-incorrect.der
new file mode 100644
index 000000000000..75bb94d71aef
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/ca-incorrect.der differ
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ca-incorrect.pem b/contrib/wpa/tests/hwsim/auth_serv/ca-incorrect.pem
new file mode 100644
index 000000000000..4afabbd42f31
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ca-incorrect.pem
@@ -0,0 +1,79 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 59:db:b0:44:3b:5a:59:c8:8e:2b:14:38:c4:3b:60:b6:1f:a5:fe:38
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C = FI, L = Tuusula, O = w1.fi, CN = TEST - Incorrect Root CA
+ Validity
+ Not Before: May 2 19:49:48 2020 GMT
+ Not After : Apr 30 19:49:48 2030 GMT
+ Subject: C = FI, L = Tuusula, O = w1.fi, CN = TEST - Incorrect Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:a8:ae:44:f3:8e:27:4c:9a:95:0d:1b:bf:16:e8:
+ 70:c4:ac:d0:61:02:06:55:5f:a8:2b:cd:7f:af:d6:
+ 5f:58:c7:35:22:b6:9c:20:40:fd:f5:f4:f9:4c:74:
+ 71:83:b5:8e:c4:d6:1d:24:c2:5a:e6:89:fb:00:ea:
+ 9e:84:8e:4d:68:e0:16:33:2e:76:6d:70:2f:e7:86:
+ ef:33:8f:20:9a:90:38:60:be:9c:1b:cc:be:66:e9:
+ 3c:15:b0:91:f2:02:15:2c:99:da:4e:64:e1:47:ff:
+ 25:58:ce:09:8d:d6:39:ca:8b:b3:13:9c:45:20:5d:
+ a7:ed:b3:96:49:f7:ec:9f:d6:3b:b5:1b:fa:4c:ca:
+ 76:69:71:ac:a5:9f:67:df:31:a8:80:cf:b6:06:6d:
+ e7:e3:18:b2:9e:92:38:0e:5f:cd:94:7d:9a:aa:be:
+ 43:2e:e9:be:6e:fa:14:91:45:17:b1:0c:4a:ac:41:
+ 9c:4e:4f:f5:fc:88:ee:15:2f:ab:56:bb:c2:d5:53:
+ e3:c9:f9:b2:92:88:87:d6:a0:d8:2a:a7:30:1d:76:
+ bb:f4:90:05:b9:f6:ef:11:0a:32:3f:7b:25:dc:7b:
+ 2c:2c:09:c1:10:d9:5d:89:c2:32:fb:a7:83:a2:97:
+ 8c:b2:cb:11:c7:02:2e:67:99:0e:d9:db:6e:54:b3:
+ 37:09
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ 0B:56:70:D1:C5:1C:DE:A7:F3:27:07:62:EA:F9:32:BD:C6:95:DD:51
+ X509v3 Authority Key Identifier:
+ keyid:0B:56:70:D1:C5:1C:DE:A7:F3:27:07:62:EA:F9:32:BD:C6:95:DD:51
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha256WithRSAEncryption
+ 9f:dd:16:ec:26:65:db:7b:49:82:83:f7:72:49:84:44:9e:b7:
+ ec:fa:35:53:f9:7c:fd:e1:1e:b0:ec:bc:44:45:6e:47:26:9a:
+ d4:03:91:e5:72:25:3d:86:93:e0:9a:9a:e2:95:f2:e9:3d:57:
+ 26:d4:7e:0a:36:9f:db:f0:76:09:51:98:9c:e9:96:cc:64:5e:
+ c6:c7:d1:59:46:da:4d:03:5a:4f:64:f6:b0:2b:f8:12:f2:a1:
+ 0a:f2:a4:b9:df:0e:5f:b4:f3:18:26:0e:ab:18:29:33:5c:40:
+ 54:48:f6:c2:37:ea:62:45:ae:d6:39:fe:75:f0:61:ff:3d:65:
+ 3e:65:38:e9:07:08:2f:ea:d0:80:8a:4d:0a:62:9c:ae:22:45:
+ aa:7e:09:be:43:ce:bd:fc:f7:8c:b4:ba:e2:52:f1:1d:79:7c:
+ ad:2f:09:29:82:6d:0d:64:d1:25:a3:9b:36:eb:1b:e0:f0:04:
+ 18:c4:29:d3:2e:c7:67:12:fa:3d:1f:81:e3:2c:5b:25:63:8c:
+ c8:1c:9b:bd:e6:c1:22:c8:34:17:fd:64:3a:3f:30:75:36:18:
+ e2:2d:49:16:07:ad:ba:ce:28:c7:df:06:81:57:55:cd:34:7b:
+ 81:fd:5e:97:5d:c5:d1:dd:f1:61:2d:f5:ce:06:7f:4d:2e:a4:
+ 5e:77:9b:d1
+-----BEGIN CERTIFICATE-----
+MIIDgjCCAmqgAwIBAgIUWduwRDtaWciOKxQ4xDtgth+l/jgwDQYJKoZIhvcNAQEL
+BQAwUjELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoMBXcx
+LmZpMSEwHwYDVQQDDBhURVNUIC0gSW5jb3JyZWN0IFJvb3QgQ0EwHhcNMjAwNTAy
+MTk0OTQ4WhcNMzAwNDMwMTk0OTQ4WjBSMQswCQYDVQQGEwJGSTEQMA4GA1UEBwwH
+VHV1c3VsYTEOMAwGA1UECgwFdzEuZmkxITAfBgNVBAMMGFRFU1QgLSBJbmNvcnJl
+Y3QgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKiuRPOO
+J0yalQ0bvxbocMSs0GECBlVfqCvNf6/WX1jHNSK2nCBA/fX0+Ux0cYO1jsTWHSTC
+WuaJ+wDqnoSOTWjgFjMudm1wL+eG7zOPIJqQOGC+nBvMvmbpPBWwkfICFSyZ2k5k
+4Uf/JVjOCY3WOcqLsxOcRSBdp+2zlkn37J/WO7Ub+kzKdmlxrKWfZ98xqIDPtgZt
+5+MYsp6SOA5fzZR9mqq+Qy7pvm76FJFFF7EMSqxBnE5P9fyI7hUvq1a7wtVT48n5
+spKIh9ag2CqnMB12u/SQBbn27xEKMj97Jdx7LCwJwRDZXYnCMvung6KXjLLLEccC
+LmeZDtnbblSzNwkCAwEAAaNQME4wHQYDVR0OBBYEFAtWcNHFHN6n8ycHYur5Mr3G
+ld1RMB8GA1UdIwQYMBaAFAtWcNHFHN6n8ycHYur5Mr3Gld1RMAwGA1UdEwQFMAMB
+Af8wDQYJKoZIhvcNAQELBQADggEBAJ/dFuwmZdt7SYKD93JJhESet+z6NVP5fP3h
+HrDsvERFbkcmmtQDkeVyJT2Gk+CamuKV8uk9VybUfgo2n9vwdglRmJzplsxkXsbH
+0VlG2k0DWk9k9rAr+BLyoQrypLnfDl+08xgmDqsYKTNcQFRI9sI36mJFrtY5/nXw
+Yf89ZT5lOOkHCC/q0ICKTQpinK4iRap+Cb5Dzr3894y0uuJS8R15fK0vCSmCbQ1k
+0SWjmzbrG+DwBBjEKdMux2cS+j0fgeMsWyVjjMgcm73mwSLINBf9ZDo/MHU2GOIt
+SRYHrbrOKMffBoFXVc00e4H9XpddxdHd8WEt9c4Gf00upF53m9E=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ca-key.pem b/contrib/wpa/tests/hwsim/auth_serv/ca-key.pem
new file mode 100644
index 000000000000..b66e03802935
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ca-key.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC89O5EYn9iT6GB
+RrrEqh79TtDt8UfLJVtmeoY5kcq1Yad+LzxjfTm4Gp7LbTIykd5JSYTaFb4r3ca8
+H9xuwC138tB7LEAZB2BVsP98Ue840fAq2qjM6tZUpO++F0QanjNwV6TzBqw97kst
+5UYlLTMJ9kmoAjGkZZsyCmf1AuE7R6au5PaF610+AmbdEZisNHLCjyVVSmrq6IIv
+vX94MaRa1zK7ZEhGI+/IyeKEAFZy6EtUlWI6WhF57kBDnhYszOZF9LuCKMKDNSxV
+NplZEbEV0APBpTfhH7tDx7S5M94U13yZRQ/BBv62JRBZt3J2f5FL6tG5amrt3Rup
+DqcpSLdNAgMBAAECggEBAJ9YofITaj8aziT545jjqfyN0c1G0vdyinCSVM0JsHtj
+Xd8gsHlp6hnigRUmAdX5gw4krJ9JBLVzchvFdpwC/pUPtFabC3bP7KJ3AAzz/5vY
+FwPcn8snIxYAfZi9gBY+YTyU/KphbzFO2iFbHttNEaSOCLFhIEH12XnKor5Q7mWQ
+5HHlTdCzvRlGQwSdrmYctQmekdSgffF56ebZzlkwrJAF+o8NX44mcNWSausnEuds
+S7Cah4dxT3Hm8luXfd1u3fCiT/p0ubMT66OVjo2cB0CIQxSpGWoIMuVrVrlzQbNt
+gtQ2cred2HKizlYpCjNd2zrRHauIc2koqQTP0+yNE5UCgYEA6Fuc4Wrq/maMQiOT
+QI46K5PktWArxFO152chdLpjy9qKmm0o7MjBZubRRW0kYHvtUwu15wcCH8Ctwucn
+JGrvtS3lMbNy14kQG7OrT87u2J5VyXNbGxOIhoeDRxEKCbDfyA+4c7sGHMxczxPc
+q6tWJ8cZeXLl8TMLacyG5aWF1WMCgYEA0C7RtPZh6J4XfsgZjO/7FoVgBp5yoche
+Hc6gwHiT7qYAbDQgOq7g41jEtYoO/e8qRsxsJHJlVzYIe3WlK5IC78sk6ZS7hZ6M
+LjfhnBPV1Ddtdq4w2VKY7fDYPvZK3DOc0FOIlaPicxWXUUDt0Tfud2qgYbebz+R6
+wmxqqcYM948CgYEAq6C/yGFJIpBsmY3dfpmPrhCXpsFakrGic0JiG+5xOGo8ZsSq
+rfu7n15uxXFQpVPkgKrtubAbiYiw0H4dE3FJjfJQkN2TvlCnbU7RAyo+khKiGyLx
+8JYFChmehie32mCjawrxm8pRQYRSKULqhIMSKF+QGX0dC4RAse041vfkWzECgYAr
+tAh2EtsO+FE6Xktu2No/KhS0jwLFj8iiPURl42o6yUKBdJfnedrgHzx8V9U53cFk
+R3nUVOeNXVx+fn4EHYzcRisjlgOf017ePQDxwQA8or4qEftTRBGyscLTxOSGQZeD
+7GVZ9KOPQVMYzaafKzy2eP3eRatCA1b6BcSGi3shZQKBgQDA1h0rSoFoXsel8MAV
+MPkGt6gzUdzKb/Qt3BOolnsIcd4Vn19uLCcLdhxRkD37MW/9/mgV+Q57DYyvS9OC
+xi6q7ukgDE3YTK5WChmqJ4p0aEP4bEt6N1VIo55HCFoJy01NIJLs4VAW4y88CzDp
+otex1UmIWQdfnDbIVW9NNFVTiQ==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ca.der b/contrib/wpa/tests/hwsim/auth_serv/ca.der
new file mode 100644
index 000000000000..b03de0177f76
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/ca.der differ
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ca.pem b/contrib/wpa/tests/hwsim/auth_serv/ca.pem
new file mode 100644
index 000000000000..7fcbdf7675f3
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ca.pem
@@ -0,0 +1,79 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 42:97:6c:30:8e:79:fc:7b:6a:e3:ef:9d:18:a4:74:9d:8b:5f:57:53
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C = FI, L = Tuusula, O = w1.fi, CN = Root CA
+ Validity
+ Not Before: May 2 19:49:48 2020 GMT
+ Not After : Apr 30 19:49:48 2030 GMT
+ Subject: C = FI, L = Tuusula, O = w1.fi, CN = Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:bc:f4:ee:44:62:7f:62:4f:a1:81:46:ba:c4:aa:
+ 1e:fd:4e:d0:ed:f1:47:cb:25:5b:66:7a:86:39:91:
+ ca:b5:61:a7:7e:2f:3c:63:7d:39:b8:1a:9e:cb:6d:
+ 32:32:91:de:49:49:84:da:15:be:2b:dd:c6:bc:1f:
+ dc:6e:c0:2d:77:f2:d0:7b:2c:40:19:07:60:55:b0:
+ ff:7c:51:ef:38:d1:f0:2a:da:a8:cc:ea:d6:54:a4:
+ ef:be:17:44:1a:9e:33:70:57:a4:f3:06:ac:3d:ee:
+ 4b:2d:e5:46:25:2d:33:09:f6:49:a8:02:31:a4:65:
+ 9b:32:0a:67:f5:02:e1:3b:47:a6:ae:e4:f6:85:eb:
+ 5d:3e:02:66:dd:11:98:ac:34:72:c2:8f:25:55:4a:
+ 6a:ea:e8:82:2f:bd:7f:78:31:a4:5a:d7:32:bb:64:
+ 48:46:23:ef:c8:c9:e2:84:00:56:72:e8:4b:54:95:
+ 62:3a:5a:11:79:ee:40:43:9e:16:2c:cc:e6:45:f4:
+ bb:82:28:c2:83:35:2c:55:36:99:59:11:b1:15:d0:
+ 03:c1:a5:37:e1:1f:bb:43:c7:b4:b9:33:de:14:d7:
+ 7c:99:45:0f:c1:06:fe:b6:25:10:59:b7:72:76:7f:
+ 91:4b:ea:d1:b9:6a:6a:ed:dd:1b:a9:0e:a7:29:48:
+ b7:4d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha256WithRSAEncryption
+ 41:f9:c3:a3:77:11:92:55:e7:4b:4a:32:6a:31:d9:51:cf:06:
+ a5:39:ea:30:98:b8:8d:4f:24:c5:34:fd:c6:98:10:59:32:7e:
+ 57:f5:8f:ba:67:c9:fc:44:68:b3:7c:f1:af:3a:5f:0d:8f:a1:
+ fe:41:21:0e:e9:08:a3:63:49:66:34:4a:cd:ce:66:74:47:30:
+ f7:dc:82:99:21:56:82:ff:2d:12:90:7d:7a:64:22:a0:ed:fa:
+ 62:d9:5a:d3:97:96:0c:04:a7:47:88:da:53:b6:33:15:15:f9:
+ da:ee:ac:25:e9:07:02:89:bc:73:a2:c6:27:6f:1f:bd:73:b8:
+ 8e:f7:94:54:57:a7:8b:5b:9a:24:aa:86:d4:04:5c:8c:cb:28:
+ a2:45:f9:34:f0:01:20:bb:06:e8:41:14:d2:d7:ca:e8:bf:4e:
+ 16:72:22:a0:0c:86:ca:73:23:09:ae:71:f1:52:0c:db:b2:8a:
+ 4d:94:a5:fa:15:81:5b:a2:95:62:50:a1:d6:64:fe:4c:0c:60:
+ 8d:9b:0f:b8:41:ac:cb:31:c2:17:6c:7b:61:13:16:9a:db:64:
+ fc:5f:47:84:3d:d2:2e:db:0b:9e:b6:1e:85:04:c1:e5:c0:b2:
+ 6d:8f:f2:99:00:3a:1a:ab:02:cf:45:7a:26:c1:b0:1f:c6:b0:
+ d0:4d:f7:52
+-----BEGIN CERTIFICATE-----
+MIIDYDCCAkigAwIBAgIUQpdsMI55/Htq4++dGKR0nYtfV1MwDQYJKoZIhvcNAQEL
+BQAwQTELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoMBXcx
+LmZpMRAwDgYDVQQDDAdSb290IENBMB4XDTIwMDUwMjE5NDk0OFoXDTMwMDQzMDE5
+NDk0OFowQTELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoM
+BXcxLmZpMRAwDgYDVQQDDAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAvPTuRGJ/Yk+hgUa6xKoe/U7Q7fFHyyVbZnqGOZHKtWGnfi88Y305
+uBqey20yMpHeSUmE2hW+K93GvB/cbsAtd/LQeyxAGQdgVbD/fFHvONHwKtqozOrW
+VKTvvhdEGp4zcFek8wasPe5LLeVGJS0zCfZJqAIxpGWbMgpn9QLhO0emruT2hetd
+PgJm3RGYrDRywo8lVUpq6uiCL71/eDGkWtcyu2RIRiPvyMnihABWcuhLVJViOloR
+ee5AQ54WLMzmRfS7gijCgzUsVTaZWRGxFdADwaU34R+7Q8e0uTPeFNd8mUUPwQb+
+tiUQWbdydn+RS+rRuWpq7d0bqQ6nKUi3TQIDAQABo1AwTjAdBgNVHQ4EFgQUpP25
+ORuBs6rriB3Ugam1EXDMp+EwHwYDVR0jBBgwFoAUpP25ORuBs6rriB3Ugam1EXDM
+p+EwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAQfnDo3cRklXnS0oy
+ajHZUc8GpTnqMJi4jU8kxTT9xpgQWTJ+V/WPumfJ/ERos3zxrzpfDY+h/kEhDukI
+o2NJZjRKzc5mdEcw99yCmSFWgv8tEpB9emQioO36Ytla05eWDASnR4jaU7YzFRX5
+2u6sJekHAom8c6LGJ28fvXO4jveUVFeni1uaJKqG1ARcjMsookX5NPABILsG6EEU
+0tfK6L9OFnIioAyGynMjCa5x8VIM27KKTZSl+hWBW6KVYlCh1mT+TAxgjZsPuEGs
+yzHCF2x7YRMWmttk/F9HhD3SLtsLnrYehQTB5cCybY/ymQA6GqsCz0V6JsGwH8aw
+0E33Ug==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/dh.conf b/contrib/wpa/tests/hwsim/auth_serv/dh.conf
new file mode 100644
index 000000000000..f8cd30fd84d9
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/dh.conf
@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEAn5Zfi2JHL512eWsfgD2un5TKWlIvNVIYedyWyzkG7JvccUIdaqx1
+xDVXldaAXW2VkDoRGpFSNk43fPfrYcDIZiWHydNjetQ4Cejv7GcjBPMbNm47eIO1
++9OXrBwpYatW8npTRGF83TqQ/wJgjfr3Cl580Qp2Tv9XjGWHjqDmJI8xVmmOjn/w
+sT1sSN0MryujDzxh7AtkX2NtJMTB1o1Z8MZPnRbxf1crECUNOhYTuTIkzJU1lROq
+HKR72RcMUfJp6GxrYRmx8CQ69UwUwyJoedkkV39HRqvZGc8b8HLFwmlhkGy+qfaN
+zNugMb5eoRAqm+6ZQjZJWAU29+OG/Ku2owIBAg==
+-----END DH PARAMETERS-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/dh2.conf b/contrib/wpa/tests/hwsim/auth_serv/dh2.conf
new file mode 100644
index 000000000000..5532efe2a6e6
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/dh2.conf
@@ -0,0 +1,8 @@
+-----BEGIN DH PARAMETERS-----
+MIIBCAKCAQEAnMarPft+gvX8Ul5WKDn3rSa67dCNNhIivHnHBTn7I6LFE4pf3NY6
+KCUcVgJtOl55+58GxkpFsTZEmcykrbTjtJIyNfXFx6n/JKZTNYT0Vv7xmpSN3v53
+208v8rY91OiqO3T8L1PAsENMwuvMZL65IdLiMmVpAktgLGCafektBkaHj29bYCGS
+oGwz65iypzZGKGZmzET168lbh1SIuZkq3JOFEvE0ZJS5XhLrVUw14uZV/7lPRE+E
+dtza3kVlJXbkgnkrBsiuBlmWiga7EjPtD2o18WhPThI8zX/FoAyQUem4DkhfSpS8
+FrJUrODwQQycS5AaexDmZqHJ/L4GdlHcAwIBAg==
+-----END DH PARAMETERS-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/dh_param_3072.pem b/contrib/wpa/tests/hwsim/auth_serv/dh_param_3072.pem
new file mode 100644
index 000000000000..cc72bc2576be
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/dh_param_3072.pem
@@ -0,0 +1,11 @@
+-----BEGIN DH PARAMETERS-----
+MIIBiAKCAYEA3HLNJq+KXn0kCgo4QNnZNmkzwAVLPyIoK24CCfXC53Ax2jAY7iCu
+recce4hWsRAXjfFLcdGlcHPQ6saSwKE80ebj2eSpiASnAMO46PaGDxpycLl+Ac92
+RTaNDFYXveOMSAQboBC6KlNuf4hf7m+ZNxNTEdhKJnx5DmE5UbRKLzndH49OSsNG
+9ip+gHvO6FmRI4bUr5tosVfcVv2nWA0aRknEWFgUw5qKzi0XIejxHf+SKl+XlHGF
+/HuFV7zvksy/wVd0aMl40QSRTLvUfK+jwjPyAKFi7pSEa+cJGJNO1AVfiDCQ8xiA
+wXM4cqU1cUgTuSZZy3itLIlr3+a0O0PQ/zYCgSZlfRBtbWoOK54RhEJ33xTUVcIH
+bMkS8lmqscVIccPVzC9cv+MASbrfE1wvSJFkW1cHy+LScyQLaXeiqovH0HWp60cN
+9UhTcBRV49JTZfTk4wcfc50q+oNNMOXiHXX6Cz7YYkWQhVarawZcOOXkL5LwyqWE
+Fd2a8VjMc7ujAgEC
+-----END DH PARAMETERS-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/dsaparam.pem b/contrib/wpa/tests/hwsim/auth_serv/dsaparam.pem
new file mode 100644
index 000000000000..890695d40592
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/dsaparam.pem
@@ -0,0 +1,14 @@
+-----BEGIN DSA PARAMETERS-----
+MIICLQKCAQEA4Gx/0VQqdHPnUdPwtyYRYPMqJqIufW2SkWMEVCMHLo6yZx+2Y1Kn
+N8Zi7TlCHshBXPS/ZF3jMpFk5lOf0M/YujayuVl1iii7B79d5NC0eehX3LnlS3WK
+npGmuCIlnxPrOvrrwx9gPznruNrLNh57IERidYtolFAPjtNKuCYbCmpS1J6mh3pR
+XlNkkTC6L2zkkgDwDZQzJmbQ5gSDY57uneSOgZaPGOYt+Uxsv4v/xxBRTALEcRDk
+AyB0OhzNx5gsNw1qfO1Ck1IOG0Z+A8VnS6Kpeh42bCTdF3OfXwK2BgOzQLCpyEfp
+MEqgRG7VUQjlsdkUy35apIvYpZbovgmbbQIhAIc7hanE2sJ1kKBMYxQx6mlxc+NI
+LxoyLRqAE0iQs08HAoIBAQCFmPw/JGlVVMMdC3RYlTdH2Lu2lGJoDmuuKhrmQOo/
+/jAcShg3n2hVSKzximtZX+KNoJ3TklWG30jPsV1CSOeX0IDeiuEiH/1bGAtHmIxo
+BLbF5fS94fAbL9IAXhuXaHozgnVoutbFUxGVCCopPmYnX8nDCHdy6cHQld1/S5Y4
+hYWQTTSJETUzqYUWQtdAzUCPFBwDGJA7CpYgGQ3mJRUt/Hk6QnEc8NrAFNvbnxWA
+me0/rZmg4lZwtA8GfrOzsZSVXCsL56KZZ8iMElfcN/E4fxWOfBoFkNI3IOc5B+j2
+EsZcXUcbK2o57BHiZ1GMcbfnuz5STFY8/vBXpyAbBDqO
+-----END DSA PARAMETERS-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/eap_user.conf b/contrib/wpa/tests/hwsim/auth_serv/eap_user.conf
new file mode 100644
index 000000000000..b5c65f17866d
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/eap_user.conf
@@ -0,0 +1,167 @@
+"pwd user" PWD "secret password"
+"pwd user@domain" PWD "secret password"
+"pwd-hash" PWD hash:e3718ece8ab74792cbbfffd316d2d19a
+"pwd-hash-sha1" PWD ssha1:046239e0660a59015231082a071c803e9f5848ae42eaccb4c08c97ae397bc879c4b071b9088ee715
+"pwd-hash-sha256" PWD ssha256:eb0fc747d940308ee5ddcb88d4998a39fa9fcad3044872cf35a1b54b8d351dadc05f525ec27be0d35eca52a328c582ebc7
+"pwd-hash-sha512" PWD ssha512:368d96e5acb41b164fe5ce038ab7c3552a82f88fae2e8481da525cc2c68c53b19390a91ccc61a1a04595b620b92e47c39bae353108035c49aaeb23859ad6d22dc08d2057cdd9f0831636a47cbac8d23ed7de8575a197b6320d5627e8f9768bd2109471bc7dff566f7a5e0e9990c285dc1d42e02ed06d6f9490323053ab252d88
+"pwd.user@test123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.example.com" PWD "secret password"
+"gpsk user" GPSK "abcdefghijklmnop0123456789abcdef"
+"gpsk user@domain" GPSK "abcdefghijklmnop0123456789abcdef"
+"sake user" SAKE 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+"sake user@domain" SAKE 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+"eke user" EKE "hello"
+"eke user@domain" EKE "hello"
+"ikev2 user" IKEV2 "ike password"
+"ikev2 user@domain" IKEV2 "ike password"
+"pax.user@example.com" PAX 0123456789abcdef0123456789abcdef
+"psk.user@example.com" PSK 0123456789abcdef0123456789abcdef
+"vendor-test" VENDOR-TEST "foo"
+"vendor-test-2" VENDOR-TEST "foo" [2]
+"osen@example.com" WFA-UNAUTH-TLS
+"unauth-tls" UNAUTH-TLS
+
+"WFA-SimpleConfig-Enrollee-1-0" WSC
+"WFA-SimpleConfig-Enrollee-unexpected" WSC
+
+"erp-fast@example.com" FAST
+"erp-fast@example.com" GTC "password" [2]
+"erp-gpsk@example.com" GPSK "abcdefghijklmnop0123456789abcdef"
+"erp-eke@example.com" EKE "hello"
+"erp-pax@example.com" PAX 0123456789abcdef0123456789abcdef
+"erp-peap@example.com" PEAP
+"erp-peap@example.com" MSCHAPV2 "password" [2]
+"erp-teap@example.com" TEAP
+"erp-teap@example.com" MSCHAPV2 "password" [2]
+"erp-psk@example.com" PSK 0123456789abcdef0123456789abcdef
+"erp-pwd@example.com" PWD "secret password"
+"erp-sake@example.com" SAKE 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+"erp-tls@example.com" TLS
+"erp-ttls@example.com" TTLS
+"erp-ttls@example.com" TTLS-PAP "password" [2]
+"erp-ttls" TTLS
+"erp-ttls" TTLS-PAP "password" [2]
+"erp-ikev2@example.com" IKEV2 "password"
+"psk@erp.example.com" PSK 0123456789abcdef0123456789abcdef
+"pwd@erp.example.com" PWD "secret password"
+
+"vlan1" PAX 0123456789abcdef0123456789abcdef
+radius_accept_attr=64:d:13
+radius_accept_attr=65:d:6
+radius_accept_attr=81:s:1
+
+"vlan2" PAX 0123456789abcdef0123456789abcdef
+radius_accept_attr=64:d:13
+radius_accept_attr=65:d:6
+radius_accept_attr=81:s:2
+
+"vlan1b" PAX 0123456789abcdef0123456789abcdef
+radius_accept_attr=56:x:32000001
+
+"vlan1tagged" PAX 0123456789abcdef0123456789abcdef
+radius_accept_attr=56:x:31000001
+
+"vlan12mixed" PAX 0123456789abcdef0123456789abcdef
+radius_accept_attr=56:x:31000001
+radius_accept_attr=64:d:13
+radius_accept_attr=65:d:6
+radius_accept_attr=81:s:2
+
+"test-class" PAX 0123456789abcdef0123456789abcdef
+radius_accept_attr=25:x:00112233445566778899
+
+"gpsk-cui" GPSK "abcdefghijklmnop0123456789abcdef"
+radius_accept_attr=89:s:gpsk-chargeable-user-identity
+radius_accept_attr=25:x:00112233445566778899aa
+
+"gpsk-vlan1" GPSK "abcdefghijklmnop0123456789abcdef"
+radius_accept_attr=64:d:13
+radius_accept_attr=65:d:6
+radius_accept_attr=81:s:1
+
+"gpsk-user-session-timeout" GPSK "abcdefghijklmnop0123456789abcdef"
+radius_accept_attr=27:d:3
+
+"phase1-user" MSCHAPV2,MD5,GTC "password"
+
+"/C=FI/O=w1.fi/CN=Test User" TLS [2]
+
+"020000000000" MACACL "020000000000"
+
+"020000000100" MACACL "020000000100"
+radius_accept_attr=1:s:test-user
+radius_accept_attr=89:s:macacl-cui-test
+
+"020000000200" MACACL "020000000200"
+radius_accept_attr=56:x:32000011
+
+"0232010000000000@ttls" TTLS,AKA
+"0232010000000000@peap" PEAP,AKA
+"0232010000000000@fast" FAST,AKA
+"1232010000000000@ttls" TTLS,SIM
+"1232010000000000@peap" PEAP,SIM
+"1232010000000000@fast" FAST,SIM
+"6555444333222111@both" AKA',AKA
+"peap-ver0" PEAP [ver=0]
+"peap-ver1" PEAP [ver=1]
+
+"0"* AKA
+"1"* SIM
+"2"* AKA
+"3"* SIM
+"4"* AKA
+"5"* SIM
+"6"* AKA'
+"7"* AKA'
+"8"* AKA'
+"TEAP" TEAP
+* TTLS,TLS,PEAP,FAST,TEAP,SIM,AKA',AKA
+
+"0"* AKA [2]
+"1"* SIM [2]
+"2"* AKA [2]
+"3"* SIM [2]
+"4"* AKA [2]
+"5"* SIM [2]
+"6"* AKA' [2]
+"7"* AKA' [2]
+"8"* AKA' [2]
+
+"pap user" TTLS-PAP "password" [2]
+"pap-secret" TTLS-PAP "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25" [2]
+"pap-secret@example.com" TTLS-PAP "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25" [2]
+"chap user" TTLS-CHAP "password" [2]
+"mschap user" TTLS-MSCHAP "password" [2]
+"DOMAIN\mschapv2 user" TTLS-MSCHAPV2 hash:8846f7eaee8fb117ad06bdd830b7586c [2]
+"mschapv2 user@domain" TTLS-MSCHAPV2 hash:8846f7eaee8fb117ad06bdd830b7586c [2]
+"hs20-test" TTLS-MSCHAPV2 "password" [2]
+"hs20-test-with-domain@example.com" TTLS-MSCHAPV2 "password" [2]
+"utf8-user" TTLS-MSCHAPV2 "secret-åäö-€-password" [2]
+"utf8-user-hash" TTLS-MSCHAPV2 hash:bd5844fad2489992da7fe8c5a01559cf [2]
+
+"user" MSCHAPV2,MD5,GTC "password" [2]
+"user@example.com" MSCHAPV2,MD5,GTC "password" [2]
+"user2" MSCHAPV2,MD5,GTC "password" [2]
+"DOMAIN\user3" MSCHAPV2 "password" [2]
+"user-no-passwd" MSCHAPV2,MD5,GTC [2]
+"machine" MSCHAPV2,MD5,GTC "machine-password" [2]
+"cert user" TLS [2]
+"user-secret" GTC "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25" [2]
+"user-pwd-2" PWD "password" [2]
+"user-eke-2" EKE "password" [2]
+
+"hs20-deauth-test" TTLS-MSCHAPV2 "password" [2]
+radius_accept_attr=26:x:00009f680405016400
+
+"hs20-subrem-test" TTLS-MSCHAPV2 "password" [2]
+radius_accept_attr=26:x:00009f6801170168747470733a2f2f6578616d706c652e636f6d2f
+
+"hs20-session-info-test" TTLS-MSCHAPV2 "password" [2]
+radius_accept_attr=27:d:63
+radius_accept_attr=26:x:00009f6805170168747470733a2f2f6578616d706c652e636f6d2f
+
+"hs20-t-c-test" TTLS-MSCHAPV2 "password" [2]
+radius_accept_attr=26:x:00009f68090601000000
+radius_accept_attr=89:s:hs20-cui
+
+"test-user" TTLS-PAP "password" [2]
+radius_accept_attr=1:s:real-user
diff --git a/contrib/wpa/tests/hwsim/auth_serv/eap_user_vlan.conf b/contrib/wpa/tests/hwsim/auth_serv/eap_user_vlan.conf
new file mode 100644
index 000000000000..f8ab168b1227
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/eap_user_vlan.conf
@@ -0,0 +1,7 @@
+"vlan1" PAX 0123456789abcdef0123456789abcdef
+radius_accept_attr=64:d:13
+radius_accept_attr=65:d:6
+radius_accept_attr=81:s:2
+
+"vlan1tagged" PAX 0123456789abcdef0123456789abcdef
+radius_accept_attr=56:x:31000002
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec-ca-openssl.cnf b/contrib/wpa/tests/hwsim/auth_serv/ec-ca-openssl.cnf
new file mode 100644
index 000000000000..c249ad4c3e1d
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec-ca-openssl.cnf
@@ -0,0 +1,111 @@
+# OpenSSL configuration file for Suite B
+
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+oid_section = new_oids
+
+[ new_oids ]
+
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+
+dir = ./ec-ca
+certs = $dir/certs
+crl_dir = $dir/crl
+database = $dir/index.txt
+unique_subject = no
+new_certs_dir = $dir/newcerts
+certificate = $dir/cacert.pem
+serial = $dir/serial
+crlnumber = $dir/crlnumber
+crl = $dir/crl.pem
+private_key = $dir/private/cakey.pem
+RANDFILE = $dir/private/.rand
+
+x509_extensions = ext_client
+
+name_opt = ca_default
+cert_opt = ca_default
+
+copy_extensions = copy
+
+default_days = 3650
+default_crl_days= 30
+default_md = default
+preserve = no
+
+policy = policy_match
+
+[ policy_match ]
+countryName = match
+stateOrProvinceName = optional
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+#emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+#emailAddress = optional
+
+[ req ]
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = FI
+countryName_min = 2
+countryName_max = 2
+
+localityName = Locality Name (eg, city)
+localityName_default = Helsinki
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = w1.fi
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max = 64
+
+[ req_attributes ]
+
+[ v3_ca ]
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid:always,issuer
+basicConstraints = critical, CA:true, pathlen:0
+keyUsage = critical, cRLSign, keyCertSign
+
+[ crl_ext ]
+
+# issuerAltName=issuer:copy
+authorityKeyIdentifier=keyid:always
+
+[ ext_client ]
+
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+#@ALTNAME@
+extendedKeyUsage = clientAuth
+keyUsage = digitalSignature, keyEncipherment
+
+[ ext_server ]
+
+basicConstraints=critical, CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+#@ALTNAME@
+extendedKeyUsage = critical, serverAuth
+keyUsage = digitalSignature, keyEncipherment
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec-ca.key b/contrib/wpa/tests/hwsim/auth_serv/ec-ca.key
new file mode 100644
index 000000000000..51898ecf0e08
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec-ca.key
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIFOOw/NwKqjhaX2da7e7SYrgsMLmr6wX3c1SuR2AsxaUoAoGCCqGSM49
+AwEHoUQDQgAEcLjqwWO1Eg+FfjDonVsEpGN0vPuJV1lGd/mKkQHFYxDzLaJHNM8i
+QKH1dAT4M/8reNYF5rzkwC6V33R6T5Hqjg==
+-----END EC PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec-ca.pem b/contrib/wpa/tests/hwsim/auth_serv/ec-ca.pem
new file mode 100644
index 000000000000..f2bb4bcad9da
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec-ca.pem
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICAjCCAaegAwIBAgIJAPdTJDJVY8FeMAoGCCqGSM49BAMCMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTI4LWJpdCBSb290IENBMB4XDTE2MDIwMTA5MjUyM1oXDTI2MDEy
+OTA5MjUyM1owUjELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4wDAYD
+VQQKDAV3MS5maTEgMB4GA1UEAwwXU3VpdGUgQiAxMjgtYml0IFJvb3QgQ0EwWTAT
+BgcqhkjOPQIBBggqhkjOPQMBBwNCAARwuOrBY7USD4V+MOidWwSkY3S8+4lXWUZ3
++YqRAcVjEPMtokc0zyJAofV0BPgz/yt41gXmvOTALpXfdHpPkeqOo2YwZDAdBgNV
+HQ4EFgQUcyrcCIxm5gVTsimSHN2Km8Amy/gwHwYDVR0jBBgwFoAUcyrcCIxm5gVT
+simSHN2Km8Amy/gwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYw
+CgYIKoZIzj0EAwIDSQAwRgIhAOHO2+N8tgUQKakQcLGR+kB3mKPmjyhu478xmrKg
+wQq9AiEAmnN7YQBgVBk/+zOri1rCCP8DJ3gE+BSUA3cyQGUvtAc=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec-generate.sh b/contrib/wpa/tests/hwsim/auth_serv/ec-generate.sh
new file mode 100755
index 000000000000..c9fdabc6b438
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec-generate.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+OPENSSL=openssl
+
+CURVE=prime256v1
+DIGEST="-sha256"
+DIGEST_CA="-md sha256"
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = Suite B 128-bit Root CA/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec-ca.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -x509 -new -key ec-ca.key -out ec-ca.pem -outform PEM -days 3650 $DIGEST
+mkdir -p ec-ca/certs ec-ca/crl ec-ca/newcerts ec-ca/private
+touch ec-ca/index.txt
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = server.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,DNS:server.w1.fi/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec-server.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -nodes -key ec-server.key -out ec-server.req -outform PEM $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile ec-ca.key -cert ec-ca.pem -create_serial -in ec-server.req -out ec-server.pem -extensions ext_server $DIGEST_CA
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ User ]-------------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = user/" |
+ sed "s/#@ALTNAME@/subjectAltName=email:user@w1.fi/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec-user.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -nodes -key ec-user.key -out ec-user.req -outform PEM -extensions ext_client $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile ec-ca.key -cert ec-ca.pem -create_serial -in ec-user.req -out ec-user.pem -extensions ext_client $DIGEST_CA
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Verify ]-----------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile ec-ca.pem ec-server.pem
+$OPENSSL verify -CAfile ec-ca.pem ec-user.pem
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec-server.key b/contrib/wpa/tests/hwsim/auth_serv/ec-server.key
new file mode 100644
index 000000000000..bb28e91883dd
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec-server.key
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIEoiI2GTM68G6vG2zpbM/a5j7e2yBCCWxaNe+nKPT47+oAoGCCqGSM49
+AwEHoUQDQgAEJu1Mahit1ZcoiSaYwew1ugckxpSGVvbrZUVf/IF13kiW+JBMcgrX
+oukSJOw2LVtLLJEf24YHRST8Dw7Kpzr+bQ==
+-----END EC PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec-server.pem b/contrib/wpa/tests/hwsim/auth_serv/ec-server.pem
new file mode 100644
index 000000000000..e5d021c0a6c4
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec-server.pem
@@ -0,0 +1,53 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 11095559361558864825 (0x99fb5873d9f9e3b9)
+ Signature Algorithm: ecdsa-with-SHA256
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 128-bit Root CA
+ Validity
+ Not Before: Feb 1 09:25:23 2016 GMT
+ Not After : Jan 29 09:25:23 2026 GMT
+ Subject: C=FI, O=w1.fi, CN=server.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: id-ecPublicKey
+ Public-Key: (256 bit)
+ pub:
+ 04:26:ed:4c:6a:18:ad:d5:97:28:89:26:98:c1:ec:
+ 35:ba:07:24:c6:94:86:56:f6:eb:65:45:5f:fc:81:
+ 75:de:48:96:f8:90:4c:72:0a:d7:a2:e9:12:24:ec:
+ 36:2d:5b:4b:2c:91:1f:db:86:07:45:24:fc:0f:0e:
+ ca:a7:3a:fe:6d
+ ASN1 OID: prime256v1
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ A4:A4:2C:68:89:C6:74:44:B4:BF:9A:BF:5F:D6:02:2C:DC:FE:4F:5A
+ X509v3 Authority Key Identifier:
+ keyid:73:2A:DC:08:8C:66:E6:05:53:B2:29:92:1C:DD:8A:9B:C0:26:CB:F8
+
+ X509v3 Subject Alternative Name: critical
+ DNS:server.w1.fi
+ X509v3 Extended Key Usage: critical
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: ecdsa-with-SHA256
+ 30:45:02:20:25:ec:c6:e5:a2:66:e9:3a:f5:fa:b0:4a:dd:24:
+ 89:fa:d0:e3:78:a6:2e:a5:da:39:8b:96:7a:ac:ae:17:1f:ef:
+ 02:21:00:a8:2a:d1:f1:54:73:b9:8e:b9:8b:48:63:54:01:b3:
+ a3:cd:02:05:ba:d0:53:63:0b:d0:9c:f2:13:74:60:7a:a2
+-----BEGIN CERTIFICATE-----
+MIICEDCCAbagAwIBAgIJAJn7WHPZ+eO5MAoGCCqGSM49BAMCMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTI4LWJpdCBSb290IENBMB4XDTE2MDIwMTA5MjUyM1oXDTI2MDEy
+OTA5MjUyM1owNDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMRUwEwYDVQQD
+DAxzZXJ2ZXIudzEuZmkwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQm7UxqGK3V
+lyiJJpjB7DW6ByTGlIZW9utlRV/8gXXeSJb4kExyCtei6RIk7DYtW0sskR/bhgdF
+JPwPDsqnOv5to4GSMIGPMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFKSkLGiJxnRE
+tL+av1/WAizc/k9aMB8GA1UdIwQYMBaAFHMq3AiMZuYFU7IpkhzdipvAJsv4MBoG
+A1UdEQEB/wQQMA6CDHNlcnZlci53MS5maTAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
+ATALBgNVHQ8EBAMCBaAwCgYIKoZIzj0EAwIDSAAwRQIgJezG5aJm6Tr1+rBK3SSJ
++tDjeKYupdo5i5Z6rK4XH+8CIQCoKtHxVHO5jrmLSGNUAbOjzQIFutBTYwvQnPIT
+dGB6og==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec-user.key b/contrib/wpa/tests/hwsim/auth_serv/ec-user.key
new file mode 100644
index 000000000000..dc6a7f030a84
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec-user.key
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEINKa/lt6n2rVp/6cLl65e8GR0vY0WKDfpBGltnggadz3oAoGCCqGSM49
+AwEHoUQDQgAEDbAoh2fby/hkxmF9Hm8fyzBHCpaDzFuAyG+SYmTBqpccxTXXfSNJ
+eYQXMoPTm14BXWgiTf7U9/C3FHolI5oBNQ==
+-----END EC PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec-user.pem b/contrib/wpa/tests/hwsim/auth_serv/ec-user.pem
new file mode 100644
index 000000000000..a4d682496969
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec-user.pem
@@ -0,0 +1,52 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 11095559361558864826 (0x99fb5873d9f9e3ba)
+ Signature Algorithm: ecdsa-with-SHA256
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 128-bit Root CA
+ Validity
+ Not Before: Feb 1 09:25:23 2016 GMT
+ Not After : Jan 29 09:25:23 2026 GMT
+ Subject: C=FI, O=w1.fi, CN=user
+ Subject Public Key Info:
+ Public Key Algorithm: id-ecPublicKey
+ Public-Key: (256 bit)
+ pub:
+ 04:0d:b0:28:87:67:db:cb:f8:64:c6:61:7d:1e:6f:
+ 1f:cb:30:47:0a:96:83:cc:5b:80:c8:6f:92:62:64:
+ c1:aa:97:1c:c5:35:d7:7d:23:49:79:84:17:32:83:
+ d3:9b:5e:01:5d:68:22:4d:fe:d4:f7:f0:b7:14:7a:
+ 25:23:9a:01:35
+ ASN1 OID: prime256v1
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 0E:0F:F9:64:AC:F9:DB:7C:45:22:9A:DF:E0:DB:1E:25:9D:8F:4D:C3
+ X509v3 Authority Key Identifier:
+ keyid:73:2A:DC:08:8C:66:E6:05:53:B2:29:92:1C:DD:8A:9B:C0:26:CB:F8
+
+ X509v3 Subject Alternative Name:
+ email:user@w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: ecdsa-with-SHA256
+ 30:44:02:20:12:a1:d9:30:43:fb:12:3d:67:72:a2:12:24:7c:
+ cb:1e:ce:f7:e6:fe:b6:79:b4:af:d8:85:72:49:2d:e9:de:01:
+ 02:20:18:f3:6a:65:5d:c0:04:df:28:5a:44:b1:5f:75:25:eb:
+ a8:56:e9:5d:35:3c:9e:8d:63:cc:47:7f:22:a1:c0:27
+-----BEGIN CERTIFICATE-----
+MIIB/DCCAaOgAwIBAgIJAJn7WHPZ+eO6MAoGCCqGSM49BAMCMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTI4LWJpdCBSb290IENBMB4XDTE2MDIwMTA5MjUyM1oXDTI2MDEy
+OTA5MjUyM1owLDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMQ0wCwYDVQQD
+DAR1c2VyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDbAoh2fby/hkxmF9Hm8f
+yzBHCpaDzFuAyG+SYmTBqpccxTXXfSNJeYQXMoPTm14BXWgiTf7U9/C3FHolI5oB
+NaOBhzCBhDAJBgNVHRMEAjAAMB0GA1UdDgQWBBQOD/lkrPnbfEUimt/g2x4lnY9N
+wzAfBgNVHSMEGDAWgBRzKtwIjGbmBVOyKZIc3YqbwCbL+DAVBgNVHREEDjAMgQp1
+c2VyQHcxLmZpMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAsGA1UdDwQEAwIFoDAKBggq
+hkjOPQQDAgNHADBEAiASodkwQ/sSPWdyohIkfMsezvfm/rZ5tK/YhXJJLeneAQIg
+GPNqZV3ABN8oWkSxX3Ul66hW6V01PJ6NY8xHfyKhwCc=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec2-ca.key b/contrib/wpa/tests/hwsim/auth_serv/ec2-ca.key
new file mode 100644
index 000000000000..96a28fde0a15
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec2-ca.key
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDA7pLB5W+c/cHKznKIRC3UH3qvF2gdij3svRR+zYaNf427Z/I0H4Xki
+HOFgPZ9ded2gBwYFK4EEACKhZANiAARWEuSpvRL6glbrbPMhDEcvHpQCirI4GtFD
+FYUEYIDqRObNZkeM4A9ygH3HUUmdm3SLHVxb+2nIVfPY3jyxwfOZGiL6ASomy1Ww
+GY0AAaXU61MCiJBny1VTsjR7Dw+VcRc=
+-----END EC PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec2-ca.pem b/contrib/wpa/tests/hwsim/auth_serv/ec2-ca.pem
new file mode 100644
index 000000000000..b745ed33238a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec2-ca.pem
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICPjCCAcSgAwIBAgIJAIEUIb9N+rpkMAoGCCqGSM49BAMDMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTkyLWJpdCBSb290IENBMB4XDTE2MDIwMTA5MjYyNFoXDTI2MDEy
+OTA5MjYyNFowUjELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4wDAYD
+VQQKDAV3MS5maTEgMB4GA1UEAwwXU3VpdGUgQiAxOTItYml0IFJvb3QgQ0EwdjAQ
+BgcqhkjOPQIBBgUrgQQAIgNiAARWEuSpvRL6glbrbPMhDEcvHpQCirI4GtFDFYUE
+YIDqRObNZkeM4A9ygH3HUUmdm3SLHVxb+2nIVfPY3jyxwfOZGiL6ASomy1WwGY0A
+AaXU61MCiJBny1VTsjR7Dw+VcRejZjBkMB0GA1UdDgQWBBS4l8m+YxKr9qCMtl77
+l24QjtxI9TAfBgNVHSMEGDAWgBS4l8m+YxKr9qCMtl77l24QjtxI9TASBgNVHRMB
+Af8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNoADBlAjEA
+v+QeMLDKAY3+9dbdzPit9WCg7erYxa0LsV6ZTr4wIYwUIkybksD1Bwlq7Sw/lVpO
+AjBy4q3wJbj6unHQq9VsCKpHWiTi/WeKRo8X0djScKsN7R92A3vGgdhVEAXP0vTl
+Rn0=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec2-generate.sh b/contrib/wpa/tests/hwsim/auth_serv/ec2-generate.sh
new file mode 100755
index 000000000000..b7287a90d922
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec2-generate.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+
+OPENSSL=openssl
+
+CURVE=secp384r1
+DIGEST="-sha384"
+DIGEST_CA="-md sha384"
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = Suite B 192-bit Root CA/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec2-ca.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -x509 -new -key ec2-ca.key -out ec2-ca.pem -outform PEM -days 3650 $DIGEST
+mkdir -p ec-ca/certs ec-ca/crl ec-ca/newcerts ec-ca/private
+touch ec-ca/index.txt
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = server.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,DNS:server.w1.fi/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec2-server.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -nodes -key ec2-server.key -out ec2-server.req -outform PEM $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile ec2-ca.key -cert ec2-ca.pem -create_serial -in ec2-server.req -out ec2-server.pem -extensions ext_server $DIGEST_CA
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ User ]-------------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = user/" |
+ sed "s/#@ALTNAME@/subjectAltName=email:user@w1.fi/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec2-user.key -name $CURVE -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -nodes -key ec2-user.key -out ec2-user.req -outform PEM -extensions ext_client $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile ec2-ca.key -cert ec2-ca.pem -create_serial -in ec2-user.req -out ec2-user.pem -extensions ext_client $DIGEST_CA
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ User p256 ]--------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = user-p256/" |
+ sed "s/#@ALTNAME@/subjectAltName=email:user-p256@w1.fi/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL ecparam -out ec2-user-p256.key -name prime256v1 -genkey
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -nodes -key ec2-user-p256.key -out ec2-user-p256.req -outform PEM -extensions ext_client -sha256
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile ec2-ca.key -cert ec2-ca.pem -create_serial -in ec2-user-p256.req -out ec2-user-p256.pem -extensions ext_client -md sha256
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Verify ]-----------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile ec2-ca.pem ec2-server.pem
+$OPENSSL verify -CAfile ec2-ca.pem ec2-user.pem
+$OPENSSL verify -CAfile ec2-ca.pem ec2-user-p256.pem
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec2-server.key b/contrib/wpa/tests/hwsim/auth_serv/ec2-server.key
new file mode 100644
index 000000000000..e59a9be11e43
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec2-server.key
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDAgwG8tK5eYT4AX09cjhztI1oSnO7iEVf8n6UdbY41gmuU+ce+HPfpt
+mRFxdKSU29CgBwYFK4EEACKhZANiAAS4CCNfatEOzJswkLMlEn+bPMUEYQEYQwad
+uiJ3hJHkHxKnjjamvn+OCHxZwX0I2ci19y+cxgCIAKHRI2C/iijvr12ZcOkVEysf
+PODhGzHDloYyEfLcPSJ9hTk1ZIvyRSU=
+-----END EC PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec2-server.pem b/contrib/wpa/tests/hwsim/auth_serv/ec2-server.pem
new file mode 100644
index 000000000000..f30e09fcba35
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec2-server.pem
@@ -0,0 +1,58 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 11652367451091730033 (0xa1b58675baa57e71)
+ Signature Algorithm: ecdsa-with-SHA384
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 192-bit Root CA
+ Validity
+ Not Before: Feb 1 09:26:24 2016 GMT
+ Not After : Jan 29 09:26:24 2026 GMT
+ Subject: C=FI, O=w1.fi, CN=server.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: id-ecPublicKey
+ Public-Key: (384 bit)
+ pub:
+ 04:b8:08:23:5f:6a:d1:0e:cc:9b:30:90:b3:25:12:
+ 7f:9b:3c:c5:04:61:01:18:43:06:9d:ba:22:77:84:
+ 91:e4:1f:12:a7:8e:36:a6:be:7f:8e:08:7c:59:c1:
+ 7d:08:d9:c8:b5:f7:2f:9c:c6:00:88:00:a1:d1:23:
+ 60:bf:8a:28:ef:af:5d:99:70:e9:15:13:2b:1f:3c:
+ e0:e1:1b:31:c3:96:86:32:11:f2:dc:3d:22:7d:85:
+ 39:35:64:8b:f2:45:25
+ ASN1 OID: secp384r1
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ EA:4A:EB:D2:AD:05:FC:FD:5F:A0:CA:8A:53:3B:4D:ED:F5:6B:EF:75
+ X509v3 Authority Key Identifier:
+ keyid:B8:97:C9:BE:63:12:AB:F6:A0:8C:B6:5E:FB:97:6E:10:8E:DC:48:F5
+
+ X509v3 Subject Alternative Name: critical
+ DNS:server.w1.fi
+ X509v3 Extended Key Usage: critical
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: ecdsa-with-SHA384
+ 30:65:02:30:1f:26:d2:79:e7:54:59:1a:b8:3b:92:26:05:1d:
+ f7:57:43:9d:8e:01:3d:57:ca:54:e1:9b:2e:ec:3a:32:a0:0d:
+ 8b:7c:70:c2:27:d2:31:8b:39:5c:64:6d:81:dd:14:56:02:31:
+ 00:f1:ac:58:25:9a:9e:cd:1c:fa:76:9d:da:1a:6b:28:b5:43:
+ 15:4e:c7:aa:4d:26:4d:44:26:23:86:a8:5f:6e:f5:42:6d:26:
+ 37:99:1d:70:b9:8e:96:4d:69:99:a9:6f:c6
+-----BEGIN CERTIFICATE-----
+MIICTTCCAdOgAwIBAgIJAKG1hnW6pX5xMAoGCCqGSM49BAMDMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTkyLWJpdCBSb290IENBMB4XDTE2MDIwMTA5MjYyNFoXDTI2MDEy
+OTA5MjYyNFowNDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMRUwEwYDVQQD
+DAxzZXJ2ZXIudzEuZmkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAS4CCNfatEOzJsw
+kLMlEn+bPMUEYQEYQwaduiJ3hJHkHxKnjjamvn+OCHxZwX0I2ci19y+cxgCIAKHR
+I2C/iijvr12ZcOkVEysfPODhGzHDloYyEfLcPSJ9hTk1ZIvyRSWjgZIwgY8wDAYD
+VR0TAQH/BAIwADAdBgNVHQ4EFgQU6krr0q0F/P1foMqKUztN7fVr73UwHwYDVR0j
+BBgwFoAUuJfJvmMSq/agjLZe+5duEI7cSPUwGgYDVR0RAQH/BBAwDoIMc2VydmVy
+LncxLmZpMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMAsGA1UdDwQEAwIFoDAKBggq
+hkjOPQQDAwNoADBlAjAfJtJ551RZGrg7kiYFHfdXQ52OAT1XylThmy7sOjKgDYt8
+cMIn0jGLOVxkbYHdFFYCMQDxrFglmp7NHPp2ndoaayi1QxVOx6pNJk1EJiOGqF9u
+9UJtJjeZHXC5jpZNaZmpb8Y=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec2-user-p256.key b/contrib/wpa/tests/hwsim/auth_serv/ec2-user-p256.key
new file mode 100644
index 000000000000..08aae75dd247
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec2-user-p256.key
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIPrr8f6NDa+p9BbWuyoFWfshi7pBwZVSltEoE3JoKMfEoAoGCCqGSM49
+AwEHoUQDQgAEt4F55Q020CgCdvgNzw3I+K/eZiDJIODExC0Qti5YJWD/Ah5KG3lh
+qmRWRLRLn+giBMgUEJeWDjWcHdzWBYhwEQ==
+-----END EC PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec2-user-p256.pem b/contrib/wpa/tests/hwsim/auth_serv/ec2-user-p256.pem
new file mode 100644
index 000000000000..7deb9c1b1160
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec2-user-p256.pem
@@ -0,0 +1,56 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 12897810923590592256 (0xb2fe3ab310c52700)
+ Signature Algorithm: ecdsa-with-SHA256
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 192-bit Root CA
+ Validity
+ Not Before: Jan 12 18:16:42 2018 GMT
+ Not After : Jan 10 18:16:42 2028 GMT
+ Subject: C=FI, O=w1.fi, CN=user-p256
+ Subject Public Key Info:
+ Public Key Algorithm: id-ecPublicKey
+ Public-Key: (256 bit)
+ pub:
+ 04:b7:81:79:e5:0d:36:d0:28:02:76:f8:0d:cf:0d:
+ c8:f8:af:de:66:20:c9:20:e0:c4:c4:2d:10:b6:2e:
+ 58:25:60:ff:02:1e:4a:1b:79:61:aa:64:56:44:b4:
+ 4b:9f:e8:22:04:c8:14:10:97:96:0e:35:9c:1d:dc:
+ d6:05:88:70:11
+ ASN1 OID: prime256v1
+ NIST CURVE: P-256
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ EC:7E:B2:10:44:3E:D2:A1:98:E4:1E:8F:7E:32:49:2E:B2:59:3C:92
+ X509v3 Authority Key Identifier:
+ keyid:B8:97:C9:BE:63:12:AB:F6:A0:8C:B6:5E:FB:97:6E:10:8E:DC:48:F5
+
+ X509v3 Subject Alternative Name:
+ email:user-p256@w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: ecdsa-with-SHA256
+ 30:65:02:31:00:c9:1e:c8:25:d5:69:1c:24:4f:09:b6:45:31:
+ c2:46:a0:44:84:ae:b1:e3:bb:34:19:f6:04:63:61:cf:37:7a:
+ 9b:a1:72:99:9d:86:36:26:35:a1:99:0a:3a:7c:06:26:3e:02:
+ 30:70:e8:c3:20:0a:c5:4f:f6:95:6c:0a:b1:7a:1b:5d:b0:d2:
+ c6:10:4d:2f:44:31:c7:1a:db:6c:25:07:4b:2d:94:0e:c9:b4:
+ b1:c8:8c:cb:ea:67:8f:37:20:f6:cc:64:fe
+-----BEGIN CERTIFICATE-----
+MIICJzCCAa2gAwIBAgIJALL+OrMQxScAMAoGCCqGSM49BAMCMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTkyLWJpdCBSb290IENBMB4XDTE4MDExMjE4MTY0MloXDTI4MDEx
+MDE4MTY0MlowMTELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMRIwEAYDVQQD
+DAl1c2VyLXAyNTYwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS3gXnlDTbQKAJ2
++A3PDcj4r95mIMkg4MTELRC2LlglYP8CHkobeWGqZFZEtEuf6CIEyBQQl5YONZwd
+3NYFiHARo4GMMIGJMAkGA1UdEwQCMAAwHQYDVR0OBBYEFOx+shBEPtKhmOQej34y
+SS6yWTySMB8GA1UdIwQYMBaAFLiXyb5jEqv2oIy2XvuXbhCO3Ej1MBoGA1UdEQQT
+MBGBD3VzZXItcDI1NkB3MS5maTATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8E
+BAMCBaAwCgYIKoZIzj0EAwIDaAAwZQIxAMkeyCXVaRwkTwm2RTHCRqBEhK6x47s0
+GfYEY2HPN3qboXKZnYY2JjWhmQo6fAYmPgIwcOjDIArFT/aVbAqxehtdsNLGEE0v
+RDHHGttsJQdLLZQOybSxyIzL6mePNyD2zGT+
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec2-user.key b/contrib/wpa/tests/hwsim/auth_serv/ec2-user.key
new file mode 100644
index 000000000000..035e25cde23e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec2-user.key
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDCkY69v8ff6oUI3wxJYeJdT500cYU9SE7LOLByjFyW5kKh0wfNI+PTj
+QCboPDTNgy6gBwYFK4EEACKhZANiAATuB6iYrTnzUXstmwJhnMBpU3SB6Hwa92ne
+S3VaDG2HGjdfBCV5JUHXt4o4JTtknjum/cKR/99xQ6pvBemWQjEcyeAyK18zIQrP
+Kce5MCGEcJ8c5GwKVwVYlBPzr85IcBg=
+-----END EC PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ec2-user.pem b/contrib/wpa/tests/hwsim/auth_serv/ec2-user.pem
new file mode 100644
index 000000000000..03253b79f746
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ec2-user.pem
@@ -0,0 +1,57 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 11652367451091730034 (0xa1b58675baa57e72)
+ Signature Algorithm: ecdsa-with-SHA384
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 192-bit Root CA
+ Validity
+ Not Before: Feb 1 09:26:24 2016 GMT
+ Not After : Jan 29 09:26:24 2026 GMT
+ Subject: C=FI, O=w1.fi, CN=user
+ Subject Public Key Info:
+ Public Key Algorithm: id-ecPublicKey
+ Public-Key: (384 bit)
+ pub:
+ 04:ee:07:a8:98:ad:39:f3:51:7b:2d:9b:02:61:9c:
+ c0:69:53:74:81:e8:7c:1a:f7:69:de:4b:75:5a:0c:
+ 6d:87:1a:37:5f:04:25:79:25:41:d7:b7:8a:38:25:
+ 3b:64:9e:3b:a6:fd:c2:91:ff:df:71:43:aa:6f:05:
+ e9:96:42:31:1c:c9:e0:32:2b:5f:33:21:0a:cf:29:
+ c7:b9:30:21:84:70:9f:1c:e4:6c:0a:57:05:58:94:
+ 13:f3:af:ce:48:70:18
+ ASN1 OID: secp384r1
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 63:19:63:3E:D9:CB:7F:DC:C9:E0:DD:4D:75:A4:34:63:18:16:C3:EF
+ X509v3 Authority Key Identifier:
+ keyid:B8:97:C9:BE:63:12:AB:F6:A0:8C:B6:5E:FB:97:6E:10:8E:DC:48:F5
+
+ X509v3 Subject Alternative Name:
+ email:user@w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: ecdsa-with-SHA384
+ 30:66:02:31:00:91:55:b8:e4:26:b6:19:10:b3:f5:47:fb:a0:
+ dc:6a:a1:1b:c6:53:28:be:bd:9e:94:48:34:45:cc:87:41:64:
+ 14:2d:d0:bb:dd:75:0a:c3:47:3a:05:7f:35:5c:1c:be:51:02:
+ 31:00:ce:4e:8d:cb:05:73:0d:f5:03:74:c5:b1:11:14:a8:0b:
+ e7:d8:26:36:bc:3b:90:60:5a:0e:bf:06:df:27:a3:59:79:52:
+ 7b:8e:7c:06:57:70:46:4c:dd:6f:dc:13:95:94
+-----BEGIN CERTIFICATE-----
+MIICOzCCAcCgAwIBAgIJAKG1hnW6pX5yMAoGCCqGSM49BAMDMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTkyLWJpdCBSb290IENBMB4XDTE2MDIwMTA5MjYyNFoXDTI2MDEy
+OTA5MjYyNFowLDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMQ0wCwYDVQQD
+DAR1c2VyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE7geomK0581F7LZsCYZzAaVN0
+geh8Gvdp3kt1Wgxthxo3XwQleSVB17eKOCU7ZJ47pv3Ckf/fcUOqbwXplkIxHMng
+MitfMyEKzynHuTAhhHCfHORsClcFWJQT86/OSHAYo4GHMIGEMAkGA1UdEwQCMAAw
+HQYDVR0OBBYEFGMZYz7Zy3/cyeDdTXWkNGMYFsPvMB8GA1UdIwQYMBaAFLiXyb5j
+Eqv2oIy2XvuXbhCO3Ej1MBUGA1UdEQQOMAyBCnVzZXJAdzEuZmkwEwYDVR0lBAww
+CgYIKwYBBQUHAwIwCwYDVR0PBAQDAgWgMAoGCCqGSM49BAMDA2kAMGYCMQCRVbjk
+JrYZELP1R/ug3GqhG8ZTKL69npRINEXMh0FkFC3Qu911CsNHOgV/NVwcvlECMQDO
+To3LBXMN9QN0xbERFKgL59gmNrw7kGBaDr8G3yejWXlSe458BldwRkzdb9wTlZQ=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/hlr_auc_gw.gsm b/contrib/wpa/tests/hwsim/auth_serv/hlr_auc_gw.gsm
new file mode 100644
index 000000000000..b67aeca3df29
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/hlr_auc_gw.gsm
@@ -0,0 +1,17 @@
+# Test triplets generated with GSM-Milenage using
+# Ki = 90dca4eda45b53cf0f12d7c9c3bc6a89
+# OPc = cb9cccc4b9258e6dca4760379fb82581
+
+# GSM authentication triplet file for EAP-SIM authenticator
+# IMSI:Kc:SRES:RAND
+# IMSI: ASCII string (numbers)
+# Kc: hex, 8 octets
+# SRES: hex, 4 octets
+# RAND: hex, 16 octets
+
+232010000000001:79747302dd684291:fbe55c44:d29b2f51f1fd20304ad0c447b4dcdc37
+232010000000001:2f2eaa1d83e43813:6e2e3ea3:e19a8e96255b88e8a8be104637d165b2
+232010000000001:b7c935bfb51f2c5a:257581f5:8079c338eb4195d0fe2d46b357979054
+232010000000001:bc93df6af0412a69:dae1faa0:a48b8e2a59b8bed468ea3d57ef9ee118
+232010000000001:626db3b0e9e321c3:a3e33208:38e7e65d0c0ef82185d1697410f2b31a
+232010000000001:df3cab53d00c622e:0b785f5d:d8a4a9efe1689d232468f316d2a84270
diff --git a/contrib/wpa/tests/hwsim/auth_serv/hlr_auc_gw.milenage_db b/contrib/wpa/tests/hwsim/auth_serv/hlr_auc_gw.milenage_db
new file mode 100644
index 000000000000..1c494f77309f
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/hlr_auc_gw.milenage_db
@@ -0,0 +1,16 @@
+# Parameters for Milenage (Example algorithms for AKA).
+# The example Ki, OPc, and AMF values here are from 3GPP TS 35.208 v6.0.0
+# 4.3.20 Test Set 20. SQN is the last used SQN value.
+# These values can be used for both UMTS (EAP-AKA) and GSM (EAP-SIM)
+# authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but
+# dummy values will need to be included in this file.
+
+# IMSI Ki OPc AMF SQN
+232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000
+
+# Modified version of the previous to allow testing with replaced SIM.
+232010000000009 a0dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000
+
+# These values are from Test Set 19 which has the AMF separation bit set to 1
+# and as such, is suitable for EAP-AKA' test.
+555444333222111 5122250214c33e723a5dd523fc145fc0 981d464c7c52eb6e5036234984ad0bcf c3ab 16f3b3f70fc1
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/ca-and-root.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/ca-and-root.pem
new file mode 100644
index 000000000000..2f10391d0d1c
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/ca-and-root.pem
@@ -0,0 +1,160 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cc:f7
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 3 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=Server Intermediate CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:a2:b0:de:7f:e6:17:69:4b:bb:8d:dc:4f:8b:95:
+ 33:5e:13:ee:a1:01:f5:82:de:6e:fc:83:db:e7:22:
+ 5f:b9:8d:2b:de:10:72:4e:da:81:c1:f7:f3:eb:0e:
+ db:5b:5f:90:92:bb:41:68:55:4f:84:d9:73:5b:0c:
+ 6d:40:e6:c5:0f:5d:5c:5e:80:1e:64:87:5a:99:44:
+ 8b:3d:61:20:f0:15:cc:87:95:5b:a0:46:0f:bc:5c:
+ 14:ee:ac:4f:c8:7c:d2:c0:ef:60:94:22:b6:74:05:
+ 4f:ca:97:01:0a:30:b4:50:44:89:d0:c2:6b:e5:7f:
+ ce:66:22:1a:d6:38:7c:ff:42:42:ca:58:a0:38:85:
+ ca:f1:b1:1f:33:27:db:bf:5c:49:96:36:7a:11:2f:
+ 62:d7:eb:7e:9f:9b:9c:0e:2b:df:cd:59:bc:ee:e8:
+ 6a:e3:7d:fa:06:ba:34:42:b5:7d:e7:be:e1:7b:85:
+ af:1b:25:a9:45:33:06:cb:cc:0d:ca:78:5c:56:52:
+ ac:43:7e:f6:0c:e7:fb:86:b4:ac:d7:f4:b2:54:ee:
+ 65:7a:5c:32:6b:33:a0:68:1b:d8:ea:c8:74:94:08:
+ 00:7f:9b:f0:da:80:0f:f2:45:13:11:63:4c:e6:d2:
+ 97:d3:ae:12:b0:7c:e8:f0:56:c0:7b:7c:82:99:6d:
+ 3b:5d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ EB:DC:8D:38:75:10:2F:E6:82:8E:FE:43:EC:9F:7E:63:22:BD:51:55
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha256WithRSAEncryption
+ 86:74:75:b2:bb:b0:85:25:48:38:e1:34:54:d5:d4:3a:9f:0e:
+ b1:96:fd:cc:ea:15:21:72:da:9e:ef:e2:fa:ae:29:74:dc:83:
+ 36:87:88:7d:75:51:9a:c5:6e:a8:80:77:3f:5c:ed:9e:ac:57:
+ 17:ed:ab:64:4f:15:8b:47:90:0a:17:2a:7e:49:a9:01:a1:41:
+ 66:d4:fe:be:18:70:d6:23:f7:0b:0a:53:d7:75:a8:7f:0a:52:
+ 1c:1d:8c:63:6f:82:ed:ed:fd:e2:fe:86:ef:0a:4c:f8:d7:93:
+ 56:9a:a3:dd:74:02:8c:b3:31:83:c1:8a:66:c6:c0:1d:dc:00:
+ 5c:57:f4:31:31:8b:d4:84:d8:da:6d:d6:f6:e4:10:7e:bb:f2:
+ 41:95:dd:a6:0c:37:c7:22:80:e6:36:3e:34:c6:1c:73:ab:42:
+ 90:6e:f8:db:e8:b6:c0:b2:f5:17:d2:6f:d3:8c:fb:14:25:8e:
+ 72:81:45:76:86:f7:d1:d9:3d:ff:b1:a2:10:6f:c0:24:e7:70:
+ 3f:2d:cf:32:ee:06:70:d5:1b:04:84:6d:48:69:26:1e:98:5a:
+ ed:e3:61:f5:29:45:88:25:cf:7f:c4:fb:f3:87:a7:11:95:9e:
+ cf:a8:aa:88:db:12:32:66:66:c4:1d:12:b1:62:1d:fa:28:f4:
+ 97:ac:df:2e
+-----BEGIN CERTIFICATE-----
+MIIDaDCCAlCgAwIBAgIJANjT46bL48z3MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDMxNTIwMTBaFw0zMDA1MDMxNTIwMTBaMD4xCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEfMB0GA1UEAwwWU2VydmVyIEludGVy
+bWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKKw3n/m
+F2lLu43cT4uVM14T7qEB9YLebvyD2+ciX7mNK94Qck7agcH38+sO21tfkJK7QWhV
+T4TZc1sMbUDmxQ9dXF6AHmSHWplEiz1hIPAVzIeVW6BGD7xcFO6sT8h80sDvYJQi
+tnQFT8qXAQowtFBEidDCa+V/zmYiGtY4fP9CQspYoDiFyvGxHzMn279cSZY2ehEv
+Ytfrfp+bnA4r381ZvO7oauN9+ga6NEK1fee+4XuFrxslqUUzBsvMDcp4XFZSrEN+
+9gzn+4a0rNf0slTuZXpcMmszoGgb2OrIdJQIAH+b8NqAD/JFExFjTObSl9OuErB8
+6PBWwHt8gpltO10CAwEAAaNmMGQwHQYDVR0OBBYEFOvcjTh1EC/mgo7+Q+yffmMi
+vVFVMB8GA1UdIwQYMBaAFKT9uTkbgbOq64gd1IGptRFwzKfhMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCGdHWy
+u7CFJUg44TRU1dQ6nw6xlv3M6hUhctqe7+L6ril03IM2h4h9dVGaxW6ogHc/XO2e
+rFcX7atkTxWLR5AKFyp+SakBoUFm1P6+GHDWI/cLClPXdah/ClIcHYxjb4Lt7f3i
+/obvCkz415NWmqPddAKMszGDwYpmxsAd3ABcV/QxMYvUhNjabdb25BB+u/JBld2m
+DDfHIoDmNj40xhxzq0KQbvjb6LbAsvUX0m/TjPsUJY5ygUV2hvfR2T3/saIQb8Ak
+53A/Lc8y7gZw1RsEhG1IaSYemFrt42H1KUWIJc9/xPvzh6cRlZ7PqKqI2xIyZmbE
+HRKxYh36KPSXrN8u
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 42:97:6c:30:8e:79:fc:7b:6a:e3:ef:9d:18:a4:74:9d:8b:5f:57:53
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C = FI, L = Tuusula, O = w1.fi, CN = Root CA
+ Validity
+ Not Before: May 2 19:49:48 2020 GMT
+ Not After : Apr 30 19:49:48 2030 GMT
+ Subject: C = FI, L = Tuusula, O = w1.fi, CN = Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:bc:f4:ee:44:62:7f:62:4f:a1:81:46:ba:c4:aa:
+ 1e:fd:4e:d0:ed:f1:47:cb:25:5b:66:7a:86:39:91:
+ ca:b5:61:a7:7e:2f:3c:63:7d:39:b8:1a:9e:cb:6d:
+ 32:32:91:de:49:49:84:da:15:be:2b:dd:c6:bc:1f:
+ dc:6e:c0:2d:77:f2:d0:7b:2c:40:19:07:60:55:b0:
+ ff:7c:51:ef:38:d1:f0:2a:da:a8:cc:ea:d6:54:a4:
+ ef:be:17:44:1a:9e:33:70:57:a4:f3:06:ac:3d:ee:
+ 4b:2d:e5:46:25:2d:33:09:f6:49:a8:02:31:a4:65:
+ 9b:32:0a:67:f5:02:e1:3b:47:a6:ae:e4:f6:85:eb:
+ 5d:3e:02:66:dd:11:98:ac:34:72:c2:8f:25:55:4a:
+ 6a:ea:e8:82:2f:bd:7f:78:31:a4:5a:d7:32:bb:64:
+ 48:46:23:ef:c8:c9:e2:84:00:56:72:e8:4b:54:95:
+ 62:3a:5a:11:79:ee:40:43:9e:16:2c:cc:e6:45:f4:
+ bb:82:28:c2:83:35:2c:55:36:99:59:11:b1:15:d0:
+ 03:c1:a5:37:e1:1f:bb:43:c7:b4:b9:33:de:14:d7:
+ 7c:99:45:0f:c1:06:fe:b6:25:10:59:b7:72:76:7f:
+ 91:4b:ea:d1:b9:6a:6a:ed:dd:1b:a9:0e:a7:29:48:
+ b7:4d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha256WithRSAEncryption
+ 41:f9:c3:a3:77:11:92:55:e7:4b:4a:32:6a:31:d9:51:cf:06:
+ a5:39:ea:30:98:b8:8d:4f:24:c5:34:fd:c6:98:10:59:32:7e:
+ 57:f5:8f:ba:67:c9:fc:44:68:b3:7c:f1:af:3a:5f:0d:8f:a1:
+ fe:41:21:0e:e9:08:a3:63:49:66:34:4a:cd:ce:66:74:47:30:
+ f7:dc:82:99:21:56:82:ff:2d:12:90:7d:7a:64:22:a0:ed:fa:
+ 62:d9:5a:d3:97:96:0c:04:a7:47:88:da:53:b6:33:15:15:f9:
+ da:ee:ac:25:e9:07:02:89:bc:73:a2:c6:27:6f:1f:bd:73:b8:
+ 8e:f7:94:54:57:a7:8b:5b:9a:24:aa:86:d4:04:5c:8c:cb:28:
+ a2:45:f9:34:f0:01:20:bb:06:e8:41:14:d2:d7:ca:e8:bf:4e:
+ 16:72:22:a0:0c:86:ca:73:23:09:ae:71:f1:52:0c:db:b2:8a:
+ 4d:94:a5:fa:15:81:5b:a2:95:62:50:a1:d6:64:fe:4c:0c:60:
+ 8d:9b:0f:b8:41:ac:cb:31:c2:17:6c:7b:61:13:16:9a:db:64:
+ fc:5f:47:84:3d:d2:2e:db:0b:9e:b6:1e:85:04:c1:e5:c0:b2:
+ 6d:8f:f2:99:00:3a:1a:ab:02:cf:45:7a:26:c1:b0:1f:c6:b0:
+ d0:4d:f7:52
+-----BEGIN CERTIFICATE-----
+MIIDYDCCAkigAwIBAgIUQpdsMI55/Htq4++dGKR0nYtfV1MwDQYJKoZIhvcNAQEL
+BQAwQTELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoMBXcx
+LmZpMRAwDgYDVQQDDAdSb290IENBMB4XDTIwMDUwMjE5NDk0OFoXDTMwMDQzMDE5
+NDk0OFowQTELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoM
+BXcxLmZpMRAwDgYDVQQDDAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAvPTuRGJ/Yk+hgUa6xKoe/U7Q7fFHyyVbZnqGOZHKtWGnfi88Y305
+uBqey20yMpHeSUmE2hW+K93GvB/cbsAtd/LQeyxAGQdgVbD/fFHvONHwKtqozOrW
+VKTvvhdEGp4zcFek8wasPe5LLeVGJS0zCfZJqAIxpGWbMgpn9QLhO0emruT2hetd
+PgJm3RGYrDRywo8lVUpq6uiCL71/eDGkWtcyu2RIRiPvyMnihABWcuhLVJViOloR
+ee5AQ54WLMzmRfS7gijCgzUsVTaZWRGxFdADwaU34R+7Q8e0uTPeFNd8mUUPwQb+
+tiUQWbdydn+RS+rRuWpq7d0bqQ6nKUi3TQIDAQABo1AwTjAdBgNVHQ4EFgQUpP25
+ORuBs6rriB3Ugam1EXDMp+EwHwYDVR0jBBgwFoAUpP25ORuBs6rriB3Ugam1EXDM
+p+EwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAQfnDo3cRklXnS0oy
+ajHZUc8GpTnqMJi4jU8kxTT9xpgQWTJ+V/WPumfJ/ERos3zxrzpfDY+h/kEhDukI
+o2NJZjRKzc5mdEcw99yCmSFWgv8tEpB9emQioO36Ytla05eWDASnR4jaU7YzFRX5
+2u6sJekHAom8c6LGJ28fvXO4jveUVFeni1uaJKqG1ARcjMsookX5NPABILsG6EEU
+0tfK6L9OFnIioAyGynMjCa5x8VIM27KKTZSl+hWBW6KVYlCh1mT+TAxgjZsPuEGs
+yzHCF2x7YRMWmttk/F9HhD3SLtsLnrYehQTB5cCybY/ymQA6GqsCz0V6JsGwH8aw
+0E33Ug==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/cacert.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/cacert.pem
new file mode 100644
index 000000000000..1ea16ecde90c
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/cacert.pem
@@ -0,0 +1,81 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cc:f7
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 3 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=Server Intermediate CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:a2:b0:de:7f:e6:17:69:4b:bb:8d:dc:4f:8b:95:
+ 33:5e:13:ee:a1:01:f5:82:de:6e:fc:83:db:e7:22:
+ 5f:b9:8d:2b:de:10:72:4e:da:81:c1:f7:f3:eb:0e:
+ db:5b:5f:90:92:bb:41:68:55:4f:84:d9:73:5b:0c:
+ 6d:40:e6:c5:0f:5d:5c:5e:80:1e:64:87:5a:99:44:
+ 8b:3d:61:20:f0:15:cc:87:95:5b:a0:46:0f:bc:5c:
+ 14:ee:ac:4f:c8:7c:d2:c0:ef:60:94:22:b6:74:05:
+ 4f:ca:97:01:0a:30:b4:50:44:89:d0:c2:6b:e5:7f:
+ ce:66:22:1a:d6:38:7c:ff:42:42:ca:58:a0:38:85:
+ ca:f1:b1:1f:33:27:db:bf:5c:49:96:36:7a:11:2f:
+ 62:d7:eb:7e:9f:9b:9c:0e:2b:df:cd:59:bc:ee:e8:
+ 6a:e3:7d:fa:06:ba:34:42:b5:7d:e7:be:e1:7b:85:
+ af:1b:25:a9:45:33:06:cb:cc:0d:ca:78:5c:56:52:
+ ac:43:7e:f6:0c:e7:fb:86:b4:ac:d7:f4:b2:54:ee:
+ 65:7a:5c:32:6b:33:a0:68:1b:d8:ea:c8:74:94:08:
+ 00:7f:9b:f0:da:80:0f:f2:45:13:11:63:4c:e6:d2:
+ 97:d3:ae:12:b0:7c:e8:f0:56:c0:7b:7c:82:99:6d:
+ 3b:5d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ EB:DC:8D:38:75:10:2F:E6:82:8E:FE:43:EC:9F:7E:63:22:BD:51:55
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha256WithRSAEncryption
+ 86:74:75:b2:bb:b0:85:25:48:38:e1:34:54:d5:d4:3a:9f:0e:
+ b1:96:fd:cc:ea:15:21:72:da:9e:ef:e2:fa:ae:29:74:dc:83:
+ 36:87:88:7d:75:51:9a:c5:6e:a8:80:77:3f:5c:ed:9e:ac:57:
+ 17:ed:ab:64:4f:15:8b:47:90:0a:17:2a:7e:49:a9:01:a1:41:
+ 66:d4:fe:be:18:70:d6:23:f7:0b:0a:53:d7:75:a8:7f:0a:52:
+ 1c:1d:8c:63:6f:82:ed:ed:fd:e2:fe:86:ef:0a:4c:f8:d7:93:
+ 56:9a:a3:dd:74:02:8c:b3:31:83:c1:8a:66:c6:c0:1d:dc:00:
+ 5c:57:f4:31:31:8b:d4:84:d8:da:6d:d6:f6:e4:10:7e:bb:f2:
+ 41:95:dd:a6:0c:37:c7:22:80:e6:36:3e:34:c6:1c:73:ab:42:
+ 90:6e:f8:db:e8:b6:c0:b2:f5:17:d2:6f:d3:8c:fb:14:25:8e:
+ 72:81:45:76:86:f7:d1:d9:3d:ff:b1:a2:10:6f:c0:24:e7:70:
+ 3f:2d:cf:32:ee:06:70:d5:1b:04:84:6d:48:69:26:1e:98:5a:
+ ed:e3:61:f5:29:45:88:25:cf:7f:c4:fb:f3:87:a7:11:95:9e:
+ cf:a8:aa:88:db:12:32:66:66:c4:1d:12:b1:62:1d:fa:28:f4:
+ 97:ac:df:2e
+-----BEGIN CERTIFICATE-----
+MIIDaDCCAlCgAwIBAgIJANjT46bL48z3MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDMxNTIwMTBaFw0zMDA1MDMxNTIwMTBaMD4xCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEfMB0GA1UEAwwWU2VydmVyIEludGVy
+bWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKKw3n/m
+F2lLu43cT4uVM14T7qEB9YLebvyD2+ciX7mNK94Qck7agcH38+sO21tfkJK7QWhV
+T4TZc1sMbUDmxQ9dXF6AHmSHWplEiz1hIPAVzIeVW6BGD7xcFO6sT8h80sDvYJQi
+tnQFT8qXAQowtFBEidDCa+V/zmYiGtY4fP9CQspYoDiFyvGxHzMn279cSZY2ehEv
+Ytfrfp+bnA4r381ZvO7oauN9+ga6NEK1fee+4XuFrxslqUUzBsvMDcp4XFZSrEN+
+9gzn+4a0rNf0slTuZXpcMmszoGgb2OrIdJQIAH+b8NqAD/JFExFjTObSl9OuErB8
+6PBWwHt8gpltO10CAwEAAaNmMGQwHQYDVR0OBBYEFOvcjTh1EC/mgo7+Q+yffmMi
+vVFVMB8GA1UdIwQYMBaAFKT9uTkbgbOq64gd1IGptRFwzKfhMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCGdHWy
+u7CFJUg44TRU1dQ6nw6xlv3M6hUhctqe7+L6ril03IM2h4h9dVGaxW6ogHc/XO2e
+rFcX7atkTxWLR5AKFyp+SakBoUFm1P6+GHDWI/cLClPXdah/ClIcHYxjb4Lt7f3i
+/obvCkz415NWmqPddAKMszGDwYpmxsAd3ABcV/QxMYvUhNjabdb25BB+u/JBld2m
+DDfHIoDmNj40xhxzq0KQbvjb6LbAsvUX0m/TjPsUJY5ygUV2hvfR2T3/saIQb8Ak
+53A/Lc8y7gZw1RsEhG1IaSYemFrt42H1KUWIJc9/xPvzh6cRlZ7PqKqI2xIyZmbE
+HRKxYh36KPSXrN8u
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/careq.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/careq.pem
new file mode 100644
index 000000000000..31445908bed8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/careq.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICljCCAX4CAQAwUTELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4w
+DAYDVQQKDAV3MS5maTEfMB0GA1UEAwwWU2VydmVyIEludGVybWVkaWF0ZSBDQTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKKw3n/mF2lLu43cT4uVM14T
+7qEB9YLebvyD2+ciX7mNK94Qck7agcH38+sO21tfkJK7QWhVT4TZc1sMbUDmxQ9d
+XF6AHmSHWplEiz1hIPAVzIeVW6BGD7xcFO6sT8h80sDvYJQitnQFT8qXAQowtFBE
+idDCa+V/zmYiGtY4fP9CQspYoDiFyvGxHzMn279cSZY2ehEvYtfrfp+bnA4r381Z
+vO7oauN9+ga6NEK1fee+4XuFrxslqUUzBsvMDcp4XFZSrEN+9gzn+4a0rNf0slTu
+ZXpcMmszoGgb2OrIdJQIAH+b8NqAD/JFExFjTObSl9OuErB86PBWwHt8gpltO10C
+AwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQA+ruQc1HRbWOLQh+vOzw4e8WMPZ3nr
+9qTDKjtpQKQUiNy7ZtCvi3kTtYVQcmyXWFf+Fq7NCHfH6GUgXfH/cCsOW0gdsPmH
+YuAfZam1njO3+qZqacCpxaVmn3XExC01oIFLf0CtepXrMDh23MoPtZt7kh7bwomG
+vrUtAecjLuZIIVzD2OkOIFZCEi4Dce85+CrDw7Es59xj1WySvHMWgU5prOf5Wp//
+7yS26YYjHcaJ3ePO/mFnB3XH4rrQwczNAHx6W4NE0IPvbE9FRN7fIQGPCN40XsPc
+XCR12oNtPBWpHExHsqCk6d6B3omfLUJe8eJiY1hPKiDI2ATdBoX5Ogbs
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/index.txt b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/index.txt
new file mode 100644
index 000000000000..fe7d248642f0
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/index.txt
@@ -0,0 +1,2 @@
+V 300501152010Z 5C9DE4A6D17A49C88375E75768F77216B2AEB782 unknown /C=FI/O=w1.fi/CN=server.w1.fi
+R 300501152010Z 200503152010Z 5C9DE4A6D17A49C88375E75768F77216B2AEB783 unknown /C=FI/O=w1.fi/CN=server-revoked.w1.fi
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/index.txt.attr b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/index.txt.attr
new file mode 100644
index 000000000000..3a7e39e6ee60
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/index.txt.attr
@@ -0,0 +1 @@
+unique_subject = no
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/private/cakey.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/private/cakey.pem
new file mode 100644
index 000000000000..67bad6718007
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/private/cakey.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCisN5/5hdpS7uN
+3E+LlTNeE+6hAfWC3m78g9vnIl+5jSveEHJO2oHB9/PrDttbX5CSu0FoVU+E2XNb
+DG1A5sUPXVxegB5kh1qZRIs9YSDwFcyHlVugRg+8XBTurE/IfNLA72CUIrZ0BU/K
+lwEKMLRQRInQwmvlf85mIhrWOHz/QkLKWKA4hcrxsR8zJ9u/XEmWNnoRL2LX636f
+m5wOK9/NWbzu6GrjffoGujRCtX3nvuF7ha8bJalFMwbLzA3KeFxWUqxDfvYM5/uG
+tKzX9LJU7mV6XDJrM6BoG9jqyHSUCAB/m/DagA/yRRMRY0zm0pfTrhKwfOjwVsB7
+fIKZbTtdAgMBAAECggEAU2/YPMn5mcQAZYnmtdSIKqiYSrThgAOp8hGCFzE23Me9
+Br9ykGRaBeuvig7tixgg4k/tBKA0DxMiqUBfS9jOmcms1L5qV+5fFZnku070AI19
+fs+n1TP5YAXtqlZu+Iij4dUit/ZxkmEjAeid3OcLotrzvz/m7CW26gR1tQX1fUdh
++YFwUjyY1g2QRrUiPI/LJnOwJeZuxjnRaabmYjKvjHcbN/kmKWxqBrrTMU6KNq0o
+4PPKragvpvePrKSPwOyQROlRXsj6qY/FMrXVrSVGfrUAk7NCmccbtJM6SY2/k3ly
+Y69YWNj2laui5loTpUcuTFFPwmHkHBJC6GJCGoaHYQKBgQDNonjx7e0dzLjRzMUg
+1SX4oH+IqL4oMq1Cs3l9iibFXFpG23gCh7ao54ehmYJE3088L7NGvGjoMl/4uj+T
+WElFENCk0lzjxPPAEkAuHY3JApL+0ZsU4RyWjNVSOaD7eQCfN/og0ZgNW6+TUiOu
+Wq6rA/KTywSUnnwdvqMTTXhDaQKBgQDKicYXLH8TmOvauRkrfWjS5XhBEplskxiQ
+uqlQEXlbT3jymyIAdt+3hnydL557lbKPfTe/rs22wnH/++aUQTzQgKNs7oWSFyDt
+km0LCGdLg7NuVNI4Sdi5SoIWsTcAkmkYA+KFy3YQK9vwr3eZ3ExbYI55QTyoVKON
+jReguOBd1QKBgQC8LTMqiYVUoNR8wTuf6Q4/cHhk0a56UK22/VBvLq5+Kx49+3be
+Md1Ywc+fdT/90LDMrgYL9Dy4R+kFT0MAjmk2d8XHHu58TO6WVN1AljD6wo1L/PpC
+6CHmL2jDPxNvLPMBwRL3V3Yiu0V3tlIKqtdujkU9NCqz6jhAbAUFk/47CQKBgQCw
+PRB868AsCl349iXbvQWwtfJdFVUhsCGpFnPr8ziZZt8EpE8C/m2PIdxfXqdWPJ2i
+1D/lcLMae7p9F/G9QcMsXzNVv3vE8pE5iLeP6SERCanhsLc4ObH3Ecl++3ez7LK8
+Le03pSK30aJRni3BWXur66ouAsFIbFXg/0v3E8hQfQKBgQCicOnaNKD/dARZZhfR
+eoX+97PRTpoloOF+jd/AOYq4ATZoNQwF2Jizs8fDbi0SUWFoFG8UmrrJ4xv4DBDq
+rAeHvZle2K8od1/NjOG5/WFAF0/Aci+R5U/bX2/2vspb5AWBdwMbvnf7GKtwrAYU
+TiK7X/iftZZWuApQauRR4yrCAw==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/serial b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/serial
new file mode 100644
index 000000000000..b40c841093d6
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/serial
@@ -0,0 +1 @@
+5C9DE4A6D17A49C88375E75768F77216B2AEB784
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.key b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.key
new file mode 100644
index 000000000000..85c11d612a6b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC6httirVW8O0Hx
+we89YRNtZYrRvlhBpuBd8sXzp8HCbJ6wX/AWLOS6fSa3aUNysdco0gY9bpxnMjg/
+PGOUjWPpf7N7ZwvWyQLs2n7hWyHkoeoB7Li9b17SkuUz2qUTpo4EtRl6B5roA+69
+SixlbOw6SDh+DTAw7hrZGr4C0OHylRchCDxJTY8Rx7SM5pNLSvrdrApy2IKK4WyZ
+HncbiBKzcs/c+lfSY+EtwFpXNtT/NyAgAbQRGSz5m/b6k//KafKE62+vRLgY49hC
+KZchAeFHofztWHSwq/l1XOhJqhZKGTHoxYxgmUif2XiS7DELIGTZVxxuaqbd+FUv
+zCt2EbWbAgMBAAECggEBAIXTM5u8mQKP0WROrALxnyqh69NIKbIQtHEzOWrzNUT1
+AXWxn2OJmiFioWB+GXI0vhX/eZKhxX0Uvt4/yYJPXxusD22+JPRZC8w7h0TQSaTr
+tiTjXjgrq3CRC/kEKePLX6Fo/Xpb8nv8NlGA4hFy8JlwL3fgpm60pnaVhTYn/7Q2
+oey4zIKwnRYp8oFf58iu5Otjga247AWiR3VDHTeSlvSQOMj+4RybQgpbeB6hVfrs
+0IjR7hq+dBoMdY5NuEPhGIvcoQq2T6K7hYqIx2ou8OCDeBefdJOm30Y2Iu1CpEx5
+dwJWtgv+ZVHUKl7MlXYiLVZjzX2ZSOJukeNs8AoRrwECgYEA25Z2/XlT2E3C24Q3
+S6GZJtbPnvYLr8xHzQ1ljA39ltu5PQXcKRYAgaOGQtl6NhrpoE8uiiVXegbw0CP2
+yyKLlqn0JvJaZREpJcCPqdt4jDgNZ3ajXhiq1SFGsZsLxmrM0Iu43Kf2nxVQyP0R
+6npuvlagOnggvCMzQWfQqsxrxDsCgYEA2XTy5BwmDhoczMbQozFFCd8MbzoDwnDR
+hXVSEbG0TjNe/d9UfD8Y9xsV8tlkcDhfS3lwMFHp/poSnvjrF2vGmMNaLEcZnQEX
+crqrL6gRpUs6dpv40Q/Dtkze3p2DoCD+La94RHjHrXtTJ5DZcliNZJt90zXz0Kmy
+6abXvv8jniECgYEAjxSQvgLjdirdEAoruZU3ZM5NhKeP3+G820iiZUrsdPMA1VlP
+JlpWxCIYJtDsR/rrRfCyQ4OnZzTEjusQMTZ2PBrLouEBs58l75p0Qdpmxv7zBPqR
+4osyLSO8m5eKaaRHho+0SdsL4IaUGBKGLQHPzShGyTJjKhPJnxGVLuV6RucCgYAI
+9XR8SVyYACNnnFlEH+eEPJg6jN1SyWsYYHj9GaEgB6XGN8k3RTI2G/uPgb1NkkT6
+ywoAM5+8SYSy3/ZvhJUt/f5dDKDVgxIAPAiJchcoBC1obYyWsFuTyx7zdPHTSwit
+wSjnSUKQtx/55VHQEC3jEzTf2r0sv5ELZ0BEMia5gQKBgQCgOkdrjh7CHy8qZ51i
+mlctafZyOnqp98i6q+R5AN5S9EDemKtUmAq7JSmBX/5cn1wgde115YiEwvt/xNmE
+c0RBpcz+ZsgJnoQsCPUJmzIkwMIjZ3t9nrdHDTegcNJM1M7V7xN4jf7ycoynEmGK
+k4XzV7M/nhOjiRwWCq6ba3Ddwg==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.pem
new file mode 100644
index 000000000000..031b9d1a9ba8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.pem
@@ -0,0 +1,86 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 5c:9d:e4:a6:d1:7a:49:c8:83:75:e7:57:68:f7:72:16:b2:ae:b7:83
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, O=w1.fi, CN=Server Intermediate CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 1 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=server-revoked.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:ba:86:db:62:ad:55:bc:3b:41:f1:c1:ef:3d:61:
+ 13:6d:65:8a:d1:be:58:41:a6:e0:5d:f2:c5:f3:a7:
+ c1:c2:6c:9e:b0:5f:f0:16:2c:e4:ba:7d:26:b7:69:
+ 43:72:b1:d7:28:d2:06:3d:6e:9c:67:32:38:3f:3c:
+ 63:94:8d:63:e9:7f:b3:7b:67:0b:d6:c9:02:ec:da:
+ 7e:e1:5b:21:e4:a1:ea:01:ec:b8:bd:6f:5e:d2:92:
+ e5:33:da:a5:13:a6:8e:04:b5:19:7a:07:9a:e8:03:
+ ee:bd:4a:2c:65:6c:ec:3a:48:38:7e:0d:30:30:ee:
+ 1a:d9:1a:be:02:d0:e1:f2:95:17:21:08:3c:49:4d:
+ 8f:11:c7:b4:8c:e6:93:4b:4a:fa:dd:ac:0a:72:d8:
+ 82:8a:e1:6c:99:1e:77:1b:88:12:b3:72:cf:dc:fa:
+ 57:d2:63:e1:2d:c0:5a:57:36:d4:ff:37:20:20:01:
+ b4:11:19:2c:f9:9b:f6:fa:93:ff:ca:69:f2:84:eb:
+ 6f:af:44:b8:18:e3:d8:42:29:97:21:01:e1:47:a1:
+ fc:ed:58:74:b0:ab:f9:75:5c:e8:49:aa:16:4a:19:
+ 31:e8:c5:8c:60:99:48:9f:d9:78:92:ec:31:0b:20:
+ 64:d9:57:1c:6e:6a:a6:dd:f8:55:2f:cc:2b:76:11:
+ b5:9b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ FB:67:34:A4:0E:E6:BB:BF:90:0D:7C:B2:69:E8:04:D5:71:8F:76:44
+ X509v3 Authority Key Identifier:
+ keyid:EB:DC:8D:38:75:10:2F:E6:82:8E:FE:43:EC:9F:7E:63:22:BD:51:55
+
+ X509v3 Subject Alternative Name: critical
+ DNS:server-revoked.w1.fi
+ X509v3 Extended Key Usage: critical
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ 22:c0:a0:7c:25:b4:4d:61:44:25:09:9c:14:8d:35:6e:36:7b:
+ 91:60:6b:35:90:48:a9:a2:ee:81:70:c4:d8:2a:9d:a3:7e:a2:
+ c9:0c:dc:b2:73:98:01:cf:db:d4:3a:17:8a:b6:3d:b5:97:47:
+ 33:e9:b6:14:ed:a6:8e:a4:6d:34:d0:03:3a:01:04:ce:28:24:
+ f9:c3:15:a9:b1:8c:2a:dc:8d:40:98:ac:78:8f:f5:fc:53:88:
+ 0e:84:28:39:86:75:59:ad:12:54:77:f2:9c:e1:d2:4e:e1:ee:
+ 8d:57:f3:41:ab:15:4d:ab:77:75:47:9a:c6:36:28:08:b5:8d:
+ c7:9f:5a:87:87:f8:a7:17:9a:44:4e:ce:84:24:12:da:7f:a8:
+ ab:15:fd:24:9b:cf:1c:ae:2f:8f:13:28:27:09:1e:57:2b:ca:
+ 1f:c8:bc:a4:95:08:27:4e:c4:21:68:a5:45:9f:5a:42:1c:7f:
+ 37:59:d7:ed:30:be:ed:26:12:5d:80:f5:7d:7d:94:ff:52:56:
+ fc:67:0f:3f:00:21:e7:b4:2f:48:7b:77:86:fb:16:28:ab:68:
+ e1:4d:80:eb:5e:4b:99:88:2f:ec:a3:1d:06:c5:04:2e:bb:56:
+ fb:6b:75:9d:5b:78:83:63:2b:70:7c:21:94:a1:58:a4:8e:8b:
+ 30:d3:28:88
+-----BEGIN CERTIFICATE-----
+MIIDozCCAougAwIBAgIUXJ3kptF6SciDdedXaPdyFrKut4MwDQYJKoZIhvcNAQEL
+BQAwPjELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMR8wHQYDVQQDDBZTZXJ2
+ZXIgSW50ZXJtZWRpYXRlIENBMB4XDTIwMDUwMzE1MjAxMFoXDTMwMDUwMTE1MjAx
+MFowPDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMR0wGwYDVQQDDBRzZXJ2
+ZXItcmV2b2tlZC53MS5maTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALqG22KtVbw7QfHB7z1hE21litG+WEGm4F3yxfOnwcJsnrBf8BYs5Lp9JrdpQ3Kx
+1yjSBj1unGcyOD88Y5SNY+l/s3tnC9bJAuzafuFbIeSh6gHsuL1vXtKS5TPapROm
+jgS1GXoHmugD7r1KLGVs7DpIOH4NMDDuGtkavgLQ4fKVFyEIPElNjxHHtIzmk0tK
++t2sCnLYgorhbJkedxuIErNyz9z6V9Jj4S3AWlc21P83ICABtBEZLPmb9vqT/8pp
+8oTrb69EuBjj2EIplyEB4Ueh/O1YdLCr+XVc6EmqFkoZMejFjGCZSJ/ZeJLsMQsg
+ZNlXHG5qpt34VS/MK3YRtZsCAwEAAaOBmjCBlzAMBgNVHRMBAf8EAjAAMB0GA1Ud
+DgQWBBT7ZzSkDua7v5ANfLJp6ATVcY92RDAfBgNVHSMEGDAWgBTr3I04dRAv5oKO
+/kPsn35jIr1RVTAiBgNVHREBAf8EGDAWghRzZXJ2ZXItcmV2b2tlZC53MS5maTAW
+BgNVHSUBAf8EDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEL
+BQADggEBACLAoHwltE1hRCUJnBSNNW42e5FgazWQSKmi7oFwxNgqnaN+oskM3LJz
+mAHP29Q6F4q2PbWXRzPpthTtpo6kbTTQAzoBBM4oJPnDFamxjCrcjUCYrHiP9fxT
+iA6EKDmGdVmtElR38pzh0k7h7o1X80GrFU2rd3VHmsY2KAi1jcefWoeH+KcXmkRO
+zoQkEtp/qKsV/SSbzxyuL48TKCcJHlcryh/IvKSVCCdOxCFopUWfWkIcfzdZ1+0w
+vu0mEl2A9X19lP9SVvxnDz8AIee0L0h7d4b7FiiraOFNgOteS5mIL+yjHQbFBC67
+VvtrdZ1beINjK3B8IZShWKSOizDTKIg=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.req b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.req
new file mode 100644
index 000000000000..b4c0f2374879
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked.req
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIClDCCAXwCAQAwTzELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4w
+DAYDVQQKDAV3MS5maTEdMBsGA1UEAwwUc2VydmVyLXJldm9rZWQudzEuZmkwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC6httirVW8O0Hxwe89YRNtZYrR
+vlhBpuBd8sXzp8HCbJ6wX/AWLOS6fSa3aUNysdco0gY9bpxnMjg/PGOUjWPpf7N7
+ZwvWyQLs2n7hWyHkoeoB7Li9b17SkuUz2qUTpo4EtRl6B5roA+69SixlbOw6SDh+
+DTAw7hrZGr4C0OHylRchCDxJTY8Rx7SM5pNLSvrdrApy2IKK4WyZHncbiBKzcs/c
++lfSY+EtwFpXNtT/NyAgAbQRGSz5m/b6k//KafKE62+vRLgY49hCKZchAeFHofzt
+WHSwq/l1XOhJqhZKGTHoxYxgmUif2XiS7DELIGTZVxxuaqbd+FUvzCt2EbWbAgMB
+AAGgADANBgkqhkiG9w0BAQsFAAOCAQEAo3V6gURcTGy95qxhogpMRD20D9VwiK5m
+O81e8wvu6bFn0881Khzi24M4D54JG7NBiVl8FyW7zfnmzpd7lxceSyOuEF7wSIYD
++GrVQ8wgcfPTy0z9iUH/lEjiesv7BEq9AuvAUXSaBC1dPaoWdmEZ+EJRSehle2fj
+Lw1OtkjAN47eXo+gXE+kW1V4oM0mI6n7EJ8lEyz6/CUf3mw3EBLXhIncVRthKbrt
+S4ujuaak3AGD+KBkxLjxTOIb0IPPsX+lYUly+PayUSe2LhQI0p34wN7Tb4oSu4qH
+nZZFb5RIysq0av7y8SqUoJJyaEiYtXxbbUKme/6xUqY4OMpP+01EOQ==
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked_and_ica.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked_and_ica.pem
new file mode 100644
index 000000000000..09619be1aaa5
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server-revoked_and_ica.pem
@@ -0,0 +1,167 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cc:f7
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 3 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=Server Intermediate CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:a2:b0:de:7f:e6:17:69:4b:bb:8d:dc:4f:8b:95:
+ 33:5e:13:ee:a1:01:f5:82:de:6e:fc:83:db:e7:22:
+ 5f:b9:8d:2b:de:10:72:4e:da:81:c1:f7:f3:eb:0e:
+ db:5b:5f:90:92:bb:41:68:55:4f:84:d9:73:5b:0c:
+ 6d:40:e6:c5:0f:5d:5c:5e:80:1e:64:87:5a:99:44:
+ 8b:3d:61:20:f0:15:cc:87:95:5b:a0:46:0f:bc:5c:
+ 14:ee:ac:4f:c8:7c:d2:c0:ef:60:94:22:b6:74:05:
+ 4f:ca:97:01:0a:30:b4:50:44:89:d0:c2:6b:e5:7f:
+ ce:66:22:1a:d6:38:7c:ff:42:42:ca:58:a0:38:85:
+ ca:f1:b1:1f:33:27:db:bf:5c:49:96:36:7a:11:2f:
+ 62:d7:eb:7e:9f:9b:9c:0e:2b:df:cd:59:bc:ee:e8:
+ 6a:e3:7d:fa:06:ba:34:42:b5:7d:e7:be:e1:7b:85:
+ af:1b:25:a9:45:33:06:cb:cc:0d:ca:78:5c:56:52:
+ ac:43:7e:f6:0c:e7:fb:86:b4:ac:d7:f4:b2:54:ee:
+ 65:7a:5c:32:6b:33:a0:68:1b:d8:ea:c8:74:94:08:
+ 00:7f:9b:f0:da:80:0f:f2:45:13:11:63:4c:e6:d2:
+ 97:d3:ae:12:b0:7c:e8:f0:56:c0:7b:7c:82:99:6d:
+ 3b:5d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ EB:DC:8D:38:75:10:2F:E6:82:8E:FE:43:EC:9F:7E:63:22:BD:51:55
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha256WithRSAEncryption
+ 86:74:75:b2:bb:b0:85:25:48:38:e1:34:54:d5:d4:3a:9f:0e:
+ b1:96:fd:cc:ea:15:21:72:da:9e:ef:e2:fa:ae:29:74:dc:83:
+ 36:87:88:7d:75:51:9a:c5:6e:a8:80:77:3f:5c:ed:9e:ac:57:
+ 17:ed:ab:64:4f:15:8b:47:90:0a:17:2a:7e:49:a9:01:a1:41:
+ 66:d4:fe:be:18:70:d6:23:f7:0b:0a:53:d7:75:a8:7f:0a:52:
+ 1c:1d:8c:63:6f:82:ed:ed:fd:e2:fe:86:ef:0a:4c:f8:d7:93:
+ 56:9a:a3:dd:74:02:8c:b3:31:83:c1:8a:66:c6:c0:1d:dc:00:
+ 5c:57:f4:31:31:8b:d4:84:d8:da:6d:d6:f6:e4:10:7e:bb:f2:
+ 41:95:dd:a6:0c:37:c7:22:80:e6:36:3e:34:c6:1c:73:ab:42:
+ 90:6e:f8:db:e8:b6:c0:b2:f5:17:d2:6f:d3:8c:fb:14:25:8e:
+ 72:81:45:76:86:f7:d1:d9:3d:ff:b1:a2:10:6f:c0:24:e7:70:
+ 3f:2d:cf:32:ee:06:70:d5:1b:04:84:6d:48:69:26:1e:98:5a:
+ ed:e3:61:f5:29:45:88:25:cf:7f:c4:fb:f3:87:a7:11:95:9e:
+ cf:a8:aa:88:db:12:32:66:66:c4:1d:12:b1:62:1d:fa:28:f4:
+ 97:ac:df:2e
+-----BEGIN CERTIFICATE-----
+MIIDaDCCAlCgAwIBAgIJANjT46bL48z3MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDMxNTIwMTBaFw0zMDA1MDMxNTIwMTBaMD4xCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEfMB0GA1UEAwwWU2VydmVyIEludGVy
+bWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKKw3n/m
+F2lLu43cT4uVM14T7qEB9YLebvyD2+ciX7mNK94Qck7agcH38+sO21tfkJK7QWhV
+T4TZc1sMbUDmxQ9dXF6AHmSHWplEiz1hIPAVzIeVW6BGD7xcFO6sT8h80sDvYJQi
+tnQFT8qXAQowtFBEidDCa+V/zmYiGtY4fP9CQspYoDiFyvGxHzMn279cSZY2ehEv
+Ytfrfp+bnA4r381ZvO7oauN9+ga6NEK1fee+4XuFrxslqUUzBsvMDcp4XFZSrEN+
+9gzn+4a0rNf0slTuZXpcMmszoGgb2OrIdJQIAH+b8NqAD/JFExFjTObSl9OuErB8
+6PBWwHt8gpltO10CAwEAAaNmMGQwHQYDVR0OBBYEFOvcjTh1EC/mgo7+Q+yffmMi
+vVFVMB8GA1UdIwQYMBaAFKT9uTkbgbOq64gd1IGptRFwzKfhMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCGdHWy
+u7CFJUg44TRU1dQ6nw6xlv3M6hUhctqe7+L6ril03IM2h4h9dVGaxW6ogHc/XO2e
+rFcX7atkTxWLR5AKFyp+SakBoUFm1P6+GHDWI/cLClPXdah/ClIcHYxjb4Lt7f3i
+/obvCkz415NWmqPddAKMszGDwYpmxsAd3ABcV/QxMYvUhNjabdb25BB+u/JBld2m
+DDfHIoDmNj40xhxzq0KQbvjb6LbAsvUX0m/TjPsUJY5ygUV2hvfR2T3/saIQb8Ak
+53A/Lc8y7gZw1RsEhG1IaSYemFrt42H1KUWIJc9/xPvzh6cRlZ7PqKqI2xIyZmbE
+HRKxYh36KPSXrN8u
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 5c:9d:e4:a6:d1:7a:49:c8:83:75:e7:57:68:f7:72:16:b2:ae:b7:83
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, O=w1.fi, CN=Server Intermediate CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 1 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=server-revoked.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:ba:86:db:62:ad:55:bc:3b:41:f1:c1:ef:3d:61:
+ 13:6d:65:8a:d1:be:58:41:a6:e0:5d:f2:c5:f3:a7:
+ c1:c2:6c:9e:b0:5f:f0:16:2c:e4:ba:7d:26:b7:69:
+ 43:72:b1:d7:28:d2:06:3d:6e:9c:67:32:38:3f:3c:
+ 63:94:8d:63:e9:7f:b3:7b:67:0b:d6:c9:02:ec:da:
+ 7e:e1:5b:21:e4:a1:ea:01:ec:b8:bd:6f:5e:d2:92:
+ e5:33:da:a5:13:a6:8e:04:b5:19:7a:07:9a:e8:03:
+ ee:bd:4a:2c:65:6c:ec:3a:48:38:7e:0d:30:30:ee:
+ 1a:d9:1a:be:02:d0:e1:f2:95:17:21:08:3c:49:4d:
+ 8f:11:c7:b4:8c:e6:93:4b:4a:fa:dd:ac:0a:72:d8:
+ 82:8a:e1:6c:99:1e:77:1b:88:12:b3:72:cf:dc:fa:
+ 57:d2:63:e1:2d:c0:5a:57:36:d4:ff:37:20:20:01:
+ b4:11:19:2c:f9:9b:f6:fa:93:ff:ca:69:f2:84:eb:
+ 6f:af:44:b8:18:e3:d8:42:29:97:21:01:e1:47:a1:
+ fc:ed:58:74:b0:ab:f9:75:5c:e8:49:aa:16:4a:19:
+ 31:e8:c5:8c:60:99:48:9f:d9:78:92:ec:31:0b:20:
+ 64:d9:57:1c:6e:6a:a6:dd:f8:55:2f:cc:2b:76:11:
+ b5:9b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ FB:67:34:A4:0E:E6:BB:BF:90:0D:7C:B2:69:E8:04:D5:71:8F:76:44
+ X509v3 Authority Key Identifier:
+ keyid:EB:DC:8D:38:75:10:2F:E6:82:8E:FE:43:EC:9F:7E:63:22:BD:51:55
+
+ X509v3 Subject Alternative Name: critical
+ DNS:server-revoked.w1.fi
+ X509v3 Extended Key Usage: critical
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ 22:c0:a0:7c:25:b4:4d:61:44:25:09:9c:14:8d:35:6e:36:7b:
+ 91:60:6b:35:90:48:a9:a2:ee:81:70:c4:d8:2a:9d:a3:7e:a2:
+ c9:0c:dc:b2:73:98:01:cf:db:d4:3a:17:8a:b6:3d:b5:97:47:
+ 33:e9:b6:14:ed:a6:8e:a4:6d:34:d0:03:3a:01:04:ce:28:24:
+ f9:c3:15:a9:b1:8c:2a:dc:8d:40:98:ac:78:8f:f5:fc:53:88:
+ 0e:84:28:39:86:75:59:ad:12:54:77:f2:9c:e1:d2:4e:e1:ee:
+ 8d:57:f3:41:ab:15:4d:ab:77:75:47:9a:c6:36:28:08:b5:8d:
+ c7:9f:5a:87:87:f8:a7:17:9a:44:4e:ce:84:24:12:da:7f:a8:
+ ab:15:fd:24:9b:cf:1c:ae:2f:8f:13:28:27:09:1e:57:2b:ca:
+ 1f:c8:bc:a4:95:08:27:4e:c4:21:68:a5:45:9f:5a:42:1c:7f:
+ 37:59:d7:ed:30:be:ed:26:12:5d:80:f5:7d:7d:94:ff:52:56:
+ fc:67:0f:3f:00:21:e7:b4:2f:48:7b:77:86:fb:16:28:ab:68:
+ e1:4d:80:eb:5e:4b:99:88:2f:ec:a3:1d:06:c5:04:2e:bb:56:
+ fb:6b:75:9d:5b:78:83:63:2b:70:7c:21:94:a1:58:a4:8e:8b:
+ 30:d3:28:88
+-----BEGIN CERTIFICATE-----
+MIIDozCCAougAwIBAgIUXJ3kptF6SciDdedXaPdyFrKut4MwDQYJKoZIhvcNAQEL
+BQAwPjELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMR8wHQYDVQQDDBZTZXJ2
+ZXIgSW50ZXJtZWRpYXRlIENBMB4XDTIwMDUwMzE1MjAxMFoXDTMwMDUwMTE1MjAx
+MFowPDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMR0wGwYDVQQDDBRzZXJ2
+ZXItcmV2b2tlZC53MS5maTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEB
+ALqG22KtVbw7QfHB7z1hE21litG+WEGm4F3yxfOnwcJsnrBf8BYs5Lp9JrdpQ3Kx
+1yjSBj1unGcyOD88Y5SNY+l/s3tnC9bJAuzafuFbIeSh6gHsuL1vXtKS5TPapROm
+jgS1GXoHmugD7r1KLGVs7DpIOH4NMDDuGtkavgLQ4fKVFyEIPElNjxHHtIzmk0tK
++t2sCnLYgorhbJkedxuIErNyz9z6V9Jj4S3AWlc21P83ICABtBEZLPmb9vqT/8pp
+8oTrb69EuBjj2EIplyEB4Ueh/O1YdLCr+XVc6EmqFkoZMejFjGCZSJ/ZeJLsMQsg
+ZNlXHG5qpt34VS/MK3YRtZsCAwEAAaOBmjCBlzAMBgNVHRMBAf8EAjAAMB0GA1Ud
+DgQWBBT7ZzSkDua7v5ANfLJp6ATVcY92RDAfBgNVHSMEGDAWgBTr3I04dRAv5oKO
+/kPsn35jIr1RVTAiBgNVHREBAf8EGDAWghRzZXJ2ZXItcmV2b2tlZC53MS5maTAW
+BgNVHSUBAf8EDDAKBggrBgEFBQcDATALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEL
+BQADggEBACLAoHwltE1hRCUJnBSNNW42e5FgazWQSKmi7oFwxNgqnaN+oskM3LJz
+mAHP29Q6F4q2PbWXRzPpthTtpo6kbTTQAzoBBM4oJPnDFamxjCrcjUCYrHiP9fxT
+iA6EKDmGdVmtElR38pzh0k7h7o1X80GrFU2rd3VHmsY2KAi1jcefWoeH+KcXmkRO
+zoQkEtp/qKsV/SSbzxyuL48TKCcJHlcryh/IvKSVCCdOxCFopUWfWkIcfzdZ1+0w
+vu0mEl2A9X19lP9SVvxnDz8AIee0L0h7d4b7FiiraOFNgOteS5mIL+yjHQbFBC67
+VvtrdZ1beINjK3B8IZShWKSOizDTKIg=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.key b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.key
new file mode 100644
index 000000000000..068c0f6d045e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCsIexV4pRs1Rtt
++neHf34q9SZOPCjUcFBwVeKp1atiFQECtZDoVZF8sPSf/RE8c3L3Vn1MtVYhjxfE
+ZVwqPw3iIqWA7RqwqIzimvePd23FJJ8rwDommxN1ltLPGUzK7ZCzyNrnIAOmCl2t
+BJ1rN51p6Wxj1RLa/8Kl1PQE3845wgY9P+yLPZ4cpy3yY1N+OqpoCrCTsmk9I9qx
+rv6Q+sbq7jWUTZrYXW+57YBrG71GVqu/KYrJIOUxPRGW4MVWWOHxhG28D+WbvJ91
+KwMBGliOiCKzCnyNs00egjF1f88oOqrA9cNFcrxI95phESwx1D1bbiXKKuqI6Vj+
+7g0A1TaLAgMBAAECggEBAKBGHeaCSK1laFro4i76GSIqjXY/Mc1MnrlaXujAGQoE
+gKJjKQAL3KF7qurlGg1tedivYY/xMLeiowCtWDnF6Els9SmsnDNtXXEJ1gRxsXXk
++YglPn//2QieXL+U0RoKRbgBB0I5XuxVro+RQno4mIurWs9B8IKVrkn2lReMxFql
+tXaAUEkvnIbEnEuXUtb7XNzAv3LeF5XrFZ5egHggKwDlA1o0wl2Nx0JVynX9EJER
+VoWWWAvJhtZLQb2EetYKA2WOIwkCwFMX/EYP4BuGRFLnrPOMMqAkS42ZdVLhwCk7
+m00J5GO1Bwf9OBy2aPVGubaU4P+BKHkCSzxs22guIWECgYEA064UlbqudqL+Jtpc
+B4gYqh4d61Cf8PaOz61YVggpcXTQtf+Ov02h3M2Iq0i42fw4hewRRiQmFyOP8daA
+OsqAHj0eOv4ibE23hR1KssBl3fBt6ubjCxeFf3Sl0e0j+5XFgCPYEdJPPsJhY91f
+TK6IUYq9DAITdmU2+TL5RdK7KVMCgYEA0CwfEaveSzeBZTMudh0WsywF6EsplWHM
+QyawOPbltZxxTQHDAillFyoPHNtFZs8KBVdmTxcITW3YyaEFbZ0rXLKFjyHiMQfG
+KSCB2nzFOhT8eXsdVyXahXkKiIsoFjaKqRRsNoxyUtbj8Pu7/CRtbabDLOj/iF43
+eo8iNn3LvukCgYEA0Bq+Zg1n436OekgGXekwxl5hb6yN8WmUMRvsUngntkDvx119
+SxnZXag7CpmuEbBjKVZSDTEQuYLeyxTkb+gRWKkhhUG/OdaV66pGe8Gm5DCw/1UK
+NSdkuU9GjkGjNH2j8zxJ+gtWmQ6kjHdgb5TOs8u/24RW+fi7uPaiFkD8e70CgYEA
+pml+9Lt12p8me2Xs0FL0oIqitk3PkjE5/rxgx0jn3MSQ9kRgRcwdmeTva9wFoOgF
+VLvHd5Yr9unHEXf9ROENlu7HQeKOVS+nw5zO8YAokgPQyLQYgmAqTeSy/PBxPUCg
+nAVNdFXV1k8erLgwUKI2MB/NiotAKx1WME1XxkPNqnECgYAR6WJkK4t4uJueta3P
+c4AUU585nTX6CyH7oxkud/s+Mg6ztbtC9/BSxtQNTJKWhbqdpI7xz2807Wzv3/6h
+IFpCPf3aXWX9yp3FlohRfE/hCRz8IhBzQNCUaXxMta3KOVw0u88aQycEI8CGad2Z
+By9hDAFwSLj4vQuD0VclFyCfZA==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.pem
new file mode 100644
index 000000000000..58d76bcbee6b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.pem
@@ -0,0 +1,86 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 5c:9d:e4:a6:d1:7a:49:c8:83:75:e7:57:68:f7:72:16:b2:ae:b7:82
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, O=w1.fi, CN=Server Intermediate CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 1 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=server.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:ac:21:ec:55:e2:94:6c:d5:1b:6d:fa:77:87:7f:
+ 7e:2a:f5:26:4e:3c:28:d4:70:50:70:55:e2:a9:d5:
+ ab:62:15:01:02:b5:90:e8:55:91:7c:b0:f4:9f:fd:
+ 11:3c:73:72:f7:56:7d:4c:b5:56:21:8f:17:c4:65:
+ 5c:2a:3f:0d:e2:22:a5:80:ed:1a:b0:a8:8c:e2:9a:
+ f7:8f:77:6d:c5:24:9f:2b:c0:3a:26:9b:13:75:96:
+ d2:cf:19:4c:ca:ed:90:b3:c8:da:e7:20:03:a6:0a:
+ 5d:ad:04:9d:6b:37:9d:69:e9:6c:63:d5:12:da:ff:
+ c2:a5:d4:f4:04:df:ce:39:c2:06:3d:3f:ec:8b:3d:
+ 9e:1c:a7:2d:f2:63:53:7e:3a:aa:68:0a:b0:93:b2:
+ 69:3d:23:da:b1:ae:fe:90:fa:c6:ea:ee:35:94:4d:
+ 9a:d8:5d:6f:b9:ed:80:6b:1b:bd:46:56:ab:bf:29:
+ 8a:c9:20:e5:31:3d:11:96:e0:c5:56:58:e1:f1:84:
+ 6d:bc:0f:e5:9b:bc:9f:75:2b:03:01:1a:58:8e:88:
+ 22:b3:0a:7c:8d:b3:4d:1e:82:31:75:7f:cf:28:3a:
+ aa:c0:f5:c3:45:72:bc:48:f7:9a:61:11:2c:31:d4:
+ 3d:5b:6e:25:ca:2a:ea:88:e9:58:fe:ee:0d:00:d5:
+ 36:8b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ E9:E3:CE:7A:C2:27:BF:88:CF:19:9E:5C:6C:DC:12:C0:D5:00:64:15
+ X509v3 Authority Key Identifier:
+ keyid:EB:DC:8D:38:75:10:2F:E6:82:8E:FE:43:EC:9F:7E:63:22:BD:51:55
+
+ X509v3 Subject Alternative Name: critical
+ DNS:server.w1.fi
+ X509v3 Extended Key Usage: critical
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ 1b:c4:4a:ea:b3:ee:c3:82:4d:98:93:49:6a:34:98:80:b6:a3:
+ dc:00:d5:ca:27:56:43:e2:71:4c:60:a1:ef:c2:41:9c:fa:93:
+ a4:61:20:f5:3f:2c:3a:91:e8:12:e1:7a:51:c0:86:2b:cf:1b:
+ 73:26:b3:0c:e7:03:2e:8e:48:49:3e:32:29:df:b2:9e:d5:29:
+ 26:bf:c3:3e:eb:7d:34:96:c7:6e:0e:ae:16:a1:a1:fa:25:dd:
+ a3:2e:3e:4e:3e:76:ff:d6:35:ef:d4:07:2f:d2:6f:48:08:ab:
+ e7:4a:09:ff:43:09:ec:32:49:19:52:cd:30:03:22:3c:f0:9c:
+ 9b:e3:fd:bc:e7:f9:d1:7a:da:c6:66:bf:e0:86:95:5c:45:43:
+ 07:26:6d:70:fc:24:66:4a:cd:86:bd:6c:d3:7a:0d:12:4b:33:
+ bc:a0:4b:81:08:1a:26:bc:42:a2:e7:37:36:56:ac:ef:85:34:
+ 52:89:33:df:b6:33:11:ac:20:67:cd:8d:ce:d7:bb:cb:bc:b5:
+ 16:3c:08:cf:c7:1a:68:60:16:9c:55:e6:b5:17:4f:3f:69:f9:
+ b4:18:70:af:60:5d:0f:c4:66:08:b9:75:a3:78:11:f7:8f:8d:
+ f1:2b:4e:05:b9:90:b6:f3:99:8b:0c:43:6a:8c:b4:cc:ff:2f:
+ 58:70:d7:8e
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIUXJ3kptF6SciDdedXaPdyFrKut4IwDQYJKoZIhvcNAQEL
+BQAwPjELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMR8wHQYDVQQDDBZTZXJ2
+ZXIgSW50ZXJtZWRpYXRlIENBMB4XDTIwMDUwMzE1MjAxMFoXDTMwMDUwMTE1MjAx
+MFowNDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMRUwEwYDVQQDDAxzZXJ2
+ZXIudzEuZmkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsIexV4pRs
+1Rtt+neHf34q9SZOPCjUcFBwVeKp1atiFQECtZDoVZF8sPSf/RE8c3L3Vn1MtVYh
+jxfEZVwqPw3iIqWA7RqwqIzimvePd23FJJ8rwDommxN1ltLPGUzK7ZCzyNrnIAOm
+Cl2tBJ1rN51p6Wxj1RLa/8Kl1PQE3845wgY9P+yLPZ4cpy3yY1N+OqpoCrCTsmk9
+I9qxrv6Q+sbq7jWUTZrYXW+57YBrG71GVqu/KYrJIOUxPRGW4MVWWOHxhG28D+Wb
+vJ91KwMBGliOiCKzCnyNs00egjF1f88oOqrA9cNFcrxI95phESwx1D1bbiXKKuqI
+6Vj+7g0A1TaLAgMBAAGjgZIwgY8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU6ePO
+esInv4jPGZ5cbNwSwNUAZBUwHwYDVR0jBBgwFoAU69yNOHUQL+aCjv5D7J9+YyK9
+UVUwGgYDVR0RAQH/BBAwDoIMc2VydmVyLncxLmZpMBYGA1UdJQEB/wQMMAoGCCsG
+AQUFBwMBMAsGA1UdDwQEAwIFoDANBgkqhkiG9w0BAQsFAAOCAQEAG8RK6rPuw4JN
+mJNJajSYgLaj3ADVyidWQ+JxTGCh78JBnPqTpGEg9T8sOpHoEuF6UcCGK88bcyaz
+DOcDLo5IST4yKd+yntUpJr/DPut9NJbHbg6uFqGh+iXdoy4+Tj52/9Y179QHL9Jv
+SAir50oJ/0MJ7DJJGVLNMAMiPPCcm+P9vOf50Xraxma/4IaVXEVDByZtcPwkZkrN
+hr1s03oNEkszvKBLgQgaJrxCouc3Nlas74U0Uokz37YzEawgZ82Nzte7y7y1FjwI
+z8caaGAWnFXmtRdPP2n5tBhwr2BdD8RmCLl1o3gR94+N8StOBbmQtvOZiwxDaoy0
+zP8vWHDXjg==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.req b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.req
new file mode 100644
index 000000000000..181564b93ade
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server.req
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICjDCCAXQCAQAwRzELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4w
+DAYDVQQKDAV3MS5maTEVMBMGA1UEAwwMc2VydmVyLncxLmZpMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCHsVeKUbNUbbfp3h39+KvUmTjwo1HBQcFXi
+qdWrYhUBArWQ6FWRfLD0n/0RPHNy91Z9TLVWIY8XxGVcKj8N4iKlgO0asKiM4pr3
+j3dtxSSfK8A6JpsTdZbSzxlMyu2Qs8ja5yADpgpdrQSdazedaelsY9US2v/CpdT0
+BN/OOcIGPT/siz2eHKct8mNTfjqqaAqwk7JpPSPasa7+kPrG6u41lE2a2F1vue2A
+axu9RlarvymKySDlMT0RluDFVljh8YRtvA/lm7yfdSsDARpYjogiswp8jbNNHoIx
+dX/PKDqqwPXDRXK8SPeaYREsMdQ9W24lyirqiOlY/u4NANU2iwIDAQABoAAwDQYJ
+KoZIhvcNAQELBQADggEBACNfUGcccnZoS3TqbWbfYMtWhi0a80xuWb+8v//aO0D2
+NeJMFmOKVgChOqHZza8rnIGMCOEbL1DkEJIZh1Z5ovy0fkGbcdCxeJIcem8PRLK0
+oFiLgIM9MeVDTSLY6FP7hjifR3x6SnO39DahiycnG45Kek7kVq25oCuyKxJrsoEQ
+pwHdPG1VWvgDy4O7u2RA6kedU2gWjgHVUCJYpeJFp953kV1qrMM/ynFYJF049etm
+Vyl/wxM69LP/bibElna/iAVFPBCe4Mav/bbI371Ju0AHzcdNxdoMnHgEhHB7c7Ye
+QmZKRVi2HHD+PZ1xdtvqJD3EtSKkOuY8JRy6EteGdR4=
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server_and_ica.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server_and_ica.pem
new file mode 100644
index 000000000000..c7798a214012
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-server/server_and_ica.pem
@@ -0,0 +1,167 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cc:f7
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 3 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=Server Intermediate CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:a2:b0:de:7f:e6:17:69:4b:bb:8d:dc:4f:8b:95:
+ 33:5e:13:ee:a1:01:f5:82:de:6e:fc:83:db:e7:22:
+ 5f:b9:8d:2b:de:10:72:4e:da:81:c1:f7:f3:eb:0e:
+ db:5b:5f:90:92:bb:41:68:55:4f:84:d9:73:5b:0c:
+ 6d:40:e6:c5:0f:5d:5c:5e:80:1e:64:87:5a:99:44:
+ 8b:3d:61:20:f0:15:cc:87:95:5b:a0:46:0f:bc:5c:
+ 14:ee:ac:4f:c8:7c:d2:c0:ef:60:94:22:b6:74:05:
+ 4f:ca:97:01:0a:30:b4:50:44:89:d0:c2:6b:e5:7f:
+ ce:66:22:1a:d6:38:7c:ff:42:42:ca:58:a0:38:85:
+ ca:f1:b1:1f:33:27:db:bf:5c:49:96:36:7a:11:2f:
+ 62:d7:eb:7e:9f:9b:9c:0e:2b:df:cd:59:bc:ee:e8:
+ 6a:e3:7d:fa:06:ba:34:42:b5:7d:e7:be:e1:7b:85:
+ af:1b:25:a9:45:33:06:cb:cc:0d:ca:78:5c:56:52:
+ ac:43:7e:f6:0c:e7:fb:86:b4:ac:d7:f4:b2:54:ee:
+ 65:7a:5c:32:6b:33:a0:68:1b:d8:ea:c8:74:94:08:
+ 00:7f:9b:f0:da:80:0f:f2:45:13:11:63:4c:e6:d2:
+ 97:d3:ae:12:b0:7c:e8:f0:56:c0:7b:7c:82:99:6d:
+ 3b:5d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ EB:DC:8D:38:75:10:2F:E6:82:8E:FE:43:EC:9F:7E:63:22:BD:51:55
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha256WithRSAEncryption
+ 86:74:75:b2:bb:b0:85:25:48:38:e1:34:54:d5:d4:3a:9f:0e:
+ b1:96:fd:cc:ea:15:21:72:da:9e:ef:e2:fa:ae:29:74:dc:83:
+ 36:87:88:7d:75:51:9a:c5:6e:a8:80:77:3f:5c:ed:9e:ac:57:
+ 17:ed:ab:64:4f:15:8b:47:90:0a:17:2a:7e:49:a9:01:a1:41:
+ 66:d4:fe:be:18:70:d6:23:f7:0b:0a:53:d7:75:a8:7f:0a:52:
+ 1c:1d:8c:63:6f:82:ed:ed:fd:e2:fe:86:ef:0a:4c:f8:d7:93:
+ 56:9a:a3:dd:74:02:8c:b3:31:83:c1:8a:66:c6:c0:1d:dc:00:
+ 5c:57:f4:31:31:8b:d4:84:d8:da:6d:d6:f6:e4:10:7e:bb:f2:
+ 41:95:dd:a6:0c:37:c7:22:80:e6:36:3e:34:c6:1c:73:ab:42:
+ 90:6e:f8:db:e8:b6:c0:b2:f5:17:d2:6f:d3:8c:fb:14:25:8e:
+ 72:81:45:76:86:f7:d1:d9:3d:ff:b1:a2:10:6f:c0:24:e7:70:
+ 3f:2d:cf:32:ee:06:70:d5:1b:04:84:6d:48:69:26:1e:98:5a:
+ ed:e3:61:f5:29:45:88:25:cf:7f:c4:fb:f3:87:a7:11:95:9e:
+ cf:a8:aa:88:db:12:32:66:66:c4:1d:12:b1:62:1d:fa:28:f4:
+ 97:ac:df:2e
+-----BEGIN CERTIFICATE-----
+MIIDaDCCAlCgAwIBAgIJANjT46bL48z3MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDMxNTIwMTBaFw0zMDA1MDMxNTIwMTBaMD4xCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEfMB0GA1UEAwwWU2VydmVyIEludGVy
+bWVkaWF0ZSBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKKw3n/m
+F2lLu43cT4uVM14T7qEB9YLebvyD2+ciX7mNK94Qck7agcH38+sO21tfkJK7QWhV
+T4TZc1sMbUDmxQ9dXF6AHmSHWplEiz1hIPAVzIeVW6BGD7xcFO6sT8h80sDvYJQi
+tnQFT8qXAQowtFBEidDCa+V/zmYiGtY4fP9CQspYoDiFyvGxHzMn279cSZY2ehEv
+Ytfrfp+bnA4r381ZvO7oauN9+ga6NEK1fee+4XuFrxslqUUzBsvMDcp4XFZSrEN+
+9gzn+4a0rNf0slTuZXpcMmszoGgb2OrIdJQIAH+b8NqAD/JFExFjTObSl9OuErB8
+6PBWwHt8gpltO10CAwEAAaNmMGQwHQYDVR0OBBYEFOvcjTh1EC/mgo7+Q+yffmMi
+vVFVMB8GA1UdIwQYMBaAFKT9uTkbgbOq64gd1IGptRFwzKfhMBIGA1UdEwEB/wQI
+MAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBCwUAA4IBAQCGdHWy
+u7CFJUg44TRU1dQ6nw6xlv3M6hUhctqe7+L6ril03IM2h4h9dVGaxW6ogHc/XO2e
+rFcX7atkTxWLR5AKFyp+SakBoUFm1P6+GHDWI/cLClPXdah/ClIcHYxjb4Lt7f3i
+/obvCkz415NWmqPddAKMszGDwYpmxsAd3ABcV/QxMYvUhNjabdb25BB+u/JBld2m
+DDfHIoDmNj40xhxzq0KQbvjb6LbAsvUX0m/TjPsUJY5ygUV2hvfR2T3/saIQb8Ak
+53A/Lc8y7gZw1RsEhG1IaSYemFrt42H1KUWIJc9/xPvzh6cRlZ7PqKqI2xIyZmbE
+HRKxYh36KPSXrN8u
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 5c:9d:e4:a6:d1:7a:49:c8:83:75:e7:57:68:f7:72:16:b2:ae:b7:82
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, O=w1.fi, CN=Server Intermediate CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 1 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=server.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:ac:21:ec:55:e2:94:6c:d5:1b:6d:fa:77:87:7f:
+ 7e:2a:f5:26:4e:3c:28:d4:70:50:70:55:e2:a9:d5:
+ ab:62:15:01:02:b5:90:e8:55:91:7c:b0:f4:9f:fd:
+ 11:3c:73:72:f7:56:7d:4c:b5:56:21:8f:17:c4:65:
+ 5c:2a:3f:0d:e2:22:a5:80:ed:1a:b0:a8:8c:e2:9a:
+ f7:8f:77:6d:c5:24:9f:2b:c0:3a:26:9b:13:75:96:
+ d2:cf:19:4c:ca:ed:90:b3:c8:da:e7:20:03:a6:0a:
+ 5d:ad:04:9d:6b:37:9d:69:e9:6c:63:d5:12:da:ff:
+ c2:a5:d4:f4:04:df:ce:39:c2:06:3d:3f:ec:8b:3d:
+ 9e:1c:a7:2d:f2:63:53:7e:3a:aa:68:0a:b0:93:b2:
+ 69:3d:23:da:b1:ae:fe:90:fa:c6:ea:ee:35:94:4d:
+ 9a:d8:5d:6f:b9:ed:80:6b:1b:bd:46:56:ab:bf:29:
+ 8a:c9:20:e5:31:3d:11:96:e0:c5:56:58:e1:f1:84:
+ 6d:bc:0f:e5:9b:bc:9f:75:2b:03:01:1a:58:8e:88:
+ 22:b3:0a:7c:8d:b3:4d:1e:82:31:75:7f:cf:28:3a:
+ aa:c0:f5:c3:45:72:bc:48:f7:9a:61:11:2c:31:d4:
+ 3d:5b:6e:25:ca:2a:ea:88:e9:58:fe:ee:0d:00:d5:
+ 36:8b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ E9:E3:CE:7A:C2:27:BF:88:CF:19:9E:5C:6C:DC:12:C0:D5:00:64:15
+ X509v3 Authority Key Identifier:
+ keyid:EB:DC:8D:38:75:10:2F:E6:82:8E:FE:43:EC:9F:7E:63:22:BD:51:55
+
+ X509v3 Subject Alternative Name: critical
+ DNS:server.w1.fi
+ X509v3 Extended Key Usage: critical
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ 1b:c4:4a:ea:b3:ee:c3:82:4d:98:93:49:6a:34:98:80:b6:a3:
+ dc:00:d5:ca:27:56:43:e2:71:4c:60:a1:ef:c2:41:9c:fa:93:
+ a4:61:20:f5:3f:2c:3a:91:e8:12:e1:7a:51:c0:86:2b:cf:1b:
+ 73:26:b3:0c:e7:03:2e:8e:48:49:3e:32:29:df:b2:9e:d5:29:
+ 26:bf:c3:3e:eb:7d:34:96:c7:6e:0e:ae:16:a1:a1:fa:25:dd:
+ a3:2e:3e:4e:3e:76:ff:d6:35:ef:d4:07:2f:d2:6f:48:08:ab:
+ e7:4a:09:ff:43:09:ec:32:49:19:52:cd:30:03:22:3c:f0:9c:
+ 9b:e3:fd:bc:e7:f9:d1:7a:da:c6:66:bf:e0:86:95:5c:45:43:
+ 07:26:6d:70:fc:24:66:4a:cd:86:bd:6c:d3:7a:0d:12:4b:33:
+ bc:a0:4b:81:08:1a:26:bc:42:a2:e7:37:36:56:ac:ef:85:34:
+ 52:89:33:df:b6:33:11:ac:20:67:cd:8d:ce:d7:bb:cb:bc:b5:
+ 16:3c:08:cf:c7:1a:68:60:16:9c:55:e6:b5:17:4f:3f:69:f9:
+ b4:18:70:af:60:5d:0f:c4:66:08:b9:75:a3:78:11:f7:8f:8d:
+ f1:2b:4e:05:b9:90:b6:f3:99:8b:0c:43:6a:8c:b4:cc:ff:2f:
+ 58:70:d7:8e
+-----BEGIN CERTIFICATE-----
+MIIDkzCCAnugAwIBAgIUXJ3kptF6SciDdedXaPdyFrKut4IwDQYJKoZIhvcNAQEL
+BQAwPjELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMR8wHQYDVQQDDBZTZXJ2
+ZXIgSW50ZXJtZWRpYXRlIENBMB4XDTIwMDUwMzE1MjAxMFoXDTMwMDUwMTE1MjAx
+MFowNDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMRUwEwYDVQQDDAxzZXJ2
+ZXIudzEuZmkwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCsIexV4pRs
+1Rtt+neHf34q9SZOPCjUcFBwVeKp1atiFQECtZDoVZF8sPSf/RE8c3L3Vn1MtVYh
+jxfEZVwqPw3iIqWA7RqwqIzimvePd23FJJ8rwDommxN1ltLPGUzK7ZCzyNrnIAOm
+Cl2tBJ1rN51p6Wxj1RLa/8Kl1PQE3845wgY9P+yLPZ4cpy3yY1N+OqpoCrCTsmk9
+I9qxrv6Q+sbq7jWUTZrYXW+57YBrG71GVqu/KYrJIOUxPRGW4MVWWOHxhG28D+Wb
+vJ91KwMBGliOiCKzCnyNs00egjF1f88oOqrA9cNFcrxI95phESwx1D1bbiXKKuqI
+6Vj+7g0A1TaLAgMBAAGjgZIwgY8wDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU6ePO
+esInv4jPGZ5cbNwSwNUAZBUwHwYDVR0jBBgwFoAU69yNOHUQL+aCjv5D7J9+YyK9
+UVUwGgYDVR0RAQH/BBAwDoIMc2VydmVyLncxLmZpMBYGA1UdJQEB/wQMMAoGCCsG
+AQUFBwMBMAsGA1UdDwQEAwIFoDANBgkqhkiG9w0BAQsFAAOCAQEAG8RK6rPuw4JN
+mJNJajSYgLaj3ADVyidWQ+JxTGCh78JBnPqTpGEg9T8sOpHoEuF6UcCGK88bcyaz
+DOcDLo5IST4yKd+yntUpJr/DPut9NJbHbg6uFqGh+iXdoy4+Tj52/9Y179QHL9Jv
+SAir50oJ/0MJ7DJJGVLNMAMiPPCcm+P9vOf50Xraxma/4IaVXEVDByZtcPwkZkrN
+hr1s03oNEkszvKBLgQgaJrxCouc3Nlas74U0Uokz37YzEawgZ82Nzte7y7y1FjwI
+z8caaGAWnFXmtRdPP2n5tBhwr2BdD8RmCLl1o3gR94+N8StOBbmQtvOZiwxDaoy0
+zP8vWHDXjg==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/ca-and-root.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/ca-and-root.pem
new file mode 100644
index 000000000000..41c8240e52a2
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/ca-and-root.pem
@@ -0,0 +1,160 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cc:f8
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 3 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=User Intermediate CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:b9:88:7a:fc:1a:f9:00:68:63:c7:40:ff:d5:38:
+ 8e:88:8c:c9:8f:66:ec:74:0a:a6:f1:18:30:30:36:
+ 9a:2a:98:c5:a0:46:02:e2:3c:64:86:79:43:45:19:
+ 83:7a:82:3d:f9:c6:af:01:11:91:2c:4f:07:f8:d7:
+ ef:da:80:6c:07:88:6a:1e:e6:0e:78:ca:08:50:4f:
+ f0:8a:2e:54:41:9f:04:63:8b:70:99:ae:6f:95:ed:
+ 5c:c8:34:8e:6b:36:64:bc:44:c9:fb:cb:50:ef:b1:
+ 5b:9b:2c:db:2a:a7:f9:e0:e2:48:57:78:cf:ba:0f:
+ 1a:af:5a:63:64:18:39:9c:d4:af:8d:f9:27:d9:10:
+ b4:67:17:a1:24:98:f1:ef:ce:ad:12:6f:e4:47:36:
+ b6:d2:b6:1c:04:03:76:43:63:fb:b6:3e:3f:1a:c8:
+ c4:8b:69:28:7c:75:dc:bb:36:7f:ad:6a:a2:c1:32:
+ f3:5e:64:86:57:f1:ee:20:af:64:bd:e0:7c:ba:68:
+ 9b:75:ed:b3:1c:0f:12:e0:52:12:ff:18:0e:8f:1d:
+ bf:c8:88:56:35:4d:9e:1f:74:1e:19:d7:0c:b4:e7:
+ 46:ee:cf:c6:63:35:ba:16:7f:05:84:8b:bf:16:72:
+ 05:ee:22:6e:3a:54:80:2b:0e:36:96:8d:65:5f:64:
+ 12:cd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ F0:F7:82:29:71:CD:AF:72:CE:F6:3C:0B:40:16:C2:FD:9F:8A:51:A7
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha256WithRSAEncryption
+ 70:45:66:14:00:22:85:1c:f8:b9:b3:2c:e8:64:4d:01:53:b8:
+ cb:23:ad:fa:01:7c:27:f7:aa:8d:d8:6c:6a:f8:72:21:63:bf:
+ 30:7e:05:8c:84:e3:d1:1e:d1:f3:1d:80:3d:e8:75:06:ae:1b:
+ 48:a9:cf:0e:c6:59:6b:f8:d1:25:5a:64:b7:46:2d:29:72:da:
+ d6:3a:79:d3:92:41:d7:31:e4:4e:5e:1b:62:88:41:77:f6:62:
+ a2:3e:c1:a2:ef:79:0c:8f:39:7c:df:a0:4b:d5:ac:58:aa:3e:
+ fd:95:6b:f7:c0:42:29:2e:86:67:5e:d9:3e:7b:e7:a6:bd:3b:
+ 7e:3b:19:54:9b:89:40:0e:39:23:8a:af:f2:db:12:5b:09:b4:
+ 45:df:c8:3e:8f:fc:fc:55:3e:35:8d:7b:82:50:d5:a3:ea:bb:
+ c4:40:6d:61:ad:92:b2:66:91:0f:5b:3d:49:5e:b5:3e:98:15:
+ 9e:2a:23:06:35:e0:13:bc:50:84:06:e4:1b:b9:fc:32:a2:4a:
+ 0d:e5:86:ac:69:47:c3:17:11:07:ac:5a:09:69:ed:99:d0:52:
+ fd:6d:ab:0d:44:35:bb:c0:76:27:50:75:df:06:78:f6:92:54:
+ fc:54:76:b5:6f:a0:f6:51:20:1f:7f:8e:aa:5f:c8:48:88:e4:
+ 1a:83:f6:b7
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAk6gAwIBAgIJANjT46bL48z4MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDMxNTIwMTBaFw0zMDA1MDMxNTIwMTBaMDwxCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEdMBsGA1UEAwwUVXNlciBJbnRlcm1l
+ZGlhdGUgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5iHr8GvkA
+aGPHQP/VOI6IjMmPZux0CqbxGDAwNpoqmMWgRgLiPGSGeUNFGYN6gj35xq8BEZEs
+Twf41+/agGwHiGoe5g54yghQT/CKLlRBnwRji3CZrm+V7VzINI5rNmS8RMn7y1Dv
+sVubLNsqp/ng4khXeM+6DxqvWmNkGDmc1K+N+SfZELRnF6EkmPHvzq0Sb+RHNrbS
+thwEA3ZDY/u2Pj8ayMSLaSh8ddy7Nn+taqLBMvNeZIZX8e4gr2S94Hy6aJt17bMc
+DxLgUhL/GA6PHb/IiFY1TZ4fdB4Z1wy050buz8ZjNboWfwWEi78WcgXuIm46VIAr
+DjaWjWVfZBLNAgMBAAGjZjBkMB0GA1UdDgQWBBTw94Ipcc2vcs72PAtAFsL9n4pR
+pzAfBgNVHSMEGDAWgBSk/bk5G4GzquuIHdSBqbURcMyn4TASBgNVHRMBAf8ECDAG
+AQH/AgEAMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAcEVmFAAi
+hRz4ubMs6GRNAVO4yyOt+gF8J/eqjdhsavhyIWO/MH4FjITj0R7R8x2APeh1Bq4b
+SKnPDsZZa/jRJVpkt0YtKXLa1jp505JB1zHkTl4bYohBd/Zioj7Bou95DI85fN+g
+S9WsWKo+/ZVr98BCKS6GZ17ZPnvnpr07fjsZVJuJQA45I4qv8tsSWwm0Rd/IPo/8
+/FU+NY17glDVo+q7xEBtYa2SsmaRD1s9SV61PpgVniojBjXgE7xQhAbkG7n8MqJK
+DeWGrGlHwxcRB6xaCWntmdBS/W2rDUQ1u8B2J1B13wZ49pJU/FR2tW+g9lEgH3+O
+ql/ISIjkGoP2tw==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 42:97:6c:30:8e:79:fc:7b:6a:e3:ef:9d:18:a4:74:9d:8b:5f:57:53
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C = FI, L = Tuusula, O = w1.fi, CN = Root CA
+ Validity
+ Not Before: May 2 19:49:48 2020 GMT
+ Not After : Apr 30 19:49:48 2030 GMT
+ Subject: C = FI, L = Tuusula, O = w1.fi, CN = Root CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:bc:f4:ee:44:62:7f:62:4f:a1:81:46:ba:c4:aa:
+ 1e:fd:4e:d0:ed:f1:47:cb:25:5b:66:7a:86:39:91:
+ ca:b5:61:a7:7e:2f:3c:63:7d:39:b8:1a:9e:cb:6d:
+ 32:32:91:de:49:49:84:da:15:be:2b:dd:c6:bc:1f:
+ dc:6e:c0:2d:77:f2:d0:7b:2c:40:19:07:60:55:b0:
+ ff:7c:51:ef:38:d1:f0:2a:da:a8:cc:ea:d6:54:a4:
+ ef:be:17:44:1a:9e:33:70:57:a4:f3:06:ac:3d:ee:
+ 4b:2d:e5:46:25:2d:33:09:f6:49:a8:02:31:a4:65:
+ 9b:32:0a:67:f5:02:e1:3b:47:a6:ae:e4:f6:85:eb:
+ 5d:3e:02:66:dd:11:98:ac:34:72:c2:8f:25:55:4a:
+ 6a:ea:e8:82:2f:bd:7f:78:31:a4:5a:d7:32:bb:64:
+ 48:46:23:ef:c8:c9:e2:84:00:56:72:e8:4b:54:95:
+ 62:3a:5a:11:79:ee:40:43:9e:16:2c:cc:e6:45:f4:
+ bb:82:28:c2:83:35:2c:55:36:99:59:11:b1:15:d0:
+ 03:c1:a5:37:e1:1f:bb:43:c7:b4:b9:33:de:14:d7:
+ 7c:99:45:0f:c1:06:fe:b6:25:10:59:b7:72:76:7f:
+ 91:4b:ea:d1:b9:6a:6a:ed:dd:1b:a9:0e:a7:29:48:
+ b7:4d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints:
+ CA:TRUE
+ Signature Algorithm: sha256WithRSAEncryption
+ 41:f9:c3:a3:77:11:92:55:e7:4b:4a:32:6a:31:d9:51:cf:06:
+ a5:39:ea:30:98:b8:8d:4f:24:c5:34:fd:c6:98:10:59:32:7e:
+ 57:f5:8f:ba:67:c9:fc:44:68:b3:7c:f1:af:3a:5f:0d:8f:a1:
+ fe:41:21:0e:e9:08:a3:63:49:66:34:4a:cd:ce:66:74:47:30:
+ f7:dc:82:99:21:56:82:ff:2d:12:90:7d:7a:64:22:a0:ed:fa:
+ 62:d9:5a:d3:97:96:0c:04:a7:47:88:da:53:b6:33:15:15:f9:
+ da:ee:ac:25:e9:07:02:89:bc:73:a2:c6:27:6f:1f:bd:73:b8:
+ 8e:f7:94:54:57:a7:8b:5b:9a:24:aa:86:d4:04:5c:8c:cb:28:
+ a2:45:f9:34:f0:01:20:bb:06:e8:41:14:d2:d7:ca:e8:bf:4e:
+ 16:72:22:a0:0c:86:ca:73:23:09:ae:71:f1:52:0c:db:b2:8a:
+ 4d:94:a5:fa:15:81:5b:a2:95:62:50:a1:d6:64:fe:4c:0c:60:
+ 8d:9b:0f:b8:41:ac:cb:31:c2:17:6c:7b:61:13:16:9a:db:64:
+ fc:5f:47:84:3d:d2:2e:db:0b:9e:b6:1e:85:04:c1:e5:c0:b2:
+ 6d:8f:f2:99:00:3a:1a:ab:02:cf:45:7a:26:c1:b0:1f:c6:b0:
+ d0:4d:f7:52
+-----BEGIN CERTIFICATE-----
+MIIDYDCCAkigAwIBAgIUQpdsMI55/Htq4++dGKR0nYtfV1MwDQYJKoZIhvcNAQEL
+BQAwQTELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoMBXcx
+LmZpMRAwDgYDVQQDDAdSb290IENBMB4XDTIwMDUwMjE5NDk0OFoXDTMwMDQzMDE5
+NDk0OFowQTELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAMBgNVBAoM
+BXcxLmZpMRAwDgYDVQQDDAdSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEAvPTuRGJ/Yk+hgUa6xKoe/U7Q7fFHyyVbZnqGOZHKtWGnfi88Y305
+uBqey20yMpHeSUmE2hW+K93GvB/cbsAtd/LQeyxAGQdgVbD/fFHvONHwKtqozOrW
+VKTvvhdEGp4zcFek8wasPe5LLeVGJS0zCfZJqAIxpGWbMgpn9QLhO0emruT2hetd
+PgJm3RGYrDRywo8lVUpq6uiCL71/eDGkWtcyu2RIRiPvyMnihABWcuhLVJViOloR
+ee5AQ54WLMzmRfS7gijCgzUsVTaZWRGxFdADwaU34R+7Q8e0uTPeFNd8mUUPwQb+
+tiUQWbdydn+RS+rRuWpq7d0bqQ6nKUi3TQIDAQABo1AwTjAdBgNVHQ4EFgQUpP25
+ORuBs6rriB3Ugam1EXDMp+EwHwYDVR0jBBgwFoAUpP25ORuBs6rriB3Ugam1EXDM
+p+EwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAQfnDo3cRklXnS0oy
+ajHZUc8GpTnqMJi4jU8kxTT9xpgQWTJ+V/WPumfJ/ERos3zxrzpfDY+h/kEhDukI
+o2NJZjRKzc5mdEcw99yCmSFWgv8tEpB9emQioO36Ytla05eWDASnR4jaU7YzFRX5
+2u6sJekHAom8c6LGJ28fvXO4jveUVFeni1uaJKqG1ARcjMsookX5NPABILsG6EEU
+0tfK6L9OFnIioAyGynMjCa5x8VIM27KKTZSl+hWBW6KVYlCh1mT+TAxgjZsPuEGs
+yzHCF2x7YRMWmttk/F9HhD3SLtsLnrYehQTB5cCybY/ymQA6GqsCz0V6JsGwH8aw
+0E33Ug==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/cacert.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/cacert.pem
new file mode 100644
index 000000000000..f55814817e4a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/cacert.pem
@@ -0,0 +1,81 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cc:f8
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 3 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=User Intermediate CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:b9:88:7a:fc:1a:f9:00:68:63:c7:40:ff:d5:38:
+ 8e:88:8c:c9:8f:66:ec:74:0a:a6:f1:18:30:30:36:
+ 9a:2a:98:c5:a0:46:02:e2:3c:64:86:79:43:45:19:
+ 83:7a:82:3d:f9:c6:af:01:11:91:2c:4f:07:f8:d7:
+ ef:da:80:6c:07:88:6a:1e:e6:0e:78:ca:08:50:4f:
+ f0:8a:2e:54:41:9f:04:63:8b:70:99:ae:6f:95:ed:
+ 5c:c8:34:8e:6b:36:64:bc:44:c9:fb:cb:50:ef:b1:
+ 5b:9b:2c:db:2a:a7:f9:e0:e2:48:57:78:cf:ba:0f:
+ 1a:af:5a:63:64:18:39:9c:d4:af:8d:f9:27:d9:10:
+ b4:67:17:a1:24:98:f1:ef:ce:ad:12:6f:e4:47:36:
+ b6:d2:b6:1c:04:03:76:43:63:fb:b6:3e:3f:1a:c8:
+ c4:8b:69:28:7c:75:dc:bb:36:7f:ad:6a:a2:c1:32:
+ f3:5e:64:86:57:f1:ee:20:af:64:bd:e0:7c:ba:68:
+ 9b:75:ed:b3:1c:0f:12:e0:52:12:ff:18:0e:8f:1d:
+ bf:c8:88:56:35:4d:9e:1f:74:1e:19:d7:0c:b4:e7:
+ 46:ee:cf:c6:63:35:ba:16:7f:05:84:8b:bf:16:72:
+ 05:ee:22:6e:3a:54:80:2b:0e:36:96:8d:65:5f:64:
+ 12:cd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ F0:F7:82:29:71:CD:AF:72:CE:F6:3C:0B:40:16:C2:FD:9F:8A:51:A7
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha256WithRSAEncryption
+ 70:45:66:14:00:22:85:1c:f8:b9:b3:2c:e8:64:4d:01:53:b8:
+ cb:23:ad:fa:01:7c:27:f7:aa:8d:d8:6c:6a:f8:72:21:63:bf:
+ 30:7e:05:8c:84:e3:d1:1e:d1:f3:1d:80:3d:e8:75:06:ae:1b:
+ 48:a9:cf:0e:c6:59:6b:f8:d1:25:5a:64:b7:46:2d:29:72:da:
+ d6:3a:79:d3:92:41:d7:31:e4:4e:5e:1b:62:88:41:77:f6:62:
+ a2:3e:c1:a2:ef:79:0c:8f:39:7c:df:a0:4b:d5:ac:58:aa:3e:
+ fd:95:6b:f7:c0:42:29:2e:86:67:5e:d9:3e:7b:e7:a6:bd:3b:
+ 7e:3b:19:54:9b:89:40:0e:39:23:8a:af:f2:db:12:5b:09:b4:
+ 45:df:c8:3e:8f:fc:fc:55:3e:35:8d:7b:82:50:d5:a3:ea:bb:
+ c4:40:6d:61:ad:92:b2:66:91:0f:5b:3d:49:5e:b5:3e:98:15:
+ 9e:2a:23:06:35:e0:13:bc:50:84:06:e4:1b:b9:fc:32:a2:4a:
+ 0d:e5:86:ac:69:47:c3:17:11:07:ac:5a:09:69:ed:99:d0:52:
+ fd:6d:ab:0d:44:35:bb:c0:76:27:50:75:df:06:78:f6:92:54:
+ fc:54:76:b5:6f:a0:f6:51:20:1f:7f:8e:aa:5f:c8:48:88:e4:
+ 1a:83:f6:b7
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAk6gAwIBAgIJANjT46bL48z4MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDMxNTIwMTBaFw0zMDA1MDMxNTIwMTBaMDwxCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEdMBsGA1UEAwwUVXNlciBJbnRlcm1l
+ZGlhdGUgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5iHr8GvkA
+aGPHQP/VOI6IjMmPZux0CqbxGDAwNpoqmMWgRgLiPGSGeUNFGYN6gj35xq8BEZEs
+Twf41+/agGwHiGoe5g54yghQT/CKLlRBnwRji3CZrm+V7VzINI5rNmS8RMn7y1Dv
+sVubLNsqp/ng4khXeM+6DxqvWmNkGDmc1K+N+SfZELRnF6EkmPHvzq0Sb+RHNrbS
+thwEA3ZDY/u2Pj8ayMSLaSh8ddy7Nn+taqLBMvNeZIZX8e4gr2S94Hy6aJt17bMc
+DxLgUhL/GA6PHb/IiFY1TZ4fdB4Z1wy050buz8ZjNboWfwWEi78WcgXuIm46VIAr
+DjaWjWVfZBLNAgMBAAGjZjBkMB0GA1UdDgQWBBTw94Ipcc2vcs72PAtAFsL9n4pR
+pzAfBgNVHSMEGDAWgBSk/bk5G4GzquuIHdSBqbURcMyn4TASBgNVHRMBAf8ECDAG
+AQH/AgEAMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAcEVmFAAi
+hRz4ubMs6GRNAVO4yyOt+gF8J/eqjdhsavhyIWO/MH4FjITj0R7R8x2APeh1Bq4b
+SKnPDsZZa/jRJVpkt0YtKXLa1jp505JB1zHkTl4bYohBd/Zioj7Bou95DI85fN+g
+S9WsWKo+/ZVr98BCKS6GZ17ZPnvnpr07fjsZVJuJQA45I4qv8tsSWwm0Rd/IPo/8
+/FU+NY17glDVo+q7xEBtYa2SsmaRD1s9SV61PpgVniojBjXgE7xQhAbkG7n8MqJK
+DeWGrGlHwxcRB6xaCWntmdBS/W2rDUQ1u8B2J1B13wZ49pJU/FR2tW+g9lEgH3+O
+ql/ISIjkGoP2tw==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/careq.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/careq.pem
new file mode 100644
index 000000000000..58a202e231dc
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/careq.pem
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIClDCCAXwCAQAwTzELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4w
+DAYDVQQKDAV3MS5maTEdMBsGA1UEAwwUVXNlciBJbnRlcm1lZGlhdGUgQ0EwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5iHr8GvkAaGPHQP/VOI6IjMmP
+Zux0CqbxGDAwNpoqmMWgRgLiPGSGeUNFGYN6gj35xq8BEZEsTwf41+/agGwHiGoe
+5g54yghQT/CKLlRBnwRji3CZrm+V7VzINI5rNmS8RMn7y1DvsVubLNsqp/ng4khX
+eM+6DxqvWmNkGDmc1K+N+SfZELRnF6EkmPHvzq0Sb+RHNrbSthwEA3ZDY/u2Pj8a
+yMSLaSh8ddy7Nn+taqLBMvNeZIZX8e4gr2S94Hy6aJt17bMcDxLgUhL/GA6PHb/I
+iFY1TZ4fdB4Z1wy050buz8ZjNboWfwWEi78WcgXuIm46VIArDjaWjWVfZBLNAgMB
+AAGgADANBgkqhkiG9w0BAQsFAAOCAQEAt/AtU5ZkTH2fksE0NkQ24G2s/3FGSPH1
+wOtQKHUaUXHWeAddimhKOCo2nStyzJ3SYkrkBaGkCf2YDVmDT2FJrEEU/8fhwWgb
+VPdqMHG+tXhzAf6AoqOZ/r/5wGLEvOXuoVlF4Ey+dfYPBpfvJRjOl/xHN7B+b5Pe
+1Q25yWo3ekdeRIWZnJx7b/5xkgSH1blqiSVVlhQ9uOUeBiOIS+CXGBo+kqcGRxm2
+awQRONpQb4dJ2+PEAFMTWHs/WWHpftDx878YafRfrcEx9iCWb4L4FKQo7VgcmgSs
+cErQMDUfGOmRKTXJ6pJAv6O8KdWaDuTiM7o6yo5VggIcUTj2XGkRLQ==
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/index.txt b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/index.txt
new file mode 100644
index 000000000000..df7ada787ca1
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/index.txt
@@ -0,0 +1 @@
+V 300501152011Z 5923F47610CA8942F55C075C62D2678BE42292A9 unknown /C=FI/O=w1.fi/CN=user.w1.fi
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/index.txt.attr b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/index.txt.attr
new file mode 100644
index 000000000000..3a7e39e6ee60
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/index.txt.attr
@@ -0,0 +1 @@
+unique_subject = no
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/private/cakey.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/private/cakey.pem
new file mode 100644
index 000000000000..fb51ae7ea3f4
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/private/cakey.pem
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5iHr8GvkAaGPH
+QP/VOI6IjMmPZux0CqbxGDAwNpoqmMWgRgLiPGSGeUNFGYN6gj35xq8BEZEsTwf4
+1+/agGwHiGoe5g54yghQT/CKLlRBnwRji3CZrm+V7VzINI5rNmS8RMn7y1DvsVub
+LNsqp/ng4khXeM+6DxqvWmNkGDmc1K+N+SfZELRnF6EkmPHvzq0Sb+RHNrbSthwE
+A3ZDY/u2Pj8ayMSLaSh8ddy7Nn+taqLBMvNeZIZX8e4gr2S94Hy6aJt17bMcDxLg
+UhL/GA6PHb/IiFY1TZ4fdB4Z1wy050buz8ZjNboWfwWEi78WcgXuIm46VIArDjaW
+jWVfZBLNAgMBAAECggEAJ3Ghm+lsGK8Yz2q9OSp9+v/bdjZOfNkq5sTasdVZ70Zt
+dYaM5GYshP5Q0+b5sdjwriKUYCjI8V+X9UqLPqvgy7UvwoPsfbeODuz/2ZDB7vWM
+rFEfzjxskrZU7GdoA9kbj38cZgCyo1LUg+gEbEwr7qiM8rPHjenaJX+U89nGndWo
+xbjy+PtpSkcNV38H690w2elxTIz4XBHRFumQ4rmlGaa9rMTKRkeVaV42cAdScwmh
+OR/Cy0XW+x2xyQolBIBRSp7Sn5xtulA1g4iaifVtY8qNQhQ9++TxkId3+VB/7HGJ
+kYmfucUPeTD1SR6yVhhXtmrpTfas0rvzAR2RAxjsbQKBgQDspqiXOGSXJS0XupVR
+Zzpb44306Zr9kobLZoIjLa4igEoJvB3IZTLNMK6UAvbDiQJYAP4Mx0x8hWhyWcb+
+PiXDfWFshlQvLKikt4hLtxBGCoGf4TcR5y2qkOlRjAC+LwgGMQ1q34E5JI1sOo/2
+frB1dYApow9IpC4Svy2QIFkicwKBgQDIs9+laNPoFwHljEF7xRA3QYt2+03ps4gF
+GsO/vb73C0sStS5M9I0MlY44Fk7dtEo6WQCORHlusc0Zr1qli6EdUh5wnR6SMqYZ
+IX3gJiGzu8AQBGTL/fZAzy4YQeiVicJCUeu2MxXhKcqstWC4UUyQgys67by0YvpF
+qn7TtYRlvwKBgQC2YN4u5IQJQ9pTnjTzLlX4eQ9u/xW2dFUzrkV+7PZ1ml70z6g4
+R112ax0v7nTUTuOihOlFWdblZD8RWYUVbTnXRepuI7v/OzCg+NyuVV/SSsiJOZ0i
+TAKSn+lgMkBkUYSimO0ZPzSsoDHphdxrAEnny+1AqWze88CaLAHmQDfRZwKBgHgy
+iWEVm7smdENWMS1/wotlHLIgJPQuSerMsajWaVSolchZa6Y423RK6QacDZVnUQEK
+pnONfLAXmb6lLwNv0QivUn2dC18BKEpqrWkdTRfH/GlbSEaHDQCZU4DBkBpmi0mO
+qDzQ8WyMy82NPqSEQ/dUJwK+uEHL+RjZ1+TQk78fAoGBAJcRCj4OBMHBSKazW/T2
+b8WXps3bEwdDBg8lBj2yv10AN6DtJqhPd4DaEdriRSMaufrPuNEdkMbOHkXGqPM6
+Fayi/ayFGGO20XrfuUHRbLCiF1f8/OZSTOEXFpungFLk2awpTkcsbwQ0GK/BU2H1
+hW28/pW9mlvPCm3HQFE8rMxS
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/serial b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/serial
new file mode 100644
index 000000000000..24ff150cb5a8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/serial
@@ -0,0 +1 @@
+5923F47610CA8942F55C075C62D2678BE42292AA
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.key b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.key
new file mode 100644
index 000000000000..1ede4cd1926e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDUXyPsAcY+0lnu
+381yjUHOQK+GvsKZaTuRo3wt5PWhcjwOuaalofWlv19TmDPCwBgkR0yES57R8kkF
+hS2HVq5DvKGJ10qpZhPHrjVKzC9ztlB+Y7qwpryV8fGaw6mymD7/J5dks1QJB/xu
+xM9/keCXwmMQodrmqX1AuQO+txxGkBBfmgrljAlW076lwUJd9Ezlvv3ZZkP56Tg2
+GmbTO3wRRnkWebjPgRQKTLe+e/KD9IcqHrH/eBwVUyF/dtFVDwi+EQ8Z60Zhrak2
+vLuBvzdtDF/RZ3Gp2iduL5DoO9Brl8Mb582BQA3WLv4kQGeE7eq38ybFIzX5fLVc
++d8fn6CFAgMBAAECggEAHiVvPPY3hP1pJL6CNGuW1sdZ4z+68fn9KbxSSVWCBKvp
+mJGD9WkbLK8QwhYN6uxHwQaZ9wGhBt5kvTLddqO4Uwc4yw9Tmt5RmnvBNt/rMHrF
+zFstyhuxE3vntvdlZGO2NZQSKopGOI34qGSpq8syXXiLhXXkU+/lRsW8oVru2Zkh
+dZMgdOmd9aTNNXA1f4uufOBnlgv0g+VeXAH/k1juybQYj2BilLZFUSkWtRxffm98
+hyMKHkcr4XEdZSyuhAM/yHn0REG//XMrKCoe5snzV9R3BKuJpMVctrePAKlKdIbk
+OJz6EJfT4whDuI9nO0aCIJCKx88Qsgka0JOHVuL0tQKBgQDsi66IONWzR2/K4K+r
+9G38T5bzg3qB5YbXHyqRgU77W13Z2CaEjzB1QfX8pFaPARMJxg+WXupbSq632iTH
+wPMjnSMYc4PeucyvEAudUeP8yc8hx5vVRCIyIyz3QhAB2bo48EKq9dSwsCJ1HrIO
+xtjEU9x+BLl6cgesdVZgl75oBwKBgQDl1n2TPUoxpYkF5cavbs2c7wPXajbd54h8
+EwwF71AWMsngjuY02pzKDniU5tKgHHWXvGdZ5ER+7JJyqenyAQgdfjkKJ4ujqsOL
+MHsLEH1qopWGNtrDxGFfibBNMrKPM2n/oYbYsrfSvKadhwZbwDVq4ZtzmszWqLtf
++weGR4jYEwKBgQCKGN5TLwMsAFe21MgalsAjXn/dOPQro8m+C7b5bcmjm2rGRJfw
+Kfx7aH/o+DSElnb77MKq4kzl8UrhkRyJ9g68yv9zRfVF8akaxz5QoT9+FH+10+gZ
+cQaZyMl2rP3VZrx+g14Ymx6J7LqhL8N6NwLUU7VVaQK0BqCOQY6lI9IIvwKBgHyn
+EARDQXIbrW0dadzL44gxuYujd45yfHuOeP7fBDiF4yd/WSthRZfwsUVQyvs7dCuP
+ax49x0hvVh4KOW+fT59vTdBMElf5zYQ4DwO5NcwX0bCxH4T9hTIjoxK7ZEx2Pg7+
+s/vjMf+BgXv+N1ybql0FbyIL2vyxFq6/nx0cvwMxAoGAMTuKJfxbN8UfQzGWWZ9g
+Q3YQhEwOytLYs3yiymSlUfNlyFGC41zM1Jn/wsx7koWyTdzVYoOR7Dm/yKMwcRrc
+Oqd+04Vn5BF81HgM8rmEUxD5x5WXWALg8pN2r8gb7QUlcjkxMEbfyKSo515JbnYc
+84mcq6qC9A8ksJ/KdFv7dTY=
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.pem
new file mode 100644
index 000000000000..4ed6c06e2d2b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.pem
@@ -0,0 +1,85 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 59:23:f4:76:10:ca:89:42:f5:5c:07:5c:62:d2:67:8b:e4:22:92:a9
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, O=w1.fi, CN=User Intermediate CA
+ Validity
+ Not Before: May 3 15:20:11 2020 GMT
+ Not After : May 1 15:20:11 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=user.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:d4:5f:23:ec:01:c6:3e:d2:59:ee:df:cd:72:8d:
+ 41:ce:40:af:86:be:c2:99:69:3b:91:a3:7c:2d:e4:
+ f5:a1:72:3c:0e:b9:a6:a5:a1:f5:a5:bf:5f:53:98:
+ 33:c2:c0:18:24:47:4c:84:4b:9e:d1:f2:49:05:85:
+ 2d:87:56:ae:43:bc:a1:89:d7:4a:a9:66:13:c7:ae:
+ 35:4a:cc:2f:73:b6:50:7e:63:ba:b0:a6:bc:95:f1:
+ f1:9a:c3:a9:b2:98:3e:ff:27:97:64:b3:54:09:07:
+ fc:6e:c4:cf:7f:91:e0:97:c2:63:10:a1:da:e6:a9:
+ 7d:40:b9:03:be:b7:1c:46:90:10:5f:9a:0a:e5:8c:
+ 09:56:d3:be:a5:c1:42:5d:f4:4c:e5:be:fd:d9:66:
+ 43:f9:e9:38:36:1a:66:d3:3b:7c:11:46:79:16:79:
+ b8:cf:81:14:0a:4c:b7:be:7b:f2:83:f4:87:2a:1e:
+ b1:ff:78:1c:15:53:21:7f:76:d1:55:0f:08:be:11:
+ 0f:19:eb:46:61:ad:a9:36:bc:bb:81:bf:37:6d:0c:
+ 5f:d1:67:71:a9:da:27:6e:2f:90:e8:3b:d0:6b:97:
+ c3:1b:e7:cd:81:40:0d:d6:2e:fe:24:40:67:84:ed:
+ ea:b7:f3:26:c5:23:35:f9:7c:b5:5c:f9:df:1f:9f:
+ a0:85
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 4F:DA:AA:81:CB:4A:79:E4:8B:4A:92:FC:38:41:92:BA:D9:F9:4C:32
+ X509v3 Authority Key Identifier:
+ keyid:F0:F7:82:29:71:CD:AF:72:CE:F6:3C:0B:40:16:C2:FD:9F:8A:51:A7
+
+ X509v3 Subject Alternative Name: critical
+ DNS:user.w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ 86:b3:8b:79:3b:32:a2:34:bb:9b:12:9d:ad:d2:c7:c6:58:cd:
+ 24:14:70:7b:4f:7c:52:9a:36:c1:72:aa:bb:a7:a8:a0:ae:82:
+ ff:ea:9e:14:29:5f:04:82:8f:0a:46:ee:6b:b8:c8:f9:4f:8d:
+ 1a:af:e6:d2:b0:87:4c:f4:a0:f9:c3:1c:cf:16:2e:28:c7:95:
+ 5c:86:a8:15:52:e8:9b:4d:40:6c:b0:82:f9:e5:8e:10:1f:f8:
+ d9:7a:4a:a6:e6:fb:00:ab:13:09:ee:4a:2b:6f:aa:a0:5d:90:
+ e9:89:40:68:fd:1e:99:f1:cf:5d:fb:d4:76:16:6b:76:52:66:
+ 17:77:68:e3:d1:7a:35:17:e3:81:9a:46:bd:c9:44:37:10:c4:
+ a4:13:dd:f6:c9:b8:08:f4:e1:92:18:7f:8c:c5:c9:14:4b:34:
+ 5b:d4:db:46:a3:6b:61:1c:5b:52:b4:24:73:98:ce:b2:5a:f3:
+ 51:72:68:bc:d0:8f:36:5d:16:58:b9:91:2e:e2:6f:09:33:40:
+ 13:f7:ba:8f:b7:36:02:36:1c:0e:c4:db:a2:dc:17:31:dd:6b:
+ 4c:e3:5e:04:ab:d5:30:fd:f6:ba:1a:00:04:ea:4b:88:34:d8:
+ 5e:f2:0a:44:61:05:1c:7b:42:86:7e:42:e8:42:f1:19:a2:48:
+ 28:44:97:3e
+-----BEGIN CERTIFICATE-----
+MIIDhzCCAm+gAwIBAgIUWSP0dhDKiUL1XAdcYtJni+QikqkwDQYJKoZIhvcNAQEL
+BQAwPDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMR0wGwYDVQQDDBRVc2Vy
+IEludGVybWVkaWF0ZSBDQTAeFw0yMDA1MDMxNTIwMTFaFw0zMDA1MDExNTIwMTFa
+MDIxCzAJBgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTETMBEGA1UEAwwKdXNlci53
+MS5maTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANRfI+wBxj7SWe7f
+zXKNQc5Ar4a+wplpO5GjfC3k9aFyPA65pqWh9aW/X1OYM8LAGCRHTIRLntHySQWF
+LYdWrkO8oYnXSqlmE8euNUrML3O2UH5jurCmvJXx8ZrDqbKYPv8nl2SzVAkH/G7E
+z3+R4JfCYxCh2uapfUC5A763HEaQEF+aCuWMCVbTvqXBQl30TOW+/dlmQ/npODYa
+ZtM7fBFGeRZ5uM+BFApMt7578oP0hyoesf94HBVTIX920VUPCL4RDxnrRmGtqTa8
+u4G/N20MX9FncanaJ24vkOg70GuXwxvnzYFADdYu/iRAZ4Tt6rfzJsUjNfl8tVz5
+3x+foIUCAwEAAaOBijCBhzAJBgNVHRMEAjAAMB0GA1UdDgQWBBRP2qqBy0p55ItK
+kvw4QZK62flMMjAfBgNVHSMEGDAWgBTw94Ipcc2vcs72PAtAFsL9n4pRpzAYBgNV
+HREBAf8EDjAMggp1c2VyLncxLmZpMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAsGA1Ud
+DwQEAwIFoDANBgkqhkiG9w0BAQsFAAOCAQEAhrOLeTsyojS7mxKdrdLHxljNJBRw
+e098Upo2wXKqu6eooK6C/+qeFClfBIKPCkbua7jI+U+NGq/m0rCHTPSg+cMczxYu
+KMeVXIaoFVLom01AbLCC+eWOEB/42XpKpub7AKsTCe5KK2+qoF2Q6YlAaP0emfHP
+XfvUdhZrdlJmF3do49F6NRfjgZpGvclENxDEpBPd9sm4CPThkhh/jMXJFEs0W9Tb
+RqNrYRxbUrQkc5jOslrzUXJovNCPNl0WWLmRLuJvCTNAE/e6j7c2AjYcDsTbotwX
+Md1rTONeBKvVMP32uhoABOpLiDTYXvIKRGEFHHtChn5C6ELxGaJIKESXPg==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.req b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.req
new file mode 100644
index 000000000000..5b5256655e1c
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user.req
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICijCCAXICAQAwRTELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4w
+DAYDVQQKDAV3MS5maTETMBEGA1UEAwwKdXNlci53MS5maTCCASIwDQYJKoZIhvcN
+AQEBBQADggEPADCCAQoCggEBANRfI+wBxj7SWe7fzXKNQc5Ar4a+wplpO5GjfC3k
+9aFyPA65pqWh9aW/X1OYM8LAGCRHTIRLntHySQWFLYdWrkO8oYnXSqlmE8euNUrM
+L3O2UH5jurCmvJXx8ZrDqbKYPv8nl2SzVAkH/G7Ez3+R4JfCYxCh2uapfUC5A763
+HEaQEF+aCuWMCVbTvqXBQl30TOW+/dlmQ/npODYaZtM7fBFGeRZ5uM+BFApMt757
+8oP0hyoesf94HBVTIX920VUPCL4RDxnrRmGtqTa8u4G/N20MX9FncanaJ24vkOg7
+0GuXwxvnzYFADdYu/iRAZ4Tt6rfzJsUjNfl8tVz53x+foIUCAwEAAaAAMA0GCSqG
+SIb3DQEBCwUAA4IBAQBXDSMg3STy5dxee9/+DnPa859cH3b3xawbT7RY4j3n/ZCL
+RiB6EqH8L0wSEwTZpF1YqNdjx1weDwxA1eM4esLslcyyCdMTRXVS7QogwuHj+Qo4
+3qqiSFOpJBh7zxdz3Eph/4rr0SdeUefHUyFvKvu7gcS1LwHY0vCGQ3FO6eVLDZl4
+eEMdz6MynkBBj1kjYWnn8jaUraNBqOFKg9ll3S5K9RH3yJZhdhcodiun2S2IaL4E
+evgt2u2Fr9Eka2wXRBlf1F+raSyVsdFY4a3aMzYQes0whGwWpmkMOo/4Ax8TL+co
+SMc3B4yezaS4iypgI9EZThe4/KaidGEqCkyAPOem
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user_and_ica.pem b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user_and_ica.pem
new file mode 100644
index 000000000000..50df34d62bd1
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/iCA-user/user_and_ica.pem
@@ -0,0 +1,166 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ 59:23:f4:76:10:ca:89:42:f5:5c:07:5c:62:d2:67:8b:e4:22:92:a9
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, O=w1.fi, CN=User Intermediate CA
+ Validity
+ Not Before: May 3 15:20:11 2020 GMT
+ Not After : May 1 15:20:11 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=user.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:d4:5f:23:ec:01:c6:3e:d2:59:ee:df:cd:72:8d:
+ 41:ce:40:af:86:be:c2:99:69:3b:91:a3:7c:2d:e4:
+ f5:a1:72:3c:0e:b9:a6:a5:a1:f5:a5:bf:5f:53:98:
+ 33:c2:c0:18:24:47:4c:84:4b:9e:d1:f2:49:05:85:
+ 2d:87:56:ae:43:bc:a1:89:d7:4a:a9:66:13:c7:ae:
+ 35:4a:cc:2f:73:b6:50:7e:63:ba:b0:a6:bc:95:f1:
+ f1:9a:c3:a9:b2:98:3e:ff:27:97:64:b3:54:09:07:
+ fc:6e:c4:cf:7f:91:e0:97:c2:63:10:a1:da:e6:a9:
+ 7d:40:b9:03:be:b7:1c:46:90:10:5f:9a:0a:e5:8c:
+ 09:56:d3:be:a5:c1:42:5d:f4:4c:e5:be:fd:d9:66:
+ 43:f9:e9:38:36:1a:66:d3:3b:7c:11:46:79:16:79:
+ b8:cf:81:14:0a:4c:b7:be:7b:f2:83:f4:87:2a:1e:
+ b1:ff:78:1c:15:53:21:7f:76:d1:55:0f:08:be:11:
+ 0f:19:eb:46:61:ad:a9:36:bc:bb:81:bf:37:6d:0c:
+ 5f:d1:67:71:a9:da:27:6e:2f:90:e8:3b:d0:6b:97:
+ c3:1b:e7:cd:81:40:0d:d6:2e:fe:24:40:67:84:ed:
+ ea:b7:f3:26:c5:23:35:f9:7c:b5:5c:f9:df:1f:9f:
+ a0:85
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 4F:DA:AA:81:CB:4A:79:E4:8B:4A:92:FC:38:41:92:BA:D9:F9:4C:32
+ X509v3 Authority Key Identifier:
+ keyid:F0:F7:82:29:71:CD:AF:72:CE:F6:3C:0B:40:16:C2:FD:9F:8A:51:A7
+
+ X509v3 Subject Alternative Name: critical
+ DNS:user.w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha256WithRSAEncryption
+ 86:b3:8b:79:3b:32:a2:34:bb:9b:12:9d:ad:d2:c7:c6:58:cd:
+ 24:14:70:7b:4f:7c:52:9a:36:c1:72:aa:bb:a7:a8:a0:ae:82:
+ ff:ea:9e:14:29:5f:04:82:8f:0a:46:ee:6b:b8:c8:f9:4f:8d:
+ 1a:af:e6:d2:b0:87:4c:f4:a0:f9:c3:1c:cf:16:2e:28:c7:95:
+ 5c:86:a8:15:52:e8:9b:4d:40:6c:b0:82:f9:e5:8e:10:1f:f8:
+ d9:7a:4a:a6:e6:fb:00:ab:13:09:ee:4a:2b:6f:aa:a0:5d:90:
+ e9:89:40:68:fd:1e:99:f1:cf:5d:fb:d4:76:16:6b:76:52:66:
+ 17:77:68:e3:d1:7a:35:17:e3:81:9a:46:bd:c9:44:37:10:c4:
+ a4:13:dd:f6:c9:b8:08:f4:e1:92:18:7f:8c:c5:c9:14:4b:34:
+ 5b:d4:db:46:a3:6b:61:1c:5b:52:b4:24:73:98:ce:b2:5a:f3:
+ 51:72:68:bc:d0:8f:36:5d:16:58:b9:91:2e:e2:6f:09:33:40:
+ 13:f7:ba:8f:b7:36:02:36:1c:0e:c4:db:a2:dc:17:31:dd:6b:
+ 4c:e3:5e:04:ab:d5:30:fd:f6:ba:1a:00:04:ea:4b:88:34:d8:
+ 5e:f2:0a:44:61:05:1c:7b:42:86:7e:42:e8:42:f1:19:a2:48:
+ 28:44:97:3e
+-----BEGIN CERTIFICATE-----
+MIIDhzCCAm+gAwIBAgIUWSP0dhDKiUL1XAdcYtJni+QikqkwDQYJKoZIhvcNAQEL
+BQAwPDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMR0wGwYDVQQDDBRVc2Vy
+IEludGVybWVkaWF0ZSBDQTAeFw0yMDA1MDMxNTIwMTFaFw0zMDA1MDExNTIwMTFa
+MDIxCzAJBgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTETMBEGA1UEAwwKdXNlci53
+MS5maTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANRfI+wBxj7SWe7f
+zXKNQc5Ar4a+wplpO5GjfC3k9aFyPA65pqWh9aW/X1OYM8LAGCRHTIRLntHySQWF
+LYdWrkO8oYnXSqlmE8euNUrML3O2UH5jurCmvJXx8ZrDqbKYPv8nl2SzVAkH/G7E
+z3+R4JfCYxCh2uapfUC5A763HEaQEF+aCuWMCVbTvqXBQl30TOW+/dlmQ/npODYa
+ZtM7fBFGeRZ5uM+BFApMt7578oP0hyoesf94HBVTIX920VUPCL4RDxnrRmGtqTa8
+u4G/N20MX9FncanaJ24vkOg70GuXwxvnzYFADdYu/iRAZ4Tt6rfzJsUjNfl8tVz5
+3x+foIUCAwEAAaOBijCBhzAJBgNVHRMEAjAAMB0GA1UdDgQWBBRP2qqBy0p55ItK
+kvw4QZK62flMMjAfBgNVHSMEGDAWgBTw94Ipcc2vcs72PAtAFsL9n4pRpzAYBgNV
+HREBAf8EDjAMggp1c2VyLncxLmZpMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAsGA1Ud
+DwQEAwIFoDANBgkqhkiG9w0BAQsFAAOCAQEAhrOLeTsyojS7mxKdrdLHxljNJBRw
+e098Upo2wXKqu6eooK6C/+qeFClfBIKPCkbua7jI+U+NGq/m0rCHTPSg+cMczxYu
+KMeVXIaoFVLom01AbLCC+eWOEB/42XpKpub7AKsTCe5KK2+qoF2Q6YlAaP0emfHP
+XfvUdhZrdlJmF3do49F6NRfjgZpGvclENxDEpBPd9sm4CPThkhh/jMXJFEs0W9Tb
+RqNrYRxbUrQkc5jOslrzUXJovNCPNl0WWLmRLuJvCTNAE/e6j7c2AjYcDsTbotwX
+Md1rTONeBKvVMP32uhoABOpLiDTYXvIKRGEFHHtChn5C6ELxGaJIKESXPg==
+-----END CERTIFICATE-----
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cc:f8
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 3 15:20:10 2020 GMT
+ Not After : May 3 15:20:10 2030 GMT
+ Subject: C=FI, O=w1.fi, CN=User Intermediate CA
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:b9:88:7a:fc:1a:f9:00:68:63:c7:40:ff:d5:38:
+ 8e:88:8c:c9:8f:66:ec:74:0a:a6:f1:18:30:30:36:
+ 9a:2a:98:c5:a0:46:02:e2:3c:64:86:79:43:45:19:
+ 83:7a:82:3d:f9:c6:af:01:11:91:2c:4f:07:f8:d7:
+ ef:da:80:6c:07:88:6a:1e:e6:0e:78:ca:08:50:4f:
+ f0:8a:2e:54:41:9f:04:63:8b:70:99:ae:6f:95:ed:
+ 5c:c8:34:8e:6b:36:64:bc:44:c9:fb:cb:50:ef:b1:
+ 5b:9b:2c:db:2a:a7:f9:e0:e2:48:57:78:cf:ba:0f:
+ 1a:af:5a:63:64:18:39:9c:d4:af:8d:f9:27:d9:10:
+ b4:67:17:a1:24:98:f1:ef:ce:ad:12:6f:e4:47:36:
+ b6:d2:b6:1c:04:03:76:43:63:fb:b6:3e:3f:1a:c8:
+ c4:8b:69:28:7c:75:dc:bb:36:7f:ad:6a:a2:c1:32:
+ f3:5e:64:86:57:f1:ee:20:af:64:bd:e0:7c:ba:68:
+ 9b:75:ed:b3:1c:0f:12:e0:52:12:ff:18:0e:8f:1d:
+ bf:c8:88:56:35:4d:9e:1f:74:1e:19:d7:0c:b4:e7:
+ 46:ee:cf:c6:63:35:ba:16:7f:05:84:8b:bf:16:72:
+ 05:ee:22:6e:3a:54:80:2b:0e:36:96:8d:65:5f:64:
+ 12:cd
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Subject Key Identifier:
+ F0:F7:82:29:71:CD:AF:72:CE:F6:3C:0B:40:16:C2:FD:9F:8A:51:A7
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ X509v3 Basic Constraints: critical
+ CA:TRUE, pathlen:0
+ X509v3 Key Usage: critical
+ Certificate Sign, CRL Sign
+ Signature Algorithm: sha256WithRSAEncryption
+ 70:45:66:14:00:22:85:1c:f8:b9:b3:2c:e8:64:4d:01:53:b8:
+ cb:23:ad:fa:01:7c:27:f7:aa:8d:d8:6c:6a:f8:72:21:63:bf:
+ 30:7e:05:8c:84:e3:d1:1e:d1:f3:1d:80:3d:e8:75:06:ae:1b:
+ 48:a9:cf:0e:c6:59:6b:f8:d1:25:5a:64:b7:46:2d:29:72:da:
+ d6:3a:79:d3:92:41:d7:31:e4:4e:5e:1b:62:88:41:77:f6:62:
+ a2:3e:c1:a2:ef:79:0c:8f:39:7c:df:a0:4b:d5:ac:58:aa:3e:
+ fd:95:6b:f7:c0:42:29:2e:86:67:5e:d9:3e:7b:e7:a6:bd:3b:
+ 7e:3b:19:54:9b:89:40:0e:39:23:8a:af:f2:db:12:5b:09:b4:
+ 45:df:c8:3e:8f:fc:fc:55:3e:35:8d:7b:82:50:d5:a3:ea:bb:
+ c4:40:6d:61:ad:92:b2:66:91:0f:5b:3d:49:5e:b5:3e:98:15:
+ 9e:2a:23:06:35:e0:13:bc:50:84:06:e4:1b:b9:fc:32:a2:4a:
+ 0d:e5:86:ac:69:47:c3:17:11:07:ac:5a:09:69:ed:99:d0:52:
+ fd:6d:ab:0d:44:35:bb:c0:76:27:50:75:df:06:78:f6:92:54:
+ fc:54:76:b5:6f:a0:f6:51:20:1f:7f:8e:aa:5f:c8:48:88:e4:
+ 1a:83:f6:b7
+-----BEGIN CERTIFICATE-----
+MIIDZjCCAk6gAwIBAgIJANjT46bL48z4MA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDMxNTIwMTBaFw0zMDA1MDMxNTIwMTBaMDwxCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEdMBsGA1UEAwwUVXNlciBJbnRlcm1l
+ZGlhdGUgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5iHr8GvkA
+aGPHQP/VOI6IjMmPZux0CqbxGDAwNpoqmMWgRgLiPGSGeUNFGYN6gj35xq8BEZEs
+Twf41+/agGwHiGoe5g54yghQT/CKLlRBnwRji3CZrm+V7VzINI5rNmS8RMn7y1Dv
+sVubLNsqp/ng4khXeM+6DxqvWmNkGDmc1K+N+SfZELRnF6EkmPHvzq0Sb+RHNrbS
+thwEA3ZDY/u2Pj8ayMSLaSh8ddy7Nn+taqLBMvNeZIZX8e4gr2S94Hy6aJt17bMc
+DxLgUhL/GA6PHb/IiFY1TZ4fdB4Z1wy050buz8ZjNboWfwWEi78WcgXuIm46VIAr
+DjaWjWVfZBLNAgMBAAGjZjBkMB0GA1UdDgQWBBTw94Ipcc2vcs72PAtAFsL9n4pR
+pzAfBgNVHSMEGDAWgBSk/bk5G4GzquuIHdSBqbURcMyn4TASBgNVHRMBAf8ECDAG
+AQH/AgEAMA4GA1UdDwEB/wQEAwIBBjANBgkqhkiG9w0BAQsFAAOCAQEAcEVmFAAi
+hRz4ubMs6GRNAVO4yyOt+gF8J/eqjdhsavhyIWO/MH4FjITj0R7R8x2APeh1Bq4b
+SKnPDsZZa/jRJVpkt0YtKXLa1jp505JB1zHkTl4bYohBd/Zioj7Bou95DI85fN+g
+S9WsWKo+/ZVr98BCKS6GZ17ZPnvnpr07fjsZVJuJQA45I4qv8tsSWwm0Rd/IPo/8
+/FU+NY17glDVo+q7xEBtYa2SsmaRD1s9SV61PpgVniojBjXgE7xQhAbkG7n8MqJK
+DeWGrGlHwxcRB6xaCWntmdBS/W2rDUQ1u8B2J1B13wZ49pJU/FR2tW+g9lEgH3+O
+ql/ISIjkGoP2tw==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ica-generate.sh b/contrib/wpa/tests/hwsim/auth_serv/ica-generate.sh
new file mode 100755
index 000000000000..d3fe7b96458f
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ica-generate.sh
@@ -0,0 +1,87 @@
+#!/bin/sh
+
+OPENSSL=openssl
+
+echo
+echo "---[ Intermediate CA - Server ]-----------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/ec-ca/rootCA/" |
+ sed "s/#@CN@/commonName_default = Server Intermediate CA/" \
+ > openssl.cnf.tmp
+mkdir -p iCA-server/certs iCA-server/crl iCA-server/newcerts iCA-server/private
+touch iCA-server/index.txt
+$OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout iCA-server/private/cakey.pem -out iCA-server/careq.pem -outform PEM -days 3652 -sha256
+$OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out iCA-server/cacert.pem -days 3652 -batch -keyfile ca-key.pem -cert ca.pem -extensions v3_ca -outdir rootCA/newcerts -infiles iCA-server/careq.pem
+cat iCA-server/cacert.pem ca.pem > iCA-server/ca-and-root.pem
+rm openssl.cnf.tmp
+
+echo
+echo "---[ Intermediate CA - User ]-------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/ec-ca/rootCA/" |
+ sed "s/#@CN@/commonName_default = User Intermediate CA/" \
+ > openssl.cnf.tmp
+mkdir -p iCA-user/certs iCA-user/crl iCA-user/newcerts iCA-user/private
+touch iCA-user/index.txt
+$OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout iCA-user/private/cakey.pem -out iCA-user/careq.pem -outform PEM -days 3652 -sha256
+$OPENSSL ca -config openssl.cnf.tmp -md sha256 -create_serial -out iCA-user/cacert.pem -days 3652 -batch -keyfile ca-key.pem -cert ca.pem -extensions v3_ca -outdir rootCA/newcerts -infiles iCA-user/careq.pem
+cat iCA-user/cacert.pem ca.pem > iCA-user/ca-and-root.pem
+rm openssl.cnf.tmp
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/ec-ca/iCA-server/" |
+ sed "s/#@CN@/commonName_default = server.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,DNS:server.w1.fi/" \
+ > openssl.cnf.tmp
+$OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout iCA-server/server.key -out iCA-server/server.req -outform PEM -sha256
+$OPENSSL ca -config openssl.cnf.tmp -batch -keyfile iCA-server/private/cakey.pem -cert iCA-server/cacert.pem -create_serial -in iCA-server/server.req -out iCA-server/server.pem -extensions ext_server -md sha256
+cat iCA-server/cacert.pem iCA-server/server.pem > iCA-server/server_and_ica.pem
+rm openssl.cnf.tmp
+
+echo
+echo "---[ Server - revoked ]-------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/ec-ca/iCA-server/" |
+ sed "s/#@CN@/commonName_default = server-revoked.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,DNS:server-revoked.w1.fi/" \
+ > openssl.cnf.tmp
+$OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout iCA-server/server-revoked.key -out iCA-server/server-revoked.req -outform PEM -sha256
+$OPENSSL ca -config openssl.cnf.tmp -batch -keyfile iCA-server/private/cakey.pem -cert iCA-server/cacert.pem -create_serial -in iCA-server/server-revoked.req -out iCA-server/server-revoked.pem -extensions ext_server -md sha256
+$OPENSSL ca -config openssl.cnf.tmp -revoke iCA-server/server-revoked.pem -keyfile iCA-server/private/cakey.pem -cert iCA-server/cacert.pem
+cat iCA-server/cacert.pem iCA-server/server-revoked.pem > iCA-server/server-revoked_and_ica.pem
+rm openssl.cnf.tmp
+
+echo
+echo "---[ User ]-----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/ec-ca/iCA-user/" |
+ sed "s/#@CN@/commonName_default = user.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,DNS:user.w1.fi/" \
+ > openssl.cnf.tmp
+$OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout iCA-user/user.key -out iCA-user/user.req -outform PEM -sha256
+$OPENSSL ca -config openssl.cnf.tmp -batch -keyfile iCA-user/private/cakey.pem -cert iCA-user/cacert.pem -create_serial -in iCA-user/user.req -out iCA-user/user.pem -extensions ext_client -md sha256
+cat iCA-user/user.pem iCA-user/cacert.pem > iCA-user/user_and_ica.pem
+rm openssl.cnf.tmp
+
+echo
+echo "---[ Verify ]-----------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile ca.pem iCA-server/cacert.pem
+$OPENSSL verify -CAfile ca.pem iCA-user/cacert.pem
+$OPENSSL verify -CAfile ca.pem -untrusted iCA-server/cacert.pem iCA-server/server.pem
+$OPENSSL verify -CAfile ca.pem -untrusted iCA-server/cacert.pem iCA-server/server-revoked.pem
+$OPENSSL verify -CAfile ca.pem iCA-user/cacert.pem
+$OPENSSL verify -CAfile ca.pem -untrusted iCA-user/cacert.pem iCA-user/user.pem
diff --git a/contrib/wpa/tests/hwsim/auth_serv/index-revoked.txt b/contrib/wpa/tests/hwsim/auth_serv/index-revoked.txt
new file mode 100644
index 000000000000..c58b7a413740
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/index-revoked.txt
@@ -0,0 +1,8 @@
+V 230627164122Z D8D3E3A6CBE3CCC1 unknown /C=FI/O=w1.fi/CN=Root CA
+V 150215075930Z D8D3E3A6CBE3CCC9 unknown /C=FI/O=w1.fi/CN=server3.w1.fi
+V 140102000000Z D8D3E3A6CBE3CCCA unknown /C=FI/O=w1.fi/CN=server4.w1.fi
+V 150215083008Z D8D3E3A6CBE3CCCB unknown /C=FI/O=w1.fi/CN=server5.w1.fi
+V 150228224144Z D8D3E3A6CBE3CCCC unknown /C=FI/O=w1.fi/CN=server6.w1.fi
+V 160111185024Z D8D3E3A6CBE3CCCD unknown /C=FI/O=w1.fi/CN=ocsp.w1.fi
+R 150929211300Z 160111185024Z D8D3E3A6CBE3CCD1 unknown /C=FI/O=w1.fi/CN=Test User
+R 210502195538Z 160111185024Z D8D3E3A6CBE3CD5F unknown /C=FI/O=w1.fi/CN=server.w1.fi
diff --git a/contrib/wpa/tests/hwsim/auth_serv/index-unknown.txt b/contrib/wpa/tests/hwsim/auth_serv/index-unknown.txt
new file mode 100644
index 000000000000..97dfbbaa61d1
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/index-unknown.txt
@@ -0,0 +1 @@
+V 230627164122Z D8D3E3A6CBE3CCC1 unknown /C=FI/O=w1.fi/CN=Root CA
diff --git a/contrib/wpa/tests/hwsim/auth_serv/index.txt b/contrib/wpa/tests/hwsim/auth_serv/index.txt
new file mode 100644
index 000000000000..090cb9235bcf
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/index.txt
@@ -0,0 +1,8 @@
+V 230627164122Z D8D3E3A6CBE3CCC1 unknown /C=FI/O=w1.fi/CN=Root CA
+V 150215075930Z D8D3E3A6CBE3CCC9 unknown /C=FI/O=w1.fi/CN=server3.w1.fi
+V 140102000000Z D8D3E3A6CBE3CCCA unknown /C=FI/O=w1.fi/CN=server4.w1.fi
+V 150215083008Z D8D3E3A6CBE3CCCB unknown /C=FI/O=w1.fi/CN=server5.w1.fi
+V 150228224144Z D8D3E3A6CBE3CCCC unknown /C=FI/O=w1.fi/CN=server6.w1.fi
+V 160111185024Z D8D3E3A6CBE3CCCD unknown /C=FI/O=w1.fi/CN=ocsp.w1.fi
+V 150929211300Z D8D3E3A6CBE3CCD1 unknown /C=FI/O=w1.fi/CN=Test User
+V 210502195538Z D8D3E3A6CBE3CD5F unknown /C=FI/O=w1.fi/CN=server.w1.fi
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ocsp-multi-server-cache.der b/contrib/wpa/tests/hwsim/auth_serv/ocsp-multi-server-cache.der
new file mode 100644
index 000000000000..15ea6647d812
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/ocsp-multi-server-cache.der differ
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ocsp-req.der b/contrib/wpa/tests/hwsim/auth_serv/ocsp-req.der
new file mode 100644
index 000000000000..ebab4a025204
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/ocsp-req.der differ
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.csr b/contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.csr
new file mode 100644
index 000000000000..d00550cdd0b1
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICiTCCAXECAQAwRDELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAM
+BgNVBAoMBXcxLmZpMRMwEQYDVQQDDApvY3NwLncxLmZpMIIBIjANBgkqhkiG9w0B
+AQEFAAOCAQ8AMIIBCgKCAQEApyKFXbs7HAKaAXOqiGNuoUMztf0C/h/IIwSLjFFS
+W+DMYmAqqrqIT0E5a/s0wKJ6dy7hVp7vyq5n1fmLX6uuh+GwFCMDrWlL3yuRcHXN
+v2VAEtDrL4nwQRUb7UwNOpKWkfjNVDgV62tFn1KFfk4Vq05L6yLCXb7/Nm9CJ9hL
+xuG++AjPrP1RMKLogBMABbYFVUIL+h5AhFAJjCU1VCVFOZ9OfArZxEoMFk4+aH8b
+rclTCFy4BNTWk2L9r/m0HfSWPpudFG0cbCOuPce0zzGQIGqAmWJ+XOnV1b1ZTaPZ
+3On76Htlh9X5SZ6+DvOpId6U6FT8gM/a44+axkx2GA7+qwIDAQABoAAwDQYJKoZI
+hvcNAQELBQADggEBAE/iM0/mhspobneVqSBhCrM2n0KUozbLRBZXfc8hCMW85XPI
+kD7bJdTwndj6wGAd2G4IQr4jeR4tGUU6XAYEsyIVfFlHlBQaUjF9EJmnqqwDAlN3
+v6em8QEv49EL2HO0Q1MFsly2CUk07WYpy0ll5wTjEXIEQ/2J9jNJfgPDs06IQAQi
+9WkFCfBogTn23ZRxomYqukqbirHxGJ2XFRM/LkssyIkMi0jEWzljXYiuzhuD/KtP
+hXXYXcJdL3WdZU9FZw/na4pBrtCTscluwaTDEaW4k60ge/ne51pB32RfsF4aEfsx
+/Xrxva+5dZexgMxK078QL2q7o43HprVa1U/wBfg=
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.key b/contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.key
new file mode 100644
index 000000000000..f5dc4e822368
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCnIoVduzscApoB
+c6qIY26hQzO1/QL+H8gjBIuMUVJb4MxiYCqquohPQTlr+zTAonp3LuFWnu/KrmfV
++Ytfq66H4bAUIwOtaUvfK5Fwdc2/ZUAS0OsvifBBFRvtTA06kpaR+M1UOBXra0Wf
+UoV+ThWrTkvrIsJdvv82b0In2EvG4b74CM+s/VEwouiAEwAFtgVVQgv6HkCEUAmM
+JTVUJUU5n058CtnESgwWTj5ofxutyVMIXLgE1NaTYv2v+bQd9JY+m50UbRxsI649
+x7TPMZAgaoCZYn5c6dXVvVlNo9nc6fvoe2WH1flJnr4O86kh3pToVPyAz9rjj5rG
+THYYDv6rAgMBAAECggEAEyuNeoPIMt1Fhtcaf0xQWyTXII+lsTo5/XI/A1gshydQ
+qhP3sN92VQjZKj6E/Xdlbpgs9n+CZ4/7jvpxdwa9HQ7Q4G5ntJM4RZ+8rdaFQ+e8
+Iqxd3XUH3p8qNdycQ9Seep28B2XrdbY3JSAU+bjBGYYAhTbWbmRC5555SxKvFl+M
+xtSbujwAEgDYpvBYpiqBf2lfglQ/UgY8xmGrAwxhEAuNTYZj8MCsMFM6s0iwq0oo
+UhpXKIVtcrlujXrQJpEfZjsqOLTPe/Jw85CW3upJuSewAPC4zX8adSv62ZMHOQXZ
+StPh1vOuA9dcC2dJCf4LCuyPDjhTnS+s/fc10kV9qQKBgQDXp99f7YPB3dEdZC5S
+Lf5dDn+7r2QrxIiky+iLgsC4SrvEGr997TRmUrBYj+HnLKjgB5qLVZwtWYhWYW04
+ly/J08croMU2C6q5iqYUvMmW65T2zsNkII4ztvKp7zYX9UnIqS3AsuvqUmFFPb1B
+o/VWvBJ+xYcb1zFyqDr2lxV4bQKBgQDGZuXzyfkxH8WuX3XxSlhYfFVwWU+q5LX2
+scg2Rm4vQ6rtaaYIznKN/jaSFanXbf48b6glkkmn9fNERktyqK3p90rOkM6MEXb7
+61+pJdAPs9DD1fi2gw/KtLEkZqPymnO/BlJJbkBbm8Co+w5oRANLa/4J+3eLibf0
+6MN7kimUdwKBgQDTqF2iRvkkE1MkZ6jW23FlX8+aI8BK/K+oHsFz+7auqhqzlBUR
+wPfG3a1anoz3WWu9xXi2/CU2lUMslJ6gBjLPAd3fQgGM09KSHDR48flg+ILR4YkA
+ArvOoeZ1RuRuiz4JhZH0KSdGaegyDzBq9kLbB+eXKMM8Xe6YO+jzEMHv2QKBgFzu
+0gOxtcHm6gfVuz883ckE5Fht3T1lSD6349pYf0AwaB4xAI7bdRlB3HntH9NDOHVC
+r/Z5YXsFX9+5NZoNnPkc1rOPbNB7VcqG5BYtGhpg1gcFcSy8k2cV4Gv2kBERe+oc
+oeq3c/n1KPd+Ma9xPEHV4fb3DXYVGk/jv71gJ43dAoGAcV2MV2vaH657r6cK+Ddh
+8GUw6eSBDfK80Q0BQ2vRsAunE4pKPwYDo60eIKAhhQAol3OVDNf67ItGLX9Mc+yQ
+pXoadPaEkFWgYR5xaUzsVJomtrb9xa3VBhOsVvCZZtNhP8PNcnAA+sfZCNysbWlX
+yIWM7r1ekF8uEPTM5XLtZY4=
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.pem b/contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.pem
new file mode 100644
index 000000000000..778f1b8f6734
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/ocsp-responder.pem
@@ -0,0 +1,76 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cd:67
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 3 14:01:18 2020 GMT
+ Not After : May 3 14:01:18 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=ocsp.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:a7:22:85:5d:bb:3b:1c:02:9a:01:73:aa:88:63:
+ 6e:a1:43:33:b5:fd:02:fe:1f:c8:23:04:8b:8c:51:
+ 52:5b:e0:cc:62:60:2a:aa:ba:88:4f:41:39:6b:fb:
+ 34:c0:a2:7a:77:2e:e1:56:9e:ef:ca:ae:67:d5:f9:
+ 8b:5f:ab:ae:87:e1:b0:14:23:03:ad:69:4b:df:2b:
+ 91:70:75:cd:bf:65:40:12:d0:eb:2f:89:f0:41:15:
+ 1b:ed:4c:0d:3a:92:96:91:f8:cd:54:38:15:eb:6b:
+ 45:9f:52:85:7e:4e:15:ab:4e:4b:eb:22:c2:5d:be:
+ ff:36:6f:42:27:d8:4b:c6:e1:be:f8:08:cf:ac:fd:
+ 51:30:a2:e8:80:13:00:05:b6:05:55:42:0b:fa:1e:
+ 40:84:50:09:8c:25:35:54:25:45:39:9f:4e:7c:0a:
+ d9:c4:4a:0c:16:4e:3e:68:7f:1b:ad:c9:53:08:5c:
+ b8:04:d4:d6:93:62:fd:af:f9:b4:1d:f4:96:3e:9b:
+ 9d:14:6d:1c:6c:23:ae:3d:c7:b4:cf:31:90:20:6a:
+ 80:99:62:7e:5c:e9:d5:d5:bd:59:4d:a3:d9:dc:e9:
+ fb:e8:7b:65:87:d5:f9:49:9e:be:0e:f3:a9:21:de:
+ 94:e8:54:fc:80:cf:da:e3:8f:9a:c6:4c:76:18:0e:
+ fe:ab
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Key Usage:
+ Digital Signature, Non Repudiation, Key Encipherment
+ X509v3 Extended Key Usage:
+ OCSP Signing
+ Signature Algorithm: sha256WithRSAEncryption
+ 5d:f3:28:20:86:b7:cd:da:e2:e8:15:7a:97:52:79:63:69:0b:
+ 92:96:53:89:69:a5:79:19:d1:7e:75:71:9c:e4:33:26:99:cc:
+ b9:fe:28:1a:40:a7:5f:83:ee:51:cd:fc:e4:cf:71:45:90:ba:
+ 36:25:51:37:4c:19:9f:0e:fc:36:d5:64:05:8e:10:20:aa:53:
+ 1e:e5:49:64:ae:54:7d:f3:51:a1:31:af:5f:30:46:5c:d0:db:
+ 6d:fc:07:68:7e:63:26:24:82:52:cd:e0:3e:d1:fd:9b:e8:00:
+ 93:e7:94:8c:d6:14:51:23:82:3b:51:ac:39:3d:6f:81:c7:ff:
+ fb:7a:92:eb:ec:c4:7e:0b:e6:16:5c:31:5f:a1:84:28:b3:ad:
+ 75:8c:c3:c6:0c:b2:1a:23:4d:6c:a5:c7:e4:47:aa:5c:0d:ab:
+ 75:40:a2:bd:9a:76:cb:50:ff:18:8c:c1:c0:bd:02:dd:51:1d:
+ d3:64:43:2c:a6:a8:40:42:c5:90:59:4c:76:56:a8:28:4d:df:
+ 2d:8f:99:c3:2a:a9:f2:cc:5a:90:fc:29:6b:8e:f0:8e:89:79:
+ c1:b1:70:8b:2e:cb:98:d6:cf:46:ed:1a:c4:f7:32:78:5d:ca:
+ b1:0c:5a:05:99:45:f1:1a:80:48:1d:4f:83:7f:30:e9:ca:8f:
+ 83:ff:f3:0b
+-----BEGIN CERTIFICATE-----
+MIIDJTCCAg2gAwIBAgIJANjT46bL481nMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDMxNDAxMThaFw0yMTA1MDMxNDAxMThaMDIxCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTETMBEGA1UEAwwKb2NzcC53MS5maTCC
+ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKcihV27OxwCmgFzqohjbqFD
+M7X9Av4fyCMEi4xRUlvgzGJgKqq6iE9BOWv7NMCiencu4Vae78quZ9X5i1+rrofh
+sBQjA61pS98rkXB1zb9lQBLQ6y+J8EEVG+1MDTqSlpH4zVQ4FetrRZ9ShX5OFatO
+S+siwl2+/zZvQifYS8bhvvgIz6z9UTCi6IATAAW2BVVCC/oeQIRQCYwlNVQlRTmf
+TnwK2cRKDBZOPmh/G63JUwhcuATU1pNi/a/5tB30lj6bnRRtHGwjrj3HtM8xkCBq
+gJliflzp1dW9WU2j2dzp++h7ZYfV+Umevg7zqSHelOhU/IDP2uOPmsZMdhgO/qsC
+AwEAAaMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwEwYDVR0lBAwwCgYIKwYB
+BQUHAwkwDQYJKoZIhvcNAQELBQADggEBAF3zKCCGt83a4ugVepdSeWNpC5KWU4lp
+pXkZ0X51cZzkMyaZzLn+KBpAp1+D7lHN/OTPcUWQujYlUTdMGZ8O/DbVZAWOECCq
+Ux7lSWSuVH3zUaExr18wRlzQ2238B2h+YyYkglLN4D7R/ZvoAJPnlIzWFFEjgjtR
+rDk9b4HH//t6kuvsxH4L5hZcMV+hhCizrXWMw8YMshojTWylx+RHqlwNq3VAor2a
+dstQ/xiMwcC9At1RHdNkQyymqEBCxZBZTHZWqChN3y2PmcMqqfLMWpD8KWuO8I6J
+ecGxcIsuy5jWz0btGsT3MnhdyrEMWgWZRfEagEgdT4N/MOnKj4P/8ws=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ocsp-server-cache.der b/contrib/wpa/tests/hwsim/auth_serv/ocsp-server-cache.der
new file mode 100644
index 000000000000..a1661ef668f3
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/ocsp-server-cache.der differ
diff --git a/contrib/wpa/tests/hwsim/auth_serv/ocsp-server-cache.der-invalid b/contrib/wpa/tests/hwsim/auth_serv/ocsp-server-cache.der-invalid
new file mode 100644
index 000000000000..218bd035a34d
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/ocsp-server-cache.der-invalid differ
diff --git a/contrib/wpa/tests/hwsim/auth_serv/openssl2.cnf b/contrib/wpa/tests/hwsim/auth_serv/openssl2.cnf
new file mode 100644
index 000000000000..5c67c4f04977
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/openssl2.cnf
@@ -0,0 +1,147 @@
+HOME = .
+RANDFILE = $ENV::HOME/.rnd
+oid_section = new_oids
+
+[ new_oids ]
+
+[ ca ]
+default_ca = CA_default
+
+[ CA_default ]
+
+dir = ./test-ca
+certs = $dir/certs
+crl_dir = $dir/crl
+database = $dir/index.txt
+unique_subject = no
+new_certs_dir = $dir/newcerts
+certificate = $dir/cacert.pem
+serial = $dir/serial
+crlnumber = $dir/crlnumber
+crl = $dir/crl.pem
+private_key = $dir/private/cakey.pem
+RANDFILE = $dir/private/.rand
+
+x509_extensions = usr_cert
+
+name_opt = ca_default
+cert_opt = ca_default
+
+default_days = 365
+default_crl_days= 30
+default_md = default
+preserve = no
+
+policy = policy_match
+
+[ policy_match ]
+countryName = match
+stateOrProvinceName = optional
+organizationName = match
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ policy_anything ]
+countryName = optional
+stateOrProvinceName = optional
+localityName = optional
+organizationName = optional
+organizationalUnitName = optional
+commonName = supplied
+emailAddress = optional
+
+[ req ]
+default_bits = 2048
+default_keyfile = privkey.pem
+distinguished_name = req_distinguished_name
+attributes = req_attributes
+x509_extensions = v3_ca
+
+string_mask = utf8only
+
+[ req_distinguished_name ]
+countryName = Country Name (2 letter code)
+countryName_default = FI
+countryName_min = 2
+countryName_max = 2
+
+localityName = Locality Name (eg, city)
+localityName_default = Tuusula
+
+0.organizationName = Organization Name (eg, company)
+0.organizationName_default = w1.fi
+
+commonName = Common Name (e.g. server FQDN or YOUR name)
+#@CN@
+commonName_max = 64
+
+emailAddress = Email Address
+emailAddress_max = 64
+
+##0.subjectAltName = dNSName:server.w1.fi
+
+[ req_attributes ]
+
+[ usr_cert ]
+
+basicConstraints=CA:FALSE
+
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+
+authorityInfoAccess = OCSP;URI:http://server.w1.fi:8888/
+
+[ v3_req ]
+
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+subjectAltName=DNS:example.com,DNS:another.example.com
+
+[ v3_ca ]
+
+subjectKeyIdentifier=hash
+
+authorityKeyIdentifier=keyid:always,issuer
+
+basicConstraints = CA:true
+
+[ crl_ext ]
+
+authorityKeyIdentifier=keyid:always
+
+[ v3_OCSP ]
+basicConstraints = CA:FALSE
+keyUsage = nonRepudiation, digitalSignature, keyEncipherment
+extendedKeyUsage = OCSPSigning
+
+[ ext_client ]
+
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:http://server.w1.fi:8888/
+#@ALTNAME@
+
+extendedKeyUsage = clientAuth
+
+[ ext_server ]
+
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:http://server.w1.fi:8888/
+#@ALTNAME@
+#@CERTPOL@
+
+extendedKeyUsage = serverAuth
+
+[ ext_client_server ]
+
+basicConstraints=CA:FALSE
+subjectKeyIdentifier=hash
+authorityKeyIdentifier=keyid,issuer
+authorityInfoAccess = OCSP;URI:http://server.w1.fi:8888/
+#@ALTNAME@
+
+extendedKeyUsage = clientAuth, serverAuth
diff --git a/contrib/wpa/tests/hwsim/auth_serv/radius_clients.conf b/contrib/wpa/tests/hwsim/auth_serv/radius_clients.conf
new file mode 100644
index 000000000000..7e340152d2a8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/radius_clients.conf
@@ -0,0 +1 @@
+0.0.0.0/0 radius
diff --git a/contrib/wpa/tests/hwsim/auth_serv/radius_clients_ipv6.conf b/contrib/wpa/tests/hwsim/auth_serv/radius_clients_ipv6.conf
new file mode 100644
index 000000000000..8723efcb677c
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/radius_clients_ipv6.conf
@@ -0,0 +1 @@
+::1 radius
diff --git a/contrib/wpa/tests/hwsim/auth_serv/radius_clients_none.conf b/contrib/wpa/tests/hwsim/auth_serv/radius_clients_none.conf
new file mode 100644
index 000000000000..f671e5936ef9
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/radius_clients_none.conf
@@ -0,0 +1,4 @@
+1.2.3.4 foo
+#
+
+2.3.4.5/32 bar
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rootCA/index.txt b/contrib/wpa/tests/hwsim/auth_serv/rootCA/index.txt
new file mode 100644
index 000000000000..7f364381bf33
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rootCA/index.txt
@@ -0,0 +1,6 @@
+V 251222193736Z D8D3E3A6CBE3CCF3 unknown /C=FI/O=w1.fi/CN=Server Intermediate CA
+V 251222193736Z D8D3E3A6CBE3CCF4 unknown /C=FI/O=w1.fi/CN=User Intermediate CA
+V 300503151922Z D8D3E3A6CBE3CCF5 unknown /C=FI/O=w1.fi/CN=Server Intermediate CA
+V 300503151922Z D8D3E3A6CBE3CCF6 unknown /C=FI/O=w1.fi/CN=User Intermediate CA
+V 300503152010Z D8D3E3A6CBE3CCF7 unknown /C=FI/O=w1.fi/CN=Server Intermediate CA
+V 300503152010Z D8D3E3A6CBE3CCF8 unknown /C=FI/O=w1.fi/CN=User Intermediate CA
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rootCA/index.txt.attr b/contrib/wpa/tests/hwsim/auth_serv/rootCA/index.txt.attr
new file mode 100644
index 000000000000..3a7e39e6ee60
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rootCA/index.txt.attr
@@ -0,0 +1 @@
+unique_subject = no
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rootCA/serial b/contrib/wpa/tests/hwsim/auth_serv/rootCA/serial
new file mode 100644
index 000000000000..4c71e29e21ca
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rootCA/serial
@@ -0,0 +1 @@
+D8D3E3A6CBE3CCF9
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-ca.key b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-ca.key
new file mode 100644
index 000000000000..023409c24140
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-ca.key
@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQDiAu/025dmYcmq
+o9AhYIhHpHjo9DCIg1tjbybtl0upoTTrO9paSG00hVnZ1hL8iL+Dez9KL+3zbsiQ
+ilnLWTLvVa1WJlytk8yhXohK2D+frPyqTmH2GjewI/N0+o2lJPzXycFTX9GjWeAg
+2Mc4GeIOHbY3QZCP8PQBxzyfiH30Pins2ZmtKVegzuaNBN2ZXp5ZZ+ABjpyBkmjv
+vb8kb89DQBVgzow5Wk77efs6Av2Js128i/PPQfDVkEuHJaaltMF5V3JCj7TR0nji
++6l6wzE4oBc5zuKYJ/Ux6H9789Zws5Q3gi+VeeJ+8PzPTmCN3mtAh7NXPKI7MlFj
+EQiSkJ7nOGtc0UKNTZXq7w0JjNlHurc/cVrYfer6+gPf623EMwCZ/zw+YyjKEjMg
+MFoaeR4G4nkPklpx4GYM0knBkcoSczBkdcpasHeCEXQoNkS7u+RjzHAsYNoSOad0
+gWLLym0EyGKj7Ws2U3jXM3r5j8n2xOv9JGAZ8/q8K1QRrxQw5tsCAwEAAQKCAYAY
++KwciLqkpD9M7EaNuYW1LLXzPy+xlZneVaSeca35cwdOylEo0oHGYMl5qQ51+oH2
+fAKVJtCKqf3dAnxDXHqlOPkq4Jgy0Xa1iaVTZ6s38DwGcRyfvWvTuVUn4psN2RVa
+nj8PADJAcyixWGJCj5GLb7r3RfY8ASpkm+fV1JXeC5RESBKTsFKvQMz2XchCLtMe
+G70DTwd5xXx0qKla1EO5MXZrOMcDezfozyRz12q98SR1NZ1dk/KRFh1SNFXCT0Mv
++yD0clnPJa13kYHvXRABHfzx/3z7NQk9UM9bd5iWsLLQm57HtfbpV089H4XsAobU
+xabRbuen9JrejsMETudCtP/ftZQNKEjAyY6y0yrOM4c/z1IL4zc75KW3gh/0ruPa
+XTlHEBvA3h29W1dLhk9oyeiFHiV8BRffjlyS325CX9z89hdoPK1cZwuIDgqdTpVw
+VL6MqKxu72oyLWZcq4CKT6ZIpLgwRAfPZ/oCsJQZbO46PIg5hRIlNEb1H5vGkDEC
+gcEA+qE5IS8kt676UXZLEjp3UtsuGHzfj+kC2x9dVepRL8bxf58W65ZsZim9xZ56
+Ls8gw8NXh7/7SRqHBpaH6Sg7YZZFzfD6RB86O7atZ2CwTMMuBcN5zZc6AwfH418Z
+wHaQeN1gYAyLdHf80rMMlElz8hjJ3uCuBWG70WinemzynlS14AtG4HB09C1vmjnD
+Q4L8lCmEQpqy3GeKDQnWTIhzoqenr1+iQF7bdCUw878yMI0x7Di+okiWFC7HnW/y
+qPiZAoHBAObarPdCbpqiUtymTRbdq1xP69pZXcMOmgL+kLEELhhl9BfJqbXY51xn
+NCIpIMH3CyhJ5/Og9TCE72gfhA2jzJK9mK6Jmiz04BViCf308yh9y6TaZSdsOEz6
+M+uVbuP+UcBLV5AV9UvrgWDcWOm46W63v7Mgqh6x7rC1rR+VFi3Lj2HoU4aM4mEM
+E5OfbgMxWUQNKkyUy58KUs2wu58v+K7N8eu3Fa4Sl63xkRi1YKgqYAxeRKknrNb+
+IkVq5zC/kwKBwHOB8k5057swDXWVyytvfqbVFP18L5yniwVqAx4hi6E1Uv+6Vlnl
+TbgX7LozO6RvGW6fjKunsywR6cEDh0fRnuxu0WUEdpMGwVPb8Tb/vMDkA0XsvSof
+VEEpSNplbfzhp9vMSyp5HZxj4EVK97Uv1RvyiLcLXahlTqZIUUd/BqIp8Fh9WgD+
+Uyhl+FVf4bovmDDAoZAAtAYYQeuYaQeEq6Z/Fi0hKin4jbONoG315C+0Ixn3XQR1
+55UNqjnI6lEtoQKBwQCi/VvHi2jJ1reIQAYHkeRN3cOYuyXe9O06Ff+Ua24cHceU
+D/a5hHX9IISHZeBR8hk3jc6tjUPvyLu7GR1EABUMub4V5OMswIuBrWF+ozYWrZJd
+RzDJ/7dUagbEWxIa+NFBYjBlc4tn2dPTzl8cTUjKugMn9nUGDPyIWQztUnaBSMpo
+Bv8J7WhbuooL3TFwIaRzzpPB1ABbvo8t2IzvXJBI4vDeSrqM12WuEvMtrcmbkaeU
+s+3oPDHk7TLHLi4ile8CgcEAmV1hwY4s78tMYrUbDypyH9r5a2QT9ezyPS64WntC
+y3I4zVwO0pqtPMXQCgby2Z+PkuBC1WWCFSZZ4Aw5P/0OShIf+ADMewFF//DvReEc
+p+kh/7vKulnX4mPQGkuSnCmO5zyMDroP8JtTnkX8K4P143vQY4n/oFogUx+4lTG/
+bedKQgI9v+ubb0JsZkENPirKyIOdiTz64fjD+IKMgq15SYifVundDC/ubG5Cr0rn
+PId0vxr7ixFQPAT1hwUT1CuI
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-ca.pem b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-ca.pem
new file mode 100644
index 000000000000..1347046d223a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-ca.pem
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE-----
+MIIEizCCAvOgAwIBAgIJAIAj56DfmvbYMA0GCSqGSIb3DQEBDAUAMFExCzAJBgNV
+BAYTAkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxHzAdBgNV
+BAMMFlN1aXRlIEIgUlNBIDNrIFJvb3QgQ0EwHhcNMTcwOTE3MTgxNjQwWhcNMjcw
+OTE1MTgxNjQwWjBRMQswCQYDVQQGEwJGSTERMA8GA1UEBwwISGVsc2lua2kxDjAM
+BgNVBAoMBXcxLmZpMR8wHQYDVQQDDBZTdWl0ZSBCIFJTQSAzayBSb290IENBMIIB
+ojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA4gLv9NuXZmHJqqPQIWCIR6R4
+6PQwiINbY28m7ZdLqaE06zvaWkhtNIVZ2dYS/Ii/g3s/Si/t827IkIpZy1ky71Wt
+ViZcrZPMoV6IStg/n6z8qk5h9ho3sCPzdPqNpST818nBU1/Ro1ngINjHOBniDh22
+N0GQj/D0Acc8n4h99D4p7NmZrSlXoM7mjQTdmV6eWWfgAY6cgZJo772/JG/PQ0AV
+YM6MOVpO+3n7OgL9ibNdvIvzz0Hw1ZBLhyWmpbTBeVdyQo+00dJ44vupesMxOKAX
+Oc7imCf1Meh/e/PWcLOUN4IvlXnifvD8z05gjd5rQIezVzyiOzJRYxEIkpCe5zhr
+XNFCjU2V6u8NCYzZR7q3P3Fa2H3q+voD3+ttxDMAmf88PmMoyhIzIDBaGnkeBuJ5
+D5JaceBmDNJJwZHKEnMwZHXKWrB3ghF0KDZEu7vkY8xwLGDaEjmndIFiy8ptBMhi
+o+1rNlN41zN6+Y/J9sTr/SRgGfP6vCtUEa8UMObbAgMBAAGjZjBkMB0GA1UdDgQW
+BBQh9+/awzQ67c3VUMCzugnuP4DXcDAfBgNVHSMEGDAWgBQh9+/awzQ67c3VUMCz
+ugnuP4DXcDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjANBgkq
+hkiG9w0BAQwFAAOCAYEAHmNoYP+c4TRPSogjCswhbzSVEpZhnjEg0Yd8XkGxKeBw
+o0hsPRFWjj/vO3uVeqoAyj2zkpiulPjBqlhLbwX31Q0T6vknWfNOsXgv2lB1yEZN
+HqxyEYsMN5RpEVqRRio66dhmALYuacX6gIphueTetaR9zeq1yy8GD0/omB7Ryig6
+5dMoTt4c9g8YFZE7AENkkbzMPqTdGKnY4uUQKgDBPH3TIlckx5zNq8GXTcAy4zyc
+4gj7NGPDdU5nk6BNRmlhFlsTaLHNc8C+5tI5fEx057AEa/7kggskvHxc7zespVMj
+RjTR9qkNC15IJHClMhBMiIDyURZF6Z3nyD0tMBJuIt2GU3gTqZLnrChp7PLXRCN/
+uByPuhJ528FzhQ1hnz93qBQ7OAamHfo44Zyk5wFnIUy+sd9QsM9zm+33/j0Vd5ar
+fzSfGRHJTb8xF7vH7TBH92CifdO17WNqH6+7KkFkEK44Dn87gjsgC8mXAOsE6HFw
+lKzThlrFLvCBIsQ4V9qH
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-generate.sh b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-generate.sh
new file mode 100755
index 000000000000..2c1c3cbebaf9
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-generate.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+OPENSSL=openssl
+
+echo
+echo "---[ DH parameters ]----------------------------------------------------"
+echo
+
+if [ -r dh_param_3072.pem ]; then
+ echo "Use already generated dh_param_3072.pem"
+else
+ openssl dhparam -out dh_param_3072.pem 3072
+fi
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+if [ -r rsa3072-ca.key ]; then
+ echo "Use already generated Root CA"
+else
+ cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = Suite B RSA 3k Root CA/" |
+ sed s%\./ec-ca$%./rsa3072-ca% \
+ > rsa3072-ca-openssl.cnf.tmp
+ $OPENSSL req -config rsa3072-ca-openssl.cnf.tmp -batch -x509 -new -newkey rsa:3072 -nodes -keyout rsa3072-ca.key -out rsa3072-ca.pem -outform PEM -days 3650 -sha384
+ mkdir -p rsa3072-ca/certs rsa3072-ca/crl rsa3072-ca/newcerts rsa3072-ca/private
+ touch rsa3072-ca/index.txt
+ rm rsa3072-ca-openssl.cnf.tmp
+fi
+
+echo
+echo "---[ Server ]-----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = rsa3072.server.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,DNS:rsa3072.server.w1.fi/" |
+ sed s%\./ec-ca$%./rsa3072-ca% \
+ > rsa3072-ca-openssl.cnf.tmp
+if [ ! -r rsa3072-server.req ]; then
+ $OPENSSL req -config rsa3072-ca-openssl.cnf.tmp -batch -new -newkey rsa:3072 -nodes -keyout rsa3072-server.key -out rsa3072-server.req -outform PEM -sha384
+fi
+$OPENSSL ca -config rsa3072-ca-openssl.cnf.tmp -batch -keyfile rsa3072-ca.key -cert rsa3072-ca.pem -create_serial -in rsa3072-server.req -out rsa3072-server.pem -extensions ext_server -days 730 -md sha384
+rm rsa3072-ca-openssl.cnf.tmp
+
+echo
+echo "---[ User SHA-384 ]-----------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = user-rsa3072/" |
+ sed "s/#@ALTNAME@/subjectAltName=email:user-rsa3072@w1.fi/" |
+ sed s%\./ec-ca$%./rsa3072-ca% \
+ > rsa3072-ca-openssl.cnf.tmp
+if [ ! -r rsa3072-user.req ]; then
+ $OPENSSL req -config rsa3072-ca-openssl.cnf.tmp -batch -new -newkey rsa:3072 -nodes -keyout rsa3072-user.key -out rsa3072-user.req -outform PEM -extensions ext_client -sha384
+fi
+$OPENSSL ca -config rsa3072-ca-openssl.cnf.tmp -batch -keyfile rsa3072-ca.key -cert rsa3072-ca.pem -create_serial -in rsa3072-user.req -out rsa3072-user.pem -extensions ext_client -days 730 -md sha384
+rm rsa3072-ca-openssl.cnf.tmp
+
+echo
+echo "---[ User RSA2048 ]-----------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = user-rsa3072-rsa2048/" |
+ sed "s/#@ALTNAME@/subjectAltName=email:user-rsa3072-rsa2048@w1.fi/" |
+ sed s%\./ec-ca$%./rsa3072-ca% \
+ > rsa3072-ca-openssl.cnf.tmp
+if [ ! -r rsa3072-user-rsa2048.req ]; then
+ $OPENSSL req -config rsa3072-ca-openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout rsa3072-user-rsa2048.key -out rsa3072-user-rsa2048.req -outform PEM -extensions ext_client -sha384
+fi
+$OPENSSL ca -config rsa3072-ca-openssl.cnf.tmp -batch -keyfile rsa3072-ca.key -cert rsa3072-ca.pem -create_serial -in rsa3072-user-rsa2048.req -out rsa3072-user-rsa2048.pem -extensions ext_client -days 730 -md sha384
+rm rsa3072-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Verify ]-----------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile rsa3072-ca.pem rsa3072-server.pem
+$OPENSSL verify -CAfile rsa3072-ca.pem rsa3072-user.pem
+$OPENSSL verify -CAfile rsa3072-ca.pem rsa3072-user-rsa2048.pem
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.key b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.key
new file mode 100644
index 000000000000..3319dd3f8011
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.key
@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+MIIG/wIBADANBgkqhkiG9w0BAQEFAASCBukwggblAgEAAoIBgQD+qVxZj6qAy7hK
+ifk66H0kUbjyBcZC4Gi1pPF+ijGi4AxYxYAgyyDwDsFrTeHX68xFSMmwD4/vgNsb
+YAKv7+gKKcgE33CS6fHcakc7Wm8Q5hlNk5LQCo6iTTKfE8g0bBpM7KTtiSoD+xgN
+fw3ePn/YXSel4XtiY3FhRVL5RxAdKMJdc/udA2i/baQSEnTH0LiHQ7Nnh85ue1gf
+LzLrEB/ndFw62YwYyASVa+M7JUwK25nzWbCOet765NtQFZCiMOKKxqkGMOPXKd0m
+qJVubvXEQtD3fkBNPf2tL89A3dTAa4CiNH4FL78yRAvUeG0qEgh8hLRNUVrlhG4X
+JkOMg2sW81/6s5+E0yur8z4sI2W5EXbhhRLOuwM4WYK/THe6O5BRnGd2sB50HkzI
+sTXWNyncfsOJzIaeCDGccOpabIeSU+uZ+zPSMvGBMXigyX1t2WsH+shKZ1csjKbO
+5X42lfEJvd+/yFM9IWf9k8uyerVWYZ4vzmn6+lYKa5xpePdOVHMCAwEAAQKCAYAG
+VHVcMIr/apDpIWbVhQPfTDy5n1UfQm633SK3j33OW51S843Mwt/Nt8AtB6GOeWj5
+a+a/fpOIU36evpMyhlcRMZqsLFWjATemz+l3WzcZh26nk/x5OVn0RND2TUqTqwA4
+W0V6NgeaU7p0U20n0gvhd+dNYz5q4qfl0BBQ6+hFoUa7he+CJpyK7ZG/dT/7239K
+tW8XKrQB4QT+uXCdkSgJ28WTHOczkn0yrZzXUoUCXBUGjHsr/3fdaqTc57xRm79p
+HDAjOavFnSDENTDsB5R9jmN70BY008xoitAtUzMkCVABbxn9npvjrTjKw/fg3oph
+1Ml8JaLDjsh3UzhqnmYKIJrZvdyfe7/Q9j7KtECPuxdf170BJ3jPrJPcWTecazfh
+szpt1beLyv8o4D7ttmgs/n1OXhGL1smcrTeIXdmrBlfIiKjY+3EE9SpxvXN/9DCy
++jnuEfy1KkbEhHSPVplGmyHb8xToA5FUfWsX/wWo0CZbH1ouquUHdcq/HsFGLZkC
+gcEA/2tBin4Hmmn5987Y17Iv3WG0XZFNW5jfvt0THo5R+Phg3DKGyYKFtrnS6bMZ
+IKl7YvpxZZ6+w0wBkaQWE/y9wN2oeiQE8WMvnYptGXs+sVXCNQJmKbZHxnZuQrwr
+KGAIwhGxShbx+rAzyXakKM0p2PUyHg9xAPu3Sfmb8zYWc8KZ62Xxf3tFErnGeJUm
+ZgdqtWvOWXJxMz2nM/Ow6FlnHr0Wo8ZEpli5kWlTwp+S7Trn6969lVJV4cGjPJxD
+7kGnAoHBAP89qeS0vx6XmMwqfgH+OTrRO6+F2sGOSvrcSWRBS0R4ninIOXASMWxH
+W/bAgzUwGB7bTUmVjQRGkFXIn0YcBlvMVJlvpQ0DqPftVY9Fa2TSUa2//M8PbIgk
+NsHa89YWkkKFMOZUH9JzIkn4H+f6mNv83sMOWGrdaymiLP7NxgA8VIeybekQ73j3
+thnDT2xyMXwO6Y0FbySvV8y6AEFTOq5vgR8A0orEEeP0eUlBzv030J/CNW4hXo0c
+qVsknTo4VQKBwQDw1s3CLPw2Wd9eDyjgmiAP+2T7JVtwF0JC0mqI0WHyBSIv/2Sg
+9fXnSmjZ/Aqhha3WspfiXkE6HZ0NG0/GIPc7uMZ4BSa0BfaL8k7VTCTdSiQJn+19
+P2eGd32YZ526QHOBqvUlC2W4IBV0ze4Umv/ul6VeOukvKCq4Eik+t62MEd7Y3BNP
+RYjoE0xVvy2p3yx7TOAR75tV2bijgBE7xbE6hsmmO/nXcKnptwtH5PfBwV2WRz00
+Y6KfcNre9+oF6tkCgcEAifVrgenML53jAd+p0iv2BPuY1it0bRAbKPKuXJkKNM05
+N/44RYIf4pXDeGDfynzfXLZOVQqXeQsm8qcIp9139mBADdsRjDJBPxiyGUl9XbZs
+XYya+dQtZnykeC1/hGUY0wmov6YSuS5wByktHbcOrkFEqotzcPeS96LnzSWt8uyp
+B9uCmuoDdg/2BoDRyh0C8DojNI0OYPbBby/N+YEiA6zTTs2j/0sxHFRExjrixW1I
+v0E6nfc9YupuA4yLyy8tAoHBAPoDc7AZeCsAIrGU/ojvPEMGYk/DyCnPyalrTeI3
+DP01lx3/URyhIawu0k9oXQy6IdyJ4BHonMRSHqoMw53W9Lvany0n1CgW9+84JZ1C
+9H6VyK+uxK/00UYEDwVf5PSZiWFxa4h7uQm/EDzTEF/cim47DbpQR8j8YkJ77+dZ
+lBleLkVv9CgT2eH4zGAjAiD5KPd0pQEPse5jNXnwI/+qa5rKZBWFcnFRiHbcMqDF
+b7FtSAoTF3UtdZ9XQUM0V4ZwuA==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.pem b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.pem
new file mode 100644
index 000000000000..546361dff4ab
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.pem
@@ -0,0 +1,106 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ ad:8c:09:e8:fb:a2:88:cb
+ Signature Algorithm: sha384WithRSAEncryption
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B RSA 3k Root CA
+ Validity
+ Not Before: Aug 16 13:19:41 2019 GMT
+ Not After : Aug 15 13:19:41 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=rsa3072.server.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (3072 bit)
+ Modulus:
+ 00:fe:a9:5c:59:8f:aa:80:cb:b8:4a:89:f9:3a:e8:
+ 7d:24:51:b8:f2:05:c6:42:e0:68:b5:a4:f1:7e:8a:
+ 31:a2:e0:0c:58:c5:80:20:cb:20:f0:0e:c1:6b:4d:
+ e1:d7:eb:cc:45:48:c9:b0:0f:8f:ef:80:db:1b:60:
+ 02:af:ef:e8:0a:29:c8:04:df:70:92:e9:f1:dc:6a:
+ 47:3b:5a:6f:10:e6:19:4d:93:92:d0:0a:8e:a2:4d:
+ 32:9f:13:c8:34:6c:1a:4c:ec:a4:ed:89:2a:03:fb:
+ 18:0d:7f:0d:de:3e:7f:d8:5d:27:a5:e1:7b:62:63:
+ 71:61:45:52:f9:47:10:1d:28:c2:5d:73:fb:9d:03:
+ 68:bf:6d:a4:12:12:74:c7:d0:b8:87:43:b3:67:87:
+ ce:6e:7b:58:1f:2f:32:eb:10:1f:e7:74:5c:3a:d9:
+ 8c:18:c8:04:95:6b:e3:3b:25:4c:0a:db:99:f3:59:
+ b0:8e:7a:de:fa:e4:db:50:15:90:a2:30:e2:8a:c6:
+ a9:06:30:e3:d7:29:dd:26:a8:95:6e:6e:f5:c4:42:
+ d0:f7:7e:40:4d:3d:fd:ad:2f:cf:40:dd:d4:c0:6b:
+ 80:a2:34:7e:05:2f:bf:32:44:0b:d4:78:6d:2a:12:
+ 08:7c:84:b4:4d:51:5a:e5:84:6e:17:26:43:8c:83:
+ 6b:16:f3:5f:fa:b3:9f:84:d3:2b:ab:f3:3e:2c:23:
+ 65:b9:11:76:e1:85:12:ce:bb:03:38:59:82:bf:4c:
+ 77:ba:3b:90:51:9c:67:76:b0:1e:74:1e:4c:c8:b1:
+ 35:d6:37:29:dc:7e:c3:89:cc:86:9e:08:31:9c:70:
+ ea:5a:6c:87:92:53:eb:99:fb:33:d2:32:f1:81:31:
+ 78:a0:c9:7d:6d:d9:6b:07:fa:c8:4a:67:57:2c:8c:
+ a6:ce:e5:7e:36:95:f1:09:bd:df:bf:c8:53:3d:21:
+ 67:fd:93:cb:b2:7a:b5:56:61:9e:2f:ce:69:fa:fa:
+ 56:0a:6b:9c:69:78:f7:4e:54:73
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 82:D7:75:95:94:9E:35:F7:1F:91:6D:37:9F:26:4F:3D:9D:C1:6E:96
+ X509v3 Authority Key Identifier:
+ keyid:21:F7:EF:DA:C3:34:3A:ED:CD:D5:50:C0:B3:BA:09:EE:3F:80:D7:70
+
+ X509v3 Subject Alternative Name: critical
+ DNS:rsa3072.server.w1.fi
+ X509v3 Extended Key Usage: critical
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha384WithRSAEncryption
+ 39:6b:3b:eb:37:00:5d:71:35:15:bd:9d:59:7b:10:f5:57:51:
+ bf:e7:40:69:4d:05:f5:64:d0:42:d2:7e:74:12:bf:dc:20:13:
+ f5:24:1f:84:18:5c:75:18:34:f6:b3:57:a1:32:de:13:ef:d9:
+ 79:6d:7a:9c:3a:3d:b0:3b:74:44:e8:9e:fc:19:6b:fa:55:74:
+ c8:e1:a1:2e:a9:ce:73:4c:7f:4d:0b:fd:33:33:10:c3:21:f1:
+ d1:80:31:ca:33:77:23:91:1d:11:b1:60:c9:ec:51:4c:70:31:
+ 3c:b6:8a:8e:e3:42:d4:e7:e1:1c:11:a7:13:99:76:3c:25:55:
+ 04:c2:e6:45:e5:21:39:5d:9f:e1:f2:35:84:ad:dd:3b:69:ab:
+ ca:f6:88:9a:4c:cc:cf:6a:82:b8:54:5a:60:40:aa:20:05:c3:
+ 39:c1:46:11:8d:e4:cb:b1:7c:ee:48:cf:31:18:89:7a:5c:f7:
+ f9:51:18:65:e1:25:28:cb:49:1c:f5:5e:f6:68:d9:8e:c5:01:
+ cb:4f:da:7e:7a:54:f5:b4:4d:0a:e8:3f:6d:26:a1:72:c8:07:
+ 50:ee:bf:64:01:8f:12:19:6d:ad:c0:6d:fa:29:ff:ab:31:9c:
+ fa:d4:55:46:83:a3:3b:53:cc:26:53:3f:b4:85:2f:90:76:6b:
+ 39:4a:06:22:72:c0:0e:45:0d:3f:80:41:03:d7:65:89:fd:01:
+ 3a:8c:8f:9c:af:77:93:ec:c0:fb:2e:f2:b0:db:ac:07:ac:e2:
+ 0f:c8:af:24:0b:57:69:9f:bb:cb:e0:d7:bc:c2:c7:6f:3f:f3:
+ 30:aa:42:88:7d:45:02:1e:ad:ac:da:89:8b:43:d9:80:0e:ab:
+ 79:c5:c4:21:97:3b:e0:99:ef:9b:50:4a:86:62:e4:af:18:ed:
+ 70:5b:e8:f8:87:9e:0c:c4:f0:6a:f4:1e:ce:05:f0:15:3f:68:
+ 02:33:1d:9d:05:e4:d8:2f:20:38:33:1a:4e:46:7e:4b:10:b2:
+ 6c:55:04:21:38:36
+-----BEGIN CERTIFICATE-----
+MIIEqzCCAxOgAwIBAgIJAK2MCej7oojLMA0GCSqGSIb3DQEBDAUAMFExCzAJBgNV
+BAYTAkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxHzAdBgNV
+BAMMFlN1aXRlIEIgUlNBIDNrIFJvb3QgQ0EwHhcNMTkwODE2MTMxOTQxWhcNMjEw
+ODE1MTMxOTQxWjA8MQswCQYDVQQGEwJGSTEOMAwGA1UECgwFdzEuZmkxHTAbBgNV
+BAMMFHJzYTMwNzIuc2VydmVyLncxLmZpMIIBojANBgkqhkiG9w0BAQEFAAOCAY8A
+MIIBigKCAYEA/qlcWY+qgMu4Son5Ouh9JFG48gXGQuBotaTxfooxouAMWMWAIMsg
+8A7Ba03h1+vMRUjJsA+P74DbG2ACr+/oCinIBN9wkunx3GpHO1pvEOYZTZOS0AqO
+ok0ynxPINGwaTOyk7YkqA/sYDX8N3j5/2F0npeF7YmNxYUVS+UcQHSjCXXP7nQNo
+v22kEhJ0x9C4h0OzZ4fObntYHy8y6xAf53RcOtmMGMgElWvjOyVMCtuZ81mwjnre
++uTbUBWQojDiisapBjDj1yndJqiVbm71xELQ935ATT39rS/PQN3UwGuAojR+BS+/
+MkQL1HhtKhIIfIS0TVFa5YRuFyZDjINrFvNf+rOfhNMrq/M+LCNluRF24YUSzrsD
+OFmCv0x3ujuQUZxndrAedB5MyLE11jcp3H7DicyGnggxnHDqWmyHklPrmfsz0jLx
+gTF4oMl9bdlrB/rISmdXLIymzuV+NpXxCb3fv8hTPSFn/ZPLsnq1VmGeL85p+vpW
+CmucaXj3TlRzAgMBAAGjgZowgZcwDAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUgtd1
+lZSeNfcfkW03nyZPPZ3BbpYwHwYDVR0jBBgwFoAUIffv2sM0Ou3N1VDAs7oJ7j+A
+13AwIgYDVR0RAQH/BBgwFoIUcnNhMzA3Mi5zZXJ2ZXIudzEuZmkwFgYDVR0lAQH/
+BAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBDAUAA4IBgQA5
+azvrNwBdcTUVvZ1ZexD1V1G/50BpTQX1ZNBC0n50Er/cIBP1JB+EGFx1GDT2s1eh
+Mt4T79l5bXqcOj2wO3RE6J78GWv6VXTI4aEuqc5zTH9NC/0zMxDDIfHRgDHKM3cj
+kR0RsWDJ7FFMcDE8toqO40LU5+EcEacTmXY8JVUEwuZF5SE5XZ/h8jWErd07aavK
+9oiaTMzPaoK4VFpgQKogBcM5wUYRjeTLsXzuSM8xGIl6XPf5URhl4SUoy0kc9V72
+aNmOxQHLT9p+elT1tE0K6D9tJqFyyAdQ7r9kAY8SGW2twG36Kf+rMZz61FVGg6M7
+U8wmUz+0hS+Qdms5SgYicsAORQ0/gEED12WJ/QE6jI+cr3eT7MD7LvKw26wHrOIP
+yK8kC1dpn7vL4Ne8wsdvP/MwqkKIfUUCHq2s2omLQ9mADqt5xcQhlzvgme+bUEqG
+YuSvGO1wW+j4h54MxPBq9B7OBfAVP2gCMx2dBeTYLyA4MxpORn5LELJsVQQhODY=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.req b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.req
new file mode 100644
index 000000000000..b06d8c695c4e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-server.req
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIDlDCCAfwCAQAwTzELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4w
+DAYDVQQKDAV3MS5maTEdMBsGA1UEAwwUcnNhMzA3Mi5zZXJ2ZXIudzEuZmkwggGi
+MA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQD+qVxZj6qAy7hKifk66H0kUbjy
+BcZC4Gi1pPF+ijGi4AxYxYAgyyDwDsFrTeHX68xFSMmwD4/vgNsbYAKv7+gKKcgE
+33CS6fHcakc7Wm8Q5hlNk5LQCo6iTTKfE8g0bBpM7KTtiSoD+xgNfw3ePn/YXSel
+4XtiY3FhRVL5RxAdKMJdc/udA2i/baQSEnTH0LiHQ7Nnh85ue1gfLzLrEB/ndFw6
+2YwYyASVa+M7JUwK25nzWbCOet765NtQFZCiMOKKxqkGMOPXKd0mqJVubvXEQtD3
+fkBNPf2tL89A3dTAa4CiNH4FL78yRAvUeG0qEgh8hLRNUVrlhG4XJkOMg2sW81/6
+s5+E0yur8z4sI2W5EXbhhRLOuwM4WYK/THe6O5BRnGd2sB50HkzIsTXWNyncfsOJ
+zIaeCDGccOpabIeSU+uZ+zPSMvGBMXigyX1t2WsH+shKZ1csjKbO5X42lfEJvd+/
+yFM9IWf9k8uyerVWYZ4vzmn6+lYKa5xpePdOVHMCAwEAAaAAMA0GCSqGSIb3DQEB
+DAUAA4IBgQBMKANR6G6HmsFZkpY1mc3JIKJgZkfuokL8NqpDOw6R+vg/nJhLVObf
+jvtQAtUxTeIvLLTpYjOePCl/SxuQ120PEEXlBiwYvo+J4NKYMTprBDfocMACHBvF
+X9QljgeZ2z1/fSncyWdSzz6dBq3e5wBSGQi/7GB85SE/3ovm1Cks3fxxSslukwxC
+9OfnRECVrlmMPY8KMkU7rM83f0rcxK193VUu7tOuP+s/bNhdto96LZj8qFfAdoSc
+622A5TH0VWhvw1c6RKSKIBAmbAJ/fyaENYZ5XQALMoagTRNg626w5DaXd81JwOC1
+TPT2o9oFXGS/W8owTwDzTubXCaRPysQIVF4mq937SIy/UIxcaBw1TzRzpW22lql7
+muWfRSsmUgHOlxhQJH3SrYnkEzm4+0NzOLqM9xoABKH91uTt7MD+3x/rpB9Yjc8I
+f0B+PiRZosmS8IiOjkexGJa3Lm1RTGxWrulLc7pY8euNcfNi4HAfQL7fhJlor68T
+R9SB/u7pBfo=
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.key b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.key
new file mode 100644
index 000000000000..a20e20ac04b6
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDbZv+LlWaWLkp0
+AoBD/An6tkF4Cfjpv2tB/Vu/zGbYB8MN7EiXjSxPWPW1JwfuSHxrmKJ26IuUEXdo
+NkMfLIif1kDf7CcWY640HNXsJsXZrXepcxSGo6QuZ2P96NfdxxhnCtOvv30gmmf9
+XMB8bszYqz0rjq4JDLUj2bT5++Jp42kFKF3uNxvHwLqDzuEwYh+p3aue1N2M5KTg
+H6XclUgCoJ/0pahxyI8GvkxOQYx/kUeoBm1Gw0m4r8UnRkHiLLZtaGbawAyEvt9h
+yklH9xlmNgkFGyebgEPwVUG0Tg8lceW6uNxysLVJqJUCSahnKnjztA6raQXp2TCt
+sL0m8I0zAgMBAAECggEBAK720V1X9CpZmggvomgUy0SDKWx3z2dgvkEusYcfkvg+
+IF+vCSjKbQaN88vV524vogEQBKd8xSp8T1PsydRHaGDGtI+dvIIPVfG6+SHMvcTc
+n+uimUDRqPFUhBoNIHB9AEnUCYJC258vYzRaTiotFfDHhg2BR+pxltaTG86q/yDe
+vmVw1RJhf9V2g4t3IIVrviYl08jk1BaBlOIMMCLA1mgfSBCD02J9cOLjVCO3QsPA
+daR3qD2zOy7elAsbR0X25xFH7fDBiYj6XasawWiuYxihuJR9yLUbu2IXIQRZgE34
+LZeoc+GNAkUGC7AiNVoC0wnhEzSXtNg5pudiq7T6hyECgYEA8arqoMGNSwzkyB7l
+YbAeICCNXi870oyB+TzfJIaDlFNmxnpSETh2cE+xwJi6MSCrtIE7/5k7zfGn8OIF
+W6fqBIIum0tKdYdIyPKxsyJJsumqYYo0ukmiHhRmPy7oJ49DGip7em/Sm72L6Nv6
+SAnswsSDOYGOFd6SUXuLQCPQ/GMCgYEA6GoKvsAsRVMMODb2dYK+LHvWrE1/a4fd
+Q2YTpAk8CED9vxY3LlmF6Guuw0AwHGNkbxw0O7Le97+Ei5iP8P4sFFHorZ0byu3d
+leo2enR2cAS9JAc3ERrZDl9kS1Y/1jNE3To+tSjtjMecMj/mOOC/9RC3xUytfTQk
+FV71TGIxfPECgYBHdrdzWkkiDGoLE7fZL295KKclVupl1M8KmQmTj4ORdShLQRjL
+ptq/U5HI1mEY7gRYmG7ZosKgn/l9rhjPhdQaCTUnDxixsJPBeKM7ycPpeFe+CFSX
+Ufby1i12ObTFHgRF1JI3HqI1E9Qvw/07GFQ/NEVp1/ngVbUHC3WePfkq6QKBgAkX
+vPvhgNnleeDpJZNLXi1XWvq6vXVzh1CPucz9H4AjKspDED2b+wUw0VGKPVee+9rs
++44qXVbMA1+CxH7lMsxIuBWyw9eYnsaytxbrX4baaJv0PE9LAZryWHYqFa1HrDYL
+hVCJHWIYnR/KKDOpd1kbIlVxvofbdl3vrSEj5lPBAoGBAMWJPg4YIL7etbE3UAXb
+aN9vuzH3Zh/yFCKJ2RHo1/tlqJKYRmW8mJSLSx2vVWg/xGIMfe8Eboj4TnlBJDOC
+gUjVCAnk3b9ZBEQqU5TKjB9h91xpJIkfd0Z7OiJJ7N+JWAtnUtFSux3kGgZi2aGP
+9rzRN4p1+s80q5+BjOUJrPi0
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.pem b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.pem
new file mode 100644
index 000000000000..56b4a5994fa7
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.pem
@@ -0,0 +1,96 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ ad:8c:09:e8:fb:a2:88:cd
+ Signature Algorithm: sha384WithRSAEncryption
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B RSA 3k Root CA
+ Validity
+ Not Before: Aug 16 13:19:41 2019 GMT
+ Not After : Aug 15 13:19:41 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=user-rsa3072-rsa2048
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:db:66:ff:8b:95:66:96:2e:4a:74:02:80:43:fc:
+ 09:fa:b6:41:78:09:f8:e9:bf:6b:41:fd:5b:bf:cc:
+ 66:d8:07:c3:0d:ec:48:97:8d:2c:4f:58:f5:b5:27:
+ 07:ee:48:7c:6b:98:a2:76:e8:8b:94:11:77:68:36:
+ 43:1f:2c:88:9f:d6:40:df:ec:27:16:63:ae:34:1c:
+ d5:ec:26:c5:d9:ad:77:a9:73:14:86:a3:a4:2e:67:
+ 63:fd:e8:d7:dd:c7:18:67:0a:d3:af:bf:7d:20:9a:
+ 67:fd:5c:c0:7c:6e:cc:d8:ab:3d:2b:8e:ae:09:0c:
+ b5:23:d9:b4:f9:fb:e2:69:e3:69:05:28:5d:ee:37:
+ 1b:c7:c0:ba:83:ce:e1:30:62:1f:a9:dd:ab:9e:d4:
+ dd:8c:e4:a4:e0:1f:a5:dc:95:48:02:a0:9f:f4:a5:
+ a8:71:c8:8f:06:be:4c:4e:41:8c:7f:91:47:a8:06:
+ 6d:46:c3:49:b8:af:c5:27:46:41:e2:2c:b6:6d:68:
+ 66:da:c0:0c:84:be:df:61:ca:49:47:f7:19:66:36:
+ 09:05:1b:27:9b:80:43:f0:55:41:b4:4e:0f:25:71:
+ e5:ba:b8:dc:72:b0:b5:49:a8:95:02:49:a8:67:2a:
+ 78:f3:b4:0e:ab:69:05:e9:d9:30:ad:b0:bd:26:f0:
+ 8d:33
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ CC:85:AA:3D:E4:37:51:3E:70:46:96:8E:00:65:C3:81:20:E0:E4:87
+ X509v3 Authority Key Identifier:
+ keyid:21:F7:EF:DA:C3:34:3A:ED:CD:D5:50:C0:B3:BA:09:EE:3F:80:D7:70
+
+ X509v3 Subject Alternative Name:
+ email:user-rsa3072-rsa2048@w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha384WithRSAEncryption
+ 56:58:31:e4:90:41:01:ca:19:97:06:e0:5a:74:a4:a6:1d:1d:
+ e4:71:bf:dc:cd:94:99:5c:20:24:73:7f:42:6e:1e:d0:4b:89:
+ 6f:e3:1e:fa:16:7d:1e:b6:92:5f:e2:f8:66:3f:9f:fe:4b:0c:
+ 39:c0:c1:bf:e3:8b:e9:cd:25:39:f6:50:4f:2a:a0:8c:1d:0c:
+ 26:6a:3a:65:42:ee:4e:2a:23:5d:54:79:ca:9e:57:9b:c0:c0:
+ 04:55:d4:ad:4f:06:88:71:f7:d8:6f:cd:7d:8e:92:a9:85:aa:
+ a0:3c:0d:47:af:f9:cd:db:d6:41:f7:e1:a2:2d:b6:4d:70:78:
+ 9f:08:07:dd:9b:27:bf:cb:85:07:55:0d:bc:55:1c:84:04:84:
+ 98:9e:62:80:ca:93:b8:16:5b:74:fe:a1:cf:d7:59:99:be:23:
+ f4:e3:a3:5f:2b:22:a5:38:09:c0:04:89:2e:f4:64:fe:b9:90:
+ 17:38:02:2c:6b:ae:ca:36:f1:3a:e0:e1:db:47:99:78:59:ed:
+ 98:b7:95:f9:06:5a:37:03:9f:96:bd:87:cd:8d:f9:5c:3b:22:
+ b2:ca:f6:b0:e6:b9:70:4e:70:ea:ab:25:bd:f7:4f:1a:5d:7b:
+ d2:36:aa:30:c1:95:cb:e5:71:3a:51:6e:e5:b4:b6:e2:19:55:
+ 05:50:e5:4d:88:8d:fd:0e:0e:e3:5b:86:61:cf:10:b7:dd:7f:
+ 12:01:b8:bf:2c:a6:86:7b:86:ff:b3:cc:b0:c7:ca:2a:c6:33:
+ 2e:81:f8:bc:19:e0:da:b4:d5:6a:69:dd:cb:c6:5d:41:7b:d0:
+ d1:02:67:7f:c0:39:e2:7c:60:9a:8b:ce:c9:1f:2a:0c:69:04:
+ 22:36:4d:50:20:bc:cd:6a:fa:5e:c2:96:ef:d0:82:55:ea:2c:
+ 64:87:59:36:f3:db:06:80:41:1b:8d:75:6e:db:bc:66:d5:15:
+ a3:72:89:d0:ee:ed:e4:37:b1:68:40:7c:9e:da:5d:01:12:91:
+ f3:bb:39:45:57:26
+-----BEGIN CERTIFICATE-----
+MIIEKDCCApCgAwIBAgIJAK2MCej7oojNMA0GCSqGSIb3DQEBDAUAMFExCzAJBgNV
+BAYTAkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxHzAdBgNV
+BAMMFlN1aXRlIEIgUlNBIDNrIFJvb3QgQ0EwHhcNMTkwODE2MTMxOTQxWhcNMjEw
+ODE1MTMxOTQxWjA8MQswCQYDVQQGEwJGSTEOMAwGA1UECgwFdzEuZmkxHTAbBgNV
+BAMMFHVzZXItcnNhMzA3Mi1yc2EyMDQ4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A
+MIIBCgKCAQEA22b/i5Vmli5KdAKAQ/wJ+rZBeAn46b9rQf1bv8xm2AfDDexIl40s
+T1j1tScH7kh8a5iiduiLlBF3aDZDHyyIn9ZA3+wnFmOuNBzV7CbF2a13qXMUhqOk
+Lmdj/ejX3ccYZwrTr799IJpn/VzAfG7M2Ks9K46uCQy1I9m0+fviaeNpBShd7jcb
+x8C6g87hMGIfqd2rntTdjOSk4B+l3JVIAqCf9KWocciPBr5MTkGMf5FHqAZtRsNJ
+uK/FJ0ZB4iy2bWhm2sAMhL7fYcpJR/cZZjYJBRsnm4BD8FVBtE4PJXHlurjccrC1
+SaiVAkmoZyp487QOq2kF6dkwrbC9JvCNMwIDAQABo4GXMIGUMAkGA1UdEwQCMAAw
+HQYDVR0OBBYEFMyFqj3kN1E+cEaWjgBlw4Eg4OSHMB8GA1UdIwQYMBaAFCH379rD
+NDrtzdVQwLO6Ce4/gNdwMCUGA1UdEQQeMByBGnVzZXItcnNhMzA3Mi1yc2EyMDQ4
+QHcxLmZpMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAsGA1UdDwQEAwIFoDANBgkqhkiG
+9w0BAQwFAAOCAYEAVlgx5JBBAcoZlwbgWnSkph0d5HG/3M2UmVwgJHN/Qm4e0EuJ
+b+Me+hZ9HraSX+L4Zj+f/ksMOcDBv+OL6c0lOfZQTyqgjB0MJmo6ZULuTiojXVR5
+yp5Xm8DABFXUrU8GiHH32G/NfY6SqYWqoDwNR6/5zdvWQffhoi22TXB4nwgH3Zsn
+v8uFB1UNvFUchASEmJ5igMqTuBZbdP6hz9dZmb4j9OOjXysipTgJwASJLvRk/rmQ
+FzgCLGuuyjbxOuDh20eZeFntmLeV+QZaNwOflr2HzY35XDsissr2sOa5cE5w6qsl
+vfdPGl170jaqMMGVy+VxOlFu5bS24hlVBVDlTYiN/Q4O41uGYc8Qt91/EgG4vyym
+hnuG/7PMsMfKKsYzLoH4vBng2rTVamndy8ZdQXvQ0QJnf8A54nxgmovOyR8qDGkE
+IjZNUCC8zWr6XsKW79CCVeosZIdZNvPbBoBBG411btu8ZtUVo3KJ0O7t5DexaEB8
+ntpdARKR87s5RVcm
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.req b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.req
new file mode 100644
index 000000000000..5dc231bc0b4c
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user-rsa2048.req
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIClDCCAXwCAQAwTzELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4w
+DAYDVQQKDAV3MS5maTEdMBsGA1UEAwwUdXNlci1yc2EzMDcyLXJzYTIwNDgwggEi
+MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDbZv+LlWaWLkp0AoBD/An6tkF4
+Cfjpv2tB/Vu/zGbYB8MN7EiXjSxPWPW1JwfuSHxrmKJ26IuUEXdoNkMfLIif1kDf
+7CcWY640HNXsJsXZrXepcxSGo6QuZ2P96NfdxxhnCtOvv30gmmf9XMB8bszYqz0r
+jq4JDLUj2bT5++Jp42kFKF3uNxvHwLqDzuEwYh+p3aue1N2M5KTgH6XclUgCoJ/0
+pahxyI8GvkxOQYx/kUeoBm1Gw0m4r8UnRkHiLLZtaGbawAyEvt9hyklH9xlmNgkF
+GyebgEPwVUG0Tg8lceW6uNxysLVJqJUCSahnKnjztA6raQXp2TCtsL0m8I0zAgMB
+AAGgADANBgkqhkiG9w0BAQwFAAOCAQEAIymC4XT/XzkwrdIS/gE7QOu144FJiy4Y
+gA+udw+z4n1e59j9nNTVcaVliSWl4J33XYdUAAiSq78gRUW8xzhrjri1QkNyYg9M
+/ZkjkFN0vqFLr/ts8SX6x8VuqHGSzP1DCOKcYGLRXZWvzA8LMz4ZmxVdzEGEAL3Z
+UQwfAPXvervh2jKtesi2UeCM3PyJmterxCuPyujybK4WvRVvxMSsCOiIT4LzxKfD
+MuRZ/7UbGoVOBXwrlYfrr+UmVLc9HJG86GvOKCgnQh0s/QaM69PPLRajXIh4eJdj
+v7YnrfnxEV8wmTUg9QjMOdnlDCmPZcAyGBEubNYSzJAzE8aOQOi0bA==
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.key b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.key
new file mode 100644
index 000000000000..11afa84b0164
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.key
@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+MIIG/QIBADANBgkqhkiG9w0BAQEFAASCBucwggbjAgEAAoIBgQCSd2eORDSDqDf5
+qcRyXHFynTUdPYw0Ilwk+IeB3t6spZN3xTikgpyMBpsUi1IJMkwxxfjpL2SKOQpw
+nk6KnLylq3gYUkR1/sMAYecfRcAScuQ4niid9nZgcLN7EcqQmCrqJsBcrqkSzFIR
+pgKs6FlWeqiT7P0G7qzorxdoV085ytRISYq0jT4hUaOW558k5fUQ5zb+jjOyfSJm
+j0Jlzw9PzKMkD+O6mIk1p1SZ7IFHxC+yOzuyTM6kqFpmEECODX1n2O0a/eVhFtv1
+THNAOeN9bycmCGgYAt87WgQKCMVTux0AkWz2OCrwqp8rNm6VJKcahNhcavjVP2IP
+IEu3lsbCG/iBZBMVeptdmO4P2XM31TyfNVKT33KdidSfIPIThRIAfCMnzvnd4reC
+CaL6JlQix/20+hrTbSmPG2cTL9ji8Fx1nqp5/MG3SF0IEgE3eBP5Uzc6qCE45190
++4VTayFrgsmlQSyjOXQUoFwDyBMXVaYVRVI8ubk//tmoFG8gxSECAwEAAQKCAYBq
+xnOPCngCNwM/lhzphi0KckMDYxgv9ZCZPzmCWxiYYkjkerm1bKZ1imdKDdsrayiS
+7JFuZad1AOp0eWQmtubsG9n8WRUhtC0yvSzB5paEnI92Gw7fQYrA+chOgwTabqRy
+ePepWYdWde+qgAzZQrXGTrtQw+ceQ6d4JhT5cxUFu7EQVdSxlXpizeJlo8uoGaCT
+xwuXfdGAYKtQe0XbdJzj/vo70v2gzYzRuX/6iqkgyYw/8eCuNkI7VaQ5XcXCCWB1
+nCf578JBXynJEpEBh6FEZj6LzBD7aop2ErYkiTRdWKTvweqVxSQtiiS//FH91tiy
+hMm61mzgf4kTf1FsFokp+xssSbHKhxTsZO4pXoupdUTfG9B8vAlbQObDiOmLUtdX
+mpXkDDnZUD//alLGxbiOmncH4K/VGuZuSXnSkbUnjrdkOGVtSy4cwxAbgii1z6D9
+jeImt4vTvFkt3jiqfPs7/c6M6giEY3OyjbR8P3jksBC4urKTWI+B010AsKUur5UC
+gcEAwnj7nNSyABEhyimKIfGIjXGiKaRevfstRTs+fRYWGkcVvVZQ2s1xxTAXGiYa
+kJgFUL3lTfbdvkTHEp5U7PrC4ErXBAV61fjv9DfRGFTIvtOM10YfrS/GeZuJYHXe
+abrVliB56jiOg2tq6XrKPe6f7vZFDaan2srh0/FN/CEHom2WS7mQL+VjwwCtBZHh
+aMMkg/bW9qaV0fWwi2dK97vcQzb3udgnnC6M2P6bK7og9Vfa8tW8kVtSBVjyoGu7
+1aCrAoHBAMDOO+LM8nPhju/jeWH2366YhGbRZM2lpqb6b7c/09AUC4ESK82S9AKe
+1Ppa8Q5KnaI0PAg7V6CebL1EjGgzUcWZWzC7Q8u+In7ktq09G0uk6vtlfpwjx4OU
+Q9DiosZdBASKmhQpmYRYawbvjQXhPIexAYSvwb+930g93+gmLOXOtg1C/y2vHtsm
+JU8bCkXceC2PsCB2D8aOKlUoyutXMW8VX0VmBab0JBuTp9T+woYp5RXj2Id1CuOC
+BlJZZNjpYwKBwQC9wGJxsi9EVXMM2N9JI21D5d5+lz1CTfTsGlRspMJIPZf+uFwI
+QnGCH9xKzWcaMtrs330AR6IxZtZ/WjIvULYZN6z45YfnhBBN0LCa9w8w8yX3XxrF
+V1pnidXPYvLzYzPIWkPav/h+Tq9wxTjUmSNAfNb/7N7XYyJaNJcNLgVO/XKqzJLd
+yQtAWEZ6qs6v88iLYqx42i5RQVNTkiPZ+Vl/1AB/O2PaxqjziepKDkDeYyzlyJtH
+kT1Ernd/A9+xICUCgcAszkCAflxBrcNH4DcPGw30RyFNu4+PctV9rGlVzpFso5vg
+zNY9Gc925G5eF9A5IAHt9fGVgCTnAKoIeeufM33nS7IzavFgYbkmgAQr0i2LsLGi
+5n07z9zHqSbxXhmxu1/5pjQUR26ToPCOVhERsrwcVHgj26xM4NUItshX7Lc2WIla
+H52pgi7LgtvcvE3w2kFbZS7q/ETCQbt4utgdRNAKHo9bU1Aw8j+J4RB5oRKXlxjT
+s3VYVUzIfij17ixPdD0CgcA8LTjVgy6jeCeCWIUjoAlPLuMB3A/zU3ZxeWJ7uEUj
+Xjz/6ToSQBWtbW1xtKBW9RZBzbIZCgiNBqO38DqKzoKY6T/muIJniLUW5lshj5B5
+XFSanprP/vp+J8lEaTiVAAfsbt2/ZuZq+IAxwDo9oifr5NtKvexaiEJ0apx/e0yg
+tvwouNj0+z89rF2INCDbWB3750mPcBRafASXyVRCwiopzfdFHZB1boUcSq7cRY2R
+cTEirRqQCdUI6fmEGFMYR6A=
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.pem b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.pem
new file mode 100644
index 000000000000..fef367b07f93
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.pem
@@ -0,0 +1,106 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ ad:8c:09:e8:fb:a2:88:cc
+ Signature Algorithm: sha384WithRSAEncryption
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B RSA 3k Root CA
+ Validity
+ Not Before: Aug 16 13:19:41 2019 GMT
+ Not After : Aug 15 13:19:41 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=user-rsa3072
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (3072 bit)
+ Modulus:
+ 00:92:77:67:8e:44:34:83:a8:37:f9:a9:c4:72:5c:
+ 71:72:9d:35:1d:3d:8c:34:22:5c:24:f8:87:81:de:
+ de:ac:a5:93:77:c5:38:a4:82:9c:8c:06:9b:14:8b:
+ 52:09:32:4c:31:c5:f8:e9:2f:64:8a:39:0a:70:9e:
+ 4e:8a:9c:bc:a5:ab:78:18:52:44:75:fe:c3:00:61:
+ e7:1f:45:c0:12:72:e4:38:9e:28:9d:f6:76:60:70:
+ b3:7b:11:ca:90:98:2a:ea:26:c0:5c:ae:a9:12:cc:
+ 52:11:a6:02:ac:e8:59:56:7a:a8:93:ec:fd:06:ee:
+ ac:e8:af:17:68:57:4f:39:ca:d4:48:49:8a:b4:8d:
+ 3e:21:51:a3:96:e7:9f:24:e5:f5:10:e7:36:fe:8e:
+ 33:b2:7d:22:66:8f:42:65:cf:0f:4f:cc:a3:24:0f:
+ e3:ba:98:89:35:a7:54:99:ec:81:47:c4:2f:b2:3b:
+ 3b:b2:4c:ce:a4:a8:5a:66:10:40:8e:0d:7d:67:d8:
+ ed:1a:fd:e5:61:16:db:f5:4c:73:40:39:e3:7d:6f:
+ 27:26:08:68:18:02:df:3b:5a:04:0a:08:c5:53:bb:
+ 1d:00:91:6c:f6:38:2a:f0:aa:9f:2b:36:6e:95:24:
+ a7:1a:84:d8:5c:6a:f8:d5:3f:62:0f:20:4b:b7:96:
+ c6:c2:1b:f8:81:64:13:15:7a:9b:5d:98:ee:0f:d9:
+ 73:37:d5:3c:9f:35:52:93:df:72:9d:89:d4:9f:20:
+ f2:13:85:12:00:7c:23:27:ce:f9:dd:e2:b7:82:09:
+ a2:fa:26:54:22:c7:fd:b4:fa:1a:d3:6d:29:8f:1b:
+ 67:13:2f:d8:e2:f0:5c:75:9e:aa:79:fc:c1:b7:48:
+ 5d:08:12:01:37:78:13:f9:53:37:3a:a8:21:38:e7:
+ 5f:74:fb:85:53:6b:21:6b:82:c9:a5:41:2c:a3:39:
+ 74:14:a0:5c:03:c8:13:17:55:a6:15:45:52:3c:b9:
+ b9:3f:fe:d9:a8:14:6f:20:c5:21
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ B1:4F:36:17:24:40:AD:6B:05:33:87:C4:AD:4F:4A:53:AF:F5:D6:23
+ X509v3 Authority Key Identifier:
+ keyid:21:F7:EF:DA:C3:34:3A:ED:CD:D5:50:C0:B3:BA:09:EE:3F:80:D7:70
+
+ X509v3 Subject Alternative Name:
+ email:user-rsa3072@w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha384WithRSAEncryption
+ b8:51:21:70:fc:ce:a2:a6:43:db:b1:54:c6:51:ee:a9:77:dd:
+ ad:3f:75:fc:23:0c:be:a5:e7:ec:f4:2d:33:04:08:48:45:58:
+ 5d:1f:83:57:57:43:b0:be:81:69:d7:51:65:f8:24:97:8e:3c:
+ 01:60:a3:b3:0a:11:43:94:2c:68:f0:a1:28:63:e4:ce:a8:27:
+ 2d:74:6f:8f:e4:10:8e:9a:56:91:72:61:fd:85:82:8a:48:dd:
+ d6:f3:40:97:de:6d:8b:51:ef:e8:a0:a5:65:45:96:aa:85:d1:
+ b6:86:8e:53:68:2d:d0:c3:6b:11:ba:8e:15:3c:a4:b7:38:fe:
+ 9f:1c:57:b8:58:a3:f6:ff:31:e4:95:f9:d8:52:80:66:b1:c4:
+ f9:ce:95:01:30:89:7b:e7:ec:86:b5:c6:95:46:55:5f:ce:36:
+ 43:8f:9c:ca:48:86:20:d0:60:89:c8:03:d0:25:1e:38:25:bb:
+ d8:b1:e1:72:9a:f5:f3:97:e6:76:41:80:0e:00:47:06:59:46:
+ 2b:37:57:07:77:e4:5e:9c:38:0e:80:81:61:ab:89:ef:43:99:
+ 7a:2c:24:b5:60:c2:5e:a8:2b:59:03:1d:e3:ab:b9:0b:02:3f:
+ 16:90:57:70:56:d7:40:42:70:0e:de:27:9e:f1:27:30:e0:2c:
+ 56:5c:bf:56:43:db:fb:a6:14:ba:0a:ef:87:d5:a4:00:73:59:
+ 8b:a0:10:1d:b1:8a:31:a8:ef:ae:c7:c5:25:65:b5:05:a0:df:
+ 16:63:0e:58:f4:0e:5f:9c:e8:95:ea:b5:18:63:6e:ae:5a:dc:
+ c5:d5:95:c7:f9:23:46:76:96:d6:d2:ec:a0:63:01:3c:63:f1:
+ 99:6e:b1:f2:3c:e7:08:ff:67:53:dd:b7:6e:83:91:cb:32:e9:
+ 5e:64:8b:5f:46:6c:80:02:a8:37:3c:a3:17:ad:33:5f:dc:75:
+ e6:41:dc:db:19:26:c0:34:76:5d:19:a5:10:89:ad:59:5e:5d:
+ 69:41:2d:3f:64:d0
+-----BEGIN CERTIFICATE-----
+MIIEmDCCAwCgAwIBAgIJAK2MCej7oojMMA0GCSqGSIb3DQEBDAUAMFExCzAJBgNV
+BAYTAkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxHzAdBgNV
+BAMMFlN1aXRlIEIgUlNBIDNrIFJvb3QgQ0EwHhcNMTkwODE2MTMxOTQxWhcNMjEw
+ODE1MTMxOTQxWjA0MQswCQYDVQQGEwJGSTEOMAwGA1UECgwFdzEuZmkxFTATBgNV
+BAMMDHVzZXItcnNhMzA3MjCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGB
+AJJ3Z45ENIOoN/mpxHJccXKdNR09jDQiXCT4h4He3qylk3fFOKSCnIwGmxSLUgky
+TDHF+OkvZIo5CnCeToqcvKWreBhSRHX+wwBh5x9FwBJy5DieKJ32dmBws3sRypCY
+KuomwFyuqRLMUhGmAqzoWVZ6qJPs/QburOivF2hXTznK1EhJirSNPiFRo5bnnyTl
+9RDnNv6OM7J9ImaPQmXPD0/MoyQP47qYiTWnVJnsgUfEL7I7O7JMzqSoWmYQQI4N
+fWfY7Rr95WEW2/VMc0A5431vJyYIaBgC3ztaBAoIxVO7HQCRbPY4KvCqnys2bpUk
+pxqE2Fxq+NU/Yg8gS7eWxsIb+IFkExV6m12Y7g/ZczfVPJ81UpPfcp2J1J8g8hOF
+EgB8IyfO+d3it4IJovomVCLH/bT6GtNtKY8bZxMv2OLwXHWeqnn8wbdIXQgSATd4
+E/lTNzqoITjnX3T7hVNrIWuCyaVBLKM5dBSgXAPIExdVphVFUjy5uT/+2agUbyDF
+IQIDAQABo4GPMIGMMAkGA1UdEwQCMAAwHQYDVR0OBBYEFLFPNhckQK1rBTOHxK1P
+SlOv9dYjMB8GA1UdIwQYMBaAFCH379rDNDrtzdVQwLO6Ce4/gNdwMB0GA1UdEQQW
+MBSBEnVzZXItcnNhMzA3MkB3MS5maTATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNV
+HQ8EBAMCBaAwDQYJKoZIhvcNAQEMBQADggGBALhRIXD8zqKmQ9uxVMZR7ql33a0/
+dfwjDL6l5+z0LTMECEhFWF0fg1dXQ7C+gWnXUWX4JJeOPAFgo7MKEUOULGjwoShj
+5M6oJy10b4/kEI6aVpFyYf2FgopI3dbzQJfebYtR7+igpWVFlqqF0baGjlNoLdDD
+axG6jhU8pLc4/p8cV7hYo/b/MeSV+dhSgGaxxPnOlQEwiXvn7Ia1xpVGVV/ONkOP
+nMpIhiDQYInIA9AlHjglu9ix4XKa9fOX5nZBgA4ARwZZRis3Vwd35F6cOA6AgWGr
+ie9DmXosJLVgwl6oK1kDHeOruQsCPxaQV3BW10BCcA7eJ57xJzDgLFZcv1ZD2/um
+FLoK74fVpABzWYugEB2xijGo767HxSVltQWg3xZjDlj0Dl+c6JXqtRhjbq5a3MXV
+lcf5I0Z2ltbS7KBjATxj8ZlusfI85wj/Z1Pdt26Dkcsy6V5ki19GbIACqDc8oxet
+M1/cdeZB3NsZJsA0dl0ZpRCJrVleXWlBLT9k0A==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.req b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.req
new file mode 100644
index 000000000000..c3d197411356
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/rsa3072-user.req
@@ -0,0 +1,21 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIDjDCCAfQCAQAwRzELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4w
+DAYDVQQKDAV3MS5maTEVMBMGA1UEAwwMdXNlci1yc2EzMDcyMIIBojANBgkqhkiG
+9w0BAQEFAAOCAY8AMIIBigKCAYEAkndnjkQ0g6g3+anEclxxcp01HT2MNCJcJPiH
+gd7erKWTd8U4pIKcjAabFItSCTJMMcX46S9kijkKcJ5Oipy8pat4GFJEdf7DAGHn
+H0XAEnLkOJ4onfZ2YHCzexHKkJgq6ibAXK6pEsxSEaYCrOhZVnqok+z9Bu6s6K8X
+aFdPOcrUSEmKtI0+IVGjluefJOX1EOc2/o4zsn0iZo9CZc8PT8yjJA/jupiJNadU
+meyBR8Qvsjs7skzOpKhaZhBAjg19Z9jtGv3lYRbb9UxzQDnjfW8nJghoGALfO1oE
+CgjFU7sdAJFs9jgq8KqfKzZulSSnGoTYXGr41T9iDyBLt5bGwhv4gWQTFXqbXZju
+D9lzN9U8nzVSk99ynYnUnyDyE4USAHwjJ8753eK3ggmi+iZUIsf9tPoa020pjxtn
+Ey/Y4vBcdZ6qefzBt0hdCBIBN3gT+VM3OqghOOdfdPuFU2sha4LJpUEsozl0FKBc
+A8gTF1WmFUVSPLm5P/7ZqBRvIMUhAgMBAAGgADANBgkqhkiG9w0BAQwFAAOCAYEA
+WDsGMRPDTMIrBUPqqRztaslOEL0wylg7frSuUPpaWBO6Gjwo+6JmmVTIUcCTTmR9
+7OUlmo8zf6+AR+as1rBb0tXmM1uifP9ml7yE2aNnPG6ACQSvYzF5ra95+qqWY9kH
+j6OWxW7hsirmNS8BfaIu0eK6OeG+4yfmOI784VR6JndnsrQ9cviv/FYX1+NLSH85
+wp2M8fXoeC7Qnwe6D8+pTj0v/HEcIL5ZRrYjeaZSyCMXkpUDyfY0L18HbklUxn+K
+nWWQoWZy3QXSc5vub7POeCnIm9mKIU+n8sAwqQ3Xx01gJBK4wrO0uGv3tsTCfs+7
+0zsECDr21fCQAJGJHEfPWYKhRHx5SiTtUK6/KiVsPYaBK8Ac5+QwscXzYKJyzi8Y
+e+v5YTFOZ4L1Ub8/TBaeB1J8CAtrwZkB5a50wRM2GHhushXTsnArvjForsmGHMhl
+7CKB06/Ry69SwE1+Nl88kdYGsVcVoJEFvz0aLIra7BeZyJPkanlmDUD61iXf7Dkk
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-certpol.csr b/contrib/wpa/tests/hwsim/auth_serv/server-certpol.csr
new file mode 100644
index 000000000000..2e1c31a2b7bd
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-certpol.csr
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIDlDCCAfwCAQAwTzELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAM
+BgNVBAoMBXcxLmZpMR4wHAYDVQQDDBVzZXJ2ZXItcG9saWNpZXMudzEuZmkwggGi
+MA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDSpu+bvWBjoXWtS9NvWV6E+mSg
+ZCQLeEj8jWaLL24dRCuuw22UusujNL4LTkeNW9mZpqgHCYdVsjd+R2dcdF8sg3my
+CEe07E/vdVhnxlhMT2jBGBqETXgjSJoUOG5bShLrhsT3TDisY6dh+rNkfIkOKfef
++HXD75DCcZahq2nTwnQTz+j3CZjtOnnWxEZJk3g7FqWp3fDrvUSn3E7O96fJP3gI
+iwXGFy7u3xGg9/VYgHbCNO+5eL7EXL5fXte3zaMSxON2/GSFZGVr2lzJOFA5iXLl
+IO+5C8wyJjx5XkqNeI1q3XM6yEInQw3dBR+8hN9WLX6YUJ6LeLDn/ag5B1cFEvwA
+74nwPwP2k1uwRFdhYUcFbMQWmGG4kzJFOfu7jjuHGF86B1fRmIkdhbde6htReZRc
+2Pq9unUAA+P0A81c2xahrLf0k37smrDmnE5dPLoBMsxwykk8kv7SiIGd2/S7gP7v
+iVDqgJW9xPoo2MCGYTfXmSuOuQZ4mghEF9oZNZcCAwEAAaAAMA0GCSqGSIb3DQEB
+CwUAA4IBgQC9HigmR7s38B1IRYNJ1WwC7UlV4fFTElisntPXiQsDZzvZ0Gufsobx
+Bk/As4DWsQEJ17EvF0LXnsgRG670bnh/YibkaVBF71XLkBAfkXGaa1nw4VNC4EEJ
+sPIcrEQGxhkAJHvT3cZ0zWQnSKbcZbt6Vn0bNoRPihDKTek6dPPI9HamDsu0OBl1
+l8FdMfG4Ge1NquABvgBSrt85XHXfCBYlXBsnJ5XeA8A2t7JtW6C51EVGGachglPB
+ajrtuD00puJ+Cx+a7k5OHniTpAUHS6EOYpcWcUrzIKVCAGlHFd4XOZdD0hP7/eFR
+H57JjFTwDENSCU1GiRwra/ACswR2XWYQH0v+CvbKUx6ZivtKLkuGr4go/YIgVeXq
+WM7b+tDopZVFsjdrbkuefkimYIJdwmZXukM5qP0pKTGNM9zeBaAs9bAKDs42jF2f
+8i9M7DpIzJ9X1Y8xhaBEjodUcCtT5LFPNh0JT5wwkbS2SGgQiti3MdcnQQYqXDUZ
+xd6npHU4F+c=
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-certpol.key b/contrib/wpa/tests/hwsim/auth_serv/server-certpol.key
new file mode 100644
index 000000000000..fdd41eb1d05c
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-certpol.key
@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQDSpu+bvWBjoXWt
+S9NvWV6E+mSgZCQLeEj8jWaLL24dRCuuw22UusujNL4LTkeNW9mZpqgHCYdVsjd+
+R2dcdF8sg3myCEe07E/vdVhnxlhMT2jBGBqETXgjSJoUOG5bShLrhsT3TDisY6dh
++rNkfIkOKfef+HXD75DCcZahq2nTwnQTz+j3CZjtOnnWxEZJk3g7FqWp3fDrvUSn
+3E7O96fJP3gIiwXGFy7u3xGg9/VYgHbCNO+5eL7EXL5fXte3zaMSxON2/GSFZGVr
+2lzJOFA5iXLlIO+5C8wyJjx5XkqNeI1q3XM6yEInQw3dBR+8hN9WLX6YUJ6LeLDn
+/ag5B1cFEvwA74nwPwP2k1uwRFdhYUcFbMQWmGG4kzJFOfu7jjuHGF86B1fRmIkd
+hbde6htReZRc2Pq9unUAA+P0A81c2xahrLf0k37smrDmnE5dPLoBMsxwykk8kv7S
+iIGd2/S7gP7viVDqgJW9xPoo2MCGYTfXmSuOuQZ4mghEF9oZNZcCAwEAAQKCAYA/
+Xm6oOCD9971Rw4S4c3cGo9iPk3Bwbt/t8Y+OgVcrwK0vZqTZYBQQZbZh6kuGD8J3
+AXZ8n3Yx5mnhOBO08WEMIAUE9I61s30ceP1+QmGfmyfVJq4bbL6eRqHrQUqZdcAZ
+UDKCflByM4xP4j4DFZ+ZPjC60+CBb9jpVYhN3CX6yP1oVFwtrJpviu7KF8NZMN6z
+T83IOvbVw9sacCDZDBFSbiBq2X+EJsc8nqhL9yu8UvDm3UvcTKF+qrOuNvbH2TkP
++vxSVC8Y81VoBR5ngsQzZc+XDrplMb/BA4UJVncxMJ8kg0U08RwDTYwoLo6vKeus
+xqGESyBbjbC5QpPdX+hjHqmNdjjbYS47zkWrZ8geE5jpIx9A1hePd/MxZeX9rZWp
+lZm8yWF5DMFF6CZxc/FlYI0aXP8C1rV+GBZ5gkRq/6E5hiLbdbbNGB6IENvACIbD
+qQwwuIl8qwIgzBey6e0WYnKH1U00YIUg8OgXXFsjzAw40ltPCjwoRt9KSOp3MxkC
+gcEA/3XjtPvq2eauJjFTJ1ewLmbu2JTV+8mrA39UD0clCqptH7fVaSdxjeniOu7/
+UiPjNoFMr8+Ec6s/RkIWyOeKVjnKhqVLR91XGXz8PzYQMvhKmfEHfe7mCZ94RapU
+Hl2k6ZpJLq384i/5KYDU3i3a+9DD+iZQ4P66HGnCKLILpbV8vz4ZFASp8ffU1snL
+JPLm3UhqTVf4ZeJlL5xbuqk9QHl7jz5UDNpytk5zkEPCDzh0OH+OyZ+nSJiKDok2
+pjM7AoHBANMY0lGwDavtTf9xghLHtevQlJSm/JbBfM7Hp81/UY+Ibl7uRyQXkEdd
+03szMFoP0UbsJHg9hPN07yv3rJpyBh2O5atgWqidYq4nJLBC1a5RALiqfaHnJNhe
+IV4d8+TE0jOLUE2cMuWQFabKpHCGZ4GdTZNMsz1VKCx3cQwQ7GF75ZKBKIYYxMNi
+yIen+dpCmEAvubMXLyB24mGQ3qbIml01cT7R1j1QNVGvXGDRhhRGlyzm8W1decza
+CX9mgUVpVQKBwQCqDWX5EkExsDd5QRhjdiHXobmY/uq643Itr9LbILbttKlTleJA
+T3ttxqVMKdBYc39KxyOvXOqEvRgvwsq8DjWuVGYW322Pdy4Fz4dy5KA/7bxrYWFl
+WWRUP42mgk3gsOGYh5XztupB/0FTeWk6RTgirMPofx0TyT1GsLgIswzB0GAsRkAX
+bUtbwWgzWr0Z6X/5Cb2Joue9mslUujbtuL8Hblbr8cetjrUR2oNfI1vJGgFzoqYA
+XYDT+IbeSkTQugUCgcEAo5pRJk4zylOYZ6kpDjUJoUF+ZdclXBGJERlby8ApDfzG
+zXwOVsKMZ0MobAs4JhSsNTM+8JF9QNIXqxPBCdHlO3NMPI3otVWE7UQZAyJJSVgu
+HvDDfX8O50HMyoycQWjpIFmQWxX7vD73CNV0rGD+R04KmWaQY7Bj+lJ3ospa6RKE
+0g6XwZXgqS0eDUT6N1X1eYmDenE1bQu2V7dXWBuQxzxsECvAxrQrHquyBLdeGsi6
+0WoLIp+XjlRNmBdxiMIhAoHANi3K+ExLqmkbspSOmRUJiDkxxoaZAvc0EfqUBRU1
+8H1syqeBzIKYbIsmipWoHgapJPuDtMKWS/7EihkkHTlMjBMORr/JgF14TYAK5nP1
+/YUUv7UgsJvBFZLLepbbcrNxeb2WC9TsdNlxxpwx89661sBiDrwPztBEqyGPBa/b
+oOwesnmVlDS/BjUUt7xNHHxGMRNE0eOg7x7NIplPb5y7+X5BTwpuuzHRcimUpIbr
+V+nPmVUHX6GcYg7TZpT+bgcO
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-certpol.pem b/contrib/wpa/tests/hwsim/auth_serv/server-certpol.pem
new file mode 100644
index 000000000000..d0145426458d
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-certpol.pem
@@ -0,0 +1,102 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cd:63
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 2 19:55:38 2020 GMT
+ Not After : May 2 19:55:38 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=server-policies.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (3072 bit)
+ Modulus:
+ 00:d2:a6:ef:9b:bd:60:63:a1:75:ad:4b:d3:6f:59:
+ 5e:84:fa:64:a0:64:24:0b:78:48:fc:8d:66:8b:2f:
+ 6e:1d:44:2b:ae:c3:6d:94:ba:cb:a3:34:be:0b:4e:
+ 47:8d:5b:d9:99:a6:a8:07:09:87:55:b2:37:7e:47:
+ 67:5c:74:5f:2c:83:79:b2:08:47:b4:ec:4f:ef:75:
+ 58:67:c6:58:4c:4f:68:c1:18:1a:84:4d:78:23:48:
+ 9a:14:38:6e:5b:4a:12:eb:86:c4:f7:4c:38:ac:63:
+ a7:61:fa:b3:64:7c:89:0e:29:f7:9f:f8:75:c3:ef:
+ 90:c2:71:96:a1:ab:69:d3:c2:74:13:cf:e8:f7:09:
+ 98:ed:3a:79:d6:c4:46:49:93:78:3b:16:a5:a9:dd:
+ f0:eb:bd:44:a7:dc:4e:ce:f7:a7:c9:3f:78:08:8b:
+ 05:c6:17:2e:ee:df:11:a0:f7:f5:58:80:76:c2:34:
+ ef:b9:78:be:c4:5c:be:5f:5e:d7:b7:cd:a3:12:c4:
+ e3:76:fc:64:85:64:65:6b:da:5c:c9:38:50:39:89:
+ 72:e5:20:ef:b9:0b:cc:32:26:3c:79:5e:4a:8d:78:
+ 8d:6a:dd:73:3a:c8:42:27:43:0d:dd:05:1f:bc:84:
+ df:56:2d:7e:98:50:9e:8b:78:b0:e7:fd:a8:39:07:
+ 57:05:12:fc:00:ef:89:f0:3f:03:f6:93:5b:b0:44:
+ 57:61:61:47:05:6c:c4:16:98:61:b8:93:32:45:39:
+ fb:bb:8e:3b:87:18:5f:3a:07:57:d1:98:89:1d:85:
+ b7:5e:ea:1b:51:79:94:5c:d8:fa:bd:ba:75:00:03:
+ e3:f4:03:cd:5c:db:16:a1:ac:b7:f4:93:7e:ec:9a:
+ b0:e6:9c:4e:5d:3c:ba:01:32:cc:70:ca:49:3c:92:
+ fe:d2:88:81:9d:db:f4:bb:80:fe:ef:89:50:ea:80:
+ 95:bd:c4:fa:28:d8:c0:86:61:37:d7:99:2b:8e:b9:
+ 06:78:9a:08:44:17:da:19:35:97
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 3E:AD:0D:4D:7E:FA:A2:4A:D5:F5:31:EA:B6:B4:BF:83:B1:55:7E:C7
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ Authority Information Access:
+ OCSP - URI:http://server.w1.fi:8888/
+
+ X509v3 Subject Alternative Name:
+ DNS:server-policies.w1.fi
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.40808.1.3.1
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ Signature Algorithm: sha256WithRSAEncryption
+ b8:ef:8e:09:f5:67:a3:d6:5c:92:d2:55:f8:f2:52:e4:cd:ea:
+ 87:a6:aa:42:73:b2:b4:30:d8:80:3f:aa:d5:f2:65:32:b9:88:
+ 7d:f1:b2:c2:c1:fe:17:c7:76:7e:d9:7b:4b:1a:87:dc:1f:f6:
+ 57:0d:8b:5f:2a:5d:e2:7f:f4:8d:39:3a:a4:9e:9d:f3:c1:58:
+ cf:04:fd:72:40:c2:9a:ef:98:b2:6a:67:86:27:2c:f6:e6:dd:
+ b1:a0:20:b1:c0:cf:fb:00:43:1f:6f:ac:b2:3f:02:a6:87:80:
+ 18:74:6b:0b:26:07:d3:7a:72:1c:c7:1d:a7:dc:13:cb:70:ac:
+ 24:2e:45:9c:bf:53:de:ea:eb:50:4a:60:87:26:8a:28:4e:16:
+ 76:91:b1:b3:e2:4d:66:fd:12:60:ed:24:59:f4:f9:47:59:d1:
+ 4c:6e:d1:9d:55:d4:72:d8:c4:da:2f:b4:73:20:d3:7e:f7:9f:
+ 6e:99:b8:06:1d:5f:8c:18:ab:a3:a8:fa:50:52:50:e5:2b:c9:
+ fa:1d:fe:f0:ce:33:19:d5:38:e6:ba:90:c9:5e:e6:67:60:e0:
+ 50:16:7c:4c:08:89:d2:e2:fe:bc:57:0f:ef:83:75:ec:1d:f3:
+ 10:07:ce:c2:d6:30:44:f2:ec:b9:78:71:c2:41:8d:78:e4:d6:
+ 67:42:d7:f5
+-----BEGIN CERTIFICATE-----
+MIIEWDCCA0CgAwIBAgIJANjT46bL481jMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDIxOTU1MzhaFw0yMTA1MDIxOTU1MzhaMD0xCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEeMBwGA1UEAwwVc2VydmVyLXBvbGlj
+aWVzLncxLmZpMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA0qbvm71g
+Y6F1rUvTb1lehPpkoGQkC3hI/I1miy9uHUQrrsNtlLrLozS+C05HjVvZmaaoBwmH
+VbI3fkdnXHRfLIN5sghHtOxP73VYZ8ZYTE9owRgahE14I0iaFDhuW0oS64bE90w4
+rGOnYfqzZHyJDin3n/h1w++QwnGWoatp08J0E8/o9wmY7Tp51sRGSZN4Oxalqd3w
+671Ep9xOzvenyT94CIsFxhcu7t8RoPf1WIB2wjTvuXi+xFy+X17Xt82jEsTjdvxk
+hWRla9pcyThQOYly5SDvuQvMMiY8eV5KjXiNat1zOshCJ0MN3QUfvITfVi1+mFCe
+i3iw5/2oOQdXBRL8AO+J8D8D9pNbsERXYWFHBWzEFphhuJMyRTn7u447hxhfOgdX
+0ZiJHYW3XuobUXmUXNj6vbp1AAPj9APNXNsWoay39JN+7Jqw5pxOXTy6ATLMcMpJ
+PJL+0oiBndv0u4D+74lQ6oCVvcT6KNjAhmE315krjrkGeJoIRBfaGTWXAgMBAAGj
+gdYwgdMwCQYDVR0TBAIwADAdBgNVHQ4EFgQUPq0NTX76okrV9THqtrS/g7FVfscw
+HwYDVR0jBBgwFoAUpP25ORuBs6rriB3Ugam1EXDMp+EwNQYIKwYBBQUHAQEEKTAn
+MCUGCCsGAQUFBzABhhlodHRwOi8vc2VydmVyLncxLmZpOjg4ODgvMCAGA1UdEQQZ
+MBeCFXNlcnZlci1wb2xpY2llcy53MS5maTAYBgNVHSAEETAPMA0GCysGAQQBgr5o
+AQMBMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IBAQC4744J
+9Wej1lyS0lX48lLkzeqHpqpCc7K0MNiAP6rV8mUyuYh98bLCwf4Xx3Z+2XtLGofc
+H/ZXDYtfKl3if/SNOTqknp3zwVjPBP1yQMKa75iyameGJyz25t2xoCCxwM/7AEMf
+b6yyPwKmh4AYdGsLJgfTenIcxx2n3BPLcKwkLkWcv1Pe6utQSmCHJoooThZ2kbGz
+4k1m/RJg7SRZ9PlHWdFMbtGdVdRy2MTaL7RzINN+959umbgGHV+MGKujqPpQUlDl
+K8n6Hf7wzjMZ1TjmupDJXuZnYOBQFnxMCInS4v68Vw/vg3XsHfMQB87C1jBE8uy5
+eHHCQY145NZnQtf1
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-certpol2.csr b/contrib/wpa/tests/hwsim/auth_serv/server-certpol2.csr
new file mode 100644
index 000000000000..63ed9abae49e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-certpol2.csr
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIDlTCCAf0CAQAwUDELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAM
+BgNVBAoMBXcxLmZpMR8wHQYDVQQDDBZzZXJ2ZXItcG9saWNpZXMyLncxLmZpMIIB
+ojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEA5lDRKAUnbNRC00LHzFOpa8Kj
+qyBvFzSd5B0x0MRoZULV6L2quOTp9u4udc1qjPaOqq9sfOs1UFWxwrP4p9AeozMm
+aEAgE3QIh++2OvF/PvV/k0R0N4vDiae6X0I5SiIgQGbGb3fPVD8FYd6rcfqfeG2X
+SuhgoBGqbLqdRGUY6OCP0d/alatBLGNl9kJC5h9CpBx0IEn01JIO4747Vf04aHQ6
+5N+aK5W/6dE4ixYkIDXbuNAVMC4vaiS54enntrW95g9Z3d+VnKsDtMVCgUhhzDwG
+F4VjbijL14jRzkDH/2FRrLu6I8lCp30nDR5TkM8iP1f1/xoFDJx6G/viR19Fy+6I
+paBUcYP309PFvLJ+haexGs+Ry4s5unwsnbLFecPggHMGME9dgVLiv0NVhV1kxJes
+6S1+MLXhUlBTDKwkjnuiV43/sQW6IzOmCKO0OEL2XNm8XXWVgv9NmttWLxs40lEF
+LJBi8Y5M7uobrqpTdIW6xsPCSzC94C7IrH4lzDJfAgMBAAGgADANBgkqhkiG9w0B
+AQsFAAOCAYEAe5pIVGtUDu9+vI7oIDAc/AkiPxCsM1W8r/geTQvGaP1FzuppXbo+
+i1U2iGTC2P/9ZJ+zMBbj7IVvPg9KWOnDP98BZB6iHSYOm6OYBsIpm9uSvET7qJ+M
+22xZe89abeYNFgDpKYJRasFEG3ze2HvNvZUolR8RYakTeBCwlO8snqiZgjJdwbFz
+0fVWqVoFCZN0AUvzfAeqFwZpZ9cQRETOB10DbVxnWe58mJgFckXwSynmxdP4o+9L
+QUq8HB9FMlUyn60usP121Wm1LC3tvJpecl4otQqu2nPnmhUWMMiBMRpPwOqB0fnn
+gfdqON5cligShTernXXtdBnXoeM+ZT2qayazuZ/3JD5ioVM2ZVVNRfPZTmDwF9+1
+w0TC4YfEuAHMfOAnfr+lOt0HI3lGIqTzbze7IPRK1mbfq6gOa0DzQw04vflLFVzx
+/f9S0K8sHeKj3DaaezCGY3T/rUMbmwT/pSNNK56zcddBcj/fFf+3NhcbC09U8V4h
+RBL7vBjsIWsH
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-certpol2.key b/contrib/wpa/tests/hwsim/auth_serv/server-certpol2.key
new file mode 100644
index 000000000000..29e59dc94408
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-certpol2.key
@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQDmUNEoBSds1ELT
+QsfMU6lrwqOrIG8XNJ3kHTHQxGhlQtXovaq45On27i51zWqM9o6qr2x86zVQVbHC
+s/in0B6jMyZoQCATdAiH77Y68X8+9X+TRHQ3i8OJp7pfQjlKIiBAZsZvd89UPwVh
+3qtx+p94bZdK6GCgEapsup1EZRjo4I/R39qVq0EsY2X2QkLmH0KkHHQgSfTUkg7j
+vjtV/ThodDrk35orlb/p0TiLFiQgNdu40BUwLi9qJLnh6ee2tb3mD1nd35WcqwO0
+xUKBSGHMPAYXhWNuKMvXiNHOQMf/YVGsu7ojyUKnfScNHlOQzyI/V/X/GgUMnHob
+++JHX0XL7oiloFRxg/fT08W8sn6Fp7Eaz5HLizm6fCydssV5w+CAcwYwT12BUuK/
+Q1WFXWTEl6zpLX4wteFSUFMMrCSOe6JXjf+xBbojM6YIo7Q4QvZc2bxddZWC/02a
+21YvGzjSUQUskGLxjkzu6huuqlN0hbrGw8JLML3gLsisfiXMMl8CAwEAAQKCAYEA
+z/4yNPManKTASKtpZjQzr3aSeiuLR6ij4msfHssRAEmwhkQrFljclbyZxpcg33aW
+drx/u/xqJEePhicjquE/meDKkaE/lnHWdnTb3DVV1dS9RpCuZ69Xgkwv+nEC7dkN
+yTtHf0jyusFDKhR+Piu4sng+Bk7/W+84OoL5Hdgy+7Q5Da8cZsfGzsBhR1ils86N
+T0nG8ZX4fbP9sFyOl2Rb+bDlsuXgA/Zz30OrzafMLi6VZDy+tckv1qqeF9A2CwHq
+avLsnqatMqZBbYkbo9munv2Fhs4z1KJQl6u3BifnFX4ZiP/tCBdc/Clgbr/dw2e+
+6GEclNT0eSiB9vUw3wHINRqnU35i8wIOmMJ7wG5q+PeRn8sEfkRSCshKjIfvBcHG
+G/rVmILERKMJQax2MavGWhYYtWEu5cMOdK3hDb7/0uODv1oJYQGp5qNom6U0efLK
+oD3la3E3KfYbCLdA1XBG8p9TcOFbm2hm7c1UFzBQ805JmR4SIvcR5gEkOadcTajp
+AoHBAP629szQlStD/1cHi4X9rQ7Nm2LqljLp6hVn+KOZztqEaT36HqU7247sII93
+axMLVMRxebK5gZ5H/UF9M/75MWoUvnlbkWPPeRdr2HJUc/h7HbV/V79NSjfLBFqG
+kX6Gx6V4PQg3dww/FPJBQuRP84gUFMDvMhoXutjVY5aoCPwyiez7qEEYjyyyIEFW
+JKRgqp1LMHH/yOWvytOdjNhTlx9AMnAyNa8LJWtxPgqtZIN4ifjPbytdZfVA6y8Y
+hZanwwKBwQDnelWxu9QxSOT9kCMWRtdkb2e04NyyDSN4XHv0UQ5tfGYnphE7cjIL
+9wmutI16mueKSkO2pECjKSnsraEwXAxMazwFjHZmq5c6LzxZ1HpmnW+31vHu5Q9R
+t9oB9eY6nrNmPtSur5bfRzC7qzBJtrjNEmzJ2aS71yMC0cuZvmjko9t0U48qbgJv
+zoOUuyCmz5PK1dOd0OyzH11XsRzfcf/nOqZUhQ0zaG0WSewmbqpVW2PsxkIEYlr6
+0hGtSjG2PTUCgcEAp4Py6h5fjDXLDxSCORvtnaexAqvfHhrifTOEvSuhc+rTQBRn
+5SlpqyQ2AcR64ep41D0A2X7Q9STJNTG/aXe/fNGptyx2gNro+3NMxVwvbQKjNkNK
+lSCip/DXqyWHOFwxnuxlzyqTG7W889nhwT+nnR3/zCdDnw9uLb6hIWrfheVC+l1D
+eZRKTQ3U0sNxk72TV6EkekTLfetQDD44a+kFoWLaCRmsXrOI55FxSRph2WkD7GOX
+7EAflt0cDzwkV0F7AoHAbiVfO5imCuGl3SZGG+aPvcHpNj+9pJft5esULJiZZe3I
+6lryXjgjql/d4p0VqV6miL535CPaggknYvDn/4v9aiuovvcsrARAjLZHYHNj3wpR
+S8hjDQtAM+FpQn+RExnLQf7p00nIX+yPOu3lp13kJ+j5jT8cTSm9Bi1wVXMulIWH
++p18RXNdg3hgUliM2/NwXxdKgBEXYNCu6PhlRcoIPC5DUXqSYoDxT6bTUSJduQoo
+zVU1usJWin2FXdEtQIt1AoHAG0JIyXgEjYlLd7neRUvMT19CyJ7H5pipRBNGPmqY
+0rTsXxPo3htYCJnPd3/vSVZ6YMhztWN9PxVcv4zyo5AkoYwXIoFezUy5Gs/81eZW
+H8TTvo/sZRwdRPfN8a8eULFVUByBrVx5+2fXEQvq6FrlI056WWNb2LbBy9V5+37I
+3DQASpLlDDFdMVXtADPDoVoSJbiDcoA9Y3KCJ4a9qgLBCzMjZRAzoCobaTjmcut4
+1Peox0uGkHST86FZUyHbn9C5
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-certpol2.pem b/contrib/wpa/tests/hwsim/auth_serv/server-certpol2.pem
new file mode 100644
index 000000000000..92c853da2fa8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-certpol2.pem
@@ -0,0 +1,102 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cd:64
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 2 19:55:38 2020 GMT
+ Not After : May 2 19:55:38 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=server-policies2.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (3072 bit)
+ Modulus:
+ 00:e6:50:d1:28:05:27:6c:d4:42:d3:42:c7:cc:53:
+ a9:6b:c2:a3:ab:20:6f:17:34:9d:e4:1d:31:d0:c4:
+ 68:65:42:d5:e8:bd:aa:b8:e4:e9:f6:ee:2e:75:cd:
+ 6a:8c:f6:8e:aa:af:6c:7c:eb:35:50:55:b1:c2:b3:
+ f8:a7:d0:1e:a3:33:26:68:40:20:13:74:08:87:ef:
+ b6:3a:f1:7f:3e:f5:7f:93:44:74:37:8b:c3:89:a7:
+ ba:5f:42:39:4a:22:20:40:66:c6:6f:77:cf:54:3f:
+ 05:61:de:ab:71:fa:9f:78:6d:97:4a:e8:60:a0:11:
+ aa:6c:ba:9d:44:65:18:e8:e0:8f:d1:df:da:95:ab:
+ 41:2c:63:65:f6:42:42:e6:1f:42:a4:1c:74:20:49:
+ f4:d4:92:0e:e3:be:3b:55:fd:38:68:74:3a:e4:df:
+ 9a:2b:95:bf:e9:d1:38:8b:16:24:20:35:db:b8:d0:
+ 15:30:2e:2f:6a:24:b9:e1:e9:e7:b6:b5:bd:e6:0f:
+ 59:dd:df:95:9c:ab:03:b4:c5:42:81:48:61:cc:3c:
+ 06:17:85:63:6e:28:cb:d7:88:d1:ce:40:c7:ff:61:
+ 51:ac:bb:ba:23:c9:42:a7:7d:27:0d:1e:53:90:cf:
+ 22:3f:57:f5:ff:1a:05:0c:9c:7a:1b:fb:e2:47:5f:
+ 45:cb:ee:88:a5:a0:54:71:83:f7:d3:d3:c5:bc:b2:
+ 7e:85:a7:b1:1a:cf:91:cb:8b:39:ba:7c:2c:9d:b2:
+ c5:79:c3:e0:80:73:06:30:4f:5d:81:52:e2:bf:43:
+ 55:85:5d:64:c4:97:ac:e9:2d:7e:30:b5:e1:52:50:
+ 53:0c:ac:24:8e:7b:a2:57:8d:ff:b1:05:ba:23:33:
+ a6:08:a3:b4:38:42:f6:5c:d9:bc:5d:75:95:82:ff:
+ 4d:9a:db:56:2f:1b:38:d2:51:05:2c:90:62:f1:8e:
+ 4c:ee:ea:1b:ae:aa:53:74:85:ba:c6:c3:c2:4b:30:
+ bd:e0:2e:c8:ac:7e:25:cc:32:5f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 4E:01:8B:7E:C2:77:94:E1:68:B3:C4:29:35:24:05:0B:DE:84:4A:89
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ Authority Information Access:
+ OCSP - URI:http://server.w1.fi:8888/
+
+ X509v3 Subject Alternative Name:
+ DNS:server-policies2.w1.fi
+ X509v3 Certificate Policies:
+ Policy: 1.3.6.1.4.1.40808.1.3.2
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ Signature Algorithm: sha256WithRSAEncryption
+ 7d:38:98:e6:21:03:e4:1a:44:08:91:ca:21:31:5c:97:66:74:
+ 4c:0a:84:21:83:92:22:63:53:8d:06:1f:48:62:c1:e3:ce:e9:
+ 74:2a:63:0b:2b:f9:b5:d0:63:37:39:4c:b4:29:9e:98:49:48:
+ 1f:cd:bc:28:5f:81:56:ee:d9:d9:f7:51:6b:31:62:3a:a4:59:
+ 98:f3:18:3d:f9:c1:d8:71:6d:85:e1:67:0e:d6:cc:ab:61:22:
+ 46:f1:38:11:53:74:41:44:22:63:ac:e7:6b:12:b6:39:20:7f:
+ fe:e2:c7:aa:e6:80:64:d7:24:92:4e:79:fa:9d:41:75:45:30:
+ 4b:2b:ce:d9:b0:38:25:79:81:b3:c4:4b:60:a1:24:9f:ad:c7:
+ 37:b9:44:d5:02:7c:2a:05:7f:d3:f1:76:21:6a:67:d7:a9:ab:
+ e0:3e:4c:90:30:28:8a:75:58:ae:6a:98:39:b6:6c:f6:eb:9f:
+ c8:24:11:a3:33:0f:aa:30:05:23:ab:1f:4f:f4:55:f3:b8:6b:
+ c5:dc:dc:32:15:58:fd:cc:cf:ba:f5:9a:1b:4e:58:68:85:b7:
+ eb:b0:db:e9:a9:87:f9:b0:4e:c9:43:79:26:97:75:ff:d4:55:
+ 01:f7:c6:f5:21:56:8b:f7:f3:80:a2:f4:3f:50:2a:e3:60:52:
+ b6:5c:83:14
+-----BEGIN CERTIFICATE-----
+MIIEWjCCA0KgAwIBAgIJANjT46bL481kMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDIxOTU1MzhaFw0yMTA1MDIxOTU1MzhaMD4xCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEfMB0GA1UEAwwWc2VydmVyLXBvbGlj
+aWVzMi53MS5maTCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAOZQ0SgF
+J2zUQtNCx8xTqWvCo6sgbxc0neQdMdDEaGVC1ei9qrjk6fbuLnXNaoz2jqqvbHzr
+NVBVscKz+KfQHqMzJmhAIBN0CIfvtjrxfz71f5NEdDeLw4mnul9COUoiIEBmxm93
+z1Q/BWHeq3H6n3htl0roYKARqmy6nURlGOjgj9Hf2pWrQSxjZfZCQuYfQqQcdCBJ
+9NSSDuO+O1X9OGh0OuTfmiuVv+nROIsWJCA127jQFTAuL2okueHp57a1veYPWd3f
+lZyrA7TFQoFIYcw8BheFY24oy9eI0c5Ax/9hUay7uiPJQqd9Jw0eU5DPIj9X9f8a
+BQycehv74kdfRcvuiKWgVHGD99PTxbyyfoWnsRrPkcuLObp8LJ2yxXnD4IBzBjBP
+XYFS4r9DVYVdZMSXrOktfjC14VJQUwysJI57oleN/7EFuiMzpgijtDhC9lzZvF11
+lYL/TZrbVi8bONJRBSyQYvGOTO7qG66qU3SFusbDwkswveAuyKx+JcwyXwIDAQAB
+o4HXMIHUMAkGA1UdEwQCMAAwHQYDVR0OBBYEFE4Bi37Cd5ThaLPEKTUkBQvehEqJ
+MB8GA1UdIwQYMBaAFKT9uTkbgbOq64gd1IGptRFwzKfhMDUGCCsGAQUFBwEBBCkw
+JzAlBggrBgEFBQcwAYYZaHR0cDovL3NlcnZlci53MS5maTo4ODg4LzAhBgNVHREE
+GjAYghZzZXJ2ZXItcG9saWNpZXMyLncxLmZpMBgGA1UdIAQRMA8wDQYLKwYBBAGC
+vmgBAwIwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAH04
+mOYhA+QaRAiRyiExXJdmdEwKhCGDkiJjU40GH0hiwePO6XQqYwsr+bXQYzc5TLQp
+nphJSB/NvChfgVbu2dn3UWsxYjqkWZjzGD35wdhxbYXhZw7WzKthIkbxOBFTdEFE
+ImOs52sStjkgf/7ix6rmgGTXJJJOefqdQXVFMEsrztmwOCV5gbPES2ChJJ+txze5
+RNUCfCoFf9PxdiFqZ9epq+A+TJAwKIp1WK5qmDm2bPbrn8gkEaMzD6owBSOrH0/0
+VfO4a8Xc3DIVWP3Mz7r1mhtOWGiFt+uw2+mph/mwTslDeSaXdf/UVQH3xvUhVov3
+84Ci9D9QKuNgUrZcgxQ=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.csr b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.csr
new file mode 100644
index 000000000000..5546903e5649
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICjDCCAXQCAQAwRzELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAM
+BgNVBAoMBXcxLmZpMRYwFAYDVQQDDA1zZXJ2ZXI2LncxLmZpMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0CP0gBK+SmRNr5mvUu+N9XOaGigrkujUkCU+
+hUefycS5ejUkhC8eURZm06wx6vFjpEzqJvD6Ycef8nRpangJxcmJdNttuse2sfBh
+H+86HNXG/JVdmzfWW8s3k1ntUPJqogFcniKvOjHZ7uszSZNORu6de4aG2isd+fOi
+AX0NVRw7Z+nJ+7ypUkxKIYVoUC/kBcE/4LOjJdRsLmF8ndXak7sZ/uq/8sj53N5I
+VOH+1LWUWj8sK4yxbO86sNIMLBN1YduXa/pr+Z33FKo1cthMC6FcCMWH1OSHHWsK
+UB+1Dj+7NovG4L0eGuEc8zekkWVMQ7SezBthaAm9HqthvcGRcQIDAQABoAAwDQYJ
+KoZIhvcNAQELBQADggEBABgknYle2ID7r8gu0VCYupOKsdU0CIfxqozbW2REuWnO
+W5EYv/oma1ONr3DPr/pLfeCVxtqRLNBC4UAi6Pxsn4A8kxm93voZ2/9b+fvwfrqo
+yKgo2X2+fn/k3IeRvKdq8o3frVzdBZmVv1irbrXeel7IRyjvG6nqwoT5jhCI4F8m
+iAht0otWVPdyuIXmHsofB6wgkmFw8AqHIuKS2gl8zeByGkfO/bCFrv1G2rEacyjt
+/pLaeI2VYZW5i+JvoAXSqAzV6xpc13Tts4MlQhSw8diE/NVsw7uBuJQaiE+vpgvm
+1jmcmIttnkZmvkhvdW3P62OttNVGiyBfq/GVPhOfeKE=
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.key b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.key
new file mode 100644
index 000000000000..42103e4ae907
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDQI/SAEr5KZE2v
+ma9S7431c5oaKCuS6NSQJT6FR5/JxLl6NSSELx5RFmbTrDHq8WOkTOom8Pphx5/y
+dGlqeAnFyYl02226x7ax8GEf7zoc1cb8lV2bN9ZbyzeTWe1Q8mqiAVyeIq86Mdnu
+6zNJk05G7p17hobaKx3586IBfQ1VHDtn6cn7vKlSTEohhWhQL+QFwT/gs6Ml1Gwu
+YXyd1dqTuxn+6r/yyPnc3khU4f7UtZRaPywrjLFs7zqw0gwsE3Vh25dr+mv5nfcU
+qjVy2EwLoVwIxYfU5IcdawpQH7UOP7s2i8bgvR4a4RzzN6SRZUxDtJ7MG2FoCb0e
+q2G9wZFxAgMBAAECggEAL7pO8p9Zq01c0wt2vJnZ/5LGn4DenQ1u0K75qy5FYcsO
+jQtqmDUFyqpMYfV2bN11b9ODTfKsN4fDEaHIGnX0J7qTHozCmLX7Gsp4Ow5sUPhk
+bL8De/hN2za6Q7u3Q3yTHxsY1do2PC36P2MHm7N/m5xT2GN8wMJqWUqYt6apS/6H
+c2UkjhopRH17WhIEIvWLhZ6IYahRpaDk6zlYTwbVJ/0T/mmK/Wmpmr/aeSVkG980
+MQUHugdOrrkV8+WFxlzzpIRa+3XFmxXNOuhXemz23tS4JEBsD0gdesvoWuszqysd
+1n+W5j+OpksiF1DFWSuMKFFqurd91yjOhAsM5ex1kQKBgQDnjTpvSBTMDGNGm8qo
+9POjIqa/8zS0yzwYLB4/pzym5eaIEOAq+H0W+EU8h5zes3E1lGLKO/2iT3lWTxzo
+E7Fq9I2AbzMSgyZiEJq6IfiLMRhh51sPTGX+KIjC44fdfofdTc2GNePsf/IP+JGy
+DPv/8mU+j0heSwyLZJCSvoNdCwKBgQDmHe2z6MIb2Rze3vgJERIrTRfxyRjwKRUd
+xI7QEe/fRjhlCNyzP9sQZzJFXNean4qNg0SOGy8+KjTgI+n9HxUTvLADxnVtey3I
+G78JVu5QJ4onJ3iAlCSlY6exiY9ZQjI6akCC748t03WNLQXO6lUsopLZqOx8oP+M
+84UFoNjA8wKBgERiTj6tQA8fHXat7gVGCmpEgpCv6AH4/6934BsWbfAwd4v5x+qI
+5pCRFAmTV33h6u5S+3YUj4yPAhu+U6AqqLwYq22h6ahu+Tf/BWMxQzEAd936MMds
+3bZZDELaZbbBdqiiIK+hXMXs53VWCNlXwljNop7+O/Y1HehQ8+2SvEMPAoGBAJhn
+5//Iv46MHBfr2qC+oqb1F0+2nYKp4udlQCTETHc23bDkzq8VMrRJdL0FwXISCkSx
+VN09Weu1LnHot1dCl8YLqRPHBAzvkSHAZqT74zhJB7Ho7WFTPHYha3YlIkC+m9+e
+cX2GxfBW5bsLv5YMEz9NqS7pNz9PrhEfU9GndwdLAoGAJ4f7qIUTweLL+295Q/dx
+lGlBzkTkfw0kiEEOgWwjXbox1NJnsfrneGvPgccTeMtimtkGk/vTUtIuo4EDwjJ7
+mcUnhXIgHGngx8bOzt4G3RGOLAaf1l+IcBhxqLJFhArDZYSVYMQ6vwwRuyXfO+I9
+4It3NqEusGrCV/ydOmKtXEg=
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.pem b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.pem
new file mode 100644
index 000000000000..b44f82c54a12
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client-server.pem
@@ -0,0 +1,85 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cd:62
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 2 19:55:38 2020 GMT
+ Not After : May 2 19:55:38 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=server6.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:d0:23:f4:80:12:be:4a:64:4d:af:99:af:52:ef:
+ 8d:f5:73:9a:1a:28:2b:92:e8:d4:90:25:3e:85:47:
+ 9f:c9:c4:b9:7a:35:24:84:2f:1e:51:16:66:d3:ac:
+ 31:ea:f1:63:a4:4c:ea:26:f0:fa:61:c7:9f:f2:74:
+ 69:6a:78:09:c5:c9:89:74:db:6d:ba:c7:b6:b1:f0:
+ 61:1f:ef:3a:1c:d5:c6:fc:95:5d:9b:37:d6:5b:cb:
+ 37:93:59:ed:50:f2:6a:a2:01:5c:9e:22:af:3a:31:
+ d9:ee:eb:33:49:93:4e:46:ee:9d:7b:86:86:da:2b:
+ 1d:f9:f3:a2:01:7d:0d:55:1c:3b:67:e9:c9:fb:bc:
+ a9:52:4c:4a:21:85:68:50:2f:e4:05:c1:3f:e0:b3:
+ a3:25:d4:6c:2e:61:7c:9d:d5:da:93:bb:19:fe:ea:
+ bf:f2:c8:f9:dc:de:48:54:e1:fe:d4:b5:94:5a:3f:
+ 2c:2b:8c:b1:6c:ef:3a:b0:d2:0c:2c:13:75:61:db:
+ 97:6b:fa:6b:f9:9d:f7:14:aa:35:72:d8:4c:0b:a1:
+ 5c:08:c5:87:d4:e4:87:1d:6b:0a:50:1f:b5:0e:3f:
+ bb:36:8b:c6:e0:bd:1e:1a:e1:1c:f3:37:a4:91:65:
+ 4c:43:b4:9e:cc:1b:61:68:09:bd:1e:ab:61:bd:c1:
+ 91:71
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ AB:D2:88:CA:9C:44:26:89:2E:C0:B9:8D:46:DD:5C:69:02:9E:01:CB
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ Authority Information Access:
+ OCSP - URI:http://server.w1.fi:8888/
+
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication, TLS Web Server Authentication
+ Signature Algorithm: sha256WithRSAEncryption
+ 5f:6e:13:f9:af:c4:47:4d:78:19:5e:d2:bb:21:55:c3:4b:64:
+ 42:94:fe:37:7b:3a:4a:fc:42:f1:fc:b3:c3:05:93:46:39:cd:
+ a3:40:c9:90:47:a2:6b:af:d8:21:a9:1e:11:02:c8:84:e2:b2:
+ 8b:52:ad:30:49:e7:80:16:98:d2:0c:01:56:c2:f5:6c:a4:98:
+ b0:a2:af:6c:e8:6e:6d:9b:31:21:22:91:51:81:e1:f0:0d:eb:
+ 97:96:98:58:84:b3:29:a6:8f:d2:b5:ce:37:a7:64:b8:7f:fb:
+ f7:15:3c:c0:c7:2a:7f:bb:50:67:a0:5b:55:65:5d:1f:0a:90:
+ 10:16:c1:93:cd:a3:ab:8b:4b:9a:f0:e2:e7:ac:e6:5a:fd:bf:
+ 46:37:92:3e:f7:f5:d8:57:87:c2:88:cc:b1:40:06:92:d5:f0:
+ f2:3d:c5:d0:fd:48:5c:bf:bf:5b:da:82:11:55:6d:95:17:f2:
+ 43:be:8e:e7:f5:0e:d3:b3:de:65:ea:8c:85:4b:bd:4d:93:f0:
+ 6f:8b:2f:0e:fb:9f:cb:65:e8:72:68:92:43:08:1d:3e:1f:5a:
+ e5:1c:5d:7e:16:06:04:23:9e:c0:82:8a:a6:33:66:c3:3f:2a:
+ ad:1a:5a:90:02:56:3a:e6:45:d9:f1:02:a5:cd:16:63:03:04:
+ 42:85:1c:49
+-----BEGIN CERTIFICATE-----
+MIIDnjCCAoagAwIBAgIJANjT46bL481iMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDIxOTU1MzhaFw0yMTA1MDIxOTU1MzhaMDUxCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEWMBQGA1UEAwwNc2VydmVyNi53MS5m
+aTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANAj9IASvkpkTa+Zr1Lv
+jfVzmhooK5Lo1JAlPoVHn8nEuXo1JIQvHlEWZtOsMerxY6RM6ibw+mHHn/J0aWp4
+CcXJiXTbbbrHtrHwYR/vOhzVxvyVXZs31lvLN5NZ7VDyaqIBXJ4irzox2e7rM0mT
+TkbunXuGhtorHfnzogF9DVUcO2fpyfu8qVJMSiGFaFAv5AXBP+CzoyXUbC5hfJ3V
+2pO7Gf7qv/LI+dzeSFTh/tS1lFo/LCuMsWzvOrDSDCwTdWHbl2v6a/md9xSqNXLY
+TAuhXAjFh9Tkhx1rClAftQ4/uzaLxuC9HhrhHPM3pJFlTEO0nswbYWgJvR6rYb3B
+kXECAwEAAaOBpDCBoTAJBgNVHRMEAjAAMB0GA1UdDgQWBBSr0ojKnEQmiS7AuY1G
+3VxpAp4ByzAfBgNVHSMEGDAWgBSk/bk5G4GzquuIHdSBqbURcMyn4TA1BggrBgEF
+BQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8w
+HQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA0GCSqGSIb3DQEBCwUAA4IB
+AQBfbhP5r8RHTXgZXtK7IVXDS2RClP43ezpK/ELx/LPDBZNGOc2jQMmQR6Jrr9gh
+qR4RAsiE4rKLUq0wSeeAFpjSDAFWwvVspJiwoq9s6G5tmzEhIpFRgeHwDeuXlphY
+hLMppo/Stc43p2S4f/v3FTzAxyp/u1BnoFtVZV0fCpAQFsGTzaOri0ua8OLnrOZa
+/b9GN5I+9/XYV4fCiMyxQAaS1fDyPcXQ/Uhcv79b2oIRVW2VF/JDvo7n9Q7Ts95l
+6oyFS71Nk/Bviy8O+5/LZehyaJJDCB0+H1rlHF1+FgYEI57AgoqmM2bDPyqtGlqQ
+AlY65kXZ8QKlzRZjAwRChRxJ
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-eku-client.csr b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client.csr
new file mode 100644
index 000000000000..8fe7071e1291
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICjDCCAXQCAQAwRzELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAM
+BgNVBAoMBXcxLmZpMRYwFAYDVQQDDA1zZXJ2ZXI1LncxLmZpMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEApZRggLjIYRiMAp3czMR00Re1O0ecOA87knT7
+6idFRIqaKk0QJY53zhoJyXMZ/txwNl6M+o9MhLv3cuSUT/xHKn2JkqMrWmmcyFoQ
+OTJhfQp4GbQ266xM1q91ABROFS9fg+5i9ax7DIG6ogg2e/DvYzFi+4amz9o2g0SN
+dSi25BDzMt2KbFvuT/EeUwsTfMe8954ygB5jPpJ1L8UhXvAqrOI05BeyNPfoKhKK
+IbgD57bY+DK1/nFFUpjeuT1B9ZCldoPBGMpQXSxSi25Pp1u72OMUJXDe0cedWc8k
+Rsf1bm+DZu0bHT5RBJRnZN9RIjzA4SQKN2rcaov9RVuWLQOsYwIDAQABoAAwDQYJ
+KoZIhvcNAQELBQADggEBAGDFw8louhTAswtYHa+aFvsSEB209lYFdHxn3wohbK1r
+q3IPcuTiQdZR2jEllGVaXZC6eAkYO8iD+NL/iCteUivY6Jqrd5cM0IAzPLuNe89O
+SSnPqUep59LObZUAsW/KaOB75xsLbm68fG2NmwOBB+8ZCRvQowcbY6nEAgaFM46V
+UxOHr3ZdluhAyVIikmZLmXEbv5OaXZfc3PiifJIDgAmMf9ePjm6QZEQJ5RdBxlWT
+IhU0rz9haagA13hXWurUCo8gWZoQqqCinjxLu0dV62kVCgq5Bk8HE4gvswJvCqME
+TKEpPJBjmKGTeU1BbFWy6nrirsCVPybj841pMQkSWHY=
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-eku-client.key b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client.key
new file mode 100644
index 000000000000..a43976ccd97a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCllGCAuMhhGIwC
+ndzMxHTRF7U7R5w4DzuSdPvqJ0VEipoqTRAljnfOGgnJcxn+3HA2Xoz6j0yEu/dy
+5JRP/EcqfYmSoytaaZzIWhA5MmF9CngZtDbrrEzWr3UAFE4VL1+D7mL1rHsMgbqi
+CDZ78O9jMWL7hqbP2jaDRI11KLbkEPMy3YpsW+5P8R5TCxN8x7z3njKAHmM+knUv
+xSFe8Cqs4jTkF7I09+gqEoohuAPnttj4MrX+cUVSmN65PUH1kKV2g8EYylBdLFKL
+bk+nW7vY4xQlcN7Rx51ZzyRGx/Vub4Nm7RsdPlEElGdk31EiPMDhJAo3atxqi/1F
+W5YtA6xjAgMBAAECggEAJ+QYX8qk0+ejC5pWsKp/7kQE8JQvCb55vq4aZu4xHPM7
+cwd/5VxudqQFSZhGYgVfr2mWE2NkrvHOCssRBDgmORFnjIFtF2osUISKNg1yOTrF
+doPZW2v2Ux6QVIWPzkDMhS9wffmg54F1okXSQofoVIB4dVqaY6cRzQw9/ETj0wvz
+JwSstS76VbTZSzXl/IMMiIlGLq2o4SVmTDgK5Uz8ouOIIzoVG4tQCjtAPCfu1pG9
+VYTCfE1gnGFx1bl3p1yoh468h1PqyYqDgo9heyU+aYk21v/Rm0ARj2TSkJcSF5Cv
+Y3JUg1oaIMw7HxXEnkw+L3sqy9alGkJ33pbOAzva6QKBgQDO9H7ToPJysx2cO2/a
+Jk7OvoyQ0AMNG7lNS4crG9SvL0SbxfDdif9yaDlasT3T18uTMuRJRo6vF8v820LY
+HIdmBT6FxC+zwBRKnXMFtSrZIsl4zpOeHW2pOTM4So92K9NlSGOogQulzQyE8yeL
+kHAJmnAevUMyxgQ/S8xpTJrzfQKBgQDM0blWZ91B4VvqP7MzdjPHI1yXt5miEY9P
+ltTtTnmjFjvLweheoyYPW64tvxyRueEPNQB39BbYax3Zweg7TPng/lOMEwMG40dT
+a4LBMK74r0OLvPfds6jSGnENmyNkUZhTCf+hgXOMeiXGqjFAIPQoA+23tNNaDPRG
+emIjx69lXwKBgE4QmfqYPnwXpna1UObYBmgkJn/FhzEdoRNQByeystJ2IQola0sV
+796nA+N68hiD0Q2wZ75gOBhCALdbueYtNMG9/qyUqW3DaaQPqkCf6w7G+Xpxaet9
+rEzl/7UfIuhvdalB2h3It60OIMfRtLwHesuUjvB5ceyoFxgNLokV1Wk9AoGANMRD
+L1OK2RIqD+thS3zEUiV2EVAnsG09so91Q73X8IAl35SRPPBjOcmw0fBOd+yfYr+Q
+41ZrHE5cXmFqZvyp06Ex/QBY40licsdb5FGagk8E49dHNEK414ggYBT7xTiQObR1
+uzIShrphSRFHpvHWdQiuEYnweV6lABM/fWBQe5kCgYBJWEJSAkyp8L4TIlt41ctK
+MSjXuSwO1ktUYxQwIRZn/qcTxAAZLeE4Ow50Eoz7qtdMpn9/UdogpVpeZ9ZbSFSh
+2OD15rQJWVWs9ftgV8Ny3LzCdchmIw4/pRFMkK1ECog6F2WecwYUspEWgfGTy50V
+JyZlR6lQlgsLo0xLZJYyYA==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-eku-client.pem b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client.pem
new file mode 100644
index 000000000000..2e6afa2876c3
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-eku-client.pem
@@ -0,0 +1,85 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cd:61
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 2 19:55:38 2020 GMT
+ Not After : May 2 19:55:38 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=server5.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:a5:94:60:80:b8:c8:61:18:8c:02:9d:dc:cc:c4:
+ 74:d1:17:b5:3b:47:9c:38:0f:3b:92:74:fb:ea:27:
+ 45:44:8a:9a:2a:4d:10:25:8e:77:ce:1a:09:c9:73:
+ 19:fe:dc:70:36:5e:8c:fa:8f:4c:84:bb:f7:72:e4:
+ 94:4f:fc:47:2a:7d:89:92:a3:2b:5a:69:9c:c8:5a:
+ 10:39:32:61:7d:0a:78:19:b4:36:eb:ac:4c:d6:af:
+ 75:00:14:4e:15:2f:5f:83:ee:62:f5:ac:7b:0c:81:
+ ba:a2:08:36:7b:f0:ef:63:31:62:fb:86:a6:cf:da:
+ 36:83:44:8d:75:28:b6:e4:10:f3:32:dd:8a:6c:5b:
+ ee:4f:f1:1e:53:0b:13:7c:c7:bc:f7:9e:32:80:1e:
+ 63:3e:92:75:2f:c5:21:5e:f0:2a:ac:e2:34:e4:17:
+ b2:34:f7:e8:2a:12:8a:21:b8:03:e7:b6:d8:f8:32:
+ b5:fe:71:45:52:98:de:b9:3d:41:f5:90:a5:76:83:
+ c1:18:ca:50:5d:2c:52:8b:6e:4f:a7:5b:bb:d8:e3:
+ 14:25:70:de:d1:c7:9d:59:cf:24:46:c7:f5:6e:6f:
+ 83:66:ed:1b:1d:3e:51:04:94:67:64:df:51:22:3c:
+ c0:e1:24:0a:37:6a:dc:6a:8b:fd:45:5b:96:2d:03:
+ ac:63
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 32:9F:9F:30:24:73:73:CB:8D:53:3A:80:23:EB:5B:5D:4C:DD:06:01
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ Authority Information Access:
+ OCSP - URI:http://server.w1.fi:8888/
+
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ Signature Algorithm: sha256WithRSAEncryption
+ 22:02:38:3d:90:2f:5d:54:b9:36:61:fd:29:40:c0:88:5d:eb:
+ 63:ec:b3:6d:9b:55:8f:10:6b:b7:4b:8a:3f:89:79:fa:52:87:
+ 8d:91:3b:2e:ee:84:ae:f8:2d:8e:1d:35:72:cd:b8:7d:9d:98:
+ d3:88:9d:05:c7:85:e7:1a:29:4d:cb:00:da:a3:21:a0:f5:f3:
+ 52:f5:80:88:cb:2a:4f:d9:9b:56:c0:37:13:61:74:64:61:fb:
+ 8c:25:18:9c:96:e2:f8:bb:e2:48:60:e3:12:d8:a9:d9:9e:93:
+ e8:cd:46:f5:eb:b3:17:62:66:d1:5d:ea:c2:09:d1:7a:34:d2:
+ e0:88:1d:7f:6f:71:25:70:50:d8:51:93:61:8e:70:da:c2:ba:
+ f0:44:81:be:81:54:d6:3c:da:a6:54:62:40:bd:d1:2e:ce:1c:
+ dd:29:49:ba:b5:12:7e:42:64:54:b2:99:93:60:67:6e:1a:63:
+ 4b:da:b4:96:28:90:81:c4:28:05:28:64:ff:c6:7a:b3:8c:68:
+ 12:e3:28:64:00:82:88:bc:75:46:d2:e7:f9:0a:93:4c:5d:c8:
+ 99:27:4c:40:65:0d:ec:b2:86:ea:76:e2:28:c5:77:6b:3d:fc:
+ 91:30:89:0a:0b:e0:d4:59:cf:30:de:5f:f6:50:15:5a:40:01:
+ e2:a5:39:cf
+-----BEGIN CERTIFICATE-----
+MIIDlDCCAnygAwIBAgIJANjT46bL481hMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDIxOTU1MzhaFw0yMTA1MDIxOTU1MzhaMDUxCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEWMBQGA1UEAwwNc2VydmVyNS53MS5m
+aTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKWUYIC4yGEYjAKd3MzE
+dNEXtTtHnDgPO5J0++onRUSKmipNECWOd84aCclzGf7ccDZejPqPTIS793LklE/8
+Ryp9iZKjK1ppnMhaEDkyYX0KeBm0NuusTNavdQAUThUvX4PuYvWsewyBuqIINnvw
+72MxYvuGps/aNoNEjXUotuQQ8zLdimxb7k/xHlMLE3zHvPeeMoAeYz6SdS/FIV7w
+KqziNOQXsjT36CoSiiG4A+e22Pgytf5xRVKY3rk9QfWQpXaDwRjKUF0sUotuT6db
+u9jjFCVw3tHHnVnPJEbH9W5vg2btGx0+UQSUZ2TfUSI8wOEkCjdq3GqL/UVbli0D
+rGMCAwEAAaOBmjCBlzAJBgNVHRMEAjAAMB0GA1UdDgQWBBQyn58wJHNzy41TOoAj
+61tdTN0GATAfBgNVHSMEGDAWgBSk/bk5G4GzquuIHdSBqbURcMyn4TA1BggrBgEF
+BQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8w
+EwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQELBQADggEBACICOD2QL11U
+uTZh/SlAwIhd62Pss22bVY8Qa7dLij+JefpSh42ROy7uhK74LY4dNXLNuH2dmNOI
+nQXHhecaKU3LANqjIaD181L1gIjLKk/Zm1bANxNhdGRh+4wlGJyW4vi74khg4xLY
+qdmek+jNRvXrsxdiZtFd6sIJ0Xo00uCIHX9vcSVwUNhRk2GOcNrCuvBEgb6BVNY8
+2qZUYkC90S7OHN0pSbq1En5CZFSymZNgZ24aY0vatJYokIHEKAUoZP/GerOMaBLj
+KGQAgoi8dUbS5/kKk0xdyJknTEBlDeyyhup24ijFd2s9/JEwiQoL4NRZzzDeX/ZQ
+FVpAAeKlOc8=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-expired.csr b/contrib/wpa/tests/hwsim/auth_serv/server-expired.csr
new file mode 100644
index 000000000000..f06a33da1426
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-expired.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICjDCCAXQCAQAwRzELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAM
+BgNVBAoMBXcxLmZpMRYwFAYDVQQDDA1zZXJ2ZXI0LncxLmZpMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsHfxPmbn/AtR+dijri/2SnU4PtRHe9YkMrTI
+2eDQpRL0iaeJAdlUQ86BSr3tFvN8wWc2i3NNIZHnS350xTsVuMZGfqmikcP1kLPP
++Qzrum/uuat3PQOenXcHv5dq1E222v02VCXjCSaJf6ERwfbcvlxXqOZFVz2YFAZy
+rOnIgQY4nM/NCg54Tp57EMJhpUPvNBbfPOCjRHdIzb7kecsxOZ9T3aMOdlpsJF5W
+NZuifbOeQvFhnOieHLiaEB4yKSHLMBbgAxH5iPKPBKXmp5xz4ZPYUS27RYOPtpNB
+OUGEX0utACWRPRYK6/C4kuBcdWWFF9KA5l5moqTfxwh2M0nPHQIDAQABoAAwDQYJ
+KoZIhvcNAQELBQADggEBAIeunczvT7br/9Jk6zARkS7gZpAeRckiMMPFHD1HLiFM
+ngU/PL4RD0TRF0cHGn+qJex7Ch97ZMHsGl0ECjXEL84UYnAdWGPddLv72XpeNX+d
+f/QTWg9jVrZGspI1he6jN9JghZatKDEPYrXhFv0JbxrA4LoUzV2qGgh2ALpmP0LV
+Xqje+tAoZbf8J7mba/Z2yqjJuJMxkOC+2cCUvN07+ndCGbixtzT2wZfPlVkp/af2
+HJyduA6qkLJWcrAER6jHaI3Cxq92u/H7D6Z++7v0vN8fV6inyZNadurUGY/VsIUn
+jorEWeP7v1UKgLXXqBTdP5YA0Gi3O0dx7iLGalbHV9s=
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-expired.key b/contrib/wpa/tests/hwsim/auth_serv/server-expired.key
new file mode 100644
index 000000000000..545beab67f03
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-expired.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCwd/E+Zuf8C1H5
+2KOuL/ZKdTg+1Ed71iQytMjZ4NClEvSJp4kB2VRDzoFKve0W83zBZzaLc00hkedL
+fnTFOxW4xkZ+qaKRw/WQs8/5DOu6b+65q3c9A56ddwe/l2rUTbba/TZUJeMJJol/
+oRHB9ty+XFeo5kVXPZgUBnKs6ciBBjicz80KDnhOnnsQwmGlQ+80Ft884KNEd0jN
+vuR5yzE5n1Pdow52WmwkXlY1m6J9s55C8WGc6J4cuJoQHjIpIcswFuADEfmI8o8E
+peannHPhk9hRLbtFg4+2k0E5QYRfS60AJZE9Fgrr8LiS4Fx1ZYUX0oDmXmaipN/H
+CHYzSc8dAgMBAAECggEBAJjiMQUJDm6UOB8nCxd7wfrb9zCnpI6rBY1QhroMRXbe
+JzGjDdWmPZTJMcZZKTC7HhhInT7PU8GDsEj9c5j0CWudi7FsscVrajJFNibkhM1u
+7/m3jYQ0wJRXbUUVn53y/jpXKVxZvopM8s658rKCdtgNFHzlkql0WW7v7yXTHLx6
+AM+559Y+LZZ3jAndrHdEpM1vCAG0VE85Ycv+1lBqlFEOthrWDL16UX6BBK5mjUsZ
+QtSUzn8q5OfX8DVKOlZNA85+kdJAK2ysx13DApmFr9unvH0kKfp06sFMOLbV09dF
+kJSNmzWGthVU5oo9rln7L2ctLzjwYfYCC2x36WREI5kCgYEA29J6FYwptBCWBiep
+UnRmGD/9UWr77jyNfYSZpYq5WZZ5swHTdkIeBu6f/u43adLjqcggsWtDZKEMbaZG
+pE3K+8NRUvw2NOt0oBVtYvSyAuDLlOroA3CcEu8089dnojSnENQe6vSsUh54qe5i
+LS7VdJGv2LyT7828Df0JhLL93CcCgYEAzYLk9DTZ5rIQ7AIhyW+IoduQUWhnfS/j
+usueMnvkpuYf+mVtbl1Xn8HiJPaiLrjwu/VSE5zim1tHEXRRhw4euG6p5s1V28S+
+mSOHr+jgLFZQ2hRKZKaV/8ayWJYYtLQ6E7n61mwvoeXUfOnRrP2/drWjJ9MUYt+/
+oTfS7eATERsCgYBfIYVoEdJydMMYQs3KO0l7sSWluJDylw38hgggVhrEpJRiXaXw
+BckM4vQm1Vzx1Sxla5CKd4sg33mLcmwb6vavYeWt7ixfVo6QQPWn35GyISq5dbeW
+1YMVxqO56zyUPAkZBVOkBuMUXs+Fav7d4ujJm8roFyRGoViDDUCzRusJ/QKBgQDF
+z0zjRg/K/vBMyoyM4D8qVDVoNk8Ob08KmDzwKNJgVzbGhGQ9i7jwu+UZYQ+gW0DU
+GgBjgmmX0dbpFQX4Mf4d1d7RmikfPROcQVe0WTmVU4vFLSyiDrpolG9L10V2gdc4
+75ViWIXMlnTduw2oLiHheFnP1ltUBDvmSN5NOpX/qwKBgQCmAR9C6xL68ZFO6HR1
+wswgZEDks4Da1ibWm8uw54YmdT5nG8CakhGwzLcS0Np3xvQ1WgUA1ic2XnHXHwuI
+piU5MbI8+O0hdPQLG4meuZeWINt3QDH5OzuwPCwhZCZkrpG9IfrIAaaaltKHaLMC
+bBd+f4vilJMr+V+VPOKFoUBibg==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-expired.pem b/contrib/wpa/tests/hwsim/auth_serv/server-expired.pem
new file mode 100644
index 000000000000..308d57fad51c
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-expired.pem
@@ -0,0 +1,85 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cd:66
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: Jan 1 00:00:00 2020 GMT
+ Not After : Jan 2 00:00:00 2020 GMT
+ Subject: C=FI, O=w1.fi, CN=server4.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:b0:77:f1:3e:66:e7:fc:0b:51:f9:d8:a3:ae:2f:
+ f6:4a:75:38:3e:d4:47:7b:d6:24:32:b4:c8:d9:e0:
+ d0:a5:12:f4:89:a7:89:01:d9:54:43:ce:81:4a:bd:
+ ed:16:f3:7c:c1:67:36:8b:73:4d:21:91:e7:4b:7e:
+ 74:c5:3b:15:b8:c6:46:7e:a9:a2:91:c3:f5:90:b3:
+ cf:f9:0c:eb:ba:6f:ee:b9:ab:77:3d:03:9e:9d:77:
+ 07:bf:97:6a:d4:4d:b6:da:fd:36:54:25:e3:09:26:
+ 89:7f:a1:11:c1:f6:dc:be:5c:57:a8:e6:45:57:3d:
+ 98:14:06:72:ac:e9:c8:81:06:38:9c:cf:cd:0a:0e:
+ 78:4e:9e:7b:10:c2:61:a5:43:ef:34:16:df:3c:e0:
+ a3:44:77:48:cd:be:e4:79:cb:31:39:9f:53:dd:a3:
+ 0e:76:5a:6c:24:5e:56:35:9b:a2:7d:b3:9e:42:f1:
+ 61:9c:e8:9e:1c:b8:9a:10:1e:32:29:21:cb:30:16:
+ e0:03:11:f9:88:f2:8f:04:a5:e6:a7:9c:73:e1:93:
+ d8:51:2d:bb:45:83:8f:b6:93:41:39:41:84:5f:4b:
+ ad:00:25:91:3d:16:0a:eb:f0:b8:92:e0:5c:75:65:
+ 85:17:d2:80:e6:5e:66:a2:a4:df:c7:08:76:33:49:
+ cf:1d
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 21:B0:31:C6:14:D4:BD:5C:DF:70:24:51:34:9E:93:F5:18:B3:1C:A1
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ Authority Information Access:
+ OCSP - URI:http://server.w1.fi:8888/
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ Signature Algorithm: sha256WithRSAEncryption
+ 89:4d:ee:04:3e:50:fc:a2:6e:4c:3e:4a:9e:3b:9c:2e:74:29:
+ 06:86:1b:bb:96:01:70:f7:46:21:b4:ef:6f:73:93:31:bd:58:
+ f5:2f:40:61:f1:53:86:20:75:cf:0e:75:70:2c:94:b8:c5:4e:
+ ec:24:0f:42:d6:8b:80:b9:fa:b5:48:83:d6:cf:c8:47:3d:09:
+ 50:11:4a:5d:83:c5:41:8b:4b:4e:1e:ff:96:95:f0:14:7a:7e:
+ cd:a6:4f:ce:0b:37:e8:f2:27:a2:72:e2:6b:18:d7:f8:86:f0:
+ 14:db:4c:c5:8a:76:9b:fc:55:15:49:3f:eb:df:5c:c7:7a:64:
+ 86:70:44:97:7e:ba:83:39:25:3b:23:8e:dc:b3:9e:59:cb:e0:
+ a2:ac:7e:9f:d2:60:91:a7:de:a9:a9:30:e1:97:81:e3:13:91:
+ 75:68:08:11:e0:ca:f9:eb:39:28:72:ab:8c:18:d2:3c:2c:cc:
+ 38:e5:73:1a:4e:7f:e6:74:25:8b:a2:40:45:59:28:b4:ec:ec:
+ 5f:c9:f5:6f:ab:02:03:70:0d:11:9b:62:df:73:7b:e0:c6:c1:
+ c1:ee:da:69:9a:91:a3:6b:2b:15:d6:fb:e4:35:38:86:fe:ac:
+ ad:77:a5:a3:03:a5:9f:f4:e7:34:91:83:9e:5b:1e:88:e1:48:
+ 5f:15:d8:de
+-----BEGIN CERTIFICATE-----
+MIIDlDCCAnygAwIBAgIJANjT46bL481mMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDAxMDEwMDAwMDBaFw0yMDAxMDIwMDAwMDBaMDUxCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEWMBQGA1UEAwwNc2VydmVyNC53MS5m
+aTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALB38T5m5/wLUfnYo64v
+9kp1OD7UR3vWJDK0yNng0KUS9ImniQHZVEPOgUq97RbzfMFnNotzTSGR50t+dMU7
+FbjGRn6popHD9ZCzz/kM67pv7rmrdz0Dnp13B7+XatRNttr9NlQl4wkmiX+hEcH2
+3L5cV6jmRVc9mBQGcqzpyIEGOJzPzQoOeE6eexDCYaVD7zQW3zzgo0R3SM2+5HnL
+MTmfU92jDnZabCReVjWbon2znkLxYZzonhy4mhAeMikhyzAW4AMR+YjyjwSl5qec
+c+GT2FEtu0WDj7aTQTlBhF9LrQAlkT0WCuvwuJLgXHVlhRfSgOZeZqKk38cIdjNJ
+zx0CAwEAAaOBmjCBlzAJBgNVHRMEAjAAMB0GA1UdDgQWBBQhsDHGFNS9XN9wJFE0
+npP1GLMcoTAfBgNVHSMEGDAWgBSk/bk5G4GzquuIHdSBqbURcMyn4TA1BggrBgEF
+BQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8w
+EwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAIlN7gQ+UPyi
+bkw+Sp47nC50KQaGG7uWAXD3RiG0729zkzG9WPUvQGHxU4Ygdc8OdXAslLjFTuwk
+D0LWi4C5+rVIg9bPyEc9CVARSl2DxUGLS04e/5aV8BR6fs2mT84LN+jyJ6Jy4msY
+1/iG8BTbTMWKdpv8VRVJP+vfXMd6ZIZwRJd+uoM5JTsjjtyznlnL4KKsfp/SYJGn
+3qmpMOGXgeMTkXVoCBHgyvnrOShyq4wY0jwszDjlcxpOf+Z0JYuiQEVZKLTs7F/J
+9W+rAgNwDRGbYt9ze+DGwcHu2mmakaNrKxXW++Q1OIb+rK13paMDpZ/05zSRg55b
+HojhSF8V2N4=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-extra.pkcs12 b/contrib/wpa/tests/hwsim/auth_serv/server-extra.pkcs12
new file mode 100644
index 000000000000..47231039647a
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/server-extra.pkcs12 differ
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-long-duration.csr b/contrib/wpa/tests/hwsim/auth_serv/server-long-duration.csr
new file mode 100644
index 000000000000..6324b778fa5b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-long-duration.csr
@@ -0,0 +1,27 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIEjDCCAnQCAQAwRzELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAM
+BgNVBAoMBXcxLmZpMRYwFAYDVQQDDA1zZXJ2ZXI3LncxLmZpMIICIjANBgkqhkiG
+9w0BAQEFAAOCAg8AMIICCgKCAgEAvxDC67+9DyFoRCZ41pAP3pxneAdvLjThUaY0
+bh1P8R8at5N6GgE2BNTjNfCbUo3MLYCTaDcACV/hDYo1dPXuHhZpkxZHqpPKaKc5
+bbXbVz1FY4k4nj5XOdi9TLKgqyefSTjGxfTojYs3up8PGqADdNh3lKTdNomrGl7W
+elrLv0AEkWqchPOBN6amSZH+aeyAoluyBBFXtuRoIurZG0zB+dOiKLG16bROCCU3
+ZIIUG9Lcdfj0SN658zWULWmxPESx9wgacUFMdMlwwXsJWP3hc206cMg6ue600Od9
+70j6+4Hv5P45h03cScAV7JDiDeqdqcAZQNbHc0NulROyDQ6Qwr8r2S2chUgqxUCe
+N2fP5xuVwiav4GGiplEHMxU1NnQz/HT4RoA2bTnfJLGoCwWFvmIc0I4JWikMSJ2B
+vMX39Mmcq+kTbhFZkMqWr8aQGPc+WFHkv9EeIFyXBtHPYRtn9WEAhwKQWs4tyzWx
+lmrZ/keQA7ddxz1IwF4quWbsNrjs3Q90MmVQLLfcGkdrSTMo9jc91Q17MHmG3dXX
+w4tV+oysWMivlBwDhosGngW5tfgUGBS2PiSV+JvEQGT3ooMR7LegH0yUrjMPvtTo
+4mtUu3Hypu8HXxM1Zja2MGQV0mDwxM6LfSuxiKI/WLdQfbuUpDl734zsZMjFjYZU
+b/GHk1ECAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4ICAQCFXmnZDE5Nyumifcrtxblr
+PyO2zktJmwwpJZJfzz0iCdtdx2fbrjbg18wLrfCAQA1VAFFv9rvq5Z9gx4FdBitR
+NLH6xWK2nFDJl8OqPW4cFmiuO90OVGayp3ZdYaJMLQOMN2V7TSvap/RBqXtfznRL
+7Ftqyn1Fryhtt6OcDf4JSSN60MwLH141bR2M0cMm0fU/A+S5XCGh+7s9m+wOjbRO
+h4AGxrIcB4vV76ljt2jVTRukTECndxtwPqtmZIP6+h4Ichh/zapwoXPxXOfo0afi
+dRnu7CXlN36rHk6rr8PhIp+kjArRDBDHJ9Agk1zudzTbK1yOEr6bX7MBtyEvvcfO
+NRO8VDKGJSeHjmeDP0LdJeyl3bOpwaS4aj+iKykN4SlA6S/3rZptJczsYtKQP46w
+HAWEZ2N+HBtClP4KJYn9lcQdsqVmBBAbrET2ttbtu+PnBD7FeQZmjxPBVXu0K8FC
+BwWMFWdAZTjOOz+AP41KBw2/kKSYlx/WBH2Ort1pKIuUr+kLEuYHnXU5+EMDJOnY
+Z4L+1zQbVz92mhOL9CbdCqgbxJ0eZjKV+LInLjgQhqD8mIV6pq/lbNlVs2om2JQS
+byRGe6baceitwjfMi/kO58JkYoZS2nvcBb4XZ3foogN+I8PIL5oGOpXGcSmuZHfL
+5fjSUaTpPgNeNHSUWxgKng==
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-long-duration.key b/contrib/wpa/tests/hwsim/auth_serv/server-long-duration.key
new file mode 100644
index 000000000000..3ae384507a4a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-long-duration.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQC/EMLrv70PIWhE
+JnjWkA/enGd4B28uNOFRpjRuHU/xHxq3k3oaATYE1OM18JtSjcwtgJNoNwAJX+EN
+ijV09e4eFmmTFkeqk8popzlttdtXPUVjiTiePlc52L1MsqCrJ59JOMbF9OiNize6
+nw8aoAN02HeUpN02iasaXtZ6Wsu/QASRapyE84E3pqZJkf5p7ICiW7IEEVe25Ggi
+6tkbTMH506IosbXptE4IJTdkghQb0tx1+PRI3rnzNZQtabE8RLH3CBpxQUx0yXDB
+ewlY/eFzbTpwyDq57rTQ533vSPr7ge/k/jmHTdxJwBXskOIN6p2pwBlA1sdzQ26V
+E7INDpDCvyvZLZyFSCrFQJ43Z8/nG5XCJq/gYaKmUQczFTU2dDP8dPhGgDZtOd8k
+sagLBYW+YhzQjglaKQxInYG8xff0yZyr6RNuEVmQypavxpAY9z5YUeS/0R4gXJcG
+0c9hG2f1YQCHApBazi3LNbGWatn+R5ADt13HPUjAXiq5Zuw2uOzdD3QyZVAst9wa
+R2tJMyj2Nz3VDXsweYbd1dfDi1X6jKxYyK+UHAOGiwaeBbm1+BQYFLY+JJX4m8RA
+ZPeigxHst6AfTJSuMw++1Ojia1S7cfKm7wdfEzVmNrYwZBXSYPDEzot9K7GIoj9Y
+t1B9u5SkOXvfjOxkyMWNhlRv8YeTUQIDAQABAoICAEJ/OefExq7yaJB3d0ztvKg0
+dQpgRACn5Nd+6fZ8+yqnMaw8hp1wWHWcBivqvpQfx7T6b9MweTHKDdChjnNTeHk+
+QaYwdebXIvYDZUhap6kYKQM4ad0hQ0hdt5xu+t42nwhj20JgN2Oz1UR9QUt571oG
+ULAKJPdrOIKoCStyWEEKrcxSd4EKIqnUtUEbr5j799UJ5s3ln0qG+ftVExSeRVCG
+qIRTPUXGO/Y3xayUXR1F6PaiG5sU8VDFD/oyM74PBoU8a7+JA2wOA9FC2gD/8ywy
+EsnX1iCKBKJEPx89niRUl8Jx/GGr5oRAdyDrV9GSGydONTvMxIPILz9xKGHE9PpH
+mxYAY2h5691PeLB9zr58puhVsMVaCZkgfhWuodE1aFuSxaAqxIT/5cvDKkW/LQ3L
+kLiV3yb4BANys5P0WCGxBndGUoyHlSRFfr70ujPk/GQi/YgD/FWy/VkIY9x0WQR0
+Br9NS3bQhlqQEkF51xdJEOc2nUD5f2siZpSm4+vJ6gdCtO+eFA87smIxV1jCORFq
+lSCSKN5ACJqfjIIAvnyTO/JIpOo3FGjJT8LZh3kj8+lQn5b8EH3nCtPETp/lzJWd
+B302qq8U3V7OR0SH2j0qyZ7xC9CWXI6nAsbVuYRDxi2x7EEYIndfxi+9bIttBtlo
+oul2knJ9zGKjGcC+5joFAoIBAQD3JlTm+Bvw54xv6VqDw0/Dy7CzYAMST14oT4D+
+wqpTvWFFS4YmFj5z37qZMeSVrIs23cXBzanGtMux2qeSChTBsgAhOV9N9vFNVm4V
+1M23NWdx0jTXGUZuEhGJ7viF2ENYyOsiSdSeSDZRk10xp/ya+YzMfytC8z35zaaH
+I4XYSxIeWAKcvlPeMf66+azB5D6hmw5hCI9Is1ZyQYUDHHZLEuGxyMJ76Vas/+5C
+6WoYe+QhMN7mn7drDtrSudauACZQNjEQ9O9RV1c2Vv0Lv6MESQnsVtIGpg6yerB+
+Oj8wgo4tSJ1sW1qESH2o4LrBPFLKCJBYeQuyinZyNCoDAYL/AoIBAQDF6Et04Qgp
+s7bAf+M/wUE7EEiNQoTkZfTHEcwNF7GcemlF85Wehq36PB2ePFaR79aZwngkn0zz
+uMTvQVJq3jH+0uJ/j/MbUJ82dpuNf7NeplnhHrWQhiayoSbTKcSawKe6ckVrfwLy
+/tV0ttkAiDPjEK/TpIJVqucmEHjHjpE6iRCqgt47eyB+VK+L7sPJQKLB4nIwqEYo
+KcwwLl/f7pKjCYnkxk44Fffoy29mwPUWY0TtNuDYZNSP19sAsFem0pevLTU+PYII
+Pvra5WnJ3JQc0jCYCL09y+HL73jvAEPhMqfO10uUbCC8W3KI9jvWwHI20tUqMXQL
+E26g95bV6/mvAoIBADT4NC4kcuiY19KMcufWjlvqZf6rzzy3YfjFwWHYmuTDq/tM
+Cn5TOiNfigCXXuRtTJD/ywiUaZS63wVJVazJGFXDLp/wSerNyD3JDmMDbuubOIZ+
+hPCs7BlfKf8kBoO5LAX1Wd/JbxZVZ77oFIs187/LSE/z2XPJ6jiFyPEhvefzfvid
+6EFr0VHH6U5tgIc1we6k6toFGaB9P0PRow5dpUTF0TVnT7d69Say23/fwutociZi
+8QMArDD3yBJt3gMA6TU7yBxYQopua7SrxCQmeGvMs6HBodXm9TNvdDA5j513/bza
+2VKF6cp8NuJg9+W+ZggC+dzcZJNpdaVYZMCsLEUCggEAPLyi07bwO2QlFQTqqDlW
+HJtNuNSOVk4YBjQnDGmWH7DNuCMeau1oXWCvsk1QQC98C+pL1ulww2eUQN0qPxP8
+AfmUe5OhB2QByMQzzwQ+9zBUaytyi20wWSft82ZhKSExGJ5TQb9UF2Ev/0bSaEBk
+tC392BUnzsTJdbweZRgS6AUCsWHCdDzAZyT0Txyyx4Pnr1sgsmAiT9csDClfUSk1
+pYWa5TQa80mCsNYmVUGotfs3PxnVfXPMbGzRkG+OJuuAk8lrCrPzwTYa5Kz9f28L
+oaC8OxyLf3ifzmerFKZfLrDOIUOftWhNz6C9EN2I1cpwAvVHaFCPDYskK5BwoSxv
+jwKCAQEAkb5RnSaRQENeHSqnU5tNyZj5Grcsd6dPlqWRe+tZfPxfNs45n9Qsuu/+
+N1W5ZoqfNKoL9Rn9FWK98/VN47CxshtIVVYLDF+1+bdi5PgBCvG7W+77mLoaRiur
+49XrQ7e5+mlKpjV1809fZGZ6UX1b7oeoBwEXAKU/vqOA/9T65SaBLo2pcxGFK+LL
+H2gynD0uB3eS8SVTQLZ1nt2siPcbfqbTJnKhgwmm0bJxwAFzC54uvtoOjlZcsqvB
+AuBc6reTuBQTn9+mJC0oDAjuyiDLuByU9BvTSjPwqMTt9SoKEAwsYo0t16LfxSZh
+7i4QyQhhpHEPAMqvU0qdRdiWQ1QFhA==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-long-duration.pem b/contrib/wpa/tests/hwsim/auth_serv/server-long-duration.pem
new file mode 100644
index 000000000000..88bd6afc919e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-long-duration.pem
@@ -0,0 +1,107 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cd:68
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 3 14:07:41 2020 GMT
+ Not After : Apr 21 14:07:41 2070 GMT
+ Subject: C=FI, O=w1.fi, CN=server7.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (4096 bit)
+ Modulus:
+ 00:bf:10:c2:eb:bf:bd:0f:21:68:44:26:78:d6:90:
+ 0f:de:9c:67:78:07:6f:2e:34:e1:51:a6:34:6e:1d:
+ 4f:f1:1f:1a:b7:93:7a:1a:01:36:04:d4:e3:35:f0:
+ 9b:52:8d:cc:2d:80:93:68:37:00:09:5f:e1:0d:8a:
+ 35:74:f5:ee:1e:16:69:93:16:47:aa:93:ca:68:a7:
+ 39:6d:b5:db:57:3d:45:63:89:38:9e:3e:57:39:d8:
+ bd:4c:b2:a0:ab:27:9f:49:38:c6:c5:f4:e8:8d:8b:
+ 37:ba:9f:0f:1a:a0:03:74:d8:77:94:a4:dd:36:89:
+ ab:1a:5e:d6:7a:5a:cb:bf:40:04:91:6a:9c:84:f3:
+ 81:37:a6:a6:49:91:fe:69:ec:80:a2:5b:b2:04:11:
+ 57:b6:e4:68:22:ea:d9:1b:4c:c1:f9:d3:a2:28:b1:
+ b5:e9:b4:4e:08:25:37:64:82:14:1b:d2:dc:75:f8:
+ f4:48:de:b9:f3:35:94:2d:69:b1:3c:44:b1:f7:08:
+ 1a:71:41:4c:74:c9:70:c1:7b:09:58:fd:e1:73:6d:
+ 3a:70:c8:3a:b9:ee:b4:d0:e7:7d:ef:48:fa:fb:81:
+ ef:e4:fe:39:87:4d:dc:49:c0:15:ec:90:e2:0d:ea:
+ 9d:a9:c0:19:40:d6:c7:73:43:6e:95:13:b2:0d:0e:
+ 90:c2:bf:2b:d9:2d:9c:85:48:2a:c5:40:9e:37:67:
+ cf:e7:1b:95:c2:26:af:e0:61:a2:a6:51:07:33:15:
+ 35:36:74:33:fc:74:f8:46:80:36:6d:39:df:24:b1:
+ a8:0b:05:85:be:62:1c:d0:8e:09:5a:29:0c:48:9d:
+ 81:bc:c5:f7:f4:c9:9c:ab:e9:13:6e:11:59:90:ca:
+ 96:af:c6:90:18:f7:3e:58:51:e4:bf:d1:1e:20:5c:
+ 97:06:d1:cf:61:1b:67:f5:61:00:87:02:90:5a:ce:
+ 2d:cb:35:b1:96:6a:d9:fe:47:90:03:b7:5d:c7:3d:
+ 48:c0:5e:2a:b9:66:ec:36:b8:ec:dd:0f:74:32:65:
+ 50:2c:b7:dc:1a:47:6b:49:33:28:f6:37:3d:d5:0d:
+ 7b:30:79:86:dd:d5:d7:c3:8b:55:fa:8c:ac:58:c8:
+ af:94:1c:03:86:8b:06:9e:05:b9:b5:f8:14:18:14:
+ b6:3e:24:95:f8:9b:c4:40:64:f7:a2:83:11:ec:b7:
+ a0:1f:4c:94:ae:33:0f:be:d4:e8:e2:6b:54:bb:71:
+ f2:a6:ef:07:5f:13:35:66:36:b6:30:64:15:d2:60:
+ f0:c4:ce:8b:7d:2b:b1:88:a2:3f:58:b7:50:7d:bb:
+ 94:a4:39:7b:df:8c:ec:64:c8:c5:8d:86:54:6f:f1:
+ 87:93:51
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 30:C9:45:D8:D3:C8:8E:E6:41:B8:29:BD:48:DE:BF:CD:9A:A5:81:CE
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ Authority Information Access:
+ OCSP - URI:http://server.w1.fi:8888/
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ Signature Algorithm: sha256WithRSAEncryption
+ 01:87:4b:93:49:c5:28:8b:2f:8a:45:f3:ed:a2:1e:2f:b0:d0:
+ 0b:d3:cc:dc:a5:bd:ff:f5:df:86:45:f3:3e:94:ff:32:16:de:
+ f4:08:4a:2d:24:f3:5b:da:a8:ea:21:6d:06:c9:9c:08:1c:0e:
+ dc:a1:82:b9:5f:67:e4:e1:1c:29:b3:b1:58:af:ce:6c:2f:e1:
+ 9b:dd:98:53:45:aa:d2:02:81:fd:a1:74:e4:75:69:07:9c:cc:
+ 5d:b7:1a:25:ba:52:3b:8e:5c:62:12:0c:0e:a2:38:2f:b5:d3:
+ 33:97:fe:d1:ec:6a:5d:15:93:67:98:d9:d0:93:03:bd:78:90:
+ df:bd:4f:50:af:79:83:70:02:9e:eb:bc:6d:d7:0f:9b:65:8d:
+ 4e:79:79:d1:03:18:3d:47:3e:78:05:1d:f5:23:d2:f8:8f:fb:
+ 56:a1:ce:ee:e0:40:25:57:cc:4d:4c:f2:ca:65:90:e0:f8:7f:
+ ed:4f:12:5f:1d:9c:5e:15:3c:5e:fa:a4:5f:85:3c:a1:47:a3:
+ 3a:db:3f:93:3a:21:f4:55:be:fb:7c:3a:3d:58:ec:91:a0:83:
+ d5:b0:b9:79:08:12:1d:3b:3c:31:8d:f5:f6:da:20:d3:ca:76:
+ fb:83:c9:20:36:32:e5:4a:44:25:c6:d5:4d:04:59:06:71:9a:
+ cc:b9:47:e7
+-----BEGIN CERTIFICATE-----
+MIIEljCCA36gAwIBAgIJANjT46bL481oMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAgFw0yMDA1MDMxNDA3NDFaGA8yMDcwMDQyMTE0MDc0MVowNTEL
+MAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMRYwFAYDVQQDDA1zZXJ2ZXI3Lncx
+LmZpMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvxDC67+9DyFoRCZ4
+1pAP3pxneAdvLjThUaY0bh1P8R8at5N6GgE2BNTjNfCbUo3MLYCTaDcACV/hDYo1
+dPXuHhZpkxZHqpPKaKc5bbXbVz1FY4k4nj5XOdi9TLKgqyefSTjGxfTojYs3up8P
+GqADdNh3lKTdNomrGl7WelrLv0AEkWqchPOBN6amSZH+aeyAoluyBBFXtuRoIurZ
+G0zB+dOiKLG16bROCCU3ZIIUG9Lcdfj0SN658zWULWmxPESx9wgacUFMdMlwwXsJ
+WP3hc206cMg6ue600Od970j6+4Hv5P45h03cScAV7JDiDeqdqcAZQNbHc0NulROy
+DQ6Qwr8r2S2chUgqxUCeN2fP5xuVwiav4GGiplEHMxU1NnQz/HT4RoA2bTnfJLGo
+CwWFvmIc0I4JWikMSJ2BvMX39Mmcq+kTbhFZkMqWr8aQGPc+WFHkv9EeIFyXBtHP
+YRtn9WEAhwKQWs4tyzWxlmrZ/keQA7ddxz1IwF4quWbsNrjs3Q90MmVQLLfcGkdr
+STMo9jc91Q17MHmG3dXXw4tV+oysWMivlBwDhosGngW5tfgUGBS2PiSV+JvEQGT3
+ooMR7LegH0yUrjMPvtTo4mtUu3Hypu8HXxM1Zja2MGQV0mDwxM6LfSuxiKI/WLdQ
+fbuUpDl734zsZMjFjYZUb/GHk1ECAwEAAaOBmjCBlzAJBgNVHRMEAjAAMB0GA1Ud
+DgQWBBQwyUXY08iO5kG4Kb1I3r/NmqWBzjAfBgNVHSMEGDAWgBSk/bk5G4GzquuI
+HdSBqbURcMyn4TA1BggrBgEFBQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9z
+ZXJ2ZXIudzEuZmk6ODg4OC8wEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcN
+AQELBQADggEBAAGHS5NJxSiLL4pF8+2iHi+w0AvTzNylvf/134ZF8z6U/zIW3vQI
+Si0k81vaqOohbQbJnAgcDtyhgrlfZ+ThHCmzsVivzmwv4ZvdmFNFqtICgf2hdOR1
+aQeczF23GiW6UjuOXGISDA6iOC+10zOX/tHsal0Vk2eY2dCTA714kN+9T1CveYNw
+Ap7rvG3XD5tljU55edEDGD1HPngFHfUj0viP+1ahzu7gQCVXzE1M8splkOD4f+1P
+El8dnF4VPF76pF+FPKFHozrbP5M6IfRVvvt8Oj1Y7JGgg9WwuXkIEh07PDGN9fba
+INPKdvuDySA2MuVKRCXG1U0EWQZxmsy5R+c=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.csr b/contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.csr
new file mode 100644
index 000000000000..6f59b705c803
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICjDCCAXQCAQAwRzELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAM
+BgNVBAoMBXcxLmZpMRYwFAYDVQQDDA1zZXJ2ZXIzLncxLmZpMIIBIjANBgkqhkiG
+9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwuutBG9M6yO4kk513ADQDdmtCSH4Ekotzdt9
+Y6EyEo64473flTk6F2VU+CDWcTd9t7RyMXp6Pbp0cvOsnRY4NRt9UDutll2nwHYL
+u0wGqND3LT2UpRQmGhHYQhstTm5gnWrPIts2ZeIXNbxpLqsw5c38muRPD9EBw+Yz
+cTHNb9qMIZ33G9Htfjl6/WmjiBzm9tM7Gh7TcMOtNSoipN5gkDdd5+DOm31MtctB
+G5yWUd9YTbdn2pUlYIr/bGKE2tYkuWXZeln4yL0DvvYX2A6GrppMfVP2sQ6CzQZh
+d91GXP1FavLoIspji9Mc9k8Q7OoaCB44PYBso0hH8hYqB1v3iQIDAQABoAAwDQYJ
+KoZIhvcNAQELBQADggEBAFOyTkNJyRKkPGdTpgnqiapijSufwkt3uETE+4SgVwSu
+ctfu4IKhwgecJl4HiyVj8vwEoB5tJLdK4EG/wSBVdqd5nN2OEm4FClqfqgiuftrU
+O+pvl+HSU3X9CrW4Is0Vmb2x5SMRPwvW3lA+fcKMpjUfbUA0E5kii18qir/UsVuH
+EHCJ18BRoqUS+x4r+nxjS+ErSkdTtQtrZVOH2z9IEVGtc7tSxd9Dy4+L8TX0UNEP
+PEZlvuLAR5py3/zeFjkekQKx2AhJqpPC+/NMmkbm+n81NgcBG02rCL2vLALtm9cf
+6VbDKAi/K0tm3s9HHm6euogDB6q4TioPLxUpIR34W4c=
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.key b/contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.key
new file mode 100644
index 000000000000..f1e96e5b6d2c
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDC660Eb0zrI7iS
+TnXcANAN2a0JIfgSSi3N231joTISjrjjvd+VOToXZVT4INZxN323tHIxeno9unRy
+86ydFjg1G31QO62WXafAdgu7TAao0PctPZSlFCYaEdhCGy1ObmCdas8i2zZl4hc1
+vGkuqzDlzfya5E8P0QHD5jNxMc1v2owhnfcb0e1+OXr9aaOIHOb20zsaHtNww601
+KiKk3mCQN13n4M6bfUy1y0EbnJZR31hNt2falSVgiv9sYoTa1iS5Zdl6WfjIvQO+
+9hfYDoaumkx9U/axDoLNBmF33UZc/UVq8ugiymOL0xz2TxDs6hoIHjg9gGyjSEfy
+FioHW/eJAgMBAAECggEAD8yKeZGL6oM6sqEpbGukcXrzS4o1UUYx8D2aLDkkldx7
+n/oD5VR+IOdVu8btmr+ksP8vQlNrFRXH2olltFXYuHVB8v7jUwzETBVFXikLYSOO
+5VvYcIjxjatkm/cX9QN9UUUXBPw/rIJm3zQmUmIN1JKdpvBaeC24tugxwzsGK8qm
+kefz6Rc+UTC0tLz62ti6fWeR6TipRFdExtXLRZlwYCDy33GBj1VRRSI5ZFxjPHqI
+tChrK2OUkZrkWUsUZiPq3hFytNyfVbSzqBHJPwhufuUZw5SsSrluqJbrtoi+kOKY
+lJ/gE7BBL7ZOEj9chHG5WXwN0hsyEzkm582Ls/CDAQKBgQD9L8WFL5UQitnVsEZB
++rv6WQwYsS5H2nsjRasiGp155eSGnFlkqZbUBkFCAU0FjHotTFl3ZPd+w2JP6mda
+zKnAj9SfgX7UF4M8Vx3KbW2qMamycFhW3YUOyk7b3oCMKLHhwC0tcOWDqxNOgR5y
+syulT8aDse9Ey/c8yIefY37VuwKBgQDFFihbgtUuR3gJR6jMWxXfNlpxyyEA77QN
+HHsLjqcq1Kte/KrV2zUXy+aZAJYjnvLMOZodxRkGv+I6309S3TG9gzDNt1iguYNa
+ETif4hDJoQHrXgWnkPKjuGyE811ArhHfdrPEjBhNX538T4fPZD0FJM4ZpVlvoMyz
+NnNieN2RiwKBgQCnir1CbUJPOBL+fS+A6dMKz3JZxKXDlqh1ptygLMyYpbCcA0qE
+elT86Ua1zvaQ/Wy8HRH3GDFPCSw9hffu9hA/BO7GvoKXBxgpDd0A33j6bvLEyeMr
+WFt8dhPJG2wlU6iiovFCaLr4bnTQNlFXxYjUU/4hl9WlyPNKnchhiQ2dkQKBgQC9
+akA7QxCzu8hn0tEuJlRtBIYEW59KkRXQjBDN2Lpc9awGTHu7sUPjrPnhDqk9buQW
+1z2BYw5caEp7HmfUUfYeF1nuPEoXnnPZOjfboZ9UyUNY/DIfC7XHF9ZkKKj1ItbW
+l/TJ74LjygPCnIUAE2x55xeVmk7MdBSIIMrgVx1LZQKBgDFj79Q2LNKZ9OpCs/X6
+fcu30wHRTraQ4mntoQYycjn3IuGfPJ+bFYaz9oyjarMbGeVftdpwWSMQBm/a7nCU
+aWPBq+INWls8NE2WtX7jzWj00AzT8TEGbJirQfOJbNXCGrVW7GrGH6JlZYpsC/lb
++CynJQUivjDzYCz1sGjNScGs
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.pem b/contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.pem
new file mode 100644
index 000000000000..a09e5116c7d3
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server-no-dnsname.pem
@@ -0,0 +1,85 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cd:60
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 2 19:55:38 2020 GMT
+ Not After : May 2 19:55:38 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=server3.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:c2:eb:ad:04:6f:4c:eb:23:b8:92:4e:75:dc:00:
+ d0:0d:d9:ad:09:21:f8:12:4a:2d:cd:db:7d:63:a1:
+ 32:12:8e:b8:e3:bd:df:95:39:3a:17:65:54:f8:20:
+ d6:71:37:7d:b7:b4:72:31:7a:7a:3d:ba:74:72:f3:
+ ac:9d:16:38:35:1b:7d:50:3b:ad:96:5d:a7:c0:76:
+ 0b:bb:4c:06:a8:d0:f7:2d:3d:94:a5:14:26:1a:11:
+ d8:42:1b:2d:4e:6e:60:9d:6a:cf:22:db:36:65:e2:
+ 17:35:bc:69:2e:ab:30:e5:cd:fc:9a:e4:4f:0f:d1:
+ 01:c3:e6:33:71:31:cd:6f:da:8c:21:9d:f7:1b:d1:
+ ed:7e:39:7a:fd:69:a3:88:1c:e6:f6:d3:3b:1a:1e:
+ d3:70:c3:ad:35:2a:22:a4:de:60:90:37:5d:e7:e0:
+ ce:9b:7d:4c:b5:cb:41:1b:9c:96:51:df:58:4d:b7:
+ 67:da:95:25:60:8a:ff:6c:62:84:da:d6:24:b9:65:
+ d9:7a:59:f8:c8:bd:03:be:f6:17:d8:0e:86:ae:9a:
+ 4c:7d:53:f6:b1:0e:82:cd:06:61:77:dd:46:5c:fd:
+ 45:6a:f2:e8:22:ca:63:8b:d3:1c:f6:4f:10:ec:ea:
+ 1a:08:1e:38:3d:80:6c:a3:48:47:f2:16:2a:07:5b:
+ f7:89
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 5E:84:D6:31:98:17:71:F8:63:5C:32:5B:7D:33:C0:D4:FA:36:A7:6A
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ Authority Information Access:
+ OCSP - URI:http://server.w1.fi:8888/
+
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ Signature Algorithm: sha256WithRSAEncryption
+ 47:5a:18:97:c2:3a:a5:4a:6c:f6:11:53:ac:d3:3f:d7:0c:7f:
+ e5:cb:9c:7d:02:f3:b7:ab:0c:a6:8d:d9:77:6c:bd:2a:41:47:
+ fb:70:7f:0d:09:53:fc:e4:a4:5e:0b:1c:4d:84:05:71:ab:f9:
+ 68:9a:df:4f:b6:73:20:fd:05:cc:e2:f1:8a:9d:20:7a:27:8a:
+ 60:a6:ed:0e:eb:cf:5f:13:32:1b:89:ec:f6:dc:eb:5f:42:f0:
+ a8:f9:42:dd:e5:e6:19:28:82:61:df:07:24:7b:c6:c9:ce:a5:
+ 44:f0:d7:ba:4b:2b:9d:d7:97:1c:13:e9:da:0a:58:26:97:48:
+ 6e:33:ec:d5:d3:32:96:23:b6:40:01:a8:e0:88:ea:2a:73:82:
+ d7:41:58:9b:b3:dc:6b:41:2f:ae:33:38:43:05:ed:04:ff:b9:
+ 63:b7:7e:9b:fa:85:ab:df:12:36:24:cf:ec:8d:f8:d5:1c:95:
+ 4e:a8:9c:e4:8a:90:ac:db:a0:4b:d8:14:e0:84:97:f7:cb:da:
+ 95:cd:02:11:65:23:8b:ad:f1:c3:46:2d:2d:20:4d:cb:63:ef:
+ ae:be:ea:19:1d:2d:c5:35:c8:aa:b9:d3:8c:4f:cd:44:9c:fc:
+ a4:37:f5:b8:80:06:af:5e:ce:bc:81:23:cd:6b:de:31:c2:4c:
+ e8:e6:68:71
+-----BEGIN CERTIFICATE-----
+MIIDlDCCAnygAwIBAgIJANjT46bL481gMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDIxOTU1MzhaFw0yMTA1MDIxOTU1MzhaMDUxCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEWMBQGA1UEAwwNc2VydmVyMy53MS5m
+aTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMLrrQRvTOsjuJJOddwA
+0A3ZrQkh+BJKLc3bfWOhMhKOuOO935U5OhdlVPgg1nE3fbe0cjF6ej26dHLzrJ0W
+ODUbfVA7rZZdp8B2C7tMBqjQ9y09lKUUJhoR2EIbLU5uYJ1qzyLbNmXiFzW8aS6r
+MOXN/JrkTw/RAcPmM3ExzW/ajCGd9xvR7X45ev1po4gc5vbTOxoe03DDrTUqIqTe
+YJA3Xefgzpt9TLXLQRucllHfWE23Z9qVJWCK/2xihNrWJLll2XpZ+Mi9A772F9gO
+hq6aTH1T9rEOgs0GYXfdRlz9RWry6CLKY4vTHPZPEOzqGggeOD2AbKNIR/IWKgdb
+94kCAwEAAaOBmjCBlzAJBgNVHRMEAjAAMB0GA1UdDgQWBBRehNYxmBdx+GNcMlt9
+M8DU+janajAfBgNVHSMEGDAWgBSk/bk5G4GzquuIHdSBqbURcMyn4TA1BggrBgEF
+BQcBAQQpMCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8w
+EwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQELBQADggEBAEdaGJfCOqVK
+bPYRU6zTP9cMf+XLnH0C87erDKaN2XdsvSpBR/twfw0JU/zkpF4LHE2EBXGr+Wia
+30+2cyD9Bczi8YqdIHonimCm7Q7rz18TMhuJ7Pbc619C8Kj5Qt3l5hkogmHfByR7
+xsnOpUTw17pLK53XlxwT6doKWCaXSG4z7NXTMpYjtkABqOCI6ipzgtdBWJuz3GtB
+L64zOEMF7QT/uWO3fpv6havfEjYkz+yN+NUclU6onOSKkKzboEvYFOCEl/fL2pXN
+AhFlI4ut8cNGLS0gTctj766+6hkdLcU1yKq504xPzUSc/KQ39biABq9ezryBI81r
+3jHCTOjmaHE=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server.csr b/contrib/wpa/tests/hwsim/auth_serv/server.csr
new file mode 100644
index 000000000000..3e8f7d96528a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICizCCAXMCAQAwRjELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAM
+BgNVBAoMBXcxLmZpMRUwEwYDVQQDDAxzZXJ2ZXIudzEuZmkwggEiMA0GCSqGSIb3
+DQEBAQUAA4IBDwAwggEKAoIBAQD9Dl7NGSxqQYPZLA42zQmwj7LJjMjSAzhuVLxD
+7s74WbHpP23UXuPFcxk9C7wp19BudQA1/PdlsbYPywZJz6lOPcJkSxgkCnC4blTc
+kD9sGP22iKs33ItLteH/7btFBaPwqlEr8XkGfy/NXfzmiq8buXvKQ3UBHY4t2RET
+hvs8S2CkKUnj0iAgy1wNnNKcMCERvLN032Swt2fuat+vPRgCt9zlVHW1bEDEsIob
+xv2rHrnv0YMJDVW6F4hO0L3PczZ8KEv2qkjU6Psl2B2vyWhzrEauy+t5Nletw3AC
+FW3wpUNzq3IEsRZgdA5KwY9SKBqVfqvQBPb6Edob0ZmkT57tAgMBAAGgADANBgkq
+hkiG9w0BAQsFAAOCAQEAUQqUbXEUfPwdJoYL1jPZFCXDMFLjQgro6uWQ+yK8NEOX
+MekF9AmJkBNHfOXfhtpuSutn+4TGLGShS+ocvR9oGJkSULZYOzbsntP6ZEcwaxo5
+rvSSmm1cx1GNQQ/dzoefeWlRnaUVcOTljMutCae1X9KTXuLW2DreEwo4aqPsu+EK
+iSL/GcdYozU+p+ZE0BP26kDeQYKYD/1XOXvGclCAG11U3M03cazqiLr+auNhAL0T
+y4PYJnrNhTZdIXLXsPTKcG2VhbGSC7NTia10XRl2Jr+yFTRvR2S0F1vZczbXc7gj
+JBMk6kojPpMvqe2WmaXWEH+tzeBvTELnJDHdDl+w9A==
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server.key b/contrib/wpa/tests/hwsim/auth_serv/server.key
new file mode 100644
index 000000000000..7dd02ae13791
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQD9Dl7NGSxqQYPZ
+LA42zQmwj7LJjMjSAzhuVLxD7s74WbHpP23UXuPFcxk9C7wp19BudQA1/PdlsbYP
+ywZJz6lOPcJkSxgkCnC4blTckD9sGP22iKs33ItLteH/7btFBaPwqlEr8XkGfy/N
+Xfzmiq8buXvKQ3UBHY4t2REThvs8S2CkKUnj0iAgy1wNnNKcMCERvLN032Swt2fu
+at+vPRgCt9zlVHW1bEDEsIobxv2rHrnv0YMJDVW6F4hO0L3PczZ8KEv2qkjU6Psl
+2B2vyWhzrEauy+t5Nletw3ACFW3wpUNzq3IEsRZgdA5KwY9SKBqVfqvQBPb6Edob
+0ZmkT57tAgMBAAECggEARr4KsIl2CipQgpi3TIibSdmSIJgT1+oAj49upgsPnwvs
+GYma2YDBagRYLOGn84aahRZ+PZl/S9WXUOjv1BiSewX23vTDRegJRPXLEXHd2QlP
+fATO4Knt4vdwu4wpCw2dm/zw/jjeHXvW0DGTLpvcggKjYZLkFbC+e4CwLmZQu7TE
+e5QnNDqzNjiHEYJ7P70/o5otgVCo2Kn/on+/dsVf5/x0zPktoKO/aWZPkC8ef+aY
+rClhFRQZtrys6/dQ5r3ZTtublCRB3hq3tx/THJKROFvkSDiI0fJJkn2PxjKaA4ot
+8Gm3598Dj5sjlluanEg72n0jreSS6WYnHfCp2/7HgQKBgQD/ZNvLsoNABNQFXhin
+6e6PBG+VU1GY5yWXsdNA1NcOaq7PhdNnpQ2SkIZIwfdNmypMtY/y7yxAlv7jgqzQ
+UEo4ZXQirq6ehKddy0olpu0gN8SOwKJ8VCSnGaZXp8E0ueRTLLD4JAvg4mQY+mJ5
+AYTa4Y75JTFOTe/x9+DbFLOSzQKBgQD9qBdywd/eEf45KPnoLnvDTxpS/4b5liMS
+q+j8bFz5NmL/6TkAZp7Clp95wMh0Qeg0yznDNUOKG48yHJuUp8TuUTXE39u5CTki
+s1wRYEtUKwKbjKyurGkR49BhJEo+aO2ai14D+pEDCoYDhICNk5IaHolmIvMPG+4I
+HAG9B1l8oQKBgAVVvQWnR/iZYThve8JeL56LMC2FXQn9ohpmG4yaJZfmgJpTKFRc
+7Uinfjac3qafXCXYERa5CkqbHN3xx9xeIP7Gl8N1tK4ZBUn/SiA5OXDX2WJGAd7o
+/w8CiSgBmBaa33shTt0QG7Np5z9iU5ZFgtnzpkHsFfiVxjJexDKvXwTtAoGBAIqK
+y7vXHoH5S+RGeDCTau+i/drFTCB4G1HwaUGtoh5P0US7SnHomt/nStVCMXomIUDs
+mvD+35PIN68EJwnLlGkiG/8a0bS/z+AfHLM06A/hqfvxmsOQ0ZZ+2mqkWpS51MTr
+R/9eVOPXVJkJPvmU12DCuwL5Jc5jz9/IBD8Ni5fhAoGBANsZyLEn9Rm2/kL6xe1k
+jqe0gtGEctd71NdurFXSDGG1CZFeUGFGshGalRu6RqcCP6r/LpZHZc6vFDDep/1G
+1fSLR3ula5rBAIrMUQPXq3LXpvC3BCZFvn3A2LRjNYB9LG5WtL/lfkeTzrx9L8hL
+6mC0BwuQ5yNIyfXv1/0dQD0+
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server.pem b/contrib/wpa/tests/hwsim/auth_serv/server.pem
new file mode 100644
index 000000000000..98fc032a43bd
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/server.pem
@@ -0,0 +1,87 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cd:5f
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 2 19:55:38 2020 GMT
+ Not After : May 2 19:55:38 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=server.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:fd:0e:5e:cd:19:2c:6a:41:83:d9:2c:0e:36:cd:
+ 09:b0:8f:b2:c9:8c:c8:d2:03:38:6e:54:bc:43:ee:
+ ce:f8:59:b1:e9:3f:6d:d4:5e:e3:c5:73:19:3d:0b:
+ bc:29:d7:d0:6e:75:00:35:fc:f7:65:b1:b6:0f:cb:
+ 06:49:cf:a9:4e:3d:c2:64:4b:18:24:0a:70:b8:6e:
+ 54:dc:90:3f:6c:18:fd:b6:88:ab:37:dc:8b:4b:b5:
+ e1:ff:ed:bb:45:05:a3:f0:aa:51:2b:f1:79:06:7f:
+ 2f:cd:5d:fc:e6:8a:af:1b:b9:7b:ca:43:75:01:1d:
+ 8e:2d:d9:11:13:86:fb:3c:4b:60:a4:29:49:e3:d2:
+ 20:20:cb:5c:0d:9c:d2:9c:30:21:11:bc:b3:74:df:
+ 64:b0:b7:67:ee:6a:df:af:3d:18:02:b7:dc:e5:54:
+ 75:b5:6c:40:c4:b0:8a:1b:c6:fd:ab:1e:b9:ef:d1:
+ 83:09:0d:55:ba:17:88:4e:d0:bd:cf:73:36:7c:28:
+ 4b:f6:aa:48:d4:e8:fb:25:d8:1d:af:c9:68:73:ac:
+ 46:ae:cb:eb:79:36:57:ad:c3:70:02:15:6d:f0:a5:
+ 43:73:ab:72:04:b1:16:60:74:0e:4a:c1:8f:52:28:
+ 1a:95:7e:ab:d0:04:f6:fa:11:da:1b:d1:99:a4:4f:
+ 9e:ed
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 71:26:7A:1F:72:81:97:24:11:AA:C0:75:FA:BF:31:10:69:49:D0:E7
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ Authority Information Access:
+ OCSP - URI:http://server.w1.fi:8888/
+
+ X509v3 Subject Alternative Name:
+ DNS:server.w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Server Authentication
+ Signature Algorithm: sha256WithRSAEncryption
+ 62:10:9c:ed:50:98:34:2e:7c:ef:1a:11:93:a5:f0:ad:8d:03:
+ 71:9a:a1:be:c0:24:9a:4d:28:cd:28:ea:55:7e:7b:b3:9c:f4:
+ ad:94:44:7b:9c:e2:0a:c0:35:7e:80:a6:aa:9c:ae:36:22:fd:
+ 4e:25:b3:1f:66:1d:2e:66:4b:d4:8c:ad:3e:0d:92:7d:3a:93:
+ 05:c6:51:e4:75:fc:b4:6c:24:cb:c4:79:06:2f:d1:b3:6c:0c:
+ d8:82:76:08:cc:9a:c4:61:14:1b:3d:38:f4:a2:2c:49:0e:d5:
+ 82:58:46:52:3c:cd:12:d9:57:dd:58:25:34:0b:d7:7b:2a:2f:
+ 60:ce:da:9f:f2:98:e2:8e:0b:6c:69:42:1c:27:75:3a:7c:ae:
+ a5:9a:19:bc:6c:67:fc:04:a9:f4:fd:2c:17:79:56:52:a3:3b:
+ 01:60:ae:ea:9b:ed:a4:30:53:fc:ef:57:bb:f1:fc:04:2a:5c:
+ 2b:74:d0:1f:0b:30:ec:0a:b2:8b:4d:4a:b4:33:0d:cd:dc:28:
+ 29:0a:d1:eb:36:09:bc:15:a7:c7:f0:f0:9c:7e:48:75:14:75:
+ 2d:ed:fb:7a:14:e4:69:4a:54:b9:ad:25:ba:bb:d9:c0:eb:a0:
+ 81:53:c7:07:ea:34:73:1f:9d:43:63:8e:f9:06:c9:4d:15:bf:
+ 68:f9:91:de
+-----BEGIN CERTIFICATE-----
+MIIDrDCCApSgAwIBAgIJANjT46bL481fMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDIxOTU1MzhaFw0yMTA1MDIxOTU1MzhaMDQxCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEVMBMGA1UEAwwMc2VydmVyLncxLmZp
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA/Q5ezRksakGD2SwONs0J
+sI+yyYzI0gM4blS8Q+7O+Fmx6T9t1F7jxXMZPQu8KdfQbnUANfz3ZbG2D8sGSc+p
+Tj3CZEsYJApwuG5U3JA/bBj9toirN9yLS7Xh/+27RQWj8KpRK/F5Bn8vzV385oqv
+G7l7ykN1AR2OLdkRE4b7PEtgpClJ49IgIMtcDZzSnDAhEbyzdN9ksLdn7mrfrz0Y
+Arfc5VR1tWxAxLCKG8b9qx6579GDCQ1VuheITtC9z3M2fChL9qpI1Oj7Jdgdr8lo
+c6xGrsvreTZXrcNwAhVt8KVDc6tyBLEWYHQOSsGPUigalX6r0AT2+hHaG9GZpE+e
+7QIDAQABo4GzMIGwMAkGA1UdEwQCMAAwHQYDVR0OBBYEFHEmeh9ygZckEarAdfq/
+MRBpSdDnMB8GA1UdIwQYMBaAFKT9uTkbgbOq64gd1IGptRFwzKfhMDUGCCsGAQUF
+BwEBBCkwJzAlBggrBgEFBQcwAYYZaHR0cDovL3NlcnZlci53MS5maTo4ODg4LzAX
+BgNVHREEEDAOggxzZXJ2ZXIudzEuZmkwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJ
+KoZIhvcNAQELBQADggEBAGIQnO1QmDQufO8aEZOl8K2NA3Gaob7AJJpNKM0o6lV+
+e7Oc9K2URHuc4grANX6ApqqcrjYi/U4lsx9mHS5mS9SMrT4Nkn06kwXGUeR1/LRs
+JMvEeQYv0bNsDNiCdgjMmsRhFBs9OPSiLEkO1YJYRlI8zRLZV91YJTQL13sqL2DO
+2p/ymOKOC2xpQhwndTp8rqWaGbxsZ/wEqfT9LBd5VlKjOwFgruqb7aQwU/zvV7vx
+/AQqXCt00B8LMOwKsotNSrQzDc3cKCkK0es2CbwVp8fw8Jx+SHUUdS3t+3oU5GlK
+VLmtJbq72cDroIFTxwfqNHMfnUNjjvkGyU0Vv2j5kd4=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/server.pkcs12 b/contrib/wpa/tests/hwsim/auth_serv/server.pkcs12
new file mode 100644
index 000000000000..a72b1644a658
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/server.pkcs12 differ
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha384-server.key b/contrib/wpa/tests/hwsim/auth_serv/sha384-server.key
new file mode 100644
index 000000000000..10ff1450e934
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha384-server.key
@@ -0,0 +1,40 @@
+-----BEGIN PRIVATE KEY-----
+MIIG/gIBADANBgkqhkiG9w0BAQEFAASCBugwggbkAgEAAoIBgQDm91jBTdZzl79p
+4ZPklcK5EoOMvj3++QdZ+7pFKmDFHX8qqfk6HXz4fkFXMYokV2pFvRh+i+wvbAPc
+OLI6cxSSSz02yspRkcN1hDlERfjIrMrJq5M5GgoT1F2zQ0Wc+inXDetgIG1QOb+q
+oQR3mxMCQLphohv8n2JkW6+Lmdt9zsx9tOQWjFSB3YFdcb4yhGP5sJ4n8EanpNdR
+k0NbdKffehDDxXUtb4O1U6i/H1NFA0/l/oN0IuhwXkdkv8ikdpPke+FqCp8H6CZM
+vZrC3ItJpIm+k/eXIyAvW4hag/75GfGeV7b4MnVegcylWtacFpaDRsklfAStMXd5
+EOiC4cmANIYupoZwfiSadthk9BbBqzRAcpvFljgFeUeR5N8St4B1noPwatoMuMzh
+WG28Iv/hNr8Rj/vzWznO0xp3lPckZPVHzrl08U1QSH9j6SqsmGMY1Y4riRGzNkUf
+o7eV9GB8kKp6oWM6TCuyRbMhS0LB/TNH3682oBJMEftK5HBiTf8CAwEAAQKCAYEA
+txfjyzGaTH5CZnxFklLKT46GrF7vpJ3jnwi37DahCgHNGpQuF0zjEdZ8k9OY0CBg
+BbLWpRLlA97b3IsxdrZd2287sqDl6+3ihdlw0Fer1eFszJxwFDc5P+j88qvkloGW
+A35sVgK+xXdSIsCMWwia9BE970Hkb8ol5KruKXupjT0PzKNGoT1TjLN85wfRIBjl
+bD3/0mdei21Yp7lXDzwWDEmm2ptAvekF/wu33PLHPxFWGFw/9yPpIZMLg6mpM/8u
+0lWjpuTuQZLAVU0yKag5mhRa9rwCqslxDZzNXYA7hUO57hT+diO1U447cdAEYzpF
+XUZrPBHUOlaRNd2f2INFAX3W0SraSTp3IX2KBd3daU2aSN6y4DMO4I9wJwJV4vrC
+1rnJB+e9DnBNUezhgpiDOE0+vOprrqGtXsdULgFuPd47b2eU4WWvFHeeKuMBgrRg
+iRSgaxButfFIryRhCYA88cXFG0qO3qNUPMmDTCRjS1S6rhQeP8dxN6kcJc+o/+Ep
+AoHBAP95nPrypMNYJAlQtlgYMbxyMRLz7HXLN/TklEtPMNsjK8LHIIa3v3uv8gbk
+1VmXc8oQa4NB8cu9CtX98fSwvbiuwXjhxFe0mMlg4QdkbCQYXj3CeDsE5PNO3aXK
+oIF1WzJZYN+KV/NgyCIP/hAgBBmDFcfgovJb3YvMGmC3/2MmP3+1tgsLabPXoJSZ
+Uqc7c5m9Tlm7NP8LAi/zRjFOD+b3BDIH8e8ADV02uAYe0fyCcihaf7ZYbiROG2KE
+BNlbgwKBwQDncNdXOgz9bb0C+HymH0LwB0KECt2NNN0DV1GXoH8IXEYtK4V2XJdU
+P63EtMfaUgk0vyL/pIWQLlyt9bW0Gnr0nDY3NiX2ctRnh6WUzKNrpdQmhquEWXXw
+pujDupY2O90lXeJdMhp2WWT+22IFMykwLY2sVBJzhXpY2lUd/EBgFiUpD8NRAt2V
+f98eJYd3lC1JEsrEgCQrvEzc+B9y7GHPm8YevJrIcNvYMjUXEMo8rmjd+GZC4SD/
+rZVcCBrYDtUCgcEA7fbkjye26zJNltO0lYgrw8GGDoZgyjP5skW8EA36jxRKrcp3
+dKTxWo+/3EYIqMQXGa+DxaaGSGWVE1DQsEB05/L3ydZZ4ewZqPJxiUY0KMb9+X8M
+KMVdUXkzojuEmDGtOc/hGTeuxsdug7Przi9UQkNE5YJLpX6GdbIvG8onu74jxZyH
+re/6jIccT24lfBo3iou30IM9URd6+RkcV87DpzqNkOCvrRSaXioa7bCFnjQPi6EI
+dtwV9AFBJtmb/q5rAoHAAt/l7NFtw+APDvBjK/ULccvFSbqQ0eYsMJRvEQEPUt1C
+ieEWgUfZIVTBJcZRDScjsiIFn0M93XKV+BsrLJd/m3YtPjZP9mWqubZ3mgeIqBeh
+MPFPRA+QZXLNRVEV+Ip5zrMB0sKCjaHCnV/AMexWwKBwOAm7SPAJev0LPZoaepcL
+0xy9Ak6UzfyOmuNAcX3Hqjavig1FZb2q/rueOGEzPc7jgRI6oe607FSDUEwHFwXb
+i5ZAPuho7oQLbN805iYZAoHAReFyMpjLEXOfyJQAOZb1BQHJ4U8AyYyPleXB/Bh1
+EmO+Qv3VhtqorN0g3t7XaupEqcPBRWQHxo8hlrP+6Rj4fVnT8gFYOb1QMmy5EW2b
+0sdt74xLv4LI6TLLZim+akYNuFxQbBnLHJgDXgjinM+jZfzve39Uhz7ojrFaySqW
+bRcQzciRgbHWrSxkLGq+gJDyjvKkszs4RN9J7LH+C8+BiyRhgxc2ZTja75bq5TQB
+Tohu1wDMgHHInYJkhZNxYIGX
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha384-server.pem b/contrib/wpa/tests/hwsim/auth_serv/sha384-server.pem
new file mode 100644
index 000000000000..d51921f4f31a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha384-server.pem
@@ -0,0 +1,115 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 14110776913249282218 (0xc3d38cd72b01a8aa)
+ Signature Algorithm: sha384WithRSAEncryption
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=SHA384 and SHA512 Root CA
+ Validity
+ Not Before: Nov 29 22:33:25 2015 GMT
+ Not After : Nov 26 22:33:25 2025 GMT
+ Subject: C=FI, O=w1.fi, CN=sha384.server.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (3072 bit)
+ Modulus:
+ 00:e6:f7:58:c1:4d:d6:73:97:bf:69:e1:93:e4:95:
+ c2:b9:12:83:8c:be:3d:fe:f9:07:59:fb:ba:45:2a:
+ 60:c5:1d:7f:2a:a9:f9:3a:1d:7c:f8:7e:41:57:31:
+ 8a:24:57:6a:45:bd:18:7e:8b:ec:2f:6c:03:dc:38:
+ b2:3a:73:14:92:4b:3d:36:ca:ca:51:91:c3:75:84:
+ 39:44:45:f8:c8:ac:ca:c9:ab:93:39:1a:0a:13:d4:
+ 5d:b3:43:45:9c:fa:29:d7:0d:eb:60:20:6d:50:39:
+ bf:aa:a1:04:77:9b:13:02:40:ba:61:a2:1b:fc:9f:
+ 62:64:5b:af:8b:99:db:7d:ce:cc:7d:b4:e4:16:8c:
+ 54:81:dd:81:5d:71:be:32:84:63:f9:b0:9e:27:f0:
+ 46:a7:a4:d7:51:93:43:5b:74:a7:df:7a:10:c3:c5:
+ 75:2d:6f:83:b5:53:a8:bf:1f:53:45:03:4f:e5:fe:
+ 83:74:22:e8:70:5e:47:64:bf:c8:a4:76:93:e4:7b:
+ e1:6a:0a:9f:07:e8:26:4c:bd:9a:c2:dc:8b:49:a4:
+ 89:be:93:f7:97:23:20:2f:5b:88:5a:83:fe:f9:19:
+ f1:9e:57:b6:f8:32:75:5e:81:cc:a5:5a:d6:9c:16:
+ 96:83:46:c9:25:7c:04:ad:31:77:79:10:e8:82:e1:
+ c9:80:34:86:2e:a6:86:70:7e:24:9a:76:d8:64:f4:
+ 16:c1:ab:34:40:72:9b:c5:96:38:05:79:47:91:e4:
+ df:12:b7:80:75:9e:83:f0:6a:da:0c:b8:cc:e1:58:
+ 6d:bc:22:ff:e1:36:bf:11:8f:fb:f3:5b:39:ce:d3:
+ 1a:77:94:f7:24:64:f5:47:ce:b9:74:f1:4d:50:48:
+ 7f:63:e9:2a:ac:98:63:18:d5:8e:2b:89:11:b3:36:
+ 45:1f:a3:b7:95:f4:60:7c:90:aa:7a:a1:63:3a:4c:
+ 2b:b2:45:b3:21:4b:42:c1:fd:33:47:df:af:36:a0:
+ 12:4c:11:fb:4a:e4:70:62:4d:ff
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ C8:A6:E4:81:75:69:7C:09:1D:A1:E6:14:CE:62:65:4E:56:D8:92:79
+ X509v3 Authority Key Identifier:
+ keyid:0E:74:B5:09:EC:FB:FA:E7:BA:6B:1A:F6:2B:28:7E:A9:70:DA:D7:18
+
+ X509v3 Subject Alternative Name: critical
+ DNS:sha384.server.w1.fi
+ X509v3 Extended Key Usage: critical
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha384WithRSAEncryption
+ 04:da:fd:8c:4d:ae:05:1a:bc:39:7d:b4:6e:b1:fa:9e:6c:39:
+ a1:58:24:49:59:0b:2a:d9:2c:c3:64:93:07:72:b0:37:3e:24:
+ 9d:b0:b4:6e:d7:4c:75:57:74:1a:4a:f1:34:4f:83:3d:eb:b3:
+ 77:a0:b3:1a:90:f2:6b:57:7b:46:a2:cb:f4:31:d8:9f:e8:1f:
+ 5c:3f:b3:ac:ff:2d:c8:d5:f2:1b:dd:7c:9b:b8:7f:61:13:3a:
+ b1:14:82:4d:52:cf:d0:dc:6f:20:e7:94:06:6b:9f:6d:49:dc:
+ 41:9b:9e:66:41:d6:45:15:af:92:00:6d:75:5f:95:93:ec:29:
+ 7d:f9:a8:57:1a:16:a4:f9:9e:ac:e1:86:f2:d3:38:25:16:e3:
+ a1:f2:9f:3b:7e:a7:9b:b9:e7:24:0f:f3:da:66:c4:de:34:3c:
+ 75:58:b2:64:e2:d1:2e:6d:ac:f8:03:d9:d2:a9:b6:67:d9:98:
+ 51:76:b5:1f:a8:a0:5f:73:65:dd:52:04:88:f4:e6:d7:cb:94:
+ 83:ac:08:29:25:c5:aa:8a:44:6d:73:14:cf:9a:48:24:ab:46:
+ d1:85:ee:29:81:e6:23:03:82:57:34:2c:f8:e1:5f:03:53:79:
+ f7:ca:b3:58:2c:60:8f:52:d1:20:6e:f0:5a:f4:7e:52:fa:a8:
+ fa:4d:6c:a8:67:d6:da:a5:da:9c:54:c6:34:3a:ca:06:32:a8:
+ 45:3b:41:95:6e:81:07:9b:f4:fb:6a:4b:7c:ee:d5:7f:30:7e:
+ c2:39:8d:88:b4:c9:62:5f:14:3a:1c:48:9d:b6:06:d8:8e:12:
+ 1c:99:e0:d6:7a:a6:e4:0a:b4:23:33:98:3a:00:5b:2d:d2:0a:
+ 05:b8:9c:1f:9d:f0:1e:a0:d4:88:35:0e:47:bc:59:f3:f2:08:
+ 5e:f6:11:b2:53:b3:b4:80:c9:3b:18:e4:51:45:43:9b:7b:8f:
+ 7d:23:0b:2e:66:da:29:b9:0c:98:16:7a:2b:b5:a7:37:e1:f6:
+ 20:cc:06:56:50:7c:36:6b:f3:c8:00:08:7b:bb:df:4d:94:e1:
+ 04:49:7b:e7:c7:77:66:c1:42:59:f3:40:91:eb:c7:98:14:cc:
+ 3f:26:0d:7c:8a:c9:9e:ce:2e:82:99:5b:b3:9a:39:a4:56:8d:
+ 46:13:fa:dc:6e:a0:6d:43:68:05:53:78:c9:d7:dd:45:ca:b1:
+ 0f:ca:ef:e5:5f:54:8e:52:94:ee:4b:ab:0d:dd:02:81:e5:92:
+ d9:b8:6a:58:7f:14:f4:a7:9a:18:9c:51:4f:ec:5f:7e:6e:b1:
+ 4a:46:bf:5d:c7:4f:19:16:f5:df:0c:fc:92:4b:d8:23:e9:7b:
+ 43:38:82:5e:82:f7:04:e1
+-----BEGIN CERTIFICATE-----
+MIIFLDCCAxSgAwIBAgIJAMPTjNcrAaiqMA0GCSqGSIb3DQEBDAUAMFQxCzAJBgNV
+BAYTAkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIjAgBgNV
+BAMMGVNIQTM4NCBhbmQgU0hBNTEyIFJvb3QgQ0EwHhcNMTUxMTI5MjIzMzI1WhcN
+MjUxMTI2MjIzMzI1WjA7MQswCQYDVQQGEwJGSTEOMAwGA1UECgwFdzEuZmkxHDAa
+BgNVBAMME3NoYTM4NC5zZXJ2ZXIudzEuZmkwggGiMA0GCSqGSIb3DQEBAQUAA4IB
+jwAwggGKAoIBgQDm91jBTdZzl79p4ZPklcK5EoOMvj3++QdZ+7pFKmDFHX8qqfk6
+HXz4fkFXMYokV2pFvRh+i+wvbAPcOLI6cxSSSz02yspRkcN1hDlERfjIrMrJq5M5
+GgoT1F2zQ0Wc+inXDetgIG1QOb+qoQR3mxMCQLphohv8n2JkW6+Lmdt9zsx9tOQW
+jFSB3YFdcb4yhGP5sJ4n8EanpNdRk0NbdKffehDDxXUtb4O1U6i/H1NFA0/l/oN0
+IuhwXkdkv8ikdpPke+FqCp8H6CZMvZrC3ItJpIm+k/eXIyAvW4hag/75GfGeV7b4
+MnVegcylWtacFpaDRsklfAStMXd5EOiC4cmANIYupoZwfiSadthk9BbBqzRAcpvF
+ljgFeUeR5N8St4B1noPwatoMuMzhWG28Iv/hNr8Rj/vzWznO0xp3lPckZPVHzrl0
+8U1QSH9j6SqsmGMY1Y4riRGzNkUfo7eV9GB8kKp6oWM6TCuyRbMhS0LB/TNH3682
+oBJMEftK5HBiTf8CAwEAAaOBmTCBljAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTI
+puSBdWl8CR2h5hTOYmVOVtiSeTAfBgNVHSMEGDAWgBQOdLUJ7Pv657prGvYrKH6p
+cNrXGDAhBgNVHREBAf8EFzAVghNzaGEzODQuc2VydmVyLncxLmZpMBYGA1UdJQEB
+/wQMMAoGCCsGAQUFBwMBMAsGA1UdDwQEAwIFoDANBgkqhkiG9w0BAQwFAAOCAgEA
+BNr9jE2uBRq8OX20brH6nmw5oVgkSVkLKtksw2STB3KwNz4knbC0btdMdVd0Gkrx
+NE+DPeuzd6CzGpDya1d7RqLL9DHYn+gfXD+zrP8tyNXyG918m7h/YRM6sRSCTVLP
+0NxvIOeUBmufbUncQZueZkHWRRWvkgBtdV+Vk+wpffmoVxoWpPmerOGG8tM4JRbj
+ofKfO36nm7nnJA/z2mbE3jQ8dViyZOLRLm2s+APZ0qm2Z9mYUXa1H6igX3Nl3VIE
+iPTm18uUg6wIKSXFqopEbXMUz5pIJKtG0YXuKYHmIwOCVzQs+OFfA1N598qzWCxg
+j1LRIG7wWvR+Uvqo+k1sqGfW2qXanFTGNDrKBjKoRTtBlW6BB5v0+2pLfO7VfzB+
+wjmNiLTJYl8UOhxInbYG2I4SHJng1nqm5Aq0IzOYOgBbLdIKBbicH53wHqDUiDUO
+R7xZ8/IIXvYRslOztIDJOxjkUUVDm3uPfSMLLmbaKbkMmBZ6K7WnN+H2IMwGVlB8
+NmvzyAAIe7vfTZThBEl758d3ZsFCWfNAkevHmBTMPyYNfIrJns4ugplbs5o5pFaN
+RhP63G6gbUNoBVN4ydfdRcqxD8rv5V9UjlKU7kurDd0CgeWS2bhqWH8U9KeaGJxR
+T+xffm6xSka/XcdPGRb13wz8kkvYI+l7QziCXoL3BOE=
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha384-user.key b/contrib/wpa/tests/hwsim/auth_serv/sha384-user.key
new file mode 100644
index 000000000000..6a15e795bb15
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha384-user.key
@@ -0,0 +1,38 @@
+-----BEGIN PRIVATE KEY-----
+MIIGnAIBADANBgkqhkiG9w0BAQEFAASCBoYwggaCAgEAAoIBaw1HuCl0ydhb9q0E
+epVENi+Gp7eksHMq2Rx97T29DDwFe8jpVlVWJ1b0oKq96+o6RSzYtp1UGhgSCXiw
+ZPZgrVmZAnJJJU9JceoJDl3PIhkDKfApKxz9LvrmajocRiezZoaTIKj31URKALae
+Id/aY/+ACoBBxIeZoH5g8zPDIg4jEPQJ8ul3WMfKY96vFne1SGjri5iwj72RV+9t
+Pi/jgNSEwgFvUIp/mxR9bT4EmfdXwFhDUlfb7YRA45fzewcualxQE1P+LX7919i5
+mz4zH+OQFvFRtx6VwHVq9Hea2Ix0k3/0JUl1arSbE8h3J5aO377wDUK9DDfjFc8t
+qV4S1rZaJo2Gw++sLni28HBj4iw9qOuLThVRuZA1uDiBvbap9VcJiiDy6RKyyE1X
+Y230W6bXOGKbcw4h1QDLoDOMxDJTXsVOzErCE6Be8K9SviwWFIpdF3xL1i+ddKhi
+dd/Kp59niREH4qvg68TUrQIDAQABAoIBawv9Rt9uh9gkVpSnaYAfIAhSOlLKhV7E
+PVTCv4+wgD4j+ThOqNnMOSIBIphjdHx3dQJi+KMbTZ+TkSd7oPrqFza/s/y32s41
+EXnoCSdaHH+WYqNml3zJe0ObCCZEZnXrOlGevKqvbMQFR5WXOB/gC82crF5Ugfim
+EsZmAssljTZUJDceUEbEr8tYBkgBrHgQmznWBaapKGxNrqUC7FTRwPqcjIY9F+qh
+/8FE535JKzOp7oYA6XNQDLCUMI5oALmc5lq/a9g+HQpr26LNxQW5fadOKQUwZFiJ
+nRtcQo6+JZzXdobH5FZ1oNi2uOHVSiQnnQgqhESJ6jLGWJxVUGXo6kqBWWmICF9D
+g07ky+mssXXCPvNwtG9Mc4yh2Mm+LzDJI1rgMgoA08N0j+q5fT8QzmDZEmM/Gzsf
+NP/GjsLIXE+KjMcILrxDtvbNRv/hx/ys9yQjK9+VR7+uZWmXYaxH3r7D8g5XHdBz
+5/XCpQKBtgPYnHvwMRsZsQZae25/FCUfBT97JmGhr8ifGv0dhSZKRcP4zNtGU4ow
+H6J/B/eecH6bVb4/ja/nSUlLIoAUqqJWWurXdkwZII3b3PzbkqkCnZayBy2x3OIi
+VE7bVUpCSz0EIyuUjqrwc/d3PqrIARwiuucbgXqC8gqaAEUoXQGRGGKoerZLv/dQ
+VyTlyjXDlJYLub7cLBBCo8mpii6AFbc0Js+1qZJfUp0D+12qlfu9qy1W3/efAoG2
+A3PuRMQtWn0q84o36zQaJRbMQlymiMOMQBzFmR8GUiAiXHUqPZgAPsvzs7RllCTL
+ItDiKMcpE6/6MN0ArmDHA5bnAnu29SkPRiD7rU/ZGTR2Y9uujj0DIBcNHS/bbDtT
+xEwFnshSz6vvpxu6DtJ8uvEf3wJyeZmdMJeDmrBnadqT2juZIseGHneHrjJK3ZyU
+iLRI0ulXAbziwxnkKj9QUaHK0XiSzjAAt5NRTLoALix0ittJgDMCgbYBnDEGgatN
+GRhO/Jony/N3BuF/jeKnhLS+XD1EMZOIUBeczw+TzIE0nKjhsiR3uVCG8CiZGKoM
+NdssX9P1orE8fMJbBhB0EyDZwm1lPdbMAlhOugDfVFKQKKb6zD+McuxkgtLmb666
+SSeDNdx6SniMes2b6pvt2dvSLF5olVk6Sq/WvYmBv3yB4JRa0ggxMcuGdSoxiKK5
+u+wthFhg1yZAKAkHc5mluVoweXZF5CAd321F8dSZKQKBtgEEpWTXqDv/nrOztSuA
+8JixMUf8RAseBnQ9R7MQJ+/9k8RJtEv3T1M1FsaN0kot00yP5bB6kc1BXfgcov/I
+f8a6L6JW0qtWES/vt+byHaVGCAcIF1/P8+T5hx9tJjmzAM9oT1vz3B9qpr9S+Lk4
+Lhl90pUTBqh+uJBEjUUG8WeQUXrPiidsSEshmfuuzs6sRkxNRRAUSFi11vQK5XHj
+u45mtASyli+AjiWTpiyGyFjVOQRdBz5rAoG1ErrUGzeHL9plx9NPsUgw2TMAt//g
+yu1a9yDl6oARMkWMXKFytPBwwBY6H0zE74qvVQVcxHEMLGTOiLTHU57meGfVbfW+
+ikWO82ztD30nSrQ2vH3sZjeftU98R7y8L+f9icNftUTo3oA1pU8QuOj+2J/ja2Pa
+ksRDoe8fqUCi3OhiG7dhBcuK4wc0p17qjmKS+fA/Ky4yV24LuxWp1ge737rjlrvm
+hoCJF/ERHMvfrviGjrs+Rg==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha384-user.pem b/contrib/wpa/tests/hwsim/auth_serv/sha384-user.pem
new file mode 100644
index 000000000000..6935dd34d3bd
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha384-user.pem
@@ -0,0 +1,113 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 14110776913249282220 (0xc3d38cd72b01a8ac)
+ Signature Algorithm: sha384WithRSAEncryption
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=SHA384 and SHA512 Root CA
+ Validity
+ Not Before: Nov 29 22:33:25 2015 GMT
+ Not After : Nov 26 22:33:25 2025 GMT
+ Subject: C=FI, O=w1.fi, CN=user-sha384
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (2900 bit)
+ Modulus:
+ 0d:47:b8:29:74:c9:d8:5b:f6:ad:04:7a:95:44:36:
+ 2f:86:a7:b7:a4:b0:73:2a:d9:1c:7d:ed:3d:bd:0c:
+ 3c:05:7b:c8:e9:56:55:56:27:56:f4:a0:aa:bd:eb:
+ ea:3a:45:2c:d8:b6:9d:54:1a:18:12:09:78:b0:64:
+ f6:60:ad:59:99:02:72:49:25:4f:49:71:ea:09:0e:
+ 5d:cf:22:19:03:29:f0:29:2b:1c:fd:2e:fa:e6:6a:
+ 3a:1c:46:27:b3:66:86:93:20:a8:f7:d5:44:4a:00:
+ b6:9e:21:df:da:63:ff:80:0a:80:41:c4:87:99:a0:
+ 7e:60:f3:33:c3:22:0e:23:10:f4:09:f2:e9:77:58:
+ c7:ca:63:de:af:16:77:b5:48:68:eb:8b:98:b0:8f:
+ bd:91:57:ef:6d:3e:2f:e3:80:d4:84:c2:01:6f:50:
+ 8a:7f:9b:14:7d:6d:3e:04:99:f7:57:c0:58:43:52:
+ 57:db:ed:84:40:e3:97:f3:7b:07:2e:6a:5c:50:13:
+ 53:fe:2d:7e:fd:d7:d8:b9:9b:3e:33:1f:e3:90:16:
+ f1:51:b7:1e:95:c0:75:6a:f4:77:9a:d8:8c:74:93:
+ 7f:f4:25:49:75:6a:b4:9b:13:c8:77:27:96:8e:df:
+ be:f0:0d:42:bd:0c:37:e3:15:cf:2d:a9:5e:12:d6:
+ b6:5a:26:8d:86:c3:ef:ac:2e:78:b6:f0:70:63:e2:
+ 2c:3d:a8:eb:8b:4e:15:51:b9:90:35:b8:38:81:bd:
+ b6:a9:f5:57:09:8a:20:f2:e9:12:b2:c8:4d:57:63:
+ 6d:f4:5b:a6:d7:38:62:9b:73:0e:21:d5:00:cb:a0:
+ 33:8c:c4:32:53:5e:c5:4e:cc:4a:c2:13:a0:5e:f0:
+ af:52:be:2c:16:14:8a:5d:17:7c:4b:d6:2f:9d:74:
+ a8:62:75:df:ca:a7:9f:67:89:11:07:e2:ab:e0:eb:
+ c4:d4:ad
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 85:5F:26:C0:68:70:33:79:E3:BA:57:A3:5F:52:94:38:F0:6E:53:05
+ X509v3 Authority Key Identifier:
+ keyid:0E:74:B5:09:EC:FB:FA:E7:BA:6B:1A:F6:2B:28:7E:A9:70:DA:D7:18
+
+ X509v3 Subject Alternative Name:
+ email:user-sha384@w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha384WithRSAEncryption
+ 81:95:03:32:e7:e5:e3:0e:22:0e:cc:a5:b5:96:3e:15:a8:6c:
+ f5:e2:1f:32:b9:09:71:b5:fa:f4:84:ae:e1:8c:d4:cb:ef:e3:
+ b4:58:aa:bd:bc:df:6a:9c:91:9b:5a:d4:e1:b0:1c:dc:dc:e9:
+ b6:68:71:83:e1:7e:1c:81:fd:a6:3b:14:67:1a:67:64:ed:a8:
+ 3c:43:2f:cf:e1:63:51:f0:9d:1d:e7:0c:0f:58:bc:bd:bf:af:
+ ee:55:f8:1f:5a:9e:1f:c2:74:f0:8a:e4:5f:b2:19:e3:e8:c2:
+ 5c:1c:39:f4:24:51:ae:d2:21:da:b8:12:97:ff:2a:d9:ff:61:
+ 02:31:1f:87:3b:14:0b:7b:9a:77:11:a8:83:25:38:6a:1d:89:
+ fc:48:75:8c:2f:38:a7:66:ee:a9:65:2c:d9:f8:bf:e0:12:d6:
+ b7:11:07:d0:72:a8:76:53:32:94:39:47:be:74:69:f6:6b:13:
+ 2f:eb:e1:a2:8e:32:43:0a:cc:13:ea:00:29:cc:99:7b:eb:5c:
+ 06:d5:4d:ef:6e:2a:96:6b:33:a3:6f:53:0c:59:4e:89:9b:56:
+ f6:a3:94:0d:7b:21:df:0e:af:b7:df:cf:56:98:81:02:9d:e2:
+ f1:29:90:2e:7f:be:4d:24:6f:46:8d:af:ff:f9:30:7b:40:48:
+ 1c:1b:68:6e:9f:ec:e2:33:51:7c:ed:ee:12:bb:3a:97:ce:85:
+ fe:d9:c3:0b:1a:a6:1b:12:bb:db:4f:f3:b1:e5:80:25:b9:62:
+ 7a:e9:8e:17:44:97:cc:54:bf:8e:c3:aa:37:b2:74:e9:58:9b:
+ d7:53:00:4d:82:c2:42:ba:c1:c2:7f:00:fa:da:06:dc:98:04:
+ 68:35:d6:3c:14:4e:dc:4d:e4:d8:b9:b5:e2:17:79:91:3b:d7:
+ c7:f1:ff:e7:a3:25:68:c4:96:29:c6:b9:45:e3:3d:1c:29:22:
+ 2f:0b:c7:8c:8e:b6:0a:0a:82:20:0b:50:ca:e6:c6:de:01:38:
+ f9:3b:31:e0:1c:85:11:bd:a9:9e:bf:8c:f7:f2:64:03:ca:60:
+ 16:2d:26:94:eb:9f:8a:d0:5e:1c:eb:3c:26:7e:03:84:d2:f0:
+ 5a:b3:8a:7b:86:86:67:ce:1e:c9:c8:ad:3b:0f:08:7f:3e:54:
+ fa:ad:e4:5e:3f:c1:cb:50:3a:dd:ba:b1:0e:d2:9b:88:46:17:
+ bb:67:cf:5c:11:f3:a3:f7:0b:95:ae:25:ce:3c:e9:ca:aa:46:
+ f8:a9:8c:cf:a9:cb:bc:00:94:a1:c7:02:98:1e:e5:b1:c7:e7:
+ 51:50:f7:5e:a5:c8:e9:ff:e0:50:17:cc:10:c5:f8:0a:68:ba:
+ ca:78:f8:1a:6c:ac:f2:10
+-----BEGIN CERTIFICATE-----
+MIIFAzCCAuugAwIBAgIJAMPTjNcrAaisMA0GCSqGSIb3DQEBDAUAMFQxCzAJBgNV
+BAYTAkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIjAgBgNV
+BAMMGVNIQTM4NCBhbmQgU0hBNTEyIFJvb3QgQ0EwHhcNMTUxMTI5MjIzMzI1WhcN
+MjUxMTI2MjIzMzI1WjAzMQswCQYDVQQGEwJGSTEOMAwGA1UECgwFdzEuZmkxFDAS
+BgNVBAMMC3VzZXItc2hhMzg0MIIBjDANBgkqhkiG9w0BAQEFAAOCAXkAMIIBdAKC
+AWsNR7gpdMnYW/atBHqVRDYvhqe3pLBzKtkcfe09vQw8BXvI6VZVVidW9KCqvevq
+OkUs2LadVBoYEgl4sGT2YK1ZmQJySSVPSXHqCQ5dzyIZAynwKSsc/S765mo6HEYn
+s2aGkyCo99VESgC2niHf2mP/gAqAQcSHmaB+YPMzwyIOIxD0CfLpd1jHymPerxZ3
+tUho64uYsI+9kVfvbT4v44DUhMIBb1CKf5sUfW0+BJn3V8BYQ1JX2+2EQOOX83sH
+LmpcUBNT/i1+/dfYuZs+Mx/jkBbxUbcelcB1avR3mtiMdJN/9CVJdWq0mxPIdyeW
+jt++8A1CvQw34xXPLaleEta2WiaNhsPvrC54tvBwY+IsPajri04VUbmQNbg4gb22
+qfVXCYog8ukSsshNV2Nt9Fum1zhim3MOIdUAy6AzjMQyU17FTsxKwhOgXvCvUr4s
+FhSKXRd8S9YvnXSoYnXfyqefZ4kRB+Kr4OvE1K0CAwEAAaOBjjCBizAJBgNVHRME
+AjAAMB0GA1UdDgQWBBSFXybAaHAzeeO6V6NfUpQ48G5TBTAfBgNVHSMEGDAWgBQO
+dLUJ7Pv657prGvYrKH6pcNrXGDAcBgNVHREEFTATgRF1c2VyLXNoYTM4NEB3MS5m
+aTATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMCBaAwDQYJKoZIhvcNAQEM
+BQADggIBAIGVAzLn5eMOIg7MpbWWPhWobPXiHzK5CXG1+vSEruGM1Mvv47RYqr28
+32qckZta1OGwHNzc6bZocYPhfhyB/aY7FGcaZ2TtqDxDL8/hY1HwnR3nDA9YvL2/
+r+5V+B9anh/CdPCK5F+yGePowlwcOfQkUa7SIdq4Epf/Ktn/YQIxH4c7FAt7mncR
+qIMlOGodifxIdYwvOKdm7qllLNn4v+AS1rcRB9ByqHZTMpQ5R750afZrEy/r4aKO
+MkMKzBPqACnMmXvrXAbVTe9uKpZrM6NvUwxZTombVvajlA17Id8Or7ffz1aYgQKd
+4vEpkC5/vk0kb0aNr//5MHtASBwbaG6f7OIzUXzt7hK7OpfOhf7ZwwsaphsSu9tP
+87HlgCW5YnrpjhdEl8xUv47DqjeydOlYm9dTAE2CwkK6wcJ/APraBtyYBGg11jwU
+TtxN5Ni5teIXeZE718fx/+ejJWjElinGuUXjPRwpIi8Lx4yOtgoKgiALUMrmxt4B
+OPk7MeAchRG9qZ6/jPfyZAPKYBYtJpTrn4rQXhzrPCZ+A4TS8FqzinuGhmfOHsnI
+rTsPCH8+VPqt5F4/wctQOt26sQ7Sm4hGF7tnz1wR86P3C5WuJc486cqqRvipjM+p
+y7wAlKHHApge5bHH51FQ916lyOn/4FAXzBDF+Apousp4+BpsrPIQ
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha512-ca.key b/contrib/wpa/tests/hwsim/auth_serv/sha512-ca.key
new file mode 100644
index 000000000000..b4f7eb24f0b1
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha512-ca.key
@@ -0,0 +1,52 @@
+-----BEGIN PRIVATE KEY-----
+MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQCgV1xtgG7ws5e4
+N+F5KfWrEMKzsP5P8PXVMiB6Rv/Wuzi1EHZjNdbUQuVV+hHRI1K2ReqXAeaNyhEE
+Jn42hGndcHKHfcAUTQWQWfCscP32Ti2YSViHD60sqZKr4agDoLkMR21BMHXgrU6W
+QED070WE0CbELVUtcb17oPh/ndh46Ftb5U6H5rpY8T13148xhNCh8yKAOcFfE9GW
+Z3mTMp+VxmcK72KL4Yga7bU3Uk5xHlFZ0rK1W0xItEi+SGfMLELalP6wAYMVtPAA
+iqepg3WSq5eNyZgTwq5jAuLP/4Bf1C7WhNveFE9WalJ3rrYEs7Rd+mOYm0QXcH8t
+IqC9UTUhfI3wJOpLGEfHieLHCDl8SYijET+hFFTYiU1jj4sQjAeY8VnC7/tmjJpC
+j6oNcbr57WwMxO/Xafo0JW3axZiyfDUlPShPbhyBjhq2/3wOKJUbeK/cY9J0CjW1
+5k8DYjSC0vGFwl4CRmhfN/5XeO9kGQR9kQjha59v2/gW4wKFnuqPyWyY4vWgcEmc
+8kqJqrkR61fwZ3H7G5ErMw/q/jR9tuqcTuRvQjh1LEtv2UxzNg68L73nhqT4UQRO
+uXkXPsUCN13Bgm3OzTul+zmbXtRlH/2oc+16IyzICj5VWpiZgC7D7Ljnlquvo1/3
+EitInFWjEl8kpsPJnBT4id/xrJHoywIDAQABAoICAFFF1ti+R/2D2ryKvqQOy7KJ
+DVfNuCpHJiSJgwLX6CgswAKvNIL3MExpGBvrZIqQkAVKDS27zeRC7zseU81IcuzG
+aZcZ+3mOzOotXJvbri7h06SkUNYs8Qd5cJnlCKfGGOYLcmqfqLBYyEPKj+JXQfKf
+G3dGzyz4wSXgIvV0ydwHUv+SjKrAHealaRXM0o00GBhuyCccn1KVFiBZlLsy5sch
+SOu3CUmD6NxVbwx6kL4vsuaHsQPSIimcEF87DjnkmYJ4EVyfd5VSIHD11yRKORc+
+GNTKwWYKwR/4v4TUqnpob9FWiKfZvZk8zU5S5XoeqKcWGtOop+wFnsD/E2DCv93K
+vbY0n5B5L5XFjtmBYuvdqXe4RH46ZgyePI3G6DSTCq5L8/+5pZlTeny4Vz77ecm8
+CU/4XOcYMIBZim9Fie4jQLpD4KNjKjhXLjpetNyHp3sIoghb90Qv5UdLZ6dnf4+D
+nxbg+tfvwltNfPgCvrOh3OcnSSLUSb5kSim0xpFtaH5QgAQhVaFaDY8GtaOnPyRG
+39XPwpAQXpt8EyW4omwtI8QL+/bciQNg0tVLHkxg7qOEPWGoaxvQbxT8EBGufRz9
+7qvl1ajGEAeaDY62HUssBW2+YhFKNLbzWBVO/S4AWPYPLMeAVQfFNVXWyS5www7P
+DfQXAt+rwLWwfvpW2C2BAoIBAQDVVXpIykVnUuQoRclLMnm8Z5DMfWOP/Oj3Em2z
+nKvnyM+rXEr7sT7XLAJa4aU0Fg27omUXZpNB6UDntWLOHXi+Zh3PgMd1CqdIclVK
+z6uGfK9TKHIQpYY7RLGcvBAjfnyv2KKcqBi2IQEXidoHLtZ2gQJJQ1Aoiw8gIQAe
+kZjXwcxlEgDdVR4+Zamj5QRfAG7Nd7PVl06gJNyCinS21/UFELz6hB4COgVjOPzr
+FvhoPrZE6QUNm76wFVXhYl9Taj/lM+0q5b88ZCJCqhma6aGOuVRaSpaTeRYC63ys
+oTYdUR5Cc7Z4h8iFW4S6Qs992MvH3k73R6/ad0o8kaUrZU1BAoIBAQDAaLP8jjc8
+4cDi3lNos0cSiF2OBazsCAsI5IRovkkugSDYK9FKOrQ0P8mFTsA+IapAe8gMIjZY
+ivRJ8dQ8beNhub9gULlNpp8VZ0QJV5jn1qp1vqW72ti28KcwPFTSmQZKMevwl6Bw
+t/s3RY3SndU3LJ1RdtZuhC9tgAGbH/Os524kvBc8sPjQkwRP8MABF/4XTS1aR7yO
+6kka1ZxJ32X7Chz7pO/IagTmAhJmbLkponWPtCvwlHx8SFE0dyC86snhb7CIr8B/
++1BBueTjUhrhADCm6X106hIgm7C2o9m+x92Y7KHM4yEjHEo8+VVC8WV5TmcXOqMM
+nqYCHeNn1tcLAoIBAQC9w2MbDJHf7uP0zt7XfPa8mIM84uyFUDsKS+I5OsvPSdkw
+XNB2QpvbWtJddQo24sckeLfjsOKcZpfNhJN3NNAKzVsHEU5a1jcnQkyMV14EKzTp
+i7irBhH49onnGYJhkEnpQZKkNwKEP6dxALZoXUl38BnQgYf0CH5T3gb1Kh4DDeh6
+nyEVwHk+l7/mgfj5aLEnI6tb+1N9MEzV8cMQQdk5wEHZtVvcLzBLqo2PsMcWM3J+
+qahNCpT8nH3gFlklIgXkI+R7nBIX7hprolNUcS28fy6Bgoeedr8VqdMk2+H/AgEg
+qz1MybucpGPUK0nWb9oU17L2U1YhxqrKZeO+TkYBAoIBAAu7Vp4jncKckUJEBBny
+NHcw0WODfRO3OdUE+f3Y/GVVgkcsBMrd4Xb+HK+AKcCgFN1xrrTusRmc/2Ay2poE
+qUSgKscYpPPTIQgRD9jx+mTIdgRP55MYuPYOnmMWiqV8pyGHAbfdxu6YiTzJhOg+
+r215zu3UrSZ38NxgXbizrgvw4Ipk3ZXZxJITJMQrDcoDSH7rOcSzcw/TwTldpPXs
+JS+1YicF24kAzeOoZK7SGkgrm7dzaOp2Y1DAqBLm4JwkRML2KHFtJfOnwzD+wLIL
+o4/sjwreWcPzMb/DPnckbnZvgVd9ti/j+XVGmFA3c7dtOJ645RhJfv+Z/M1MPT1r
+oBkCggEAWqkhZDArd1EqwauhCKwAb2Bp8IVd3rAyaBMtQZVebf467rMh0o4FaPZb
+a4oTZQEAAvlabbKiNW16kmdhzhXNIpgtsTQ1ZfagjHP3fPp0mz3XQiATudaxQBVH
+o7hKafsr2YJLWD03RO+hO2UPr/rLqN9+8/MlT5pityn1QZHpHf9qzliUhw/2zAjq
+kgygbM8UCEXl7zb7ptxaiPsxBn+ynwxgzibwLRHiePOfil8fD5hyE/5gRISrQidA
+VO5RzhqnD3kDH7Da8BPJt917CsgvZ+VffzDP4D/V+L1a/R1ldyXG+omn7qED1ui6
+V2qOAd4RYJFC9FFgNAWy22r3lSYw+g==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha512-ca.pem b/contrib/wpa/tests/hwsim/auth_serv/sha512-ca.pem
new file mode 100644
index 000000000000..2ed9314ba65a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha512-ca.pem
@@ -0,0 +1,32 @@
+-----BEGIN CERTIFICATE-----
+MIIFkTCCA3mgAwIBAgIJAPz9Jkl2amj5MA0GCSqGSIb3DQEBDQUAMFQxCzAJBgNV
+BAYTAkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIjAgBgNV
+BAMMGVNIQTM4NCBhbmQgU0hBNTEyIFJvb3QgQ0EwHhcNMTUxMTI5MjIzMzI0WhcN
+MjUxMTI2MjIzMzI0WjBUMQswCQYDVQQGEwJGSTERMA8GA1UEBwwISGVsc2lua2kx
+DjAMBgNVBAoMBXcxLmZpMSIwIAYDVQQDDBlTSEEzODQgYW5kIFNIQTUxMiBSb290
+IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoFdcbYBu8LOXuDfh
+eSn1qxDCs7D+T/D11TIgekb/1rs4tRB2YzXW1ELlVfoR0SNStkXqlwHmjcoRBCZ+
+NoRp3XByh33AFE0FkFnwrHD99k4tmElYhw+tLKmSq+GoA6C5DEdtQTB14K1OlkBA
+9O9FhNAmxC1VLXG9e6D4f53YeOhbW+VOh+a6WPE9d9ePMYTQofMigDnBXxPRlmd5
+kzKflcZnCu9ii+GIGu21N1JOcR5RWdKytVtMSLRIvkhnzCxC2pT+sAGDFbTwAIqn
+qYN1kquXjcmYE8KuYwLiz/+AX9Qu1oTb3hRPVmpSd662BLO0XfpjmJtEF3B/LSKg
+vVE1IXyN8CTqSxhHx4nixwg5fEmIoxE/oRRU2IlNY4+LEIwHmPFZwu/7ZoyaQo+q
+DXG6+e1sDMTv12n6NCVt2sWYsnw1JT0oT24cgY4atv98DiiVG3iv3GPSdAo1teZP
+A2I0gtLxhcJeAkZoXzf+V3jvZBkEfZEI4Wufb9v4FuMChZ7qj8lsmOL1oHBJnPJK
+iaq5EetX8Gdx+xuRKzMP6v40fbbqnE7kb0I4dSxLb9lMczYOvC+954ak+FEETrl5
+Fz7FAjddwYJtzs07pfs5m17UZR/9qHPteiMsyAo+VVqYmYAuw+y455arr6Nf9xIr
+SJxVoxJfJKbDyZwU+Inf8ayR6MsCAwEAAaNmMGQwHQYDVR0OBBYEFA50tQns+/rn
+umsa9isofqlw2tcYMB8GA1UdIwQYMBaAFA50tQns+/rnumsa9isofqlw2tcYMBIG
+A1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBDQUA
+A4ICAQCSG0Tk6pMvUxSMi9q4Bnq1jjMehEVZH6JHoADSjYDpRYj26Zjzd5k1qRua
+rdAlugMaV/Jq0kcebcvIRl1de0BPx7qfRzq39lSVDojtefjj824EsYj+rBssmwUZ
+e5XfXzxSmfEtT7Ot1PMLyCUCeg7JPr0dbdo7EVjh6XhYo0IlLsOJcKwfj/z74K14
+SlL1jXknhQgizCzt/gFkrdrFFCg0cCTjG5gKVHnn97GY61PI1CEMYGjkP2x7wv2o
+dJE09ElEPjrQOiShqfmfeUOuM6xmYzZFtVWp/M+tEQdL5WsBrN2dujHA+Ftf3xrF
+aRlGLFCzqlC+HU1CSsiI4gXXJT4Bp6WrVP/insAuuS/a8KQRkox8JvPBOqnYf/m4
+JvGJbhukEgbhUdUON4UtWwr3pTkt16SKmE3IdG/Umabi+bSkMmSzw3Iy12LOkrhT
+5OVbU+EwFolc6WUmp5VnhD/NtNdTvaTIjuujU4MyXkBfHvPj4bR62/cSXdGL4LzL
+UjlrFEN3RnFiF4/slrT4z4VRa4FqaYg+aRnMuGwPMHBjUTmyQp1yjF0kp/MYDGF7
+YO46+ep1pwx7zboB1nsKPdLzwEHvO7p6yBO2daEBCiE4RpedSjkpNhYgfHn4WVoX
+NPZChYbX8mGds3xnB6BGsfvzXEtCtmOGbDRIJm6aB7qTrfHiYA==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha512-generate.sh b/contrib/wpa/tests/hwsim/auth_serv/sha512-generate.sh
new file mode 100755
index 000000000000..d692465e8d21
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha512-generate.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+
+OPENSSL=openssl
+
+DIGEST="-sha512"
+DIGEST_CA="-md sha512"
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = SHA384 and SHA512 Root CA/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -x509 -new -newkey rsa:4096 -nodes -keyout sha512-ca.key -out sha512-ca.pem -outform PEM -days 3650 $DIGEST
+mkdir -p ec-ca/certs ec-ca/crl ec-ca/newcerts ec-ca/private
+touch ec-ca/index.txt
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Server SHA-512 ]---------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = sha512.server.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,DNS:sha512.server.w1.fi/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -newkey rsa:3500 -nodes -keyout sha512-server.key -out sha512-server.req -outform PEM $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile sha512-ca.key -cert sha512-ca.pem -create_serial -in sha512-server.req -out sha512-server.pem -extensions ext_server $DIGEST_CA
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Server SHA-384 ]---------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = sha384.server.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=critical,DNS:sha384.server.w1.fi/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -newkey rsa:3072 -nodes -keyout sha384-server.key -out sha384-server.req -outform PEM $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile sha512-ca.key -cert sha512-ca.pem -create_serial -in sha384-server.req -out sha384-server.pem -extensions ext_server -md sha384
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ User SHA-512 ]-----------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = user-sha512/" |
+ sed "s/#@ALTNAME@/subjectAltName=email:user-sha512@w1.fi/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -newkey rsa:3400 -nodes -keyout sha512-user.key -out sha512-user.req -outform PEM -extensions ext_client $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile sha512-ca.key -cert sha512-ca.pem -create_serial -in sha512-user.req -out sha512-user.pem -extensions ext_client $DIGEST_CA
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ User SHA-384 ]-----------------------------------------------------"
+echo
+
+cat ec-ca-openssl.cnf |
+ sed "s/#@CN@/commonName_default = user-sha384/" |
+ sed "s/#@ALTNAME@/subjectAltName=email:user-sha384@w1.fi/" \
+ > ec-ca-openssl.cnf.tmp
+$OPENSSL req -config ec-ca-openssl.cnf.tmp -batch -new -newkey rsa:2900 -nodes -keyout sha384-user.key -out sha384-user.req -outform PEM -extensions ext_client $DIGEST
+$OPENSSL ca -config ec-ca-openssl.cnf.tmp -batch -keyfile sha512-ca.key -cert sha512-ca.pem -create_serial -in sha384-user.req -out sha384-user.pem -extensions ext_client -md sha384
+rm ec-ca-openssl.cnf.tmp
+
+echo
+echo "---[ Verify ]-----------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile sha512-ca.pem sha512-server.pem
+$OPENSSL verify -CAfile sha512-ca.pem sha384-server.pem
+$OPENSSL verify -CAfile sha512-ca.pem sha512-user.pem
+$OPENSSL verify -CAfile sha512-ca.pem sha384-user.pem
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha512-server.key b/contrib/wpa/tests/hwsim/auth_serv/sha512-server.key
new file mode 100644
index 000000000000..8cc5e80a0cd8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha512-server.key
@@ -0,0 +1,45 @@
+-----BEGIN PRIVATE KEY-----
+MIIH7AIBADANBgkqhkiG9w0BAQEFAASCB9YwggfSAgEAAoIBtgyUrc5DYSg0X9fe
+xaXrNjYldxn7pZsOu+1u2RAbcNNKFPs+XFCihLMu/QnNxKv5+n7njugzUIoBg0I5
+Oydoi/rmXGCG6NBWWnm8KsqpC/WJ1aMldsFxi4oyKieBizGo+alsYgApzWp8LMwD
+NHdfk+fyGoyneJowKdGVO2BridD0abGCfBdztSta76bse8eb4wo8TEGYrbSkBTBV
+YFmpDRw5tLTzcPy300fmRD+PPm7QMY6F7i8s9Z2GwYJ3Ec30Fah4KhidtiwfllHW
+PLYu9ONl1J9OkuZVGOI+bh7FPV11ISx6r8r+Cz0YlkyG8qf4bbMPDcv3RYa/iCFV
+9BzMp++ySRNCzpV0+mSw5P842hTvXBBwCqgwTyL4+Vao9Pcf6TGISn3tESeJSecG
+plrTJ9xgey2RWkgRP+Cj0r2Jr8ijhKAntYmY/TxY9KjbgXu6CAvVXzB86hnVd9+F
++sT060f/cGXZ0ZF5EKcbEcqu2N+98fFU2Q2LeBxgzZ5jBAWGYKsZ58/dz+o9Df+B
+F4t0W4Wp5JPKdIbkTETUCwaNBtM1TvetKvZ30HZc/DxjLv0/QkE2ctZ4PwIDAQAB
+AoIBtglR7/y5T/W/7yz2HUhQmyW71aMLGWFopfI8x1O2cHwnCqoiRbN64oH4En87
+0DJzi46OLwF4WncSrdHWoisMuX7TP6ZG65zEDFzY+H6Qg4qQZRNrArJGUtC7Xx1L
+S/orK2HHKEbksQftHCa7TRgOV31ijyaaPq9DdQeN3aINqNiC6PYXK6UYFCQdRyNL
+WFvE8Yq4pumFmVIUnL900F877Ll3SpcvLgoaiMxgGzlW/jKLy9rA/3Y1gXQEy8DC
+Qw9nftACzXTVCYtgjzTrWKjC2qrH2p92Tz+R1VBuXUPZLMhCGGI9eYWdaaoCFsPy
+RRsDhLpExK+WcQyinPrhUinr8p8DsgL2Qn6LJCBRbSS7kYa4cT//e+ohsvelE+sv
+9nhUEpyndUW5FNm4fp29wi+mKfhFWlYXsMA5xJ2btcKZPH1iVuV3oPI6ykj2RSRc
+/ZfnXyyCKxnYlZ2mQCmB2HpU6gTwp7LJY6X//dODFtuuVXXdDKAQLTfcCIhBWcAW
+MRFjs7UeY9D+iiSmMH7NAgaWWLJUIVFKbRj6hItCjfJV/bub6kfy5AXmx9kAqv58
++muUMiWBHC15iQKB2z1ZBWNWFaa4fPlLUNo0orxDOv1vPUChs1D8SyD/7486XE9K
+VHolpWy6d/XmuQTFSbRs2E4XaWQNirXPG5FVpe4D8YnL57E1sITYTdqnYbeifZVb
++Wt+u89d4a1jEECFqHXUKixm0/kcl++UJwkYLm9eol5Ip8pp6OC6upht8p+8HHHr
+1lL4Alzwq/8iUkIy79BQQurcIAzAE2CBgzbU4Dc63yiyqRhepeOhqdCZDr35F1E3
+RxgsCsmKDIJQ2bV80Q+yrGVcR4Uz+oeG3MjAzsmQR4IvsU5jGSd2TQKB2zR/rWEJ
+WsVgMFiAyFdYI45x+bpG9tOuc6VHNX/7f0DeOlM/eXnvRwpvW9LDBwCXIkJkwOSx
+bMtlQge/RI0AwchzvEz3UMYpHYtaJmYCxUXLHc/ziYrNHfsZOVIIw1y6yrrfWGiV
+54nFdFRZx2aXZpPqa2oVUqxl3qkKEpPgLzQancS5xDcm14J65pCYbuodNkz02Buv
+8RzSakF+UJmLhBulMBHMgN0NGNnTYfomUFCb2/Plu/kK58raokOxiAgusCDgpgsR
+ohwdTjBb/GJ8VceGvooqBXVB9FJGuwKB2wn5zN9/YxvuShw7vBUPlBrrD8+gZCR7
+Zu5XaFwgYjM3OUWHf78xGfpZRCKPdyyobDjTrkTcl722WgUAfaofdFEFAST4LF/r
+rB2eEuLobwJugN9lymRJgn7Q9F6+iikD0wX+DzGTMwsCNE64F7Tp4rsnd12/kNFg
+cYcBtlANKPQCodmAkSSCwdedJ0rgqOGs6MRGGOuaCfgoz73UsaKb8S3p+9nhW4/j
+7DPc2QFbskMAG2OQipYKq2JkeW5DeXOCrXhRBKmi3NpzPl0FeCeI+tdaB9bKrJlj
+kfaRzQKB2xGoJ44NcWQU9+2sUrVdxYexOca2+2kysV6TVqEW2zPcCk3ZeWfTXFdc
+IMwAICjIrBMcg+vm6Kp4Af9NCydJa+OZWWmhfJ9JUNB/n46LvAG/UOWSCV6PolfD
+og2/OMXFZU34tGlokZk6ueds0q3kmfGB1GK3jTjCgmLkKl9l9TpXAV0bBa1u+Oi/
+oTBctGE1NgAoEU8um6+P7YDdlQovbF1H76Ts3SJ7tzH78GYL4NNbuFyNle0rB86o
+Pg76dMAbw80rAW2TnqxKBvQo2/fbzZXv8cHSZEeNyTiq5QKB2xDSGoN8kLg3VpUf
+tqhy60WuLPOlzrOmRPRFAPzT/tOqKSfa59/QXuXcZ1z3tI7DN0TLMGc9weNcytvD
+KJrZTtqsyjRrdpTQfMBzn3+sP4dXg9vw9fRmKOHLo323Vr4WPXeWBBkUGGCLr7qM
+Lw2iWNO64lwG7qOz53IVAkgQv4hHkpZdc5UKdFBef6OT+pgSG8MOEVkMfnGcqWh8
+dMbbIQ5xvHOhsplzQm/V2x7ucmFMaT/HO6FGSeIYmX/ZsWyBvfA5zJtAc79KGX0k
+Jer426hlmX1bSvfG1YJlXg==
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha512-server.pem b/contrib/wpa/tests/hwsim/auth_serv/sha512-server.pem
new file mode 100644
index 000000000000..9e669937104e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha512-server.pem
@@ -0,0 +1,120 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 14110776913249282217 (0xc3d38cd72b01a8a9)
+ Signature Algorithm: sha512WithRSAEncryption
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=SHA384 and SHA512 Root CA
+ Validity
+ Not Before: Nov 29 22:33:25 2015 GMT
+ Not After : Nov 26 22:33:25 2025 GMT
+ Subject: C=FI, O=w1.fi, CN=sha512.server.w1.fi
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (3500 bit)
+ Modulus:
+ 0c:94:ad:ce:43:61:28:34:5f:d7:de:c5:a5:eb:36:
+ 36:25:77:19:fb:a5:9b:0e:bb:ed:6e:d9:10:1b:70:
+ d3:4a:14:fb:3e:5c:50:a2:84:b3:2e:fd:09:cd:c4:
+ ab:f9:fa:7e:e7:8e:e8:33:50:8a:01:83:42:39:3b:
+ 27:68:8b:fa:e6:5c:60:86:e8:d0:56:5a:79:bc:2a:
+ ca:a9:0b:f5:89:d5:a3:25:76:c1:71:8b:8a:32:2a:
+ 27:81:8b:31:a8:f9:a9:6c:62:00:29:cd:6a:7c:2c:
+ cc:03:34:77:5f:93:e7:f2:1a:8c:a7:78:9a:30:29:
+ d1:95:3b:60:6b:89:d0:f4:69:b1:82:7c:17:73:b5:
+ 2b:5a:ef:a6:ec:7b:c7:9b:e3:0a:3c:4c:41:98:ad:
+ b4:a4:05:30:55:60:59:a9:0d:1c:39:b4:b4:f3:70:
+ fc:b7:d3:47:e6:44:3f:8f:3e:6e:d0:31:8e:85:ee:
+ 2f:2c:f5:9d:86:c1:82:77:11:cd:f4:15:a8:78:2a:
+ 18:9d:b6:2c:1f:96:51:d6:3c:b6:2e:f4:e3:65:d4:
+ 9f:4e:92:e6:55:18:e2:3e:6e:1e:c5:3d:5d:75:21:
+ 2c:7a:af:ca:fe:0b:3d:18:96:4c:86:f2:a7:f8:6d:
+ b3:0f:0d:cb:f7:45:86:bf:88:21:55:f4:1c:cc:a7:
+ ef:b2:49:13:42:ce:95:74:fa:64:b0:e4:ff:38:da:
+ 14:ef:5c:10:70:0a:a8:30:4f:22:f8:f9:56:a8:f4:
+ f7:1f:e9:31:88:4a:7d:ed:11:27:89:49:e7:06:a6:
+ 5a:d3:27:dc:60:7b:2d:91:5a:48:11:3f:e0:a3:d2:
+ bd:89:af:c8:a3:84:a0:27:b5:89:98:fd:3c:58:f4:
+ a8:db:81:7b:ba:08:0b:d5:5f:30:7c:ea:19:d5:77:
+ df:85:fa:c4:f4:eb:47:ff:70:65:d9:d1:91:79:10:
+ a7:1b:11:ca:ae:d8:df:bd:f1:f1:54:d9:0d:8b:78:
+ 1c:60:cd:9e:63:04:05:86:60:ab:19:e7:cf:dd:cf:
+ ea:3d:0d:ff:81:17:8b:74:5b:85:a9:e4:93:ca:74:
+ 86:e4:4c:44:d4:0b:06:8d:06:d3:35:4e:f7:ad:2a:
+ f6:77:d0:76:5c:fc:3c:63:2e:fd:3f:42:41:36:72:
+ d6:78:3f
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints: critical
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 39:3B:83:DB:3C:59:8F:5C:66:D8:86:6A:22:F9:F6:6C:B4:29:37:A3
+ X509v3 Authority Key Identifier:
+ keyid:0E:74:B5:09:EC:FB:FA:E7:BA:6B:1A:F6:2B:28:7E:A9:70:DA:D7:18
+
+ X509v3 Subject Alternative Name: critical
+ DNS:sha512.server.w1.fi
+ X509v3 Extended Key Usage: critical
+ TLS Web Server Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha512WithRSAEncryption
+ 8f:42:08:a0:bc:c1:eb:50:ef:6a:26:b7:3e:54:a6:7a:ad:b0:
+ 66:d3:1d:4c:d4:bc:63:9f:f9:b8:58:ec:33:82:9a:7e:60:28:
+ e8:2b:ee:a6:51:46:7f:bf:c4:39:71:8e:a4:d8:11:88:2e:89:
+ 60:82:4a:d7:e9:a5:6e:cb:ec:4b:79:d4:48:3f:e3:fd:1f:e2:
+ 6f:7e:43:bf:63:ff:e3:ec:d3:82:7f:bd:2a:3a:66:45:50:d5:
+ f7:ea:5b:28:4c:b0:f8:89:8a:03:e3:22:6a:eb:ee:09:46:6a:
+ 8a:c6:c9:a7:62:41:ae:ea:42:5a:7c:16:0a:b3:33:51:5c:b7:
+ 26:51:68:cb:ec:7e:6e:7c:cd:1b:24:be:c9:91:53:0f:dc:d2:
+ e0:d3:df:18:05:ca:f6:98:bf:d2:d6:c2:88:8f:93:91:2d:7b:
+ 6d:3c:56:c2:0d:90:11:93:29:67:5e:c5:b7:c5:0f:e0:b3:09:
+ d6:60:ca:b5:d5:8d:ff:fd:57:6b:fb:05:23:62:8f:4e:bf:03:
+ bc:da:ba:81:a3:7f:53:f4:8f:d1:49:1c:e0:32:47:b6:b9:71:
+ d4:85:5e:a8:44:63:47:1d:9d:6b:34:eb:c5:da:02:2a:5a:07:
+ 5b:3f:0c:47:f2:a3:54:5d:e0:3a:0c:eb:77:3b:d5:fd:03:1e:
+ 01:f6:c5:68:3f:d6:ed:cb:f9:4c:03:06:65:a9:9a:39:6b:20:
+ d7:11:eb:62:c7:09:0d:b0:51:b4:49:ff:3e:02:7d:e4:a1:6b:
+ 36:bf:f3:04:33:1f:7e:b2:69:af:7d:bb:a8:ef:7f:7e:0b:d3:
+ 33:4f:8e:61:09:fa:a3:b9:d5:97:8c:0b:90:17:ce:72:52:2a:
+ de:b8:96:4d:36:c0:b8:d7:7d:9e:56:e0:38:6b:a7:02:a0:90:
+ 6f:e8:ee:4f:f2:26:f3:6b:a4:75:80:8f:b0:c4:1b:d4:37:49:
+ 75:4b:d9:ed:2b:11:3c:ed:a8:dd:4b:8f:01:60:4d:26:f4:2d:
+ 6b:74:d5:75:79:88:2f:18:5e:76:6c:80:2c:eb:da:e2:cc:46:
+ a1:67:89:f5:f6:29:35:ae:b2:f6:79:a8:c3:43:f6:6a:a3:39:
+ d7:64:65:b7:bd:a6:c9:2d:60:70:4b:d9:60:1b:a9:a6:5e:b0:
+ cd:88:02:ae:28:57:b0:46:44:1a:ad:dc:1f:bb:e3:90:db:3c:
+ 07:a1:bf:a9:31:1c:0d:97:37:78:80:8a:7f:f8:7a:60:0b:0f:
+ fe:d1:bc:38:ff:b3:72:72:80:e0:65:1d:86:90:b0:f6:7a:38:
+ 1b:7b:05:b7:d9:f9:44:3e:4a:1c:2b:d4:3a:cc:db:75:20:eb:
+ 6d:bf:22:4b:83:1c:4f:39
+-----BEGIN CERTIFICATE-----
+MIIFYTCCA0mgAwIBAgIJAMPTjNcrAaipMA0GCSqGSIb3DQEBDQUAMFQxCzAJBgNV
+BAYTAkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIjAgBgNV
+BAMMGVNIQTM4NCBhbmQgU0hBNTEyIFJvb3QgQ0EwHhcNMTUxMTI5MjIzMzI1WhcN
+MjUxMTI2MjIzMzI1WjA7MQswCQYDVQQGEwJGSTEOMAwGA1UECgwFdzEuZmkxHDAa
+BgNVBAMME3NoYTUxMi5zZXJ2ZXIudzEuZmkwggHXMA0GCSqGSIb3DQEBAQUAA4IB
+xAAwggG/AoIBtgyUrc5DYSg0X9fexaXrNjYldxn7pZsOu+1u2RAbcNNKFPs+XFCi
+hLMu/QnNxKv5+n7njugzUIoBg0I5Oydoi/rmXGCG6NBWWnm8KsqpC/WJ1aMldsFx
+i4oyKieBizGo+alsYgApzWp8LMwDNHdfk+fyGoyneJowKdGVO2BridD0abGCfBdz
+tSta76bse8eb4wo8TEGYrbSkBTBVYFmpDRw5tLTzcPy300fmRD+PPm7QMY6F7i8s
+9Z2GwYJ3Ec30Fah4KhidtiwfllHWPLYu9ONl1J9OkuZVGOI+bh7FPV11ISx6r8r+
+Cz0YlkyG8qf4bbMPDcv3RYa/iCFV9BzMp++ySRNCzpV0+mSw5P842hTvXBBwCqgw
+TyL4+Vao9Pcf6TGISn3tESeJSecGplrTJ9xgey2RWkgRP+Cj0r2Jr8ijhKAntYmY
+/TxY9KjbgXu6CAvVXzB86hnVd9+F+sT060f/cGXZ0ZF5EKcbEcqu2N+98fFU2Q2L
+eBxgzZ5jBAWGYKsZ58/dz+o9Df+BF4t0W4Wp5JPKdIbkTETUCwaNBtM1TvetKvZ3
+0HZc/DxjLv0/QkE2ctZ4PwIDAQABo4GZMIGWMAwGA1UdEwEB/wQCMAAwHQYDVR0O
+BBYEFDk7g9s8WY9cZtiGaiL59my0KTejMB8GA1UdIwQYMBaAFA50tQns+/rnumsa
+9isofqlw2tcYMCEGA1UdEQEB/wQXMBWCE3NoYTUxMi5zZXJ2ZXIudzEuZmkwFgYD
+VR0lAQH/BAwwCgYIKwYBBQUHAwEwCwYDVR0PBAQDAgWgMA0GCSqGSIb3DQEBDQUA
+A4ICAQCPQgigvMHrUO9qJrc+VKZ6rbBm0x1M1Lxjn/m4WOwzgpp+YCjoK+6mUUZ/
+v8Q5cY6k2BGILolggkrX6aVuy+xLedRIP+P9H+JvfkO/Y//j7NOCf70qOmZFUNX3
+6lsoTLD4iYoD4yJq6+4JRmqKxsmnYkGu6kJafBYKszNRXLcmUWjL7H5ufM0bJL7J
+kVMP3NLg098YBcr2mL/S1sKIj5ORLXttPFbCDZARkylnXsW3xQ/gswnWYMq11Y3/
+/Vdr+wUjYo9OvwO82rqBo39T9I/RSRzgMke2uXHUhV6oRGNHHZ1rNOvF2gIqWgdb
+PwxH8qNUXeA6DOt3O9X9Ax4B9sVoP9bty/lMAwZlqZo5ayDXEetixwkNsFG0Sf8+
+An3koWs2v/MEMx9+smmvfbuo739+C9MzT45hCfqjudWXjAuQF85yUireuJZNNsC4
+132eVuA4a6cCoJBv6O5P8ibza6R1gI+wxBvUN0l1S9ntKxE87ajdS48BYE0m9C1r
+dNV1eYgvGF52bIAs69rizEahZ4n19ik1rrL2eajDQ/ZqoznXZGW3vabJLWBwS9lg
+G6mmXrDNiAKuKFewRkQardwfu+OQ2zwHob+pMRwNlzd4gIp/+HpgCw/+0bw4/7Ny
+coDgZR2GkLD2ejgbewW32flEPkocK9Q6zNt1IOttvyJLgxxPOQ==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha512-user.key b/contrib/wpa/tests/hwsim/auth_serv/sha512-user.key
new file mode 100644
index 000000000000..5985ea1ba233
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha512-user.key
@@ -0,0 +1,44 @@
+-----BEGIN PRIVATE KEY-----
+MIIHtQIBADANBgkqhkiG9w0BAQEFAASCB58wggebAgEAAoIBqgDEVq3fqyK8sXHD
+9eeLAaxkWFeX9PoC8rLx/kuMBuEz/IuY1/JwkPPeOUUXn/fDNQCvS12aGtoyxBHE
+ucBF6j6ojz+tGS88Sfzvp+VTjbIdryyd+oMZondO4GIflxCTnY0GE+ZuUeltD9mX
+HkGEFwl1Jf9cB3iUXz6X5mTXSeXlmKHL4g34wDMtc+shaINM5G0gJEWl7qebruCp
+FFVpGkkjy+QzMpjnwSYJSaC31Y/w9QTjFERdfy0TwmATJ+7mPbhsQ6Rob5olyZBt
+6a4pXdjbJm/7RAW4ov11NV0aChHzAkv1hC62Gmp51PamFu7VrxrZ9TlzNGQnJied
+IqpuK0C/eap7zjtlbsgkkOwkKZiHWf7l/opJ8yWX524STzd+ekA5EoUr7YDd0Ig1
+UYkxvIsLIza7+hOyq6ugcNeZuurCOE4OG+GGTLFSpu+FqBfS/DqptZ3xxQINKugI
+g9srE1scoP8ZvKyclHgqLZPXJZM0fZnkPEzN9EK84vPH7D387cX2LMBO/6vudL/F
+VTDwdLSbvKw9lwsnNP7hiy8LUIqk+3vhClWztNwd4QIDAQABAoIBqX/9zZ1or914
+g6RuksHglmRX3spVzgHL+3GcB93Bwl+ke9Bovkg5hnDQvsTW6KpdzBN6OwuyA5pi
+jP7E5J3vRWeW8rjCGTPhO71zoyDj10BYqOpXm81DRympRu43CXxhkcRrhqVWSqhp
+U7ya4bTdW7H90klJKYb3zBM0A3sBVphk9ty1gcwuZ25zCc4elrGym5z/aFPyj1sD
+FrvCYKeCljl6uknR6zHxjP0y/U26L/qxj70Ewkh9FoKyDPWP68I2bBpPwka+sit+
+CeRNGNcosayzkv6m2BcPm3LZMzLouoWCkAszYhwXhAqSHQzFNqoCv/IW68qYGVgr
+XtKbDB/SYGbatP2+SIbPt3oW907yW/Zfv7hDvSZF3BNEVooPC6f6NWHTdGBLHCWJ
+1FvBWpSKF2uwy+s6aV2YTdB5S0myE7p9KlY/SZiHSNjWt16puC6mHyOpBc2yEGGi
+zCCANSxUEg1C6EKquk8bfJ/6WRnnEt96d5DFRFMKHDFEIxITThQDIcITuh8YLLBM
+fPDMc2HxSR5fyfkgR9wj7+hxHSoRmWNnnAnm2kr5SwMBAoHVDpslqXfYhhtcdrCm
+NqJpQxHqCYHiWLDCGl52Tjyq21quYQC4IDPk1qb9bIUjw9cYk0OlSY1tT+0z2DfG
+iN7lWSjVqlxXZihjQIkh1d2UV2SQHuAqnWnDSRwKjkWmDzLPFM4ZC52x7ZH54Rba
+iEnSDOy59Kuw05gE0+ia1AEROSmV5ROgFevEnwp3fX7/T0e9cEr5+z3LyNDY/6I4
+PHgBN9/OXhbBO+rWSFEEezQ8JEu5DfgZ9vAN8y3dNoRZcsYR/mafimygcVhKwySj
+hDi/lEoVcxIxAoHVDXE5jfRVrdKSIVMkngFuMM+p8IItpDDX3MHmFzqW8PDZu/BD
+VdqKa3Thc9f/rPwdUwOIFUfUxqIZum8n809rkAtBhxcases+E3NcHESulnM4kJNO
+gVS0TdfdaANeLXxd6zuXgxsxXU/omdqH2ZqlSFy2VvQncNJov1s6R6m5y22aOKdu
+fdgA9suzEh3PAHhqVRXGEGXakbJg/irVX15odZMn0jume5+2ajWyt6skoCR1UI9H
+FMHU5KS3mpFba7L1sG39dZS5C4R4sV3Vz4NpTtnTuKqxAoHVBcDn3RHwoSM2ESsU
+zoC7pkfwQT6Awx8d1vVO9RLA2xeliWCXJ1hJ1KSDP7RSmlqou5nyCj9DyDBQM+QE
+uPXUsoJ7aFhntT5DmrBqO6zFOofKnd7/6nI3Ex2QquqUt3f0SuXEx+aeqE7Qd/Jg
+sz8hFyZJOjBHv5IqlK8UDl89QZ84BLzuWrQ9B8k/uJfGSpyf6xQd1PJKzBg44ros
+HOXI1RG8YSPlioy/TE1dzoqNBl8tzmrlD3kQhbKTaV5JPgT4IZabUhPoP73f9W8B
+WpxgGzHzsRiBAoHVAiizU8dwk3CEo/GUqgQUzKYt50EhT4PAx+YkrIpuf9hPQE5A
+omNtZADrzQ1eNUxw8UpZ6wEUfaq/cyAzTHLvFb2ZErd1RCpCWCFpI+ksGWH8Lcxb
+CY6vPuly2CfiUm/tLcDuftI5RYF2HG/q7dpWvZ6WcrTYONiyljlyzNvHyZKa+Ip7
+xp6Q2RKyejBrdOniOjQ7EZsqVchOp8P9bUnegTPXa37FKInoZHqnb7R/N3FjnkLE
+6eKP7QE/tAWwp0WhDGs+EEqIxf7K28H0e1Xh7hYyWTChAoHVBn76/JDy92nDEWjG
+JkEJBeh88mT6ocKvFYbKrSAjBpFrPiTbHJ6Md0duRgvSB2ikGQnJfESz7Q/M5bdo
+Ssy0qH9Paugbye4FgVwQCECkfJkB3Ijn9VuR+8cdoCySXPzhWDU6UXh5taSvsHvT
+LRLyBjFC/6UTsElVCam28aujJmNI7X+LbXBij4a3J8nXO3BFqK8HIopeG2z1lyBi
+fgtLN2fTl6adq8lDYxoMBPmrEmob5lKwCPsOMCLz50WEacNK8CFZ6QUiwx1ZhJeA
+HR9JilLQkZRY
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/sha512-user.pem b/contrib/wpa/tests/hwsim/auth_serv/sha512-user.pem
new file mode 100644
index 000000000000..df2a0bd1d623
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/sha512-user.pem
@@ -0,0 +1,119 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number: 14110776913249282219 (0xc3d38cd72b01a8ab)
+ Signature Algorithm: sha512WithRSAEncryption
+ Issuer: C=FI, L=Helsinki, O=w1.fi, CN=SHA384 and SHA512 Root CA
+ Validity
+ Not Before: Nov 29 22:33:25 2015 GMT
+ Not After : Nov 26 22:33:25 2025 GMT
+ Subject: C=FI, O=w1.fi, CN=user-sha512
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ Public-Key: (3400 bit)
+ Modulus:
+ 00:c4:56:ad:df:ab:22:bc:b1:71:c3:f5:e7:8b:01:
+ ac:64:58:57:97:f4:fa:02:f2:b2:f1:fe:4b:8c:06:
+ e1:33:fc:8b:98:d7:f2:70:90:f3:de:39:45:17:9f:
+ f7:c3:35:00:af:4b:5d:9a:1a:da:32:c4:11:c4:b9:
+ c0:45:ea:3e:a8:8f:3f:ad:19:2f:3c:49:fc:ef:a7:
+ e5:53:8d:b2:1d:af:2c:9d:fa:83:19:a2:77:4e:e0:
+ 62:1f:97:10:93:9d:8d:06:13:e6:6e:51:e9:6d:0f:
+ d9:97:1e:41:84:17:09:75:25:ff:5c:07:78:94:5f:
+ 3e:97:e6:64:d7:49:e5:e5:98:a1:cb:e2:0d:f8:c0:
+ 33:2d:73:eb:21:68:83:4c:e4:6d:20:24:45:a5:ee:
+ a7:9b:ae:e0:a9:14:55:69:1a:49:23:cb:e4:33:32:
+ 98:e7:c1:26:09:49:a0:b7:d5:8f:f0:f5:04:e3:14:
+ 44:5d:7f:2d:13:c2:60:13:27:ee:e6:3d:b8:6c:43:
+ a4:68:6f:9a:25:c9:90:6d:e9:ae:29:5d:d8:db:26:
+ 6f:fb:44:05:b8:a2:fd:75:35:5d:1a:0a:11:f3:02:
+ 4b:f5:84:2e:b6:1a:6a:79:d4:f6:a6:16:ee:d5:af:
+ 1a:d9:f5:39:73:34:64:27:26:27:9d:22:aa:6e:2b:
+ 40:bf:79:aa:7b:ce:3b:65:6e:c8:24:90:ec:24:29:
+ 98:87:59:fe:e5:fe:8a:49:f3:25:97:e7:6e:12:4f:
+ 37:7e:7a:40:39:12:85:2b:ed:80:dd:d0:88:35:51:
+ 89:31:bc:8b:0b:23:36:bb:fa:13:b2:ab:ab:a0:70:
+ d7:99:ba:ea:c2:38:4e:0e:1b:e1:86:4c:b1:52:a6:
+ ef:85:a8:17:d2:fc:3a:a9:b5:9d:f1:c5:02:0d:2a:
+ e8:08:83:db:2b:13:5b:1c:a0:ff:19:bc:ac:9c:94:
+ 78:2a:2d:93:d7:25:93:34:7d:99:e4:3c:4c:cd:f4:
+ 42:bc:e2:f3:c7:ec:3d:fc:ed:c5:f6:2c:c0:4e:ff:
+ ab:ee:74:bf:c5:55:30:f0:74:b4:9b:bc:ac:3d:97:
+ 0b:27:34:fe:e1:8b:2f:0b:50:8a:a4:fb:7b:e1:0a:
+ 55:b3:b4:dc:1d:e1
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ 2F:60:49:97:43:3C:7A:7E:22:C1:44:0B:43:78:D4:9D:7C:DF:A6:12
+ X509v3 Authority Key Identifier:
+ keyid:0E:74:B5:09:EC:FB:FA:E7:BA:6B:1A:F6:2B:28:7E:A9:70:DA:D7:18
+
+ X509v3 Subject Alternative Name:
+ email:user-sha512@w1.fi
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ X509v3 Key Usage:
+ Digital Signature, Key Encipherment
+ Signature Algorithm: sha512WithRSAEncryption
+ 9d:58:98:97:95:49:c5:bc:be:f2:1d:01:65:ff:2b:5c:24:81:
+ 71:87:05:3e:11:1d:2f:f2:16:12:d3:0e:36:72:af:87:6b:81:
+ c1:7c:aa:c8:be:be:a7:90:2c:7b:35:7c:0f:8b:67:e2:9c:da:
+ 26:ad:09:fc:56:28:78:3b:3a:00:91:8d:f9:d1:39:a2:c5:3f:
+ e4:97:42:70:5c:93:93:23:5f:01:67:37:b7:d9:12:0c:14:dd:
+ 9d:73:be:9e:46:47:90:21:26:6d:0e:4c:af:0b:80:41:06:94:
+ 86:ef:49:66:1c:70:83:9c:1b:71:83:16:38:22:f5:a0:47:09:
+ bd:69:0f:9f:5b:19:1b:d4:44:f5:15:65:d5:6c:2b:d1:8c:c7:
+ 3a:f4:a7:22:b0:53:e0:27:ff:06:c6:37:a7:cf:a6:25:2a:d4:
+ 24:90:3e:46:59:6a:9b:dd:57:71:d1:79:3a:e2:6c:b5:22:19:
+ 0f:dd:e6:d4:04:eb:fc:65:98:da:fd:e3:7c:04:d6:a0:2a:9e:
+ 19:d8:aa:44:a7:8e:c6:7d:35:00:e5:ac:24:2f:ec:53:0a:7b:
+ 3d:bc:67:f3:23:95:fd:98:8b:ba:ac:e0:25:90:b2:38:e1:bb:
+ 62:a3:0c:39:bb:3b:79:40:53:91:20:10:86:88:f3:ae:ba:5a:
+ 7a:eb:61:72:4d:3b:cc:fc:1c:ff:86:fb:6a:83:b8:ca:9a:34:
+ dc:66:46:e7:d9:39:59:a1:91:a9:d2:b9:38:c7:84:b9:23:10:
+ a6:21:e3:de:a1:56:90:bd:63:48:c7:10:d6:2d:2d:e0:90:ba:
+ 19:3a:57:c4:ea:e4:d8:62:f3:84:c4:dd:a9:e9:fe:07:33:dc:
+ ed:7e:27:9d:4a:9e:d4:3d:12:35:84:f0:df:cf:d3:8d:7c:f8:
+ 2d:cd:2b:24:70:92:40:b1:9f:38:b5:b1:34:b5:47:1b:19:6c:
+ 5a:a2:ce:04:5a:e6:ce:a4:18:11:88:2c:d6:53:80:3d:87:88:
+ 5b:89:63:47:0e:ed:52:7b:49:7f:0b:31:66:9c:54:5f:08:7d:
+ d7:e3:6e:6c:d6:12:a0:a8:cf:d9:69:6a:53:10:bf:67:d6:0c:
+ 2e:8e:6a:9a:35:c6:0a:bd:ee:28:2b:9f:d9:af:89:0f:19:5f:
+ 23:d2:f8:ce:04:69:78:a8:a3:33:3d:dc:d7:09:77:cb:51:8c:
+ 80:0e:aa:07:60:34:32:b1:b7:e6:04:1c:5d:8e:53:1f:be:fe:
+ 49:8a:21:a2:d8:f0:f8:ce:70:a4:b7:6e:90:ec:9d:68:f7:33:
+ 08:67:59:d7:ff:f6:20:00:f5:51:79:66:e6:35:bd:29:85:62:
+ d3:e1:3a:1c:b3:8c:ef:8d
+-----BEGIN CERTIFICATE-----
+MIIFQjCCAyqgAwIBAgIJAMPTjNcrAairMA0GCSqGSIb3DQEBDQUAMFQxCzAJBgNV
+BAYTAkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIjAgBgNV
+BAMMGVNIQTM4NCBhbmQgU0hBNTEyIFJvb3QgQ0EwHhcNMTUxMTI5MjIzMzI1WhcN
+MjUxMTI2MjIzMzI1WjAzMQswCQYDVQQGEwJGSTEOMAwGA1UECgwFdzEuZmkxFDAS
+BgNVBAMMC3VzZXItc2hhNTEyMIIByzANBgkqhkiG9w0BAQEFAAOCAbgAMIIBswKC
+AaoAxFat36sivLFxw/XniwGsZFhXl/T6AvKy8f5LjAbhM/yLmNfycJDz3jlFF5/3
+wzUAr0tdmhraMsQRxLnAReo+qI8/rRkvPEn876flU42yHa8snfqDGaJ3TuBiH5cQ
+k52NBhPmblHpbQ/Zlx5BhBcJdSX/XAd4lF8+l+Zk10nl5Zihy+IN+MAzLXPrIWiD
+TORtICRFpe6nm67gqRRVaRpJI8vkMzKY58EmCUmgt9WP8PUE4xREXX8tE8JgEyfu
+5j24bEOkaG+aJcmQbemuKV3Y2yZv+0QFuKL9dTVdGgoR8wJL9YQuthpqedT2phbu
+1a8a2fU5czRkJyYnnSKqbitAv3mqe847ZW7IJJDsJCmYh1n+5f6KSfMll+duEk83
+fnpAORKFK+2A3dCINVGJMbyLCyM2u/oTsquroHDXmbrqwjhODhvhhkyxUqbvhagX
+0vw6qbWd8cUCDSroCIPbKxNbHKD/GbysnJR4Ki2T1yWTNH2Z5DxMzfRCvOLzx+w9
+/O3F9izATv+r7nS/xVUw8HS0m7ysPZcLJzT+4YsvC1CKpPt74QpVs7TcHeECAwEA
+AaOBjjCBizAJBgNVHRMEAjAAMB0GA1UdDgQWBBQvYEmXQzx6fiLBRAtDeNSdfN+m
+EjAfBgNVHSMEGDAWgBQOdLUJ7Pv657prGvYrKH6pcNrXGDAcBgNVHREEFTATgRF1
+c2VyLXNoYTUxMkB3MS5maTATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8EBAMC
+BaAwDQYJKoZIhvcNAQENBQADggIBAJ1YmJeVScW8vvIdAWX/K1wkgXGHBT4RHS/y
+FhLTDjZyr4drgcF8qsi+vqeQLHs1fA+LZ+Kc2iatCfxWKHg7OgCRjfnROaLFP+SX
+QnBck5MjXwFnN7fZEgwU3Z1zvp5GR5AhJm0OTK8LgEEGlIbvSWYccIOcG3GDFjgi
+9aBHCb1pD59bGRvURPUVZdVsK9GMxzr0pyKwU+An/wbGN6fPpiUq1CSQPkZZapvd
+V3HReTribLUiGQ/d5tQE6/xlmNr943wE1qAqnhnYqkSnjsZ9NQDlrCQv7FMKez28
+Z/Mjlf2Yi7qs4CWQsjjhu2KjDDm7O3lAU5EgEIaI8666WnrrYXJNO8z8HP+G+2qD
+uMqaNNxmRufZOVmhkanSuTjHhLkjEKYh496hVpC9Y0jHENYtLeCQuhk6V8Tq5Nhi
+84TE3anp/gcz3O1+J51KntQ9EjWE8N/P0418+C3NKyRwkkCxnzi1sTS1RxsZbFqi
+zgRa5s6kGBGILNZTgD2HiFuJY0cO7VJ7SX8LMWacVF8IfdfjbmzWEqCoz9lpalMQ
+v2fWDC6Oapo1xgq97igrn9mviQ8ZXyPS+M4EaXioozM93NcJd8tRjIAOqgdgNDKx
+t+YEHF2OUx++/kmKIaLY8PjOcKS3bpDsnWj3MwhnWdf/9iAA9VF5ZuY1vSmFYtPh
+OhyzjO+N
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/update.sh b/contrib/wpa/tests/hwsim/auth_serv/update.sh
new file mode 100755
index 000000000000..b2296b2fbf20
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/update.sh
@@ -0,0 +1,181 @@
+#!/bin/sh
+
+OPENSSL=openssl
+
+mkdir -p test-ca/newcerts
+
+echo
+echo "---[ DH parameters ]----------------------------------------------------"
+echo
+
+if [ -r dh.conf ]; then
+ echo "Use already generated dh.conf"
+else
+ openssl dhparam -out dh.conf 2048
+fi
+
+echo
+echo "---[ Root CA ]----------------------------------------------------------"
+echo
+
+if [ -r ca-key.pem ]; then
+ echo "Use already generated Root CA"
+else
+ cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = TEST - Incorrect Root CA/" \
+ > ca-openssl.cnf.tmp
+ $OPENSSL req -config ca-openssl.cnf.tmp -batch -x509 -new -newkey rsa:2048 -nodes -keyout ca-incorrect-key.pem -out ca-incorrect.der -outform DER -days 3650 -sha256
+ $OPENSSL x509 -in ca-incorrect.der -inform DER -out ca-incorrect.pem -outform PEM -text
+
+ cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = Root CA/" \
+ > ca-openssl.cnf.tmp
+ $OPENSSL req -config ca-openssl.cnf.tmp -batch -x509 -new -newkey rsa:2048 -nodes -keyout ca-key.pem -out ca.der -outform DER -days 3650 -sha256
+ $OPENSSL x509 -in ca.der -inform DER -out ca.pem -outform PEM -text
+ mkdir -p test-ca/certs test-ca/crl test-ca/newcerts test-ca/private
+ touch test-ca/index.txt
+ echo 01 > test-ca/crlnumber
+ cp ca.pem test-ca/cacert.pem
+ cp ca-key.pem test-ca/private/cakey.pem
+ $OPENSSL ca -config ca-openssl.cnf.tmp -gencrl -crldays 2922 -out crl.pem
+ cat ca.pem crl.pem > ca-and-crl.pem
+ faketime yesterday $OPENSSL ca -config ca-openssl.cnf.tmp -gencrl -crlhours 1 -out crl.pem
+ cat ca.pem crl.pem > ca-and-crl-expired.pem
+ rm crl.pem
+ rm ca-openssl.cnf.tmp
+fi
+
+echo
+echo "---[ Update server certificates ]---------------------------------------"
+echo
+
+cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = server.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=DNS:server.w1.fi/" \
+ > openssl.cnf.tmp
+if [ ! -r server.csr ]; then
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout server.key -out server.csr -outform PEM -sha256
+fi
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -in server.csr -out server.pem -extensions ext_server
+
+$OPENSSL pkcs12 -export -out server.pkcs12 -in server.pem -inkey server.key -passout pass:
+$OPENSSL pkcs12 -export -out server-extra.pkcs12 -in server.pem -inkey server.key -descert -certfile user.pem -passout pass:whatever -name server
+
+cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = server3.w1.fi/" \
+ > openssl.cnf.tmp
+if [ ! -r server-no-dnsname.csr ]; then
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout server-no-dnsname.key -out server-no-dnsname.csr -outform PEM -sha256
+fi
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -in server-no-dnsname.csr -out server-no-dnsname.pem -extensions ext_server
+
+cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = server4.w1.fi/" \
+ > openssl.cnf.tmp
+if [ ! -r server-expired.csr ]; then
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout server-expired.key -out server-expired.csr -outform PEM -sha256
+fi
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -in server-expired.csr -out server-expired.pem -extensions ext_server -startdate 200101000000Z -enddate 200102000000Z
+
+cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = server5.w1.fi/" \
+ > openssl.cnf.tmp
+if [ ! -r server-eku-client.csr ]; then
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout server-eku-client.key -out server-eku-client.csr -outform PEM -sha256
+fi
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -in server-eku-client.csr -out server-eku-client.pem -extensions ext_client
+
+cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = server6.w1.fi/" \
+ > openssl.cnf.tmp
+if [ ! -r server-eku-client-server.csr ]; then
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout server-eku-client-server.key -out server-eku-client-server.csr -outform PEM -sha256
+fi
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -in server-eku-client-server.csr -out server-eku-client-server.pem -extensions ext_client_server
+
+cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = server7.w1.fi/" \
+ > openssl.cnf.tmp
+if [ ! -r server-long-duration.csr ]; then
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:4096 -nodes -keyout server-long-duration.key -out server-long-duration.csr -outform PEM -sha256
+fi
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -in server-long-duration.csr -out server-long-duration.pem -extensions ext_server -days 18250
+
+cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = server-policies.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=DNS:server-policies.w1.fi/" |
+ sed "s/#@CERTPOL@/certificatePolicies = 1.3.6.1.4.1.40808.1.3.1/" \
+ > openssl.cnf.tmp
+if [ ! -r server-certpol.csr ]; then
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:3072 -nodes -keyout server-certpol.key -out server-certpol.csr -outform PEM -sha256
+fi
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -in server-certpol.csr -out server-certpol.pem -extensions ext_server
+
+cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = server-policies2.w1.fi/" |
+ sed "s/#@ALTNAME@/subjectAltName=DNS:server-policies2.w1.fi/" |
+ sed "s/#@CERTPOL@/certificatePolicies = 1.3.6.1.4.1.40808.1.3.2/" \
+ > openssl.cnf.tmp
+if [ ! -r server-certpol2.csr ]; then
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:3072 -nodes -keyout server-certpol2.key -out server-certpol2.csr -outform PEM -sha256
+fi
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -in server-certpol2.csr -out server-certpol2.pem -extensions ext_server
+
+echo
+echo "---[ Update user certificates ]-----------------------------------------"
+echo
+
+cat openssl2.cnf | sed "s/#@CN@/commonName_default = Test User/" > openssl.cnf.tmp
+if [ ! -r user.csr ]; then
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout user.key -out user.csr -outform PEM -sha256
+ $OPENSSL rsa -in user.key -out user.rsa-key
+ $OPENSSL pkcs8 -topk8 -in user.key -out user.key.pkcs8 -inform PEM -v2 des-ede3-cbc -v2prf hmacWithSHA1 -passout pass:whatever
+ $OPENSSL pkcs8 -topk8 -in user.key -out user.key.pkcs8.pkcs5v15 -inform PEM -v1 pbeWithMD5AndDES-CBC -passout pass:whatever
+fi
+
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -in user.csr -out user.pem -extensions ext_client
+rm openssl.cnf.tmp
+
+$OPENSSL pkcs12 -export -out user.pkcs12 -in user.pem -inkey user.key -descert -passout pass:whatever
+$OPENSSL pkcs12 -export -out user2.pkcs12 -in user.pem -inkey user.key -descert -name Test -certfile server.pem -passout pass:whatever
+$OPENSSL pkcs12 -export -out user3.pkcs12 -in user.pem -inkey user.key -descert -name "my certificates" -certfile ca.pem -passout pass:whatever
+
+echo
+echo "---[ Update OCSP ]------------------------------------------------------"
+echo
+
+cat openssl2.cnf |
+ sed "s/#@CN@/commonName_default = ocsp.w1.fi/" \
+ > openssl.cnf.tmp
+if [ ! -r ocsp-responder.csr ]; then
+ $OPENSSL req -config openssl.cnf.tmp -batch -new -newkey rsa:2048 -nodes -keyout ocsp-responder.key -out ocsp-responder.csr -outform PEM -sha256
+fi
+$OPENSSL ca -config $PWD/openssl.cnf.tmp -batch -in ocsp-responder.csr -out ocsp-responder.pem -extensions v3_OCSP
+
+$OPENSSL ocsp -CAfile test-ca/cacert.pem -issuer test-ca/cacert.pem -cert server.pem -reqout ocsp-req.der -no_nonce
+$OPENSSL ocsp -index test-ca/index.txt -rsigner test-ca/cacert.pem -rkey test-ca/private/cakey.pem -CA test-ca/cacert.pem -resp_no_certs -reqin ocsp-req.der -respout ocsp-server-cache.der
+SIZ=`ls -l ocsp-server-cache.der | cut -f5 -d' '`
+(echo -n 000; echo "obase=16;$SIZ" | bc) | xxd -r -ps > ocsp-multi-server-cache.der
+cat ocsp-server-cache.der >> ocsp-multi-server-cache.der
+
+echo
+echo "---[ Additional steps ]-------------------------------------------------"
+echo
+
+echo "test_ap_eap.py: ap_wpa2_eap_ttls_server_cert_hash srv_cert_hash"
+
+$OPENSSL x509 -in server.pem -out server.der -outform DER
+HASH=`sha256sum server.der | cut -f1 -d' '`
+rm server.der
+sed -i "s/srv_cert_hash =.*/srv_cert_hash = \"$HASH\"/" ../test_ap_eap.py
+
+echo "index.txt: server time+serial"
+
+grep -v CN=server.w1.fi index.txt > index.txt.new
+grep CN=server.w1.fi test-ca/index.txt | tail -1 >> index.txt.new
+mv index.txt.new index.txt
+
+echo "start.sh: openssl ocsp -reqout serial"
+
+SERIAL=`grep CN=server.w1.fi test-ca/index.txt | tail -1 | cut -f4`
+sed -i "s/'-serial', '0x[^']*'/'-serial', '0x$SERIAL'/" ../test_ap_eap.py
diff --git a/contrib/wpa/tests/hwsim/auth_serv/user.csr b/contrib/wpa/tests/hwsim/auth_serv/user.csr
new file mode 100644
index 000000000000..49c179f26e1c
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/user.csr
@@ -0,0 +1,16 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIICiDCCAXACAQAwQzELMAkGA1UEBhMCRkkxEDAOBgNVBAcMB1R1dXN1bGExDjAM
+BgNVBAoMBXcxLmZpMRIwEAYDVQQDDAlUZXN0IFVzZXIwggEiMA0GCSqGSIb3DQEB
+AQUAA4IBDwAwggEKAoIBAQC8JAjZB20ZDpRca9mKhBsDIuIaBkLTC2CNXzEEBvqi
+QGoMJClYzYO/Op1/Nw1NJ13VV1cc8751PzpMRG8CL1uLLLBcRykZMQjRKPUenFuc
+t+drQDUQPn9mZxahTmiKccwyWOMIh6IqBy1/c/ZD5X/09nSuJ+jSXK6bgtmlKnJ2
+hC/jYqpBHt4zt2T4eW/6CbW8sIzNNIItxBBViepsOJL+YdZkhATUCjcyN43DVdH8
+q7hVBqkATzHiQvHntC6cRQ3cqfmtsnfB8LHDolomkL0ZL0YdpSE98DVilMjPMz/Y
+ol3fzFgACI3tsQTFKz8aFfaSCNTxQs1s2/L/4G5phNiLAgMBAAGgADANBgkqhkiG
+9w0BAQsFAAOCAQEAJB2hPF8r/cPO7+4fPf3oXwEwf0YsXHzVwUnawgtMNyU10rJX
+qqQIi2elEJfgYqmUhmXBrBIm06bRXlcR+QoWwX4sHk3rmHQYPy190bNpTaHVN9bZ
+kZndOcdaog1a3Lbui+e/brpzo0kGskW9TsDsOkYjzgIzQHGQtfcPfLhnLj4+sRyq
+tnV0vvHl3SyPmsVxrazRO4LgMBmwGx6QC4Yf3w25Us3aLzJqsZHDBRsVRdoHyLYd
+Jwt2fE2dLzGNkGFUP3BeJMHze1CqRybfoVlBU1IZRYlRQ8yOO/IpO7pa363Xx5Ar
+DsD2xcG7WvAjQ08vVgW2ShQRIIeX0Luhd6fqQg==
+-----END CERTIFICATE REQUEST-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/user.key b/contrib/wpa/tests/hwsim/auth_serv/user.key
new file mode 100644
index 000000000000..1e114958ada2
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/user.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC8JAjZB20ZDpRc
+a9mKhBsDIuIaBkLTC2CNXzEEBvqiQGoMJClYzYO/Op1/Nw1NJ13VV1cc8751PzpM
+RG8CL1uLLLBcRykZMQjRKPUenFuct+drQDUQPn9mZxahTmiKccwyWOMIh6IqBy1/
+c/ZD5X/09nSuJ+jSXK6bgtmlKnJ2hC/jYqpBHt4zt2T4eW/6CbW8sIzNNIItxBBV
+iepsOJL+YdZkhATUCjcyN43DVdH8q7hVBqkATzHiQvHntC6cRQ3cqfmtsnfB8LHD
+olomkL0ZL0YdpSE98DVilMjPMz/Yol3fzFgACI3tsQTFKz8aFfaSCNTxQs1s2/L/
+4G5phNiLAgMBAAECggEAAVFTSonVxsYmXdtXg7PXKJd28+21TBsZSwQuqLOPz9EZ
+rQoXzApscMttTPXKvw6Whqb88jP20oVl2CDmkiJYxsnCVnMdI5MHV8esp9E6hwd2
+tHaXqIx3gfUY4HpXGxke7/9VX7rrdNXmCK18PQ9/bOzI9mtLIyYJBwfMlG6OrKvP
+QoeLwoZiMDvA3nS8a/TeTPNXI1md7GHfPXqOumAngV0E4FuT7XfkaeBVzataUStY
+D9WEhjtiEahCJWqtN7U/Zq4qKB6XrrVye8BixDNRf/Qnz5SPrhWk3rWPEAqPBcqO
+EirQapQAI+e974irowq1WOUV4xDYWq8QoXrMWFwFEQKBgQDjwxs+i+P33efaeTXG
+AjK/qieLn7JvnHIxAOPK+qzPY75b01U0JH9qRKs1J2dxUQQFWu2rWtbLAVDf8et/
+URL8ZAqCy2U8HOUJuu+x9kNoLRxREuY9EhMeQ6P6clTGx1fgIc9BXsT+UjMK6I+R
+3JwZEwLYf3E81KU9CmKpCfUZwwKBgQDTd2TRu9fIbmIrAf6StvsSD2OWQ/RBavNE
+pISLH5/orvt3kXy9I6bAyW+FyHZ4620CE24fzstWH8l3F1jIvCf32wa8cTi1EA7l
+Rh8gVRC0s2CdETse7lUHTqqoqO6ckT9p0ZLLHfbALsy0jIOZUno0uVgYPzbWkvXW
+j76Q27uRmQKBgG38qgtqQoxP/MbkAbO9HASyhqZGWETNp9dCNr6ujwUXhbWSOHMV
+rPIEdykT/kAaf3aWkm9NTqx51jRO/wpcfG6lYO4IirqcuX4ZZ+bopWSJZENCfSzA
+rbrPr0AiNg5H9YemzA8lVLv7tepuo+YsQrVZGOazpFtb1O/FNB+tT09BAoGBAMlG
+zCyhOasp/cBn3pJxHhq7kROWzKdzj+cXHJ17VW5ZFztgvDUe+PppAQB6pOFFXHVs
+XxZhc8Me8FitXTF7fiN5UzkMH2ifxz3Nd5UDwqnsTppRq08ulLom02NDoBJgYdZr
+xugUNigIo4l6cUv/aBhFDifOC3lDWkGGgmFI9IiJAoGAUMIiyteV7zqrc3ncrnYu
+Nvg90VeA7CMa0SvmOMYS0M8fUUHtbthfr64c2fWM9JFflApRtVkHSclRMI1RqmVW
+SvDqS4kxb0NC5HiWioRHb3PlEqL8F076lySD/25sNwWwHwMq1MJPOi7cEXc1YtWS
+26WD/C6dauV5FnMa7eX1D3I=
+-----END PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/user.key.pkcs8 b/contrib/wpa/tests/hwsim/auth_serv/user.key.pkcs8
new file mode 100644
index 000000000000..8302fbbbf8d3
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/user.key.pkcs8
@@ -0,0 +1,30 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIFDjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQI2spAA/WpqE0CAggA
+MBQGCCqGSIb3DQMHBAi7csj8UwjU3ASCBMgK0/gob5gOgeDoZVERabnAazadQ7I9
+/QuAc75PEkl99YfjhiRXJyWWiOKNkGjgqTsXEfGp73/c3ilzKeoOtg/lhppZJ8VZ
+z2ePXO+BOx/xlgeLj+fEmXAjKZZBJ76FCUH/P6sWe8MTLMN1g67QeK6akwj7KCzO
+yPNOtL9FJbcq/Y0HxDy+266G2Y9ZVwtY45uOFXClWli1aRMz0/FRt7ijr5x7JsZj
+kqUgB7qPl2+wMEXLhU/m8Gn2JdkxRsrDn7Hq86TOAF+CpWpjxGTEZFqzrMcVXJ2I
+2oXntqwQdq9clLaMjPNP7eGZ7tmu3cQX6IypScZirJWgsb2+t3Hv3108wS9WZ7ZS
+eTvg8rVgrorP8lMPFqCuljEZyIWryN5EZ85/WlxjgpZQnnvEmNgckPcQM9KfjT9l
+UXsd3yaS+K6vA/NMgutSEQSFXLilpgr6wz19IvzIEeaGIU78GbBfSK/7kFWSb3WD
+mmub9LN9CKgwGMc640hHHY7EUX71QIxLitcA/lIxftnjPQ5brh7P+0gwray01i1Y
+SGaqLPNbgU+tLC7WnbadanTYRuMheTLmMgf8aIOiU9IT+JFiwm1s0jcVLlZyIsWF
+HH9xNcf4SYz1lUGcHnn/IlMd6PGucicmd17Qewi5Y/vil48r799Fr1KO/GCbxtkP
+qMjVqql1vRIo6tY++n9uwnTgg5NGrgLsFAWusulvEaC2BCTh1XqwtIC09zaonhy5
+30nadFWDnmR4MpPksjCBs5MCt2dBNF72JH/cFSTH7JpqDuthMJcprKc9b3MhQEqK
+srSr2HnDlOir6Ubvz2LCyAsuKui1B4UOnZ8q3lHPjIXHCZBWcdj+hLCmgncf/Sjq
+xvh0AmwPvn+kcG7W1DQ/ZfG7uh5oui4F9laVV3nuyOPzxdiuNFqYjHLb7rVgQOvK
+jjHYhd2pJAYDO13GrXVG1H2ipEUr8u/uDlr7HLdtvAZqsTSNMQZk3t2/TI2nJDli
+BPyJO72LVvwV0Jy5yH24qFwrzOmOcnNv1H14hPdwOOvhEQIWbAXRPTdf/ukC7HEb
+mKBfToPHALxnKPmcp9iERkcNZ8OfLqRdjCvZoD7K1caZFqv9Uc3U2kaQT0b9lm+1
+3UDd1n0YfxA/iJ0S2/KSroDAt/wWnznuJ9PlzpQfZ2BqlAxeFQ0RH1K97D6be5IW
+bxFxwkOwVK98j/g0+Y8/I0P2kNYH7WZ0ipkyM16TuhzUMKIuaoFxywwWaZg16F1x
+YJsKjjpDCAWAl1fARvMiUHko28drBRGbFzrRzBwtE9+jxGWAF48xStswrzQV2/Ct
+8Vll5BAfzSW8MLlvgJUECxFMOgsjS1GveHDcmEQ3z3JWBxGEXrYOYj0iDFNprrYu
+Q5e7q41R4BL7CxN6JYiLWJXw3K5hXEzaS8vB96r+2CCRMw2IQ2n7OTBBsEIqIV5f
+v++PKOwtZKpasnA7lblRZp8M/XFSOj591EBzwKGsXkOme7StZTngKjZKzf2xE56p
+TRPpxNeyi4Y9U7QuO0q/AOcQG0spI0c4X8QlIPwByL1CvVLiWnnmlCiU9g8fl0pk
+pVFASYyQPe0wAMU5C+zogf6K5N6rEcLbM3kNbvNE1zvPgzZpkAT6W41UZmB2828C
+Dx8=
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/user.key.pkcs8.pkcs5v15 b/contrib/wpa/tests/hwsim/auth_serv/user.key.pkcs8.pkcs5v15
new file mode 100644
index 000000000000..028177658db6
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/user.key.pkcs8.pkcs5v15
@@ -0,0 +1,29 @@
+-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIIE6TAbBgkqhkiG9w0BBQMwDgQIuIvpaVfmxx8CAggABIIEyMeuOVWbTSBwOkM1
+W3SdLlK0quEYilKanms32+3L7esusdyVU2u8J8tiiAvh84/xaCet99V7g/qYIYE7
+7wHTuB77BPGHRMZCKX2GOwJWxNVWUimB+kUX0yYIUwJx3CO3SExVPd2lcjnTMq5D
+PtaIHgJsPLU8wE3A1sVEGnfjE+73JtU/NJ7tS49WXNvQGd9ZeL4z3SqWt4HWmDdE
+TVOiAcQ2V7xrpfLP71ElW3D9oiL5Ba7NTCSYtLNfpQOKTiz8QQWzpkT1+IXNZvig
+yyYsNEZDzHQLmZXJVPsbHL8eoqzEpeH7mz/guQPBv1Rl8/PoiNY1bT8RV+EIp0Ne
+6FNv2OiQtV50XNRlM5c3gS9RvWEYwZ8PCCc3ZCaRw5dH1dghiBk91i17//GLkyV1
+gZUNrT7YqlwPBiksOwFYsGPgWP7tT1aKeEXaLXXtx7pIhRqUN9IJFSVs5hmSkSmk
+a4IuZGWEhhXpPm8cKsmrrMOXH3t48qR7OwUaERhcC4Qr23J1/8Whh8xNabIO0VXY
+HCiAdtlMO70GPlYq06lM+L5eF697qB/065Pf4bhjav8rs5QNvt5GzWWYo9uDaEib
+8n6tuOTxcf6yoe2fWgEpRk6jh9G9IS89pksusKDfizQg68q/Na1kmor7zT2FJAbC
+S/VCOfs8MH2zd8ZnEsvoUhR5ibjBU8aUe9ir5OT5vjaazRLpod2X7LKWdcb7irZA
+MvR9e1L+Z3RPLPoR8moYxLXZjd4F78rDDTYVFrYJGZTRmkJ9ukNzsI2ZzeRzNhqN
+kHDsSsjMYCI/QkTOFCOmoNrUOGiH1cXqRc0JD5PY7FRS+8qvw73uAFV7yTAmxikQ
+5IeNZvD+zJ6cvDb2ZR5iCmTA2f5uxsKl2hBe6uCdLLDPtlYHS0ZSmUolco6JrkDP
+ns5BR3e06C6YeHwM694dTGeffIFfKmVEkYBaJ8Hcuey9I2L2N69222pPcrUT947o
+TCXgZdjTNKSQEvEBPDHo9GRoJimnZODomJ9f/Da7BBIp+gHfE2rTS2+rUsU+5Kby
+AXJnaLpWu9zgSri6lNAtQZkmk3haL255AtycyLGuITxKTxjVSmZBQV/6zQQwcJNv
+e9PGNpI+EccjdjcI/UxDnW9ShuBbPTClrFmrE0jQjg4LZIR87pSO8jaBpg/2Q5ws
+nUnLrHbXuHuJqeFR1gg0zfkvulS5ldPdqDYeTEOpATmTcFHYTolwUa+cdJbeeo3P
+6s2RAyY9eGOgkgW8P0/nmfaHhVe8JBaHATx9liB3CFQ6kiU63YcBdgGdyzsYcIOK
+MR20MT9dq4l+Sij3EZABFqgCEypMgt+TUlzIXZbWBSaWmi8ScFAlQw9LmVuaOMMV
+hpTIuCenWVJVtWaHUIZkT316uENX3rQnDm1jR6UBSxFhKIsh+afeQMS6F656giAe
+rcwqkIDbglfvhN6NmjQppbfMpmiFE71XFmPBzofC3MvIh/2hB9tvEBnYKoJCMy1c
+XEbHynt7bRDm47Ev6gjAVWMZ1h40r79vvavK/vCA1b6Nd/F4gU+lGdB0tj/Pv2di
+4jvhiyE90tDCSHAe0BOnPFdWkLvru9BU2xixb/pegRT722jZj9PJb4jY79wT3PdM
+QHb1ZpXzRuidMI1ICQ==
+-----END ENCRYPTED PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/user.pem b/contrib/wpa/tests/hwsim/auth_serv/user.pem
new file mode 100644
index 000000000000..66be8f81a188
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/user.pem
@@ -0,0 +1,85 @@
+Certificate:
+ Data:
+ Version: 3 (0x2)
+ Serial Number:
+ d8:d3:e3:a6:cb:e3:cd:65
+ Signature Algorithm: sha256WithRSAEncryption
+ Issuer: C=FI, L=Tuusula, O=w1.fi, CN=Root CA
+ Validity
+ Not Before: May 2 19:55:38 2020 GMT
+ Not After : May 2 19:55:38 2021 GMT
+ Subject: C=FI, O=w1.fi, CN=Test User
+ Subject Public Key Info:
+ Public Key Algorithm: rsaEncryption
+ RSA Public-Key: (2048 bit)
+ Modulus:
+ 00:bc:24:08:d9:07:6d:19:0e:94:5c:6b:d9:8a:84:
+ 1b:03:22:e2:1a:06:42:d3:0b:60:8d:5f:31:04:06:
+ fa:a2:40:6a:0c:24:29:58:cd:83:bf:3a:9d:7f:37:
+ 0d:4d:27:5d:d5:57:57:1c:f3:be:75:3f:3a:4c:44:
+ 6f:02:2f:5b:8b:2c:b0:5c:47:29:19:31:08:d1:28:
+ f5:1e:9c:5b:9c:b7:e7:6b:40:35:10:3e:7f:66:67:
+ 16:a1:4e:68:8a:71:cc:32:58:e3:08:87:a2:2a:07:
+ 2d:7f:73:f6:43:e5:7f:f4:f6:74:ae:27:e8:d2:5c:
+ ae:9b:82:d9:a5:2a:72:76:84:2f:e3:62:aa:41:1e:
+ de:33:b7:64:f8:79:6f:fa:09:b5:bc:b0:8c:cd:34:
+ 82:2d:c4:10:55:89:ea:6c:38:92:fe:61:d6:64:84:
+ 04:d4:0a:37:32:37:8d:c3:55:d1:fc:ab:b8:55:06:
+ a9:00:4f:31:e2:42:f1:e7:b4:2e:9c:45:0d:dc:a9:
+ f9:ad:b2:77:c1:f0:b1:c3:a2:5a:26:90:bd:19:2f:
+ 46:1d:a5:21:3d:f0:35:62:94:c8:cf:33:3f:d8:a2:
+ 5d:df:cc:58:00:08:8d:ed:b1:04:c5:2b:3f:1a:15:
+ f6:92:08:d4:f1:42:cd:6c:db:f2:ff:e0:6e:69:84:
+ d8:8b
+ Exponent: 65537 (0x10001)
+ X509v3 extensions:
+ X509v3 Basic Constraints:
+ CA:FALSE
+ X509v3 Subject Key Identifier:
+ FB:85:00:A8:DF:D6:0C:0E:A7:E3:39:61:D9:BE:CE:2A:EF:6D:28:D8
+ X509v3 Authority Key Identifier:
+ keyid:A4:FD:B9:39:1B:81:B3:AA:EB:88:1D:D4:81:A9:B5:11:70:CC:A7:E1
+
+ Authority Information Access:
+ OCSP - URI:http://server.w1.fi:8888/
+
+ X509v3 Extended Key Usage:
+ TLS Web Client Authentication
+ Signature Algorithm: sha256WithRSAEncryption
+ 94:10:ec:75:db:4d:98:80:bd:b7:b2:b1:4d:b8:99:0a:ba:e1:
+ 47:d4:ef:50:48:5b:89:97:8b:ee:ee:56:2e:e6:ba:2d:0c:90:
+ 59:29:a1:c9:10:08:9a:c7:e9:57:42:5a:f6:7e:72:cd:d9:ff:
+ 8b:b2:13:6f:6e:e1:49:04:a5:82:cd:10:59:37:a5:9a:b2:2c:
+ 6e:a7:9e:ba:1f:e3:b7:79:79:37:65:a8:9b:49:39:c2:13:7d:
+ 6d:a8:37:23:c4:10:c9:73:25:67:1f:78:fb:b6:86:00:c1:1a:
+ 60:d7:5e:b9:63:c6:43:41:dd:37:0f:39:c9:fa:ff:8a:f9:62:
+ 59:00:e6:91:cd:79:28:82:db:30:88:c5:b8:79:8e:63:4c:65:
+ 50:3d:d2:65:b3:45:62:e5:d1:6f:1c:c1:1f:c2:b5:1a:0f:31:
+ 75:62:b3:7d:0b:8d:36:f9:43:eb:26:59:59:29:39:ad:37:0c:
+ 4f:95:7e:86:05:f5:70:fa:45:de:3c:f5:7e:e1:29:bc:82:d3:
+ a0:63:73:a3:e1:25:f3:5a:14:2d:c7:78:da:aa:e2:8a:df:08:
+ c5:be:1f:d3:9f:70:0b:7d:ea:5b:f4:2d:22:94:e6:95:92:50:
+ e2:55:72:13:c5:a1:3a:44:c4:25:18:9d:9d:a9:c8:c0:ea:7a:
+ d6:76:91:4e
+-----BEGIN CERTIFICATE-----
+MIIDkDCCAnigAwIBAgIJANjT46bL481lMA0GCSqGSIb3DQEBCwUAMEExCzAJBgNV
+BAYTAkZJMRAwDgYDVQQHDAdUdXVzdWxhMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UE
+AwwHUm9vdCBDQTAeFw0yMDA1MDIxOTU1MzhaFw0yMTA1MDIxOTU1MzhaMDExCzAJ
+BgNVBAYTAkZJMQ4wDAYDVQQKDAV3MS5maTESMBAGA1UEAwwJVGVzdCBVc2VyMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvCQI2QdtGQ6UXGvZioQbAyLi
+GgZC0wtgjV8xBAb6okBqDCQpWM2DvzqdfzcNTSdd1VdXHPO+dT86TERvAi9biyyw
+XEcpGTEI0Sj1HpxbnLfna0A1ED5/ZmcWoU5oinHMMljjCIeiKgctf3P2Q+V/9PZ0
+rifo0lyum4LZpSpydoQv42KqQR7eM7dk+Hlv+gm1vLCMzTSCLcQQVYnqbDiS/mHW
+ZIQE1Ao3MjeNw1XR/Ku4VQapAE8x4kLx57QunEUN3Kn5rbJ3wfCxw6JaJpC9GS9G
+HaUhPfA1YpTIzzM/2KJd38xYAAiN7bEExSs/GhX2kgjU8ULNbNvy/+BuaYTYiwID
+AQABo4GaMIGXMAkGA1UdEwQCMAAwHQYDVR0OBBYEFPuFAKjf1gwOp+M5Ydm+zirv
+bSjYMB8GA1UdIwQYMBaAFKT9uTkbgbOq64gd1IGptRFwzKfhMDUGCCsGAQUFBwEB
+BCkwJzAlBggrBgEFBQcwAYYZaHR0cDovL3NlcnZlci53MS5maTo4ODg4LzATBgNV
+HSUEDDAKBggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOCAQEAlBDsddtNmIC9t7Kx
+TbiZCrrhR9TvUEhbiZeL7u5WLua6LQyQWSmhyRAImsfpV0Ja9n5yzdn/i7ITb27h
+SQSlgs0QWTelmrIsbqeeuh/jt3l5N2Wom0k5whN9bag3I8QQyXMlZx94+7aGAMEa
+YNdeuWPGQ0HdNw85yfr/ivliWQDmkc15KILbMIjFuHmOY0xlUD3SZbNFYuXRbxzB
+H8K1Gg8xdWKzfQuNNvlD6yZZWSk5rTcMT5V+hgX1cPpF3jz1fuEpvILToGNzo+El
+81oULcd42qriit8Ixb4f059wC33qW/QtIpTmlZJQ4lVyE8WhOkTEJRidnanIwOp6
+1naRTg==
+-----END CERTIFICATE-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/user.pkcs12 b/contrib/wpa/tests/hwsim/auth_serv/user.pkcs12
new file mode 100644
index 000000000000..13d97acc9db0
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/user.pkcs12 differ
diff --git a/contrib/wpa/tests/hwsim/auth_serv/user.rsa-key b/contrib/wpa/tests/hwsim/auth_serv/user.rsa-key
new file mode 100644
index 000000000000..c77924a3ee43
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/auth_serv/user.rsa-key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAvCQI2QdtGQ6UXGvZioQbAyLiGgZC0wtgjV8xBAb6okBqDCQp
+WM2DvzqdfzcNTSdd1VdXHPO+dT86TERvAi9biyywXEcpGTEI0Sj1HpxbnLfna0A1
+ED5/ZmcWoU5oinHMMljjCIeiKgctf3P2Q+V/9PZ0rifo0lyum4LZpSpydoQv42Kq
+QR7eM7dk+Hlv+gm1vLCMzTSCLcQQVYnqbDiS/mHWZIQE1Ao3MjeNw1XR/Ku4VQap
+AE8x4kLx57QunEUN3Kn5rbJ3wfCxw6JaJpC9GS9GHaUhPfA1YpTIzzM/2KJd38xY
+AAiN7bEExSs/GhX2kgjU8ULNbNvy/+BuaYTYiwIDAQABAoIBAAFRU0qJ1cbGJl3b
+V4Oz1yiXdvPttUwbGUsELqizj8/RGa0KF8wKbHDLbUz1yr8Oloam/PIz9tKFZdgg
+5pIiWMbJwlZzHSOTB1fHrKfROocHdrR2l6iMd4H1GOB6VxsZHu//VV+663TV5git
+fD0Pf2zsyPZrSyMmCQcHzJRujqyrz0KHi8KGYjA7wN50vGv03kzzVyNZnexh3z16
+jrpgJ4FdBOBbk+135GngVc2rWlErWA/VhIY7YhGoQiVqrTe1P2auKigel661cnvA
+YsQzUX/0J8+Uj64VpN61jxAKjwXKjhIq0GqUACPnve+Iq6MKtVjlFeMQ2FqvEKF6
+zFhcBRECgYEA48MbPovj993n2nk1xgIyv6oni5+yb5xyMQDjyvqsz2O+W9NVNCR/
+akSrNSdncVEEBVrtq1rWywFQ3/Hrf1ES/GQKgstlPBzlCbrvsfZDaC0cURLmPRIT
+HkOj+nJUxsdX4CHPQV7E/lIzCuiPkdycGRMC2H9xPNSlPQpiqQn1GcMCgYEA03dk
+0bvXyG5iKwH+krb7Eg9jlkP0QWrzRKSEix+f6K77d5F8vSOmwMlvhch2eOttAhNu
+H87LVh/JdxdYyLwn99sGvHE4tRAO5UYfIFUQtLNgnRE7Hu5VB06qqKjunJE/adGS
+yx32wC7MtIyDmVJ6NLlYGD821pL11o++kNu7kZkCgYBt/KoLakKMT/zG5AGzvRwE
+soamRlhEzafXQja+ro8FF4W1kjhzFazyBHcpE/5AGn92lpJvTU6sedY0Tv8KXHxu
+pWDuCIq6nLl+GWfm6KVkiWRDQn0swK26z69AIjYOR/WHpswPJVS7+7XqbqPmLEK1
+WRjms6RbW9TvxTQfrU9PQQKBgQDJRswsoTmrKf3AZ96ScR4au5ETlsync4/nFxyd
+e1VuWRc7YLw1Hvj6aQEAeqThRVx1bF8WYXPDHvBYrV0xe34jeVM5DB9on8c9zXeV
+A8Kp7E6aUatPLpS6JtNjQ6ASYGHWa8boFDYoCKOJenFL/2gYRQ4nzgt5Q1pBhoJh
+SPSIiQKBgFDCIsrXle86q3N53K52Ljb4PdFXgOwjGtEr5jjGEtDPH1FB7W7YX6+u
+HNn1jPSRX5QKUbVZB0nJUTCNUaplVkrw6kuJMW9DQuR4loqER29z5RKi/BdO+pck
+g/9ubDcFsB8DKtTCTzou3BF3NWLVktulg/wunWrleRZzGu3l9Q9y
+-----END RSA PRIVATE KEY-----
diff --git a/contrib/wpa/tests/hwsim/auth_serv/user2.pkcs12 b/contrib/wpa/tests/hwsim/auth_serv/user2.pkcs12
new file mode 100644
index 000000000000..8957a56556a6
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/user2.pkcs12 differ
diff --git a/contrib/wpa/tests/hwsim/auth_serv/user3.pkcs12 b/contrib/wpa/tests/hwsim/auth_serv/user3.pkcs12
new file mode 100644
index 000000000000..46ae62e82451
Binary files /dev/null and b/contrib/wpa/tests/hwsim/auth_serv/user3.pkcs12 differ
diff --git a/contrib/wpa/tests/hwsim/build.sh b/contrib/wpa/tests/hwsim/build.sh
new file mode 100755
index 000000000000..cb4700166f82
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/build.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+set -e
+
+cd $(dirname $0)
+
+usage()
+{
+ echo "$0 [-c | --codecov] [-f | --force-config]"
+ exit 1
+}
+
+use_lcov=0
+force_config=0
+while [ "$1" != "" ]; do
+ case $1 in
+ -c | --codecov ) shift
+ echo "$0: use code coverage specified"
+ use_lcov=1
+ ;;
+ -f | --force-config ) shift
+ force_config=1
+ echo "$0: force copy config specified"
+ ;;
+ * ) usage
+ esac
+done
+
+JOBS=`nproc`
+if [ -z "$ABC" ]; then
+ JOBS=8
+fi
+
+echo "Building TNC testing tools"
+cd tnc
+make QUIET=1 -j$JOBS
+
+echo "Building wlantest"
+cd ../../../wlantest
+make QUIET=1 -j$JOBS > /dev/null
+
+echo "Building hs20-osu-client"
+cd ../hs20/client/
+make QUIET=1 CONFIG_NO_BROWSER=1
+
+echo "Building hostapd"
+cd ../../hostapd
+if [ ! -e .config -o $force_config -eq 1 ]; then
+ if ! cmp ../tests/hwsim/example-hostapd.config .config >/dev/null 2>&1 ; then
+ cp ../tests/hwsim/example-hostapd.config .config
+ fi
+fi
+
+if [ $use_lcov -eq 1 ]; then
+ if ! grep -q CONFIG_CODE_COVERAGE .config; then
+ echo CONFIG_CODE_COVERAGE=y >> .config
+ else
+ echo "CONFIG_CODE_COVERAGE already exists in hostapd/.config. Ignore"
+ fi
+fi
+
+make QUIET=1 -j$JOBS hostapd hostapd_cli hlr_auc_gw
+
+echo "Building wpa_supplicant"
+cd ../wpa_supplicant
+if [ ! -e .config -o $force_config -eq 1 ]; then
+ if ! cmp ../tests/hwsim/example-wpa_supplicant.config .config >/dev/null 2>&1 ; then
+ cp ../tests/hwsim/example-wpa_supplicant.config .config
+ fi
+fi
+
+if [ $use_lcov -eq 1 ]; then
+ if ! grep -q CONFIG_CODE_COVERAGE .config; then
+ echo CONFIG_CODE_COVERAGE=y >> .config
+ else
+ echo "CONFIG_CODE_COVERAGE already exists in wpa_supplicant/.config. Ignore"
+ fi
+fi
+
+if [ -z $FIPSLD_CC ]; then
+export FIPSLD_CC=gcc
+fi
+make QUIET=1 -j$JOBS
diff --git a/contrib/wpa/tests/hwsim/check_kernel.py b/contrib/wpa/tests/hwsim/check_kernel.py
new file mode 100644
index 000000000000..446c9a04e914
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/check_kernel.py
@@ -0,0 +1,31 @@
+# kernel message checker module
+#
+# Copyright (c) 2013, Intel Corporation
+#
+# Author: Johannes Berg <johannes@sipsolutions.net>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+#
+"""
+Tests for kernel messages to find if there were any issues in them.
+"""
+
+import re
+
+lockdep_messages = [
+ 'possible circular locking dependency',
+ '.*-safe -> .*unsafe lock order detected',
+ 'possible recursive locking detected',
+ 'inconsistent lock state',
+ 'possible irq lock inversion dependency',
+ 'suspicious RCU usage',
+]
+lockdep = r'(\[\s*)?(INFO|WARNING): (%s)|\*\*\* DEADLOCK \*\*\*' % ('|'.join(lockdep_messages), )
+issue = re.compile('(\[[0-9 .]*\] )?(WARNING:|BUG:|%s|RTNL: assertion failed).*' % lockdep)
+
+def check_kernel(logfile):
+ for line in open(logfile, 'r'):
+ if issue.match(line):
+ return False
+ return True
diff --git a/contrib/wpa/tests/hwsim/devdetail.xml b/contrib/wpa/tests/hwsim/devdetail.xml
new file mode 100644
index 000000000000..6d0389e8a133
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/devdetail.xml
@@ -0,0 +1,47 @@
+<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/tests/hwsim/devinfo.xml b/contrib/wpa/tests/hwsim/devinfo.xml
new file mode 100644
index 000000000000..d48a520a98a1
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/devinfo.xml
@@ -0,0 +1,7 @@
+<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/tests/hwsim/dictionary.radius b/contrib/wpa/tests/hwsim/dictionary.radius
new file mode 100644
index 000000000000..d2112dad3f48
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/dictionary.radius
@@ -0,0 +1,20 @@
+ATTRIBUTE User-Name 1 string
+ATTRIBUTE User-Password 2 string
+ATTRIBUTE NAS-IP-Address 4 ipaddr
+ATTRIBUTE State 24 octets
+ATTRIBUTE Vendor-Specific 26 octets
+ATTRIBUTE Session-Timeout 27 integer
+ATTRIBUTE Calling-Station-Id 31 string
+ATTRIBUTE NAS-Identifier 32 string
+ATTRIBUTE Acct-Session-Id 44 string
+ATTRIBUTE Acct-Multi-Session-Id 50 string
+ATTRIBUTE Event-Timestamp 55 date
+ATTRIBUTE Tunnel-Type 64 integer
+ATTRIBUTE Tunnel-Medium-Type 65 integer
+ATTRIBUTE Tunnel-Password 69 octets
+ATTRIBUTE EAP-Message 79 string
+ATTRIBUTE Message-Authenticator 80 octets
+ATTRIBUTE Tunnel-Private-Group-ID 81 string
+ATTRIBUTE Acct-Interim-Interval 85 integer
+ATTRIBUTE Chargeable-User-Identity 89 string
+ATTRIBUTE Error-Cause 101 integer
diff --git a/contrib/wpa/tests/hwsim/example-hostapd.config b/contrib/wpa/tests/hwsim/example-hostapd.config
new file mode 100644
index 000000000000..d01a1d2edcfe
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/example-hostapd.config
@@ -0,0 +1,116 @@
+#CC=ccache gcc
+
+CONFIG_DRIVER_NONE=y
+CONFIG_DRIVER_NL80211=y
+CONFIG_RSN_PREAUTH=y
+
+#CONFIG_TLS=internal
+#CONFIG_INTERNAL_LIBTOMMATH=y
+#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+CONFIG_TLS=openssl
+
+CONFIG_EAP=y
+CONFIG_ERP=y
+CONFIG_EAP_MD5=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_MSCHAPV2=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_AKA_PRIME=y
+CONFIG_EAP_GPSK=y
+CONFIG_EAP_GPSK_SHA256=y
+CONFIG_EAP_SAKE=y
+CONFIG_EAP_PAX=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_VENDOR_TEST=y
+CONFIG_EAP_FAST=y
+CONFIG_EAP_TEAP=y
+CONFIG_EAP_IKEV2=y
+CONFIG_EAP_TNC=y
+CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
+LIBS += -rdynamic
+CONFIG_EAP_UNAUTH_TLS=y
+ifeq ($(CONFIG_TLS), openssl)
+CONFIG_EAP_PWD=y
+endif
+CONFIG_EAP_EKE=y
+CONFIG_PKCS12=y
+CONFIG_RADIUS_SERVER=y
+CONFIG_IPV6=y
+CONFIG_TLSV11=y
+CONFIG_TLSV12=y
+
+CONFIG_FULL_DYNAMIC_VLAN=y
+CONFIG_VLAN_NETLINK=y
+CONFIG_LIBNL32=y
+CONFIG_LIBNL3_ROUTE=y
+CONFIG_IEEE80211R=y
+CONFIG_IEEE80211AC=y
+CONFIG_IEEE80211AX=y
+
+CONFIG_OCV=y
+
+CONFIG_WPS=y
+CONFIG_WPS_UPNP=y
+CONFIG_WPS_NFC=y
+#CONFIG_WPS_STRICT=y
+CONFIG_WPA_TRACE=y
+CONFIG_WPA_TRACE_BFD=y
+
+CONFIG_P2P_MANAGER=y
+CONFIG_DEBUG_FILE=y
+CONFIG_DEBUG_LINUX_TRACING=y
+CONFIG_WPA_CLI_EDIT=y
+CONFIG_ACS=y
+CONFIG_NO_RANDOM_POOL=y
+CONFIG_WNM=y
+CONFIG_INTERWORKING=y
+CONFIG_HS20=y
+CONFIG_SQLITE=y
+CONFIG_SAE=y
+CONFIG_SAE_PK=y
+CFLAGS += -DALL_DH_GROUPS
+
+CONFIG_FST=y
+CONFIG_FST_TEST=y
+
+CONFIG_TESTING_OPTIONS=y
+CFLAGS += -DCONFIG_RADIUS_TEST
+CONFIG_MODULE_TESTS=y
+
+CONFIG_SUITEB=y
+
+# AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
+# This can be used as a more efficient memory error detector than valgrind
+# (though, with still some CPU and memory cost, so VM cases will need more
+# memory allocated for the guest).
+#CFLAGS += -fsanitize=address -O1 -fno-omit-frame-pointer -g
+#LIBS += -fsanitize=address -fno-omit-frame-pointer -g
+#LIBS_h += -fsanitize=address -fno-omit-frame-pointer -g
+#LIBS_n += -fsanitize=address -fno-omit-frame-pointer -g
+#LIBS_c += -fsanitize=address -fno-omit-frame-pointer -g
+
+# Undefined Behavior Sanitizer (UBSan) can be enabled by uncommenting the
+# following lines.
+#CFLAGS += -Wno-format-nonliteral
+#CFLAGS += -fsanitize=undefined
+##CFLAGS += -fno-sanitize-recover
+#LIBS += -fsanitize=undefined
+##LIBS += -fno-sanitize-recover
+#LIBS_h += -fsanitize=undefined
+#LIBS_n += -fsanitize=undefined
+#LIBS_c += -fsanitize=undefined
+CONFIG_MBO=y
+
+CONFIG_TAXONOMY=y
+CONFIG_FILS=y
+CONFIG_FILS_SK_PFS=y
+CONFIG_OWE=y
+CONFIG_DPP=y
+CONFIG_DPP2=y
+CONFIG_WEP=y
+CONFIG_PASN=y
+CONFIG_AIRTIME_POLICY=y
diff --git a/contrib/wpa/tests/hwsim/example-setup.txt b/contrib/wpa/tests/hwsim/example-setup.txt
new file mode 100644
index 000000000000..cf5cf3ba6761
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/example-setup.txt
@@ -0,0 +1,191 @@
+Step-by-step guide for setting up hostapd/wpa_supplicant test framework
+-----------------------------------------------------------------------
+
+This document can be used as a quick guide for getting started with
+hostapd/wpa_supplicant test framework with mac80211_hwsim. While the
+example here uses Ubuntu 14.04.1 server to have a list of exact steps,
+there are no requirements for using that specific distribution in the
+testing setup.
+
+The steps here describe how to run a full Linux installation in a
+virtual machine with any host system (e.g., Linux, Windows, or OS X as
+the host and using kvm, VirtualBox, etc. for running the virtual guest
+system). For more advanced (and significantly faster and with more
+testing coverage) configuration on a Linux host system, parallel virtual
+machines can be used as an alternative setup. See tests/hwsim/vm/README
+for more details on that.
+
+
+Install Ubuntu Server 14.04.1 in the virtual machine
+
+- download installation image, e.g.,
+ http://releases.ubuntu.com/14.04.1/ubuntu-14.04.1-server-amd64.iso
+- use virtualization software specific steps to create a new VM and
+ install the the guest system with default settings (i.e., no need to
+ select any extra packages during initial installation)
+- if the host system has multiple CPU cores, it is likely a good idea to
+ enabled at least two CPUs in the guest; 1024 MB of RAM should be enough
+ for testing purposes
+- 8 GB of virtual hard driver should be fine for this purpose
+- boot to the installed operating system
+
+
+Install the prerequisite packages that may not have been installed by default
+
+sudo apt-get install build-essential git libpcap-dev libsqlite3-dev binutils-dev libnl-3-dev libnl-genl-3-dev libnl-route-3-dev libssl-dev libiberty-dev libdbus-1-dev iw bridge-utils python-pyrad python-crypto tshark
+
+optional:
+sudo apt-get install python-netifaces
+
+
+Install a recent kernel wireless components (mac80211_hwsim, mac80211,
+cfg80211)
+
+For this step, the kernel version may be updated, but the simpler option
+is to install the latest version of Backports package. For example:
+
+wget http://www.kernel.org/pub/linux/kernel/projects/backports/stable/v3.19-rc1/backports-3.19-rc1-1.tar.xz
+tar xJf backports-3.19-rc1-1.tar.xz
+cd backports-3.19-rc1-1
+
+cat > defconfigs/mac80211_hwsim <<EOF
+CPTCFG_CFG80211=m
+CPTCFG_CFG80211_WEXT=y
+CPTCFG_MAC80211=m
+CPTCFG_MAC80211_LEDS=y
+CPTCFG_MAC80211_MESH=y
+CPTCFG_WLAN=y
+CPTCFG_MAC80211_HWSIM=m
+EOF
+
+make defconfig-mac80211_hwsim
+make
+sudo make install
+cd ..
+
+
+Update iw based on custom iw.git build
+
+Couple of the test cases expect iw to have support for requesting
+cfg80211 scan results to be flushed. That functionality is not included
+in the version that Ubuntu 14.04.1 includes (iw v3.4). Following steps
+can be used to replace that version with a custom build. This is
+optional, i.e., most test cases will work with the old iw version, but
+some test cases are skipped and some are more likely to fail if iw does
+not get updated.
+
+wget https://www.kernel.org/pub/software/network/iw/iw-3.17.tar.gz
+tar xf iw-3.17.tar.gz
+cd iw-3.17
+make
+sudo mv /sbin/iw{,-distro}
+sudo cp iw /sbin/iw
+cd ..
+
+
+Update wireless-regdb
+
+Number of VHT and DFS test cases are skipped if the old wireless-regdb
+version from Ubuntu 14.04 (2013.02.13) is used. Following steps can
+optionally be used to update wireless-regdb to a newer snapshot to
+enable additional test cases:
+
+wget http://kernel.org/pub/software/network/wireless-regdb/wireless-regdb-2014.10.07.tar.xz
+tar xJf wireless-regdb-2014.10.07.tar.xz
+sudo mv /lib/crda/regulatory.bin{,-distro}
+sudo cp wireless-regdb-2014.10.07/regulatory.bin /lib/crda/regulatory.bin
+
+# following command can be used to verify that the new version is trusted
+regdbdump /lib/crda/regulatory.bin
+
+
+Download a snapshot of the hostap.git repository and build the programs
+
+git clone git://w1.fi/hostap.git
+cd hostap/tests/hwsim
+./build.sh
+
+
+Setup is now ready for testing. You can run a quick test to confirm that
+things work as expected:
+
+# load mac80211_hwsim and start test software
+./start.sh
+
+# run a single test case ap_open
+sudo ./run-tests.py ap_open
+
+This should print out following style results:
+
+DEV: wlan0: 02:00:00:00:00:00
+DEV: wlan1: 02:00:00:00:01:00
+DEV: wlan2: 02:00:00:00:02:00
+APDEV: wlan3
+APDEV: wlan4
+START ap_open 1/1
+Test: AP with open mode (no security) configuration
+Starting AP wlan3
+Connect STA wlan0 to AP
+PASS ap_open 0.175895 2015-01-17 20:12:07.486006
+passed all 1 test case(s)
+
+(If that "PASS ap_open" line does not show up, something unexpected has
+happened and the setup is not in working condition.)
+
+# to stop test software and unload mac80211_hwsim
+./stop.sh
+
+
+To run all available test cases (about thousand or so), you can run following:
+
+./run-all.sh
+
+This will take about half an hour to hour to run (if that sounds long, see
+vm/README for information on how parallel VMs can be used to speed this
+up; e.g., a 4-core i7-4770K can run these in under 10 minutes with 7
+parallel VMs).
+
+The results may look something like this:
+
+START grpform_goneg_fail_with_group_iface 1/981
+PASS grpform_goneg_fail_with_group_iface 0.371424 2015-01-17 22:17:16.659803
+START grpform2 2/981
+PASS grpform2 1.476142 2015-01-17 22:17:18.136539
+...
+START ext_password_psk_not_found 981/981
+PASS ext_password_psk_not_found 1.544709 2015-01-17 22:46:56.489764
+failed tests: wext_wpa2_psk wext_wep_open_auth wext_open wext_rfkill wext_scan_hidden wext_pmksa_cache wext_wep_shared_key_auth
+
+
+In this example, about 860 test cases passed and about 100 were skipped.
+
+Most of the skipped test cases are in following categories:
+- D-Bus (requires kvm-based test run, see vm/README)
+- VHT 80 and 160 MHz channels (requires wireless-regdb update)
+- DFS (requires wireless-regdb updates)
+
+The following test failed every time (i.e., other failed cases could be
+passed on second attempt):
+
+wext_pmf wext_wpa2_psk wext_wep_open_auth wext_open wext_rfkill wext_scan_hidden wext_pmksa_cache wext_wep_shared_key_auth
+
+WEXT failures are due to the specific cfg80211/mac80211 version from
+Backports not allowing WEXT support to be enabled. A newer build
+addresses that and these WEXT test cases pass, e.g., with this snapshot
+build:
+http://buildbot.w1.fi/backports-wireless-testing/backports-wireless-testing-2015-01-18-ba3f765.tar.bz2
+
+With that version, ibss_rsn is failing due to a known cfg80211
+regression in the specific snapshot build. All other test cases passed
+at least on retry or were skipped due to missing testing capability.
+
+With systemd based distros, e.g., Ubuntu 16.04, systemd-rfkill.service might
+block the mac80211_hwsim network devices.
+The tests will fail with
+ Exception: Failed to enable hostapd interface wlan3
+In the *.hostapd log, would will read
+ nl80211: Could not yet enable interface 'wlan3' due to rfkill
+Your syslog will read
+ systemd[1]: Starting Load/Save RF Kill Switch Status...
+This can be fixed by
+ systemctl mask systemd-rfkill.service
diff --git a/contrib/wpa/tests/hwsim/example-wpa_supplicant.config b/contrib/wpa/tests/hwsim/example-wpa_supplicant.config
new file mode 100644
index 000000000000..5e5acd695729
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/example-wpa_supplicant.config
@@ -0,0 +1,160 @@
+#CC=ccache gcc
+
+CONFIG_TLS=openssl
+#CONFIG_TLS=wolfssl
+#CONFIG_TLS=internal
+#CONFIG_INTERNAL_LIBTOMMATH=y
+#CONFIG_INTERNAL_LIBTOMMATH_FAST=y
+
+CONFIG_IEEE8021X_EAPOL=y
+
+CONFIG_ERP=y
+CONFIG_EAP_MD5=y
+CONFIG_MSCHAPV2=y
+CONFIG_EAP_TLS=y
+CONFIG_EAP_PEAP=y
+CONFIG_EAP_TTLS=y
+CONFIG_EAP_GTC=y
+CONFIG_EAP_OTP=y
+CONFIG_EAP_PSK=y
+CONFIG_EAP_PAX=y
+CONFIG_EAP_LEAP=y
+CONFIG_EAP_SIM=y
+CONFIG_EAP_AKA=y
+CONFIG_EAP_AKA_PRIME=y
+CONFIG_EAP_VENDOR_TEST=y
+CONFIG_EAP_TLV=y
+CONFIG_EAP_SAKE=y
+CONFIG_EAP_GPSK=y
+CONFIG_EAP_GPSK_SHA256=y
+CONFIG_EAP_EKE=y
+CONFIG_EAP_TNC=y
+CFLAGS += -DTNC_CONFIG_FILE=\"tnc/tnc_config\"
+LIBS += -rdynamic
+CONFIG_EAP_FAST=y
+CONFIG_EAP_TEAP=y
+CONFIG_EAP_IKEV2=y
+
+ifeq ($(CONFIG_TLS), openssl)
+CONFIG_EAP_PWD=y
+endif
+
+CONFIG_USIM_SIMULATOR=y
+CONFIG_SIM_SIMULATOR=y
+
+#CONFIG_PCSC=y
+CONFIG_IPV6=y
+CONFIG_DRIVER_NONE=y
+CONFIG_PKCS12=y
+CONFIG_CTRL_IFACE=unix
+
+CONFIG_WPA_CLI_EDIT=y
+
+CONFIG_OCSP=y
+
+#CONFIG_ELOOP_POLL=y
+
+CONFIG_CTRL_IFACE_DBUS_NEW=y
+CONFIG_CTRL_IFACE_DBUS_INTRO=y
+
+CONFIG_IEEE80211R=y
+CONFIG_IEEE80211AC=y
+CONFIG_IEEE80211AX=y
+
+CONFIG_OCV=y
+
+CONFIG_DEBUG_FILE=y
+
+CONFIG_WPS=y
+#CONFIG_WPS_STRICT=y
+CONFIG_WPS_UPNP=y
+CONFIG_WPS_NFC=y
+CONFIG_WPS_ER=y
+#CONFIG_WPS_REG_DISABLE_OPEN=y
+
+CONFIG_DRIVER_WEXT=y
+
+CONFIG_DRIVER_NL80211=y
+CFLAGS += -I/usr/include/libnl3
+CONFIG_LIBNL32=y
+
+CONFIG_IBSS_RSN=y
+
+CONFIG_AP=y
+CONFIG_MESH=y
+CONFIG_P2P=y
+CONFIG_WIFI_DISPLAY=y
+
+CONFIG_ACS=y
+
+CONFIG_BGSCAN_SIMPLE=y
+CONFIG_BGSCAN_LEARN=y
+
+CONFIG_WPA_TRACE=y
+CONFIG_WPA_TRACE_BFD=y
+
+CONFIG_TDLS=y
+CONFIG_TDLS_TESTING=y
+CONFIG_NO_RANDOM_POOL=y
+
+CONFIG_TLSV11=y
+CONFIG_TLSV12=y
+
+CONFIG_HT_OVERRIDES=y
+CONFIG_VHT_OVERRIDES=y
+CONFIG_HE_OVERRIDES=y
+
+CONFIG_DEBUG_LINUX_TRACING=y
+
+CONFIG_INTERWORKING=y
+CONFIG_HS20=y
+
+CONFIG_AUTOSCAN_EXPONENTIAL=y
+CONFIG_AUTOSCAN_PERIODIC=y
+
+CONFIG_EXT_PASSWORD_TEST=y
+CONFIG_EXT_PASSWORD_FILE=y
+
+CONFIG_EAP_UNAUTH_TLS=y
+
+CONFIG_SAE=y
+CONFIG_SAE_PK=y
+CFLAGS += -DALL_DH_GROUPS
+
+CONFIG_WNM=y
+
+CONFIG_FST=y
+CONFIG_FST_TEST=y
+
+CONFIG_TESTING_OPTIONS=y
+CONFIG_MODULE_TESTS=y
+
+CONFIG_SUITEB=y
+
+# AddressSanitizer (ASan) can be enabled by uncommenting the following lines.
+# This can be used as a more efficient memory error detector than valgrind
+# (though, with still some CPU and memory cost, so VM cases will need more
+# memory allocated for the guest).
+#CFLAGS += -fsanitize=address -O1 -fno-omit-frame-pointer -g
+#LIBS += -fsanitize=address -fno-omit-frame-pointer -g
+#LIBS_c += -fsanitize=address -fno-omit-frame-pointer -g
+#LIBS_p += -fsanitize=address -fno-omit-frame-pointer -g
+
+# Undefined Behavior Sanitizer (UBSan) can be enabled by uncommenting the
+# following lines.
+#CFLAGS += -Wno-format-nonliteral
+#CFLAGS += -fsanitize=undefined
+##CFLAGS += -fno-sanitize-recover
+#LIBS += -fsanitize=undefined
+##LIBS += -fno-sanitize-recover
+#LIBS_c += -fsanitize=undefined
+#LIBS_p += -fsanitize=undefined
+CONFIG_MBO=y
+CONFIG_FILS=y
+CONFIG_FILS_SK_PFS=y
+CONFIG_PMKSA_CACHE_EXTERNAL=y
+CONFIG_OWE=y
+CONFIG_DPP=y
+CONFIG_DPP2=y
+CONFIG_WEP=y
+CONFIG_PASN=y
diff --git a/contrib/wpa/tests/hwsim/fst_module_aux.py b/contrib/wpa/tests/hwsim/fst_module_aux.py
new file mode 100644
index 000000000000..03a0bd73e5fc
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/fst_module_aux.py
@@ -0,0 +1,832 @@
+# FST tests related classes
+# Copyright (c) 2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+import os
+import signal
+import time
+import re
+
+import hostapd
+import wpaspy
+import utils
+from wpasupplicant import WpaSupplicant
+
+import fst_test_common
+
+logger = logging.getLogger()
+
+def parse_fst_iface_event(ev):
+ """Parses FST iface event that comes as a string, e.g.
+ "<3>FST-EVENT-IFACE attached ifname=wlan9 group=fstg0"
+ Returns a dictionary with parsed "event_type", "ifname", and "group"; or
+ None if not an FST event or can't be parsed."""
+ event = {}
+ if ev.find("FST-EVENT-IFACE") == -1:
+ return None
+ if ev.find("attached") != -1:
+ event['event_type'] = 'attached'
+ elif ev.find("detached") != -1:
+ event['event_type'] = 'detached'
+ else:
+ return None
+ f = re.search("ifname=(\S+)", ev)
+ if f is not None:
+ event['ifname'] = f.group(1)
+ f = re.search("group=(\S+)", ev)
+ if f is not None:
+ event['group'] = f.group(1)
+ return event
+
+def parse_fst_session_event(ev):
+ """Parses FST session event that comes as a string, e.g.
+ "<3>FST-EVENT-SESSION event_type=EVENT_FST_SESSION_STATE session_id=0 reason=REASON_STT"
+ Returns a dictionary with parsed "type", "id", and "reason"; or None if not
+ a FST event or can't be parsed"""
+ event = {}
+ if ev.find("FST-EVENT-SESSION") == -1:
+ return None
+ event['new_state'] = '' # The field always exists in the dictionary
+ f = re.search("event_type=(\S+)", ev)
+ if f is None:
+ return None
+ event['type'] = f.group(1)
+ f = re.search("session_id=(\d+)", ev)
+ if f is not None:
+ event['id'] = f.group(1)
+ f = re.search("old_state=(\S+)", ev)
+ if f is not None:
+ event['old_state'] = f.group(1)
+ f = re.search("new_state=(\S+)", ev)
+ if f is not None:
+ event['new_state'] = f.group(1)
+ f = re.search("reason=(\S+)", ev)
+ if f is not None:
+ event['reason'] = f.group(1)
+ return event
+
+def start_two_ap_sta_pairs(apdev, rsn=False):
+ """auxiliary function that creates two pairs of APs and STAs"""
+ ap1 = FstAP(apdev[0]['ifname'], 'fst_11a', 'a',
+ fst_test_common.fst_test_def_chan_a,
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_low,
+ fst_test_common.fst_test_def_llt, rsn=rsn)
+ ap1.start()
+ ap2 = FstAP(apdev[1]['ifname'], 'fst_11g', 'g',
+ fst_test_common.fst_test_def_chan_g,
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_high,
+ fst_test_common.fst_test_def_llt, rsn=rsn)
+ ap2.start()
+
+ sta1 = FstSTA('wlan5',
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_low,
+ fst_test_common.fst_test_def_llt, rsn=rsn)
+ sta1.start()
+ sta2 = FstSTA('wlan6',
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_high,
+ fst_test_common.fst_test_def_llt, rsn=rsn)
+ sta2.start()
+
+ return ap1, ap2, sta1, sta2
+
+def stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2):
+ sta1.stop()
+ sta2.stop()
+ ap1.stop()
+ ap2.stop()
+ fst_test_common.fst_clear_regdom()
+
+def connect_two_ap_sta_pairs(ap1, ap2, dev1, dev2, rsn=False):
+ """Connects a pair of stations, each one to a separate AP"""
+ dev1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ dev2.scan(freq=fst_test_common.fst_test_def_freq_g)
+
+ if rsn:
+ dev1.connect(ap1, psk="12345678",
+ scan_freq=fst_test_common.fst_test_def_freq_a)
+ dev2.connect(ap2, psk="12345678",
+ scan_freq=fst_test_common.fst_test_def_freq_g)
+ else:
+ dev1.connect(ap1, key_mgmt="NONE",
+ scan_freq=fst_test_common.fst_test_def_freq_a)
+ dev2.connect(ap2, key_mgmt="NONE",
+ scan_freq=fst_test_common.fst_test_def_freq_g)
+
+def disconnect_two_ap_sta_pairs(ap1, ap2, dev1, dev2):
+ dev1.disconnect()
+ dev2.disconnect()
+
+def external_sta_connect(sta, ap, **kwargs):
+ """Connects the external station to the given AP"""
+ if not isinstance(sta, WpaSupplicant):
+ raise Exception("Bad STA object")
+ if not isinstance(ap, FstAP):
+ raise Exception("Bad AP object to connect to")
+ hap = ap.get_instance()
+ sta.connect(ap.get_ssid(), **kwargs)
+
+def disconnect_external_sta(sta, ap, check_disconnect=True):
+ """Disconnects the external station from the AP"""
+ if not isinstance(sta, WpaSupplicant):
+ raise Exception("Bad STA object")
+ if not isinstance(ap, FstAP):
+ raise Exception("Bad AP object to connect to")
+ sta.request("DISCONNECT")
+ if check_disconnect:
+ hap = ap.get_instance()
+ ev = hap.wait_event(["AP-STA-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("No disconnection event received from %s" % ap.get_ssid())
+
+#
+# FstDevice class
+# This is the parent class for the AP (FstAP) and STA (FstSTA) that implements
+# FST functionality.
+#
+class FstDevice:
+ def __init__(self, iface, fst_group, fst_pri, fst_llt=None, rsn=False):
+ self.iface = iface
+ self.fst_group = fst_group
+ self.fst_pri = fst_pri
+ self.fst_llt = fst_llt # None llt means no llt parameter will be set
+ self.instance = None # Hostapd/WpaSupplicant instance
+ self.peer_obj = None # Peer object, must be a FstDevice child object
+ self.new_peer_addr = None # Peer MAC address for new session iface
+ self.old_peer_addr = None # Peer MAC address for old session iface
+ self.role = 'initiator' # Role: initiator/responder
+ s = self.grequest("FST-MANAGER TEST_REQUEST IS_SUPPORTED")
+ if not s.startswith('OK'):
+ raise utils.HwsimSkip("FST not supported")
+ self.rsn = rsn
+
+ def ifname(self):
+ return self.iface
+
+ def get_instance(self):
+ """Gets the Hostapd/WpaSupplicant instance"""
+ raise Exception("Virtual get_instance() called!")
+
+ def get_own_mac_address(self):
+ """Gets the device's own MAC address"""
+ raise Exception("Virtual get_own_mac_address() called!")
+
+ def get_new_peer_addr(self):
+ return self.new_peer_addr
+
+ def get_old_peer_addr(self):
+ return self.old_peer_addr
+
+ def get_actual_peer_addr(self):
+ """Gets the peer address. A connected AP/station address is returned."""
+ raise Exception("Virtual get_actual_peer_addr() called!")
+
+ def grequest(self, req):
+ """Send request on the global control interface"""
+ raise Exception("Virtual grequest() called!")
+
+ def wait_gevent(self, events, timeout=None):
+ """Wait for a list of events on the global interface"""
+ raise Exception("Virtual wait_gevent() called!")
+
+ def request(self, req):
+ """Issue a request to the control interface"""
+ h = self.get_instance()
+ return h.request(req)
+
+ def wait_event(self, events, timeout=None):
+ """Wait for an event from the control interface"""
+ h = self.get_instance()
+ if timeout is not None:
+ return h.wait_event(events, timeout=timeout)
+ else:
+ return h.wait_event(events)
+
+ def set_old_peer_addr(self, peer_addr=None):
+ """Sets the peer address"""
+ if peer_addr is not None:
+ self.old_peer_addr = peer_addr
+ else:
+ self.old_peer_addr = self.get_actual_peer_addr()
+
+ def set_new_peer_addr(self, peer_addr=None):
+ """Sets the peer address"""
+ if peer_addr is not None:
+ self.new_peer_addr = peer_addr
+ else:
+ self.new_peer_addr = self.get_actual_peer_addr()
+
+ def add_peer(self, obj, old_peer_addr=None, new_peer_addr=None):
+ """Add peer for FST session(s). 'obj' is a FstDevice subclass object.
+ The method must be called before add_session().
+ If peer_addr is not specified, the address of the currently connected
+ station is used."""
+ if not isinstance(obj, FstDevice):
+ raise Exception("Peer must be a FstDevice object")
+ self.peer_obj = obj
+ self.set_old_peer_addr(old_peer_addr)
+ self.set_new_peer_addr(new_peer_addr)
+
+ def get_peer(self):
+ """Returns peer object"""
+ return self.peer_obj
+
+ def set_fst_parameters(self, group_id=None, pri=None, llt=None):
+ """Change/set new FST parameters. Can be used to start FST sessions with
+ different FST parameters than defined in the configuration file."""
+ if group_id is not None:
+ self.fst_group = group_id
+ if pri is not None:
+ self.fst_pri = pri
+ if llt is not None:
+ self.fst_llt = llt
+
+ def get_local_mbies(self, ifname=None):
+ if_name = ifname if ifname is not None else self.iface
+ return self.grequest("FST-MANAGER TEST_REQUEST GET_LOCAL_MBIES " + if_name)
+
+ def add_session(self):
+ """Adds an FST session. add_peer() must be called calling this
+ function"""
+ if self.peer_obj is None:
+ raise Exception("Peer wasn't added before starting session")
+ self.dump_monitor()
+ grp = ' ' + self.fst_group if self.fst_group != '' else ''
+ sid = self.grequest("FST-MANAGER SESSION_ADD" + grp)
+ sid = sid.strip()
+ if sid.startswith("FAIL"):
+ raise Exception("Cannot add FST session with groupid ==" + grp)
+ self.dump_monitor()
+ return sid
+
+ def set_session_param(self, params):
+ request = "FST-MANAGER SESSION_SET"
+ if params is not None and params != '':
+ request = request + ' ' + params
+ return self.grequest(request)
+
+ def get_session_params(self, sid):
+ request = "FST-MANAGER SESSION_GET " + sid
+ res = self.grequest(request)
+ if res.startswith("FAIL"):
+ return None
+ params = {}
+ for i in res.splitlines():
+ p = i.split('=')
+ params[p[0]] = p[1]
+ return params
+
+ def iface_peers(self, ifname):
+ grp = self.fst_group if self.fst_group != '' else ''
+ res = self.grequest("FST-MANAGER IFACE_PEERS " + grp + ' ' + ifname)
+ if res.startswith("FAIL"):
+ return None
+ return res.splitlines()
+
+ def get_peer_mbies(self, ifname, peer_addr):
+ return self.grequest("FST-MANAGER GET_PEER_MBIES %s %s" % (ifname, peer_addr))
+
+ def list_ifaces(self):
+ grp = self.fst_group if self.fst_group != '' else ''
+ res = self.grequest("FST-MANAGER LIST_IFACES " + grp)
+ if res.startswith("FAIL"):
+ return None
+ ifaces = []
+ for i in res.splitlines():
+ p = i.split(':')
+ iface = {}
+ iface['name'] = p[0]
+ iface['priority'] = p[1]
+ iface['llt'] = p[2]
+ ifaces.append(iface)
+ return ifaces
+
+ def list_groups(self):
+ res = self.grequest("FST-MANAGER LIST_GROUPS")
+ if res.startswith("FAIL"):
+ return None
+ return res.splitlines()
+
+ def configure_session(self, sid, new_iface, old_iface=None):
+ """Calls session_set for a number of parameters some of which are stored
+ in "self" while others are passed to this function explicitly. If
+ old_iface is None, current iface is used; if old_iface is an empty
+ string."""
+ self.dump_monitor()
+ oldiface = old_iface if old_iface is not None else self.iface
+ s = self.set_session_param(sid + ' old_ifname=' + oldiface)
+ if not s.startswith("OK"):
+ raise Exception("Cannot set FST session old_ifname: " + s)
+ if new_iface is not None:
+ s = self.set_session_param(sid + " new_ifname=" + new_iface)
+ if not s.startswith("OK"):
+ raise Exception("Cannot set FST session new_ifname:" + s)
+ if self.new_peer_addr is not None and self.new_peer_addr != '':
+ s = self.set_session_param(sid + " new_peer_addr=" + self.new_peer_addr)
+ if not s.startswith("OK"):
+ raise Exception("Cannot set FST session peer address:" + s + " (new)")
+ if self.old_peer_addr is not None and self.old_peer_addr != '':
+ s = self.set_session_param(sid + " old_peer_addr=" + self.old_peer_addr)
+ if not s.startswith("OK"):
+ raise Exception("Cannot set FST session peer address:" + s + " (old)")
+ if self.fst_llt is not None and self.fst_llt != '':
+ s = self.set_session_param(sid + " llt=" + self.fst_llt)
+ if not s.startswith("OK"):
+ raise Exception("Cannot set FST session llt:" + s)
+ self.dump_monitor()
+
+ def send_iface_attach_request(self, ifname, group, llt, priority):
+ request = "FST-ATTACH " + ifname + ' ' + group
+ if llt is not None:
+ request += " llt=" + llt
+ if priority is not None:
+ request += " priority=" + priority
+ res = self.grequest(request)
+ if not res.startswith("OK"):
+ raise Exception("Cannot attach FST iface: " + res)
+
+ def send_iface_detach_request(self, ifname):
+ res = self.grequest("FST-DETACH " + ifname)
+ if not res.startswith("OK"):
+ raise Exception("Cannot detach FST iface: " + res)
+
+ def send_session_setup_request(self, sid):
+ s = self.grequest("FST-MANAGER SESSION_INITIATE " + sid)
+ if not s.startswith('OK'):
+ raise Exception("Cannot send setup request: %s" % s)
+ return s
+
+ def send_session_setup_response(self, sid, response):
+ request = "FST-MANAGER SESSION_RESPOND " + sid + " " + response
+ s = self.grequest(request)
+ if not s.startswith('OK'):
+ raise Exception("Cannot send setup response: %s" % s)
+ return s
+
+ def send_test_session_setup_request(self, fsts_id,
+ additional_parameter=None):
+ request = "FST-MANAGER TEST_REQUEST SEND_SETUP_REQUEST " + fsts_id
+ if additional_parameter is not None:
+ request += " " + additional_parameter
+ s = self.grequest(request)
+ if not s.startswith('OK'):
+ raise Exception("Cannot send FST setup request: %s" % s)
+ return s
+
+ def send_test_session_setup_response(self, fsts_id,
+ response, additional_parameter=None):
+ request = "FST-MANAGER TEST_REQUEST SEND_SETUP_RESPONSE " + fsts_id + " " + response
+ if additional_parameter is not None:
+ request += " " + additional_parameter
+ s = self.grequest(request)
+ if not s.startswith('OK'):
+ raise Exception("Cannot send FST setup response: %s" % s)
+ return s
+
+ def send_test_ack_request(self, fsts_id):
+ s = self.grequest("FST-MANAGER TEST_REQUEST SEND_ACK_REQUEST " + fsts_id)
+ if not s.startswith('OK'):
+ raise Exception("Cannot send FST ack request: %s" % s)
+ return s
+
+ def send_test_ack_response(self, fsts_id):
+ s = self.grequest("FST-MANAGER TEST_REQUEST SEND_ACK_RESPONSE " + fsts_id)
+ if not s.startswith('OK'):
+ raise Exception("Cannot send FST ack response: %s" % s)
+ return s
+
+ def send_test_tear_down(self, fsts_id):
+ s = self.grequest("FST-MANAGER TEST_REQUEST SEND_TEAR_DOWN " + fsts_id)
+ if not s.startswith('OK'):
+ raise Exception("Cannot send FST tear down: %s" % s)
+ return s
+
+ def get_fsts_id_by_sid(self, sid):
+ s = self.grequest("FST-MANAGER TEST_REQUEST GET_FSTS_ID " + sid)
+ if s == ' ' or s.startswith('FAIL'):
+ raise Exception("Cannot get fsts_id for sid == %s" % sid)
+ return int(s)
+
+ def wait_for_iface_event(self, timeout):
+ while True:
+ ev = self.wait_gevent(["FST-EVENT-IFACE"], timeout)
+ if ev is None:
+ raise Exception("No FST-EVENT-IFACE received")
+ event = parse_fst_iface_event(ev)
+ if event is None:
+ # We can't parse so it's not our event, wait for next one
+ continue
+ return event
+
+ def wait_for_session_event(self, timeout, events_to_ignore=[],
+ events_to_count=[]):
+ while True:
+ ev = self.wait_gevent(["FST-EVENT-SESSION"], timeout)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION received")
+ event = parse_fst_session_event(ev)
+ if event is None:
+ # We can't parse so it's not our event, wait for next one
+ continue
+ if len(events_to_ignore) > 0:
+ if event['type'] in events_to_ignore:
+ continue
+ elif len(events_to_count) > 0:
+ if event['type'] not in events_to_count:
+ continue
+ return event
+
+ def initiate_session(self, sid, response="accept"):
+ """Initiates FST session with given session id 'sid'.
+ 'response' is the session respond answer: "accept", "reject", or a
+ special "timeout" value to skip the response in order to test session
+ timeouts.
+ Returns: "OK" - session has been initiated, otherwise the reason for the
+ reset: REASON_REJECT, REASON_STT."""
+ strsid = ' ' + sid if sid != '' else ''
+ s = self.grequest("FST-MANAGER SESSION_INITIATE"+ strsid)
+ if not s.startswith('OK'):
+ raise Exception("Cannot initiate fst session: %s" % s)
+ ev = self.peer_obj.wait_gevent(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION received")
+ # We got FST event
+ event = parse_fst_session_event(ev)
+ if event == None:
+ raise Exception("Unrecognized FST event: " % ev)
+ if event['type'] != 'EVENT_FST_SETUP':
+ raise Exception("Expected FST_SETUP event, got: " + event['type'])
+ ev = self.peer_obj.wait_gevent(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION received")
+ event = parse_fst_session_event(ev)
+ if event == None:
+ raise Exception("Unrecognized FST event: " % ev)
+ if event['type'] != 'EVENT_FST_SESSION_STATE':
+ raise Exception("Expected EVENT_FST_SESSION_STATE event, got: " + event['type'])
+ if event['new_state'] != "SETUP_COMPLETION":
+ raise Exception("Expected new state SETUP_COMPLETION, got: " + event['new_state'])
+ if response == '':
+ return 'OK'
+ if response != "timeout":
+ s = self.peer_obj.grequest("FST-MANAGER SESSION_RESPOND "+ event['id'] + " " + response) # Or reject
+ if not s.startswith('OK'):
+ raise Exception("Error session_respond: %s" % s)
+ # Wait for EVENT_FST_SESSION_STATE events. We should get at least 2
+ # events. The 1st event will be EVENT_FST_SESSION_STATE
+ # old_state=INITIAL new_state=SETUP_COMPLETED. The 2nd event will be
+ # either EVENT_FST_ESTABLISHED with the session id or
+ # EVENT_FST_SESSION_STATE with new_state=INITIAL if the session was
+ # reset, the reason field will tell why.
+ result = ''
+ while result == '':
+ ev = self.wait_gevent(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ break # No session event received
+ event = parse_fst_session_event(ev)
+ if event == None:
+ # We can't parse so it's not our event, wait for next one
+ continue
+ if event['type'] == 'EVENT_FST_ESTABLISHED':
+ result = "OK"
+ break
+ elif event['type'] == "EVENT_FST_SESSION_STATE":
+ if event['new_state'] == "INITIAL":
+ # Session was reset, the only reason to get back to initial
+ # state.
+ result = event['reason']
+ break
+ if result == '':
+ raise Exception("No event for session respond")
+ return result
+
+ def transfer_session(self, sid):
+ """Transfers the session. 'sid' is the session id. 'hsta' is the
+ station-responder object.
+ Returns: REASON_SWITCH - the session has been transferred successfully
+ or a REASON_... reported by the reset event."""
+ request = "FST-MANAGER SESSION_TRANSFER"
+ self.dump_monitor()
+ if sid != '':
+ request += ' ' + sid
+ s = self.grequest(request)
+ if not s.startswith('OK'):
+ raise Exception("Cannot transfer fst session: %s" % s)
+ result = ''
+ while result == '':
+ ev = self.peer_obj.wait_gevent(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("Missing session transfer event")
+ # We got FST event. We expect TRANSITION_CONFIRMED state and then
+ # INITIAL (reset) with the reason (e.g. "REASON_SWITCH").
+ # Right now we'll be waiting for the reset event and record the
+ # reason.
+ event = parse_fst_session_event(ev)
+ if event == None:
+ raise Exception("Unrecognized FST event: " % ev)
+ if event['new_state'] == 'INITIAL':
+ result = event['reason']
+ self.dump_monitor()
+ return result
+
+ def wait_for_tear_down(self):
+ ev = self.wait_gevent(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION received")
+ # We got FST event
+ event = parse_fst_session_event(ev)
+ if event == None:
+ raise Exception("Unrecognized FST event: " % ev)
+ if event['type'] != 'EVENT_FST_SESSION_STATE':
+ raise Exception("Expected EVENT_FST_SESSION_STATE event, got: " + event['type'])
+ if event['new_state'] != "INITIAL":
+ raise Exception("Expected new state INITIAL, got: " + event['new_state'])
+ if event['reason'] != 'REASON_TEARDOWN':
+ raise Exception("Expected reason REASON_TEARDOWN, got: " + event['reason'])
+
+ def teardown_session(self, sid):
+ """Tears down FST session with a given session id ('sid')"""
+ strsid = ' ' + sid if sid != '' else ''
+ s = self.grequest("FST-MANAGER SESSION_TEARDOWN" + strsid)
+ if not s.startswith('OK'):
+ raise Exception("Cannot tear down fst session: %s" % s)
+ self.peer_obj.wait_for_tear_down()
+
+
+ def remove_session(self, sid, wait_for_tear_down=True):
+ """Removes FST session with a given session id ('sid')"""
+ strsid = ' ' + sid if sid != '' else ''
+ s = self.grequest("FST-MANAGER SESSION_REMOVE" + strsid)
+ if not s.startswith('OK'):
+ raise Exception("Cannot remove fst session: %s" % s)
+ if wait_for_tear_down == True:
+ self.peer_obj.wait_for_tear_down()
+
+ def remove_all_sessions(self):
+ """Removes FST session with a given session id ('sid')"""
+ grp = ' ' + self.fst_group if self.fst_group != '' else ''
+ s = self.grequest("FST-MANAGER LIST_SESSIONS" + grp)
+ if not s.startswith('FAIL'):
+ for sid in s.splitlines():
+ sid = sid.strip()
+ if len(sid) != 0:
+ self.remove_session(sid, wait_for_tear_down=False)
+
+
+#
+# FstAP class
+#
+class FstAP(FstDevice):
+ def __init__(self, iface, ssid, mode, chan, fst_group, fst_pri,
+ fst_llt=None, rsn=False):
+ """If fst_group is empty, then FST parameters will not be set
+ If fst_llt is empty, the parameter will not be set and the default value
+ is expected to be configured."""
+ self.ssid = ssid
+ self.mode = mode
+ self.chan = chan
+ self.reg_ctrl = fst_test_common.HapdRegCtrl()
+ self.reg_ctrl.add_ap(iface, self.chan)
+ self.global_instance = hostapd.HostapdGlobal()
+ FstDevice.__init__(self, iface, fst_group, fst_pri, fst_llt, rsn)
+
+ def start(self, return_early=False):
+ """Starts AP the "standard" way as it was intended by hostapd tests.
+ This will work only when FST supports fully dynamically loading
+ parameters in hostapd."""
+ params = {}
+ params['ssid'] = self.ssid
+ params['hw_mode'] = self.mode
+ params['channel'] = self.chan
+ params['country_code'] = 'US'
+ if self.rsn:
+ params['wpa'] = '2'
+ params['wpa_key_mgmt'] = 'WPA-PSK'
+ params['rsn_pairwise'] = 'CCMP'
+ params['wpa_passphrase'] = '12345678'
+ self.hapd = hostapd.add_ap(self.iface, params)
+ if not self.hapd.ping():
+ raise Exception("Could not ping FST hostapd")
+ self.reg_ctrl.start()
+ self.get_global_instance()
+ if return_early:
+ return self.hapd
+ if len(self.fst_group) != 0:
+ self.send_iface_attach_request(self.iface, self.fst_group,
+ self.fst_llt, self.fst_pri)
+ return self.hapd
+
+ def stop(self):
+ """Removes the AP, To be used when dynamic fst APs are implemented in
+ hostapd."""
+ if len(self.fst_group) != 0:
+ self.remove_all_sessions()
+ try:
+ self.send_iface_detach_request(self.iface)
+ except Exception as e:
+ logger.info(str(e))
+ self.reg_ctrl.stop()
+ del self.global_instance
+ self.global_instance = None
+
+ def get_instance(self):
+ """Return the Hostapd/WpaSupplicant instance"""
+ if self.instance is None:
+ self.instance = hostapd.Hostapd(self.iface)
+ return self.instance
+
+ def get_global_instance(self):
+ return self.global_instance
+
+ def get_own_mac_address(self):
+ """Gets the device's own MAC address"""
+ h = self.get_instance()
+ status = h.get_status()
+ return status['bssid[0]']
+
+ def get_actual_peer_addr(self):
+ """Gets the peer address. A connected station address is returned."""
+ # Use the device instance, the global control interface doesn't have
+ # station address
+ h = self.get_instance()
+ sta = h.get_sta(None)
+ if sta is None or 'addr' not in sta:
+ # Maybe station is not connected?
+ addr = None
+ else:
+ addr = sta['addr']
+ return addr
+
+ def grequest(self, req):
+ """Send request on the global control interface"""
+ logger.debug("FstAP::grequest: " + req)
+ h = self.get_global_instance()
+ return h.request(req)
+
+ def wait_gevent(self, events, timeout=None):
+ """Wait for a list of events on the global interface"""
+ h = self.get_global_instance()
+ if timeout is not None:
+ return h.wait_event(events, timeout=timeout)
+ else:
+ return h.wait_event(events)
+
+ def get_ssid(self):
+ return self.ssid
+
+ def dump_monitor(self):
+ """Dump control interface monitor events"""
+ if self.instance:
+ self.instance.dump_monitor()
+
+#
+# FstSTA class
+#
+class FstSTA(FstDevice):
+ def __init__(self, iface, fst_group, fst_pri, fst_llt=None, rsn=False):
+ """If fst_group is empty, then FST parameters will not be set
+ If fst_llt is empty, the parameter will not be set and the default value
+ is expected to be configured."""
+ FstDevice.__init__(self, iface, fst_group, fst_pri, fst_llt, rsn)
+ self.connected = None # FstAP object the station is connected to
+
+ def start(self):
+ """Current implementation involves running another instance of
+ wpa_supplicant with fixed FST STAs configurations. When any type of
+ dynamic STA loading is implemented, rewrite the function similarly to
+ FstAP."""
+ h = self.get_instance()
+ h.interface_add(self.iface, drv_params="force_connect_cmd=1")
+ if not h.global_ping():
+ raise Exception("Could not ping FST wpa_supplicant")
+ if len(self.fst_group) != 0:
+ self.send_iface_attach_request(self.iface, self.fst_group,
+ self.fst_llt, self.fst_pri)
+ return None
+
+ def stop(self):
+ """Removes the STA. In a static (temporary) implementation does nothing,
+ the STA will be removed when the fst wpa_supplicant process is killed by
+ fstap.cleanup()."""
+ h = self.get_instance()
+ h.dump_monitor()
+ if len(self.fst_group) != 0:
+ self.remove_all_sessions()
+ self.send_iface_detach_request(self.iface)
+ h.dump_monitor()
+ h.interface_remove(self.iface)
+ h.close_ctrl()
+ del h
+ self.instance = None
+
+ def get_instance(self):
+ """Return the Hostapd/WpaSupplicant instance"""
+ if self.instance is None:
+ self.instance = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ return self.instance
+
+ def get_own_mac_address(self):
+ """Gets the device's own MAC address"""
+ h = self.get_instance()
+ status = h.get_status()
+ return status['address']
+
+ def get_actual_peer_addr(self):
+ """Gets the peer address. A connected station address is returned"""
+ h = self.get_instance()
+ status = h.get_status()
+ return status['bssid']
+
+ def grequest(self, req):
+ """Send request on the global control interface"""
+ logger.debug("FstSTA::grequest: " + req)
+ h = self.get_instance()
+ return h.global_request(req)
+
+ def wait_gevent(self, events, timeout=None):
+ """Wait for a list of events on the global interface"""
+ h = self.get_instance()
+ if timeout is not None:
+ return h.wait_global_event(events, timeout=timeout)
+ else:
+ return h.wait_global_event(events)
+
+ def scan(self, freq=None, no_wait=False, only_new=False):
+ """Issue Scan with given parameters. Returns the BSS dictionary for the
+ AP found (the 1st BSS found. TODO: What if the AP required is not the
+ 1st in list?) or None if no BSS found. None call be also a result of
+ no_wait=True. Note, request("SCAN_RESULTS") can be used to get all the
+ results at once."""
+ h = self.get_instance()
+ h.dump_monitor()
+ h.scan(None, freq, no_wait, only_new)
+ r = h.get_bss('0')
+ h.dump_monitor()
+ return r
+
+ def connect(self, ap, **kwargs):
+ """Connects to the given AP"""
+ if not isinstance(ap, FstAP):
+ raise Exception("Bad AP object to connect to")
+ h = self.get_instance()
+ hap = ap.get_instance()
+ h.dump_monitor()
+ h.connect(ap.get_ssid(), **kwargs)
+ h.dump_monitor()
+ self.connected = ap
+
+ def connect_to_external_ap(self, ap, ssid, check_connection=True, **kwargs):
+ """Connects to the given external AP"""
+ if not isinstance(ap, hostapd.Hostapd):
+ raise Exception("Bad AP object to connect to")
+ h = self.get_instance()
+ h.dump_monitor()
+ h.connect(ssid, **kwargs)
+ self.connected = ap
+ if check_connection:
+ ev = ap.wait_event(["AP-STA-CONNECTED"], timeout=10)
+ if ev is None:
+ self.connected = None
+ raise Exception("No connection event received from %s" % ssid)
+ h.dump_monitor()
+
+ def disconnect(self, check_disconnect=True):
+ """Disconnects from the AP the station is currently connected to"""
+ if self.connected is not None:
+ h = self.get_instance()
+ h.dump_monitor()
+ h.request("DISCONNECT")
+ if check_disconnect:
+ hap = self.connected.get_instance()
+ ev = hap.wait_event(["AP-STA-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("No disconnection event received from %s" % self.connected.get_ssid())
+ h.dump_monitor()
+ self.connected = None
+
+
+ def disconnect_from_external_ap(self, check_disconnect=True):
+ """Disconnects from the external AP the station is currently connected
+ to"""
+ if self.connected is not None:
+ h = self.get_instance()
+ h.dump_monitor()
+ h.request("DISCONNECT")
+ if check_disconnect:
+ hap = self.connected
+ ev = hap.wait_event(["AP-STA-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("No disconnection event received from AP")
+ h.dump_monitor()
+ self.connected = None
+
+ def dump_monitor(self):
+ """Dump control interface monitor events"""
+ if self.instance:
+ self.instance.dump_monitor()
diff --git a/contrib/wpa/tests/hwsim/fst_test_common.py b/contrib/wpa/tests/hwsim/fst_test_common.py
new file mode 100644
index 000000000000..4b6bab07d780
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/fst_test_common.py
@@ -0,0 +1,97 @@
+# FST tests related definitions
+# Copyright (c) 2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import subprocess
+import time
+import logging
+
+import hostapd
+
+logger = logging.getLogger()
+
+fst_test_def_group = 'fstg0'
+fst_test_def_freq_g = '2412' # Channel 1
+fst_test_def_freq_a = '5180' # Channel 36
+fst_test_def_chan_g = '1'
+fst_test_def_chan_a = '36'
+fst_test_def_prio_low = '100'
+fst_test_def_prio_high = '110'
+fst_test_def_llt = '100'
+fst_test_def_reg_domain = '00'
+
+class HapdRegCtrl:
+ def __init__(self):
+ self.refcnt = 0
+ self.ifname = None
+ self.changed = False
+
+ def __del__(self):
+ if self.refcnt != 0 and self.changed == True:
+ self.restore_reg_domain()
+
+ def start(self):
+ if self.ifname != None:
+ hapd = hostapd.Hostapd(self.ifname)
+ self.changed = self.wait_hapd_reg_change(hapd)
+
+ def stop(self):
+ if self.changed == True:
+ self.restore_reg_domain()
+ self.changed = False
+
+ def add_ap(self, ifname, chan):
+ if self.changed == False and self.channel_may_require_reg_change(chan):
+ self.ifname = ifname
+
+ @staticmethod
+ def channel_may_require_reg_change(chan):
+ if int(chan) > 14:
+ return True
+ return False
+
+ @staticmethod
+ def wait_hapd_reg_change(hapd):
+ state = hapd.get_status_field("state")
+ if state != "COUNTRY_UPDATE":
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected COUNTRY_UPDATE")
+ else:
+ logger.debug("fst hostapd: regulatory domain already set")
+ return True
+
+ logger.debug("fst hostapd: waiting for regulatory domain to be set...")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ logger.debug("fst hostapd: regulatory domain set")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ logger.debug("fst hostapd: regulatory domain ready")
+ return True
+
+ @staticmethod
+ def restore_reg_domain():
+ logger.debug("fst hostapd: waiting for regulatory domain to be restored...")
+
+ res = subprocess.call(['iw', 'reg', 'set', fst_test_def_reg_domain])
+ if res != 0:
+ raise Exception("Cannot restore regulatory domain")
+
+ logger.debug("fst hostapd: regulatory domain ready")
+
+def fst_clear_regdom():
+ cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ if "country 00:" not in res:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ time.sleep(0.1)
diff --git a/contrib/wpa/tests/hwsim/hostapd.py b/contrib/wpa/tests/hwsim/hostapd.py
new file mode 100644
index 000000000000..5ea68444c1b9
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/hostapd.py
@@ -0,0 +1,882 @@
+# Python class for controlling hostapd
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import re
+import time
+import logging
+import binascii
+import struct
+import tempfile
+import wpaspy
+import remotehost
+import utils
+import subprocess
+
+logger = logging.getLogger()
+hapd_ctrl = '/var/run/hostapd'
+hapd_global = '/var/run/hostapd-global'
+
+def mac2tuple(mac):
+ return struct.unpack('6B', binascii.unhexlify(mac.replace(':', '')))
+
+class HostapdGlobal:
+ def __init__(self, apdev=None, global_ctrl_override=None):
+ try:
+ hostname = apdev['hostname']
+ port = apdev['port']
+ except:
+ hostname = None
+ port = 8878
+ self.host = remotehost.Host(hostname)
+ self.hostname = hostname
+ self.port = port
+ if hostname is None:
+ global_ctrl = hapd_global
+ if global_ctrl_override:
+ global_ctrl = global_ctrl_override
+ self.ctrl = wpaspy.Ctrl(global_ctrl)
+ self.mon = wpaspy.Ctrl(global_ctrl)
+ self.dbg = ""
+ else:
+ self.ctrl = wpaspy.Ctrl(hostname, port)
+ self.mon = wpaspy.Ctrl(hostname, port)
+ self.dbg = hostname + "/" + str(port)
+ self.mon.attach()
+
+ def cmd_execute(self, cmd_array, shell=False):
+ if self.hostname is None:
+ if shell:
+ cmd = ' '.join(cmd_array)
+ else:
+ cmd = cmd_array
+ proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE, shell=shell)
+ out = proc.communicate()[0]
+ ret = proc.returncode
+ return ret, out.decode()
+ else:
+ return self.host.execute(cmd_array)
+
+ def request(self, cmd, timeout=10):
+ logger.debug(self.dbg + ": CTRL(global): " + cmd)
+ return self.ctrl.request(cmd, timeout)
+
+ def wait_event(self, events, timeout):
+ start = os.times()[4]
+ while True:
+ while self.mon.pending():
+ ev = self.mon.recv()
+ logger.debug(self.dbg + "(global): " + ev)
+ for event in events:
+ if event in ev:
+ return ev
+ now = os.times()[4]
+ remaining = start + timeout - now
+ if remaining <= 0:
+ break
+ if not self.mon.pending(timeout=remaining):
+ break
+ return None
+
+ def add(self, ifname, driver=None):
+ cmd = "ADD " + ifname + " " + hapd_ctrl
+ if driver:
+ cmd += " " + driver
+ res = self.request(cmd)
+ if "OK" not in res:
+ raise Exception("Could not add hostapd interface " + ifname)
+
+ def add_iface(self, ifname, confname):
+ res = self.request("ADD " + ifname + " config=" + confname)
+ if "OK" not in res:
+ raise Exception("Could not add hostapd interface")
+
+ def add_bss(self, phy, confname, ignore_error=False):
+ res = self.request("ADD bss_config=" + phy + ":" + confname)
+ if "OK" not in res:
+ if not ignore_error:
+ raise Exception("Could not add hostapd BSS")
+
+ def remove(self, ifname):
+ self.request("REMOVE " + ifname, timeout=30)
+
+ def relog(self):
+ self.request("RELOG")
+
+ def flush(self):
+ self.request("FLUSH")
+
+ def get_ctrl_iface_port(self, ifname):
+ if self.hostname is None:
+ return None
+
+ res = self.request("INTERFACES ctrl")
+ lines = res.splitlines()
+ found = False
+ for line in lines:
+ words = line.split()
+ if words[0] == ifname:
+ found = True
+ break
+ if not found:
+ raise Exception("Could not find UDP port for " + ifname)
+ res = line.find("ctrl_iface=udp:")
+ if res == -1:
+ raise Exception("Wrong ctrl_interface format")
+ words = line.split(":")
+ return int(words[1])
+
+ def terminate(self):
+ self.mon.detach()
+ self.mon.close()
+ self.mon = None
+ self.ctrl.terminate()
+ self.ctrl = None
+
+ def send_file(self, src, dst):
+ self.host.send_file(src, dst)
+
+class Hostapd:
+ def __init__(self, ifname, bssidx=0, hostname=None, port=8877):
+ self.hostname = hostname
+ self.host = remotehost.Host(hostname, ifname)
+ self.ifname = ifname
+ if hostname is None:
+ self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
+ self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
+ self.dbg = ifname
+ else:
+ self.ctrl = wpaspy.Ctrl(hostname, port)
+ self.mon = wpaspy.Ctrl(hostname, port)
+ self.dbg = hostname + "/" + ifname
+ self.mon.attach()
+ self.bssid = None
+ self.bssidx = bssidx
+
+ def cmd_execute(self, cmd_array, shell=False):
+ if self.hostname is None:
+ if shell:
+ cmd = ' '.join(cmd_array)
+ else:
+ cmd = cmd_array
+ proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE, shell=shell)
+ out = proc.communicate()[0]
+ ret = proc.returncode
+ return ret, out.decode()
+ else:
+ return self.host.execute(cmd_array)
+
+ def close_ctrl(self):
+ if self.mon is not None:
+ self.mon.detach()
+ self.mon.close()
+ self.mon = None
+ self.ctrl.close()
+ self.ctrl = None
+
+ def own_addr(self):
+ if self.bssid is None:
+ self.bssid = self.get_status_field('bssid[%d]' % self.bssidx)
+ return self.bssid
+
+ def get_addr(self, group=False):
+ return self.own_addr()
+
+ def request(self, cmd):
+ logger.debug(self.dbg + ": CTRL: " + cmd)
+ return self.ctrl.request(cmd)
+
+ def ping(self):
+ return "PONG" in self.request("PING")
+
+ def set(self, field, value):
+ if "OK" not in self.request("SET " + field + " " + value):
+ if "TKIP" in value and (field == "wpa_pairwise" or \
+ field == "rsn_pairwise"):
+ raise utils.HwsimSkip("Cipher TKIP not supported")
+ raise Exception("Failed to set hostapd parameter " + field)
+
+ def set_defaults(self):
+ self.set("driver", "nl80211")
+ self.set("hw_mode", "g")
+ self.set("channel", "1")
+ self.set("ieee80211n", "1")
+ self.set("logger_stdout", "-1")
+ self.set("logger_stdout_level", "0")
+
+ def set_open(self, ssid):
+ self.set_defaults()
+ self.set("ssid", ssid)
+
+ def set_wpa2_psk(self, ssid, passphrase):
+ self.set_defaults()
+ self.set("ssid", ssid)
+ self.set("wpa_passphrase", passphrase)
+ self.set("wpa", "2")
+ self.set("wpa_key_mgmt", "WPA-PSK")
+ self.set("rsn_pairwise", "CCMP")
+
+ def set_wpa_psk(self, ssid, passphrase):
+ self.set_defaults()
+ self.set("ssid", ssid)
+ self.set("wpa_passphrase", passphrase)
+ self.set("wpa", "1")
+ self.set("wpa_key_mgmt", "WPA-PSK")
+ self.set("wpa_pairwise", "TKIP")
+
+ def set_wpa_psk_mixed(self, ssid, passphrase):
+ self.set_defaults()
+ self.set("ssid", ssid)
+ self.set("wpa_passphrase", passphrase)
+ self.set("wpa", "3")
+ self.set("wpa_key_mgmt", "WPA-PSK")
+ self.set("wpa_pairwise", "TKIP")
+ self.set("rsn_pairwise", "CCMP")
+
+ def set_wep(self, ssid, key):
+ self.set_defaults()
+ self.set("ssid", ssid)
+ self.set("wep_key0", key)
+
+ def enable(self):
+ if "OK" not in self.request("ENABLE"):
+ raise Exception("Failed to enable hostapd interface " + self.ifname)
+
+ def disable(self):
+ if "OK" not in self.request("DISABLE"):
+ raise Exception("Failed to disable hostapd interface " + self.ifname)
+
+ def dump_monitor(self):
+ while self.mon.pending():
+ ev = self.mon.recv()
+ logger.debug(self.dbg + ": " + ev)
+
+ def wait_event(self, events, timeout):
+ if not isinstance(events, list):
+ raise Exception("Hostapd.wait_event() called with incorrect events argument type")
+ start = os.times()[4]
+ while True:
+ while self.mon.pending():
+ ev = self.mon.recv()
+ logger.debug(self.dbg + ": " + ev)
+ for event in events:
+ if event in ev:
+ return ev
+ now = os.times()[4]
+ remaining = start + timeout - now
+ if remaining <= 0:
+ break
+ if not self.mon.pending(timeout=remaining):
+ break
+ return None
+
+ def wait_sta(self, addr=None, timeout=2):
+ ev = self.wait_event(["AP-STA-CONNECT"], timeout=timeout)
+ if ev is None:
+ raise Exception("AP did not report STA connection")
+ if addr and addr not in ev:
+ raise Exception("Unexpected STA address in connection event: " + ev)
+
+ def wait_ptkinitdone(self, addr, timeout=2):
+ while timeout > 0:
+ sta = self.get_sta(addr)
+ if 'hostapdWPAPTKState' not in sta:
+ raise Exception("GET_STA did not return hostapdWPAPTKState")
+ state = sta['hostapdWPAPTKState']
+ if state == "11":
+ return
+ time.sleep(0.1)
+ timeout -= 0.1
+ raise Exception("Timeout while waiting for PTKINITDONE")
+
+ def get_status(self):
+ res = self.request("STATUS")
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ return vals
+
+ def get_status_field(self, field):
+ vals = self.get_status()
+ if field in vals:
+ return vals[field]
+ return None
+
+ def get_driver_status(self):
+ res = self.request("STATUS-DRIVER")
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ return vals
+
+ def get_driver_status_field(self, field):
+ vals = self.get_driver_status()
+ if field in vals:
+ return vals[field]
+ return None
+
+ def get_config(self):
+ res = self.request("GET_CONFIG")
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ return vals
+
+ def mgmt_rx(self, timeout=5):
+ ev = self.wait_event(["MGMT-RX"], timeout=timeout)
+ if ev is None:
+ return None
+ msg = {}
+ frame = binascii.unhexlify(ev.split(' ')[1])
+ msg['frame'] = frame
+
+ hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
+ msg['fc'] = hdr[0]
+ msg['subtype'] = (hdr[0] >> 4) & 0xf
+ hdr = hdr[1:]
+ msg['duration'] = hdr[0]
+ hdr = hdr[1:]
+ msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+ hdr = hdr[6:]
+ msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+ hdr = hdr[6:]
+ msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+ hdr = hdr[6:]
+ msg['seq_ctrl'] = hdr[0]
+ msg['payload'] = frame[24:]
+
+ return msg
+
+ def mgmt_tx(self, msg):
+ t = (msg['fc'], 0) + mac2tuple(msg['da']) + mac2tuple(msg['sa']) + mac2tuple(msg['bssid']) + (0,)
+ hdr = struct.pack('<HH6B6B6BH', *t)
+ res = self.request("MGMT_TX " + binascii.hexlify(hdr + msg['payload']).decode())
+ if "OK" not in res:
+ raise Exception("MGMT_TX command to hostapd failed")
+
+ def get_sta(self, addr, info=None, next=False):
+ cmd = "STA-NEXT " if next else "STA "
+ if addr is None:
+ res = self.request("STA-FIRST")
+ elif info:
+ res = self.request(cmd + addr + " " + info)
+ else:
+ res = self.request(cmd + addr)
+ lines = res.splitlines()
+ vals = dict()
+ first = True
+ for l in lines:
+ if first and '=' not in l:
+ vals['addr'] = l
+ first = False
+ else:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ return vals
+
+ def get_mib(self, param=None):
+ if param:
+ res = self.request("MIB " + param)
+ else:
+ res = self.request("MIB")
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ name_val = l.split('=', 1)
+ if len(name_val) > 1:
+ vals[name_val[0]] = name_val[1]
+ return vals
+
+ def get_pmksa(self, addr):
+ res = self.request("PMKSA")
+ lines = res.splitlines()
+ for l in lines:
+ if addr not in l:
+ continue
+ vals = dict()
+ [index, aa, pmkid, expiration, opportunistic] = l.split(' ')
+ vals['index'] = index
+ vals['pmkid'] = pmkid
+ vals['expiration'] = expiration
+ vals['opportunistic'] = opportunistic
+ return vals
+ return None
+
+ def dpp_qr_code(self, uri):
+ res = self.request("DPP_QR_CODE " + uri)
+ if "FAIL" in res:
+ raise Exception("Failed to parse QR Code URI")
+ return int(res)
+
+ def dpp_nfc_uri(self, uri):
+ res = self.request("DPP_NFC_URI " + uri)
+ if "FAIL" in res:
+ raise Exception("Failed to parse NFC URI")
+ return int(res)
+
+ def dpp_bootstrap_gen(self, type="qrcode", chan=None, mac=None, info=None,
+ curve=None, key=None):
+ cmd = "DPP_BOOTSTRAP_GEN type=" + type
+ if chan:
+ cmd += " chan=" + chan
+ if mac:
+ if mac is True:
+ mac = self.own_addr()
+ cmd += " mac=" + mac.replace(':', '')
+ if info:
+ cmd += " info=" + info
+ if curve:
+ cmd += " curve=" + curve
+ if key:
+ cmd += " key=" + key
+ res = self.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to generate bootstrapping info")
+ return int(res)
+
+ def dpp_bootstrap_set(self, id, conf=None, configurator=None, ssid=None,
+ extra=None):
+ cmd = "DPP_BOOTSTRAP_SET %d" % id
+ if ssid:
+ cmd += " ssid=" + binascii.hexlify(ssid.encode()).decode()
+ if extra:
+ cmd += " " + extra
+ if conf:
+ cmd += " conf=" + conf
+ if configurator is not None:
+ cmd += " configurator=%d" % configurator
+ if "OK" not in self.request(cmd):
+ raise Exception("Failed to set bootstrapping parameters")
+
+ def dpp_listen(self, freq, netrole=None, qr=None, role=None):
+ cmd = "DPP_LISTEN " + str(freq)
+ if netrole:
+ cmd += " netrole=" + netrole
+ if qr:
+ cmd += " qr=" + qr
+ if role:
+ cmd += " role=" + role
+ if "OK" not in self.request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ def dpp_auth_init(self, peer=None, uri=None, conf=None, configurator=None,
+ extra=None, own=None, role=None, neg_freq=None,
+ ssid=None, passphrase=None, expect_fail=False,
+ conn_status=False, nfc_uri=None):
+ cmd = "DPP_AUTH_INIT"
+ if peer is None:
+ if nfc_uri:
+ peer = self.dpp_nfc_uri(nfc_uri)
+ else:
+ peer = self.dpp_qr_code(uri)
+ cmd += " peer=%d" % peer
+ if own is not None:
+ cmd += " own=%d" % own
+ if role:
+ cmd += " role=" + role
+ if extra:
+ cmd += " " + extra
+ if conf:
+ cmd += " conf=" + conf
+ if configurator is not None:
+ cmd += " configurator=%d" % configurator
+ if neg_freq:
+ cmd += " neg_freq=%d" % neg_freq
+ if ssid:
+ cmd += " ssid=" + binascii.hexlify(ssid.encode()).decode()
+ if passphrase:
+ cmd += " pass=" + binascii.hexlify(passphrase.encode()).decode()
+ if conn_status:
+ cmd += " conn_status=1"
+ res = self.request(cmd)
+ if expect_fail:
+ if "FAIL" not in res:
+ raise Exception("DPP authentication started unexpectedly")
+ return
+ if "OK" not in res:
+ raise Exception("Failed to initiate DPP Authentication")
+
+ def dpp_pkex_init(self, identifier, code, role=None, key=None, curve=None,
+ extra=None, use_id=None):
+ if use_id is None:
+ id1 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve)
+ else:
+ id1 = use_id
+ cmd = "own=%d " % id1
+ if identifier:
+ cmd += "identifier=%s " % identifier
+ cmd += "init=1 "
+ if role:
+ cmd += "role=%s " % role
+ if extra:
+ cmd += extra + " "
+ cmd += "code=%s" % code
+ res = self.request("DPP_PKEX_ADD " + cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to set PKEX data (initiator)")
+ return id1
+
+ def dpp_pkex_resp(self, freq, identifier, code, key=None, curve=None,
+ listen_role=None):
+ id0 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve)
+ cmd = "own=%d " % id0
+ if identifier:
+ cmd += "identifier=%s " % identifier
+ cmd += "code=%s" % code
+ res = self.request("DPP_PKEX_ADD " + cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to set PKEX data (responder)")
+ self.dpp_listen(freq, role=listen_role)
+
+ def dpp_configurator_add(self, curve=None, key=None):
+ cmd = "DPP_CONFIGURATOR_ADD"
+ if curve:
+ cmd += " curve=" + curve
+ if key:
+ cmd += " key=" + key
+ res = self.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ return int(res)
+
+ def dpp_configurator_remove(self, conf_id):
+ res = self.request("DPP_CONFIGURATOR_REMOVE %d" % conf_id)
+ if "OK" not in res:
+ raise Exception("DPP_CONFIGURATOR_REMOVE failed")
+
+ def note(self, txt):
+ self.request("NOTE " + txt)
+
+ def send_file(self, src, dst):
+ self.host.send_file(src, dst)
+
+ def get_ptksa(self, bssid, cipher):
+ res = self.request("PTKSA_CACHE_LIST")
+ lines = res.splitlines()
+ for l in lines:
+ if bssid not in l or cipher not in l:
+ continue
+ vals = dict()
+ [index, addr, cipher, expiration, tk, kdk] = l.split(' ', 5)
+ vals['index'] = index
+ vals['addr'] = addr
+ vals['cipher'] = cipher
+ vals['expiration'] = expiration
+ vals['tk'] = tk
+ vals['kdk'] = kdk
+ return vals
+ return None
+
+def add_ap(apdev, params, wait_enabled=True, no_enable=False, timeout=30,
+ global_ctrl_override=None, driver=False):
+ if isinstance(apdev, dict):
+ ifname = apdev['ifname']
+ try:
+ hostname = apdev['hostname']
+ port = apdev['port']
+ logger.info("Starting AP " + hostname + "/" + port + " " + ifname)
+ except:
+ logger.info("Starting AP " + ifname)
+ hostname = None
+ port = 8878
+ else:
+ ifname = apdev
+ logger.info("Starting AP " + ifname + " (old add_ap argument type)")
+ hostname = None
+ port = 8878
+ hapd_global = HostapdGlobal(apdev,
+ global_ctrl_override=global_ctrl_override)
+ hapd_global.remove(ifname)
+ hapd_global.add(ifname, driver=driver)
+ port = hapd_global.get_ctrl_iface_port(ifname)
+ hapd = Hostapd(ifname, hostname=hostname, port=port)
+ if not hapd.ping():
+ raise Exception("Could not ping hostapd")
+ hapd.set_defaults()
+ fields = ["ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
+ "wpa", "wpa_deny_ptk0_rekey",
+ "wpa_pairwise", "rsn_pairwise", "auth_server_addr",
+ "acct_server_addr", "osu_server_uri"]
+ for field in fields:
+ if field in params:
+ hapd.set(field, params[field])
+ for f, v in list(params.items()):
+ if f in fields:
+ continue
+ if isinstance(v, list):
+ for val in v:
+ hapd.set(f, val)
+ else:
+ hapd.set(f, v)
+ if no_enable:
+ return hapd
+ hapd.enable()
+ if wait_enabled:
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=timeout)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ if "AP-ENABLED" not in ev:
+ raise Exception("AP startup failed")
+ return hapd
+
+def add_bss(apdev, ifname, confname, ignore_error=False):
+ phy = utils.get_phy(apdev)
+ try:
+ hostname = apdev['hostname']
+ port = apdev['port']
+ logger.info("Starting BSS " + hostname + "/" + port + " phy=" + phy + " ifname=" + ifname)
+ except:
+ logger.info("Starting BSS phy=" + phy + " ifname=" + ifname)
+ hostname = None
+ port = 8878
+ hapd_global = HostapdGlobal(apdev)
+ confname = cfg_file(apdev, confname, ifname)
+ hapd_global.send_file(confname, confname)
+ hapd_global.add_bss(phy, confname, ignore_error)
+ port = hapd_global.get_ctrl_iface_port(ifname)
+ hapd = Hostapd(ifname, hostname=hostname, port=port)
+ if not hapd.ping():
+ raise Exception("Could not ping hostapd")
+ return hapd
+
+def add_iface(apdev, confname):
+ ifname = apdev['ifname']
+ try:
+ hostname = apdev['hostname']
+ port = apdev['port']
+ logger.info("Starting interface " + hostname + "/" + port + " " + ifname)
+ except:
+ logger.info("Starting interface " + ifname)
+ hostname = None
+ port = 8878
+ hapd_global = HostapdGlobal(apdev)
+ confname = cfg_file(apdev, confname, ifname)
+ hapd_global.send_file(confname, confname)
+ hapd_global.add_iface(ifname, confname)
+ port = hapd_global.get_ctrl_iface_port(ifname)
+ hapd = Hostapd(ifname, hostname=hostname, port=port)
+ if not hapd.ping():
+ raise Exception("Could not ping hostapd")
+ return hapd
+
+def remove_bss(apdev, ifname=None):
+ if ifname == None:
+ ifname = apdev['ifname']
+ try:
+ hostname = apdev['hostname']
+ port = apdev['port']
+ logger.info("Removing BSS " + hostname + "/" + port + " " + ifname)
+ except:
+ logger.info("Removing BSS " + ifname)
+ hapd_global = HostapdGlobal(apdev)
+ hapd_global.remove(ifname)
+
+def terminate(apdev):
+ try:
+ hostname = apdev['hostname']
+ port = apdev['port']
+ logger.info("Terminating hostapd " + hostname + "/" + port)
+ except:
+ logger.info("Terminating hostapd")
+ hapd_global = HostapdGlobal(apdev)
+ hapd_global.terminate()
+
+def wpa2_params(ssid=None, passphrase=None, wpa_key_mgmt="WPA-PSK",
+ ieee80211w=None):
+ params = {"wpa": "2",
+ "wpa_key_mgmt": wpa_key_mgmt,
+ "rsn_pairwise": "CCMP"}
+ if ssid:
+ params["ssid"] = ssid
+ if passphrase:
+ params["wpa_passphrase"] = passphrase
+ if ieee80211w is not None:
+ params["ieee80211w"] = ieee80211w
+ return params
+
+def wpa_params(ssid=None, passphrase=None):
+ params = {"wpa": "1",
+ "wpa_key_mgmt": "WPA-PSK",
+ "wpa_pairwise": "TKIP"}
+ if ssid:
+ params["ssid"] = ssid
+ if passphrase:
+ params["wpa_passphrase"] = passphrase
+ return params
+
+def wpa_mixed_params(ssid=None, passphrase=None):
+ params = {"wpa": "3",
+ "wpa_key_mgmt": "WPA-PSK",
+ "wpa_pairwise": "TKIP",
+ "rsn_pairwise": "CCMP"}
+ if ssid:
+ params["ssid"] = ssid
+ if passphrase:
+ params["wpa_passphrase"] = passphrase
+ return params
+
+def radius_params():
+ params = {"auth_server_addr": "127.0.0.1",
+ "auth_server_port": "1812",
+ "auth_server_shared_secret": "radius",
+ "nas_identifier": "nas.w1.fi"}
+ return params
+
+def wpa_eap_params(ssid=None):
+ params = radius_params()
+ params["wpa"] = "1"
+ params["wpa_key_mgmt"] = "WPA-EAP"
+ params["wpa_pairwise"] = "TKIP"
+ params["ieee8021x"] = "1"
+ if ssid:
+ params["ssid"] = ssid
+ return params
+
+def wpa2_eap_params(ssid=None):
+ params = radius_params()
+ params["wpa"] = "2"
+ params["wpa_key_mgmt"] = "WPA-EAP"
+ params["rsn_pairwise"] = "CCMP"
+ params["ieee8021x"] = "1"
+ if ssid:
+ params["ssid"] = ssid
+ return params
+
+def b_only_params(channel="1", ssid=None, country=None):
+ params = {"hw_mode": "b",
+ "channel": channel}
+ if ssid:
+ params["ssid"] = ssid
+ if country:
+ params["country_code"] = country
+ return params
+
+def g_only_params(channel="1", ssid=None, country=None):
+ params = {"hw_mode": "g",
+ "channel": channel}
+ if ssid:
+ params["ssid"] = ssid
+ if country:
+ params["country_code"] = country
+ return params
+
+def a_only_params(channel="36", ssid=None, country=None):
+ params = {"hw_mode": "a",
+ "channel": channel}
+ if ssid:
+ params["ssid"] = ssid
+ if country:
+ params["country_code"] = country
+ return params
+
+def ht20_params(channel="1", ssid=None, country=None):
+ params = {"ieee80211n": "1",
+ "channel": channel,
+ "hw_mode": "g"}
+ if int(channel) > 14:
+ params["hw_mode"] = "a"
+ if ssid:
+ params["ssid"] = ssid
+ if country:
+ params["country_code"] = country
+ return params
+
+def ht40_plus_params(channel="1", ssid=None, country=None):
+ params = ht20_params(channel, ssid, country)
+ params['ht_capab'] = "[HT40+]"
+ return params
+
+def ht40_minus_params(channel="1", ssid=None, country=None):
+ params = ht20_params(channel, ssid, country)
+ params['ht_capab'] = "[HT40-]"
+ return params
+
+def cmd_execute(apdev, cmd, shell=False):
+ hapd_global = HostapdGlobal(apdev)
+ return hapd_global.cmd_execute(cmd, shell=shell)
+
+def send_file(apdev, src, dst):
+ hapd_global = HostapdGlobal(apdev)
+ return hapd_global.send_file(src, dst)
+
+def acl_file(dev, apdev, conf):
+ fd, filename = tempfile.mkstemp(dir='/tmp', prefix=conf + '-')
+ f = os.fdopen(fd, 'w')
+
+ if conf == 'hostapd.macaddr':
+ mac0 = dev[0].get_status_field("address")
+ f.write(mac0 + '\n')
+ f.write("02:00:00:00:00:12\n")
+ f.write("02:00:00:00:00:34\n")
+ f.write("-02:00:00:00:00:12\n")
+ f.write("-02:00:00:00:00:34\n")
+ f.write("01:01:01:01:01:01\n")
+ f.write("03:01:01:01:01:03\n")
+ elif conf == 'hostapd.accept':
+ mac0 = dev[0].get_status_field("address")
+ mac1 = dev[1].get_status_field("address")
+ f.write(mac0 + " 1\n")
+ f.write(mac1 + " 2\n")
+ elif conf == 'hostapd.accept2':
+ mac0 = dev[0].get_status_field("address")
+ mac1 = dev[1].get_status_field("address")
+ mac2 = dev[2].get_status_field("address")
+ f.write(mac0 + " 1\n")
+ f.write(mac1 + " 2\n")
+ f.write(mac2 + " 3\n")
+ else:
+ f.close()
+ os.unlink(filename)
+ return conf
+
+ return filename
+
+def bssid_inc(apdev, inc=1):
+ parts = apdev['bssid'].split(':')
+ parts[5] = '%02x' % (int(parts[5], 16) + int(inc))
+ bssid = '%s:%s:%s:%s:%s:%s' % (parts[0], parts[1], parts[2],
+ parts[3], parts[4], parts[5])
+ return bssid
+
+def cfg_file(apdev, conf, ifname=None):
+ match = re.search(r'^bss-.+', conf)
+ if match:
+ # put cfg file in /tmp directory
+ fd, fname = tempfile.mkstemp(dir='/tmp', prefix=conf + '-')
+ f = os.fdopen(fd, 'w')
+ idx = ''.join(filter(str.isdigit, conf.split('-')[-1]))
+ if ifname is None:
+ ifname = apdev['ifname']
+ if idx != '1':
+ ifname = ifname + '-' + idx
+
+ f.write("driver=nl80211\n")
+ f.write("ctrl_interface=/var/run/hostapd\n")
+ f.write("hw_mode=g\n")
+ f.write("channel=1\n")
+ f.write("ieee80211n=1\n")
+ if conf.startswith('bss-ht40-'):
+ f.write("ht_capab=[HT40+]\n")
+ f.write("interface=%s\n" % ifname)
+
+ f.write("ssid=bss-%s\n" % idx)
+ if conf == 'bss-2-dup.conf':
+ bssid = apdev['bssid']
+ else:
+ bssid = bssid_inc(apdev, int(idx) - 1)
+ f.write("bssid=%s\n" % bssid)
+
+ return fname
+
+ return conf
diff --git a/contrib/wpa/tests/hwsim/hostapd.vlan b/contrib/wpa/tests/hwsim/hostapd.vlan
new file mode 100644
index 000000000000..b0e905bf5afb
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/hostapd.vlan
@@ -0,0 +1,2 @@
+1 hwsimvlan1
+* testvlan#
diff --git a/contrib/wpa/tests/hwsim/hostapd.vlan2 b/contrib/wpa/tests/hwsim/hostapd.vlan2
new file mode 100644
index 000000000000..46bf6281ddf5
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/hostapd.vlan2
@@ -0,0 +1,3 @@
+1 hwsimvlan1
+3 hwsimvlan3 hwsimbr3
+* testvlan#
diff --git a/contrib/wpa/tests/hwsim/hostapd.wlan3.vlan b/contrib/wpa/tests/hwsim/hostapd.wlan3.vlan
new file mode 100644
index 000000000000..768fad7bd42b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/hostapd.wlan3.vlan
@@ -0,0 +1,2 @@
+1 wlan3.1
+* wlan3.#
diff --git a/contrib/wpa/tests/hwsim/hostapd.wlan4.vlan b/contrib/wpa/tests/hwsim/hostapd.wlan4.vlan
new file mode 100644
index 000000000000..744e84fc3c65
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/hostapd.wlan4.vlan
@@ -0,0 +1,2 @@
+1 wlan4.1
+* wlan4.#
diff --git a/contrib/wpa/tests/hwsim/hostapd.wpa_psk b/contrib/wpa/tests/hwsim/hostapd.wpa_psk
new file mode 100644
index 000000000000..7644f894a27f
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/hostapd.wpa_psk
@@ -0,0 +1,5 @@
+00:00:00:00:00:00 secret passphrase
+02:00:00:00:00:00 very secret
+00:11:22:33:44:55 another passphrase
+00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+00:00:00:00:00:00 another passphrase for all STAs
diff --git a/contrib/wpa/tests/hwsim/hwsim.py b/contrib/wpa/tests/hwsim/hwsim.py
new file mode 100644
index 000000000000..bc8aabdd49c2
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/hwsim.py
@@ -0,0 +1,114 @@
+#
+# HWSIM generic netlink controller code
+# Copyright (c) 2014 Intel Corporation
+#
+# Author: Johannes Berg <johannes.berg@intel.com>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import netlink, os
+
+# constants
+HWSIM_CMD_CREATE_RADIO = 4
+HWSIM_CMD_DESTROY_RADIO = 5
+
+HWSIM_ATTR_CHANNELS = 9
+HWSIM_ATTR_RADIO_ID = 10
+HWSIM_ATTR_SUPPORT_P2P_DEVICE = 14
+HWSIM_ATTR_USE_CHANCTX = 15
+
+# the controller class
+class HWSimController(object):
+ def __init__(self):
+ self._conn = netlink.Connection(netlink.NETLINK_GENERIC)
+ self._fid = netlink.genl_controller.get_family_id(b'MAC80211_HWSIM')
+
+ def create_radio(self, n_channels=None, use_chanctx=False,
+ use_p2p_device=False):
+ attrs = []
+ if n_channels:
+ attrs.append(netlink.U32Attr(HWSIM_ATTR_CHANNELS, n_channels))
+ if use_chanctx:
+ attrs.append(netlink.FlagAttr(HWSIM_ATTR_USE_CHANCTX))
+ if use_p2p_device:
+ attrs.append(netlink.FlagAttr(HWSIM_ATTR_SUPPORT_P2P_DEVICE))
+
+ msg = netlink.GenlMessage(self._fid, HWSIM_CMD_CREATE_RADIO,
+ flags=netlink.NLM_F_REQUEST |
+ netlink.NLM_F_ACK,
+ attrs=attrs)
+ return msg.send_and_recv(self._conn).ret
+
+ def destroy_radio(self, radio_id):
+ attrs = [netlink.U32Attr(HWSIM_ATTR_RADIO_ID, radio_id)]
+ msg = netlink.GenlMessage(self._fid, HWSIM_CMD_DESTROY_RADIO,
+ flags=netlink.NLM_F_REQUEST |
+ netlink.NLM_F_ACK,
+ attrs=attrs)
+ msg.send_and_recv(self._conn)
+
+class HWSimRadio(object):
+ def __init__(self, n_channels=None, use_chanctx=False,
+ use_p2p_device=False):
+ self._controller = HWSimController()
+ self._n_channels = n_channels
+ self._use_chanctx = use_chanctx
+ self._use_p2p_dev = use_p2p_device
+
+ def __enter__(self):
+ self._radio_id = self._controller.create_radio(
+ n_channels=self._n_channels,
+ use_chanctx=self._use_chanctx,
+ use_p2p_device=self._use_p2p_dev)
+ if self._radio_id < 0:
+ raise Exception("Failed to create radio (err:%d)" % self._radio_id)
+ try:
+ iface = os.listdir('/sys/class/mac80211_hwsim/hwsim%d/net/' % self._radio_id)[0]
+ except Exception as e:
+ self._controller.destroy_radio(self._radio_id)
+ raise e
+ return self._radio_id, iface
+
+ def __exit__(self, type, value, traceback):
+ self._controller.destroy_radio(self._radio_id)
+
+
+def create(args):
+ print('Created radio %d' % c.create_radio(n_channels=args.channels,
+ use_chanctx=args.chanctx))
+
+def destroy(args):
+ print(c.destroy_radio(args.radio))
+
+if __name__ == '__main__':
+ import argparse
+ c = HWSimController()
+
+ parser = argparse.ArgumentParser(description='send hwsim control commands')
+ subparsers = parser.add_subparsers(help="Commands", dest='command')
+ parser_create = subparsers.add_parser('create', help='create a radio')
+ parser_create.add_argument('--channels', metavar='<number_of_channels>', type=int,
+ default=0,
+ help='Number of concurrent channels supported ' +
+ 'by the radio. If not specified, the number ' +
+ 'of channels specified in the ' +
+ 'mac80211_hwsim.channels module parameter is ' +
+ 'used')
+ parser_create.add_argument('--chanctx', action="store_true",
+ help='Use channel contexts, regardless of ' +
+ 'whether the number of channels is 1 or ' +
+ 'greater. By default channel contexts are ' +
+ 'only used if the number of channels is ' +
+ 'greater than 1.')
+ parser_create.set_defaults(func=create)
+
+ parser_destroy = subparsers.add_parser('destroy', help='destroy a radio')
+ parser_destroy.add_argument('radio', metavar='<radio>', type=int,
+ default=0,
+ help='The number of the radio to be ' +
+ 'destroyed (i.e., 0 for phy0, 1 for phy1...)')
+ parser_destroy.set_defaults(func=destroy)
+
+ args = parser.parse_args()
+ args.func(args)
diff --git a/contrib/wpa/tests/hwsim/hwsim_utils.py b/contrib/wpa/tests/hwsim/hwsim_utils.py
new file mode 100644
index 000000000000..eb312bf96b2b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/hwsim_utils.py
@@ -0,0 +1,246 @@
+# hwsim testing utilities
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import time
+import logging
+logger = logging.getLogger()
+
+from wpasupplicant import WpaSupplicant
+
+def config_data_test(dev1, dev2, dev1group, dev2group, ifname1, ifname2):
+ cmd = "DATA_TEST_CONFIG 1"
+ if ifname1:
+ cmd = cmd + " ifname=" + ifname1
+ if dev1group:
+ res = dev1.group_request(cmd)
+ else:
+ res = dev1.request(cmd)
+ if "OK" not in res:
+ raise Exception("Failed to enable data test functionality")
+
+ cmd = "DATA_TEST_CONFIG 1"
+ if ifname2:
+ cmd = cmd + " ifname=" + ifname2
+ if dev2group:
+ res = dev2.group_request(cmd)
+ else:
+ res = dev2.request(cmd)
+ if "OK" not in res:
+ raise Exception("Failed to enable data test functionality")
+
+def run_multicast_connectivity_test(dev1, dev2, tos=None,
+ dev1group=False, dev2group=False,
+ ifname1=None, ifname2=None,
+ config=True, timeout=5,
+ send_len=None, multicast_to_unicast=False,
+ broadcast_retry_c=1):
+ addr1 = dev1.get_addr(dev1group)
+ addr2 = dev2.get_addr(dev2group)
+
+ if config:
+ config_data_test(dev1, dev2, dev1group, dev2group, ifname1, ifname2)
+
+ cmd = "DATA_TEST_TX ff:ff:ff:ff:ff:ff {} {}".format(addr1, tos)
+ if send_len is not None:
+ cmd += " len=" + str(send_len)
+ for i in range(broadcast_retry_c):
+ try:
+ if dev1group:
+ dev1.group_request(cmd)
+ else:
+ dev1.request(cmd)
+ if dev2group:
+ ev = dev2.wait_group_event(["DATA-TEST-RX"],
+ timeout=timeout)
+ else:
+ ev = dev2.wait_event(["DATA-TEST-RX"], timeout=timeout)
+ if ev is None:
+ raise Exception("dev1->dev2 broadcast data delivery failed")
+ if multicast_to_unicast:
+ if "DATA-TEST-RX ff:ff:ff:ff:ff:ff {}".format(addr1) in ev:
+ raise Exception("Unexpected dev1->dev2 broadcast data result: multicast to unicast conversion missing")
+ if "DATA-TEST-RX {} {}".format(addr2, addr1) not in ev:
+ raise Exception("Unexpected dev1->dev2 broadcast data result (multicast to unicast enabled)")
+ else:
+ if "DATA-TEST-RX ff:ff:ff:ff:ff:ff {}".format(addr1) not in ev:
+ raise Exception("Unexpected dev1->dev2 broadcast data result")
+ if send_len is not None:
+ if " len=" + str(send_len) not in ev:
+ raise Exception("Unexpected dev1->dev2 broadcast data length")
+ else:
+ if " len=" in ev:
+ raise Exception("Unexpected dev1->dev2 broadcast data length")
+ break
+ except Exception as e:
+ if i == broadcast_retry_c - 1:
+ raise
+
+def run_connectivity_test(dev1, dev2, tos, dev1group=False, dev2group=False,
+ ifname1=None, ifname2=None, config=True, timeout=5,
+ multicast_to_unicast=False, broadcast=True,
+ send_len=None):
+ addr1 = dev1.get_addr(dev1group)
+ addr2 = dev2.get_addr(dev2group)
+
+ dev1.dump_monitor()
+ dev2.dump_monitor()
+
+ if dev1.hostname is None and dev2.hostname is None:
+ broadcast_retry_c = 1
+ else:
+ broadcast_retry_c = 10
+
+ try:
+ if config:
+ config_data_test(dev1, dev2, dev1group, dev2group, ifname1, ifname2)
+
+ cmd = "DATA_TEST_TX {} {} {}".format(addr2, addr1, tos)
+ if send_len is not None:
+ cmd += " len=" + str(send_len)
+ if dev1group:
+ dev1.group_request(cmd)
+ else:
+ dev1.request(cmd)
+ if dev2group:
+ ev = dev2.wait_group_event(["DATA-TEST-RX"], timeout=timeout)
+ else:
+ ev = dev2.wait_event(["DATA-TEST-RX"], timeout=timeout)
+ if ev is None:
+ raise Exception("dev1->dev2 unicast data delivery failed")
+ if "DATA-TEST-RX {} {}".format(addr2, addr1) not in ev:
+ raise Exception("Unexpected dev1->dev2 unicast data result")
+ if send_len is not None:
+ if " len=" + str(send_len) not in ev:
+ raise Exception("Unexpected dev1->dev2 unicast data length")
+ else:
+ if " len=" in ev:
+ raise Exception("Unexpected dev1->dev2 unicast data length")
+
+ if broadcast:
+ run_multicast_connectivity_test(dev1, dev2, tos,
+ dev1group, dev2group,
+ ifname1, ifname2, False, timeout,
+ send_len, False, broadcast_retry_c)
+
+ cmd = "DATA_TEST_TX {} {} {}".format(addr1, addr2, tos)
+ if send_len is not None:
+ cmd += " len=" + str(send_len)
+ if dev2group:
+ dev2.group_request(cmd)
+ else:
+ dev2.request(cmd)
+ if dev1group:
+ ev = dev1.wait_group_event(["DATA-TEST-RX"], timeout=timeout)
+ else:
+ ev = dev1.wait_event(["DATA-TEST-RX"], timeout=timeout)
+ if ev is None:
+ raise Exception("dev2->dev1 unicast data delivery failed")
+ if "DATA-TEST-RX {} {}".format(addr1, addr2) not in ev:
+ raise Exception("Unexpected dev2->dev1 unicast data result")
+ if send_len is not None:
+ if " len=" + str(send_len) not in ev:
+ raise Exception("Unexpected dev2->dev1 unicast data length")
+ else:
+ if " len=" in ev:
+ raise Exception("Unexpected dev2->dev1 unicast data length")
+
+ if broadcast:
+ run_multicast_connectivity_test(dev2, dev1, tos,
+ dev2group, dev1group,
+ ifname2, ifname1, False, timeout,
+ send_len, multicast_to_unicast,
+ broadcast_retry_c)
+
+ finally:
+ if config:
+ if dev1group:
+ dev1.group_request("DATA_TEST_CONFIG 0")
+ else:
+ dev1.request("DATA_TEST_CONFIG 0")
+ if dev2group:
+ dev2.group_request("DATA_TEST_CONFIG 0")
+ else:
+ dev2.request("DATA_TEST_CONFIG 0")
+
+def test_connectivity(dev1, dev2, dscp=None, tos=None, max_tries=1,
+ dev1group=False, dev2group=False,
+ ifname1=None, ifname2=None, config=True, timeout=5,
+ multicast_to_unicast=False, success_expected=True,
+ broadcast=True, send_len=None):
+ if dscp:
+ tos = dscp << 2
+ if not tos:
+ tos = 0
+
+ success = False
+ last_err = None
+ for i in range(0, max_tries):
+ try:
+ run_connectivity_test(dev1, dev2, tos, dev1group, dev2group,
+ ifname1, ifname2, config=config,
+ timeout=timeout,
+ multicast_to_unicast=multicast_to_unicast,
+ broadcast=broadcast, send_len=send_len)
+ success = True
+ break
+ except Exception as e:
+ last_err = e
+ if i + 1 < max_tries:
+ time.sleep(1)
+ if success_expected and not success:
+ raise Exception(last_err)
+ if not success_expected and success:
+ raise Exception("Unexpected connectivity detected")
+
+def test_connectivity_iface(dev1, dev2, ifname, dscp=None, tos=None,
+ max_tries=1, timeout=5):
+ test_connectivity(dev1, dev2, dscp, tos, ifname2=ifname,
+ max_tries=max_tries, timeout=timeout)
+
+def test_connectivity_p2p(dev1, dev2, dscp=None, tos=None):
+ test_connectivity(dev1, dev2, dscp, tos, dev1group=True, dev2group=True)
+
+def test_connectivity_p2p_sta(dev1, dev2, dscp=None, tos=None):
+ test_connectivity(dev1, dev2, dscp, tos, dev1group=True, dev2group=False)
+
+def test_connectivity_sta(dev1, dev2, dscp=None, tos=None):
+ test_connectivity(dev1, dev2, dscp, tos)
+
+(PS_DISABLED, PS_ENABLED, PS_AUTO_POLL, PS_MANUAL_POLL) = list(range(4))
+
+def set_powersave(dev, val):
+ phy = dev.get_driver_status_field("phyname")
+ fname = '/sys/kernel/debug/ieee80211/%s/hwsim/ps' % phy
+ data = '%d' % val
+ (res, data) = dev.cmd_execute(["echo", data, ">", fname], shell=True)
+ if res != 0:
+ raise Exception("Failed to set power save for device")
+
+def set_group_map(dev, val):
+ phy = dev.get_driver_status_field("phyname")
+ fname = '/sys/kernel/debug/ieee80211/%s/hwsim/group' % phy
+ data = '%d' % val
+ (res, data) = dev.cmd_execute(["echo", data, ">", fname], shell=True)
+ if res != 0:
+ raise Exception("Failed to set group map for %s" % phy)
+
+def set_rx_rssi(dev, val):
+ """
+ Configure signal strength when receiving transmitted frames.
+ mac80211_hwsim driver sets rssi to: TX power - 50
+ According to that set tx_power in order to get the desired RSSI.
+ Valid RSSI range: -50 to -30.
+ """
+ tx_power = (val + 50) * 100
+ ifname = dev.get_driver_status_field("ifname")
+ (res, data) = dev.cmd_execute(['iw', ifname, 'set', 'txpower',
+ 'fixed', str(tx_power)])
+ if res != 0:
+ raise Exception("Failed to set RSSI to %d" % val)
+
+def reset_rx_rssi(dev):
+ set_rx_rssi(dev, -30)
diff --git a/contrib/wpa/tests/hwsim/multi-bss-acs.conf b/contrib/wpa/tests/hwsim/multi-bss-acs.conf
new file mode 100644
index 000000000000..f5a25e82bb55
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/multi-bss-acs.conf
@@ -0,0 +1,28 @@
+driver=nl80211
+
+hw_mode=g
+channel=0
+ieee80211n=1
+
+interface=wlan3
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-1
+
+bss=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+ssid=bss-2
+wpa=2
+wpa_key_mgmt=WPA-PSK
+rsn_pairwise=CCMP
+wpa_passphrase=12345678
+
+bss=wlan3-3
+bssid=02:00:00:00:03:02
+ctrl_interface=/var/run/hostapd
+ssid=bss-3
+wpa=2
+wpa_key_mgmt=SAE
+rsn_pairwise=CCMP
+sae_password=qwertyuiop
diff --git a/contrib/wpa/tests/hwsim/multi-bss-iface-per_sta_vif.conf b/contrib/wpa/tests/hwsim/multi-bss-iface-per_sta_vif.conf
new file mode 100644
index 000000000000..f07c13b212ea
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/multi-bss-iface-per_sta_vif.conf
@@ -0,0 +1,42 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-1
+dynamic_vlan=1
+vlan_tagged_interface=dummy0
+vlan_bridge=brvlan
+wpa=2
+wpa_key_mgmt=WPA-EAP
+rsn_pairwise=CCMP
+ieee8021x=1
+auth_server_addr=127.0.0.1
+auth_server_port=18128
+auth_server_shared_secret=radius
+nas_identifier=nas.w1.fi
+vlan_naming=1
+per_sta_vif=1
+
+bss=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+ssid=bss-2
+
+dynamic_vlan=1
+vlan_tagged_interface=dummy0
+vlan_bridge=brvlan
+wpa=2
+wpa_key_mgmt=WPA-EAP
+rsn_pairwise=CCMP
+ieee8021x=1
+auth_server_addr=127.0.0.1
+auth_server_port=18128
+auth_server_shared_secret=radius
+nas_identifier=nas.w1.fi
+vlan_naming=1
+per_sta_vif=1
diff --git a/contrib/wpa/tests/hwsim/multi-bss-iface.conf b/contrib/wpa/tests/hwsim/multi-bss-iface.conf
new file mode 100644
index 000000000000..6b6167f51cc1
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/multi-bss-iface.conf
@@ -0,0 +1,40 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-1
+dynamic_vlan=1
+vlan_tagged_interface=dummy0
+vlan_bridge=brvlan
+wpa=2
+wpa_key_mgmt=WPA-EAP
+rsn_pairwise=CCMP
+ieee8021x=1
+auth_server_addr=127.0.0.1
+auth_server_port=18128
+auth_server_shared_secret=radius
+nas_identifier=nas.w1.fi
+vlan_naming=1
+
+bss=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+ssid=bss-2
+
+dynamic_vlan=1
+vlan_tagged_interface=dummy0
+vlan_bridge=brvlan
+wpa=2
+wpa_key_mgmt=WPA-EAP
+rsn_pairwise=CCMP
+ieee8021x=1
+auth_server_addr=127.0.0.1
+auth_server_port=18128
+auth_server_shared_secret=radius
+nas_identifier=nas.w1.fi
+vlan_naming=1
diff --git a/contrib/wpa/tests/hwsim/multi-bss.conf b/contrib/wpa/tests/hwsim/multi-bss.conf
new file mode 100644
index 000000000000..64584b64b2ed
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/multi-bss.conf
@@ -0,0 +1,21 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3
+ctrl_interface=/var/run/hostapd
+
+ssid=bss-1
+
+
+bss=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+ssid=bss-2
+
+bss=wlan3-3
+bssid=02:00:00:00:03:02
+ctrl_interface=/var/run/hostapd
+ssid=bss-3
diff --git a/contrib/wpa/tests/hwsim/netlink.py b/contrib/wpa/tests/hwsim/netlink.py
new file mode 100644
index 000000000000..7e6327a7b775
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/netlink.py
@@ -0,0 +1,237 @@
+#
+# (Generic) Netlink message generation/parsing
+# Copyright (c) 2007 Johannes Berg <johannes@sipsolutions.net>
+# Copyright (c) 2014 Intel Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import struct, socket
+
+# flags
+NLM_F_REQUEST = 1
+NLM_F_MULTI = 2
+NLM_F_ACK = 4
+NLM_F_ECHO = 8
+
+# types
+NLMSG_NOOP = 1
+NLMSG_ERROR = 2
+NLMSG_DONE = 3
+NLMSG_OVERRUN = 4
+NLMSG_MIN_TYPE = 0x10
+
+class Attr(object):
+ def __init__(self, attr_type, data, *values):
+ self._type = attr_type
+ if len(values):
+ self._data = struct.pack(data, *values)
+ else:
+ self._data = data
+
+ def _dump(self):
+ hdr = struct.pack("HH", len(self._data) + 4, self._type)
+ length = len(self._data)
+ pad = ((length + 4 - 1) & ~3) - length
+ return hdr + self._data + b'\x00' * pad
+
+ def __repr__(self):
+ return '<Attr type %d, data "%s">' % (self._type, repr(self._data))
+
+ def u16(self):
+ return struct.unpack('H', self._data)[0]
+ def s16(self):
+ return struct.unpack('h', self._data)[0]
+ def u32(self):
+ return struct.unpack('I', self._data)[0]
+ def s32(self):
+ return struct.unpack('i', self._data)[0]
+ def str(self):
+ return self._data
+ def nulstr(self):
+ return self._data.split('\0')[0]
+ def nested(self):
+ return parse_attributes(self._data)
+
+class StrAttr(Attr):
+ def __init__(self, attr_type, data):
+ Attr.__init__(self, attr_type, "%ds" % len(data), data)
+
+class NulStrAttr(Attr):
+ def __init__(self, attr_type, data):
+ Attr.__init__(self, attr_type, "%dsB" % len(data), data, 0)
+
+class U32Attr(Attr):
+ def __init__(self, attr_type, val):
+ Attr.__init__(self, attr_type, "I", val)
+
+class U8Attr(Attr):
+ def __init__(self, attr_type, val):
+ Attr.__init__(self, attr_type, "B", val)
+
+class FlagAttr(Attr):
+ def __init__(self, attr_type):
+ Attr.__init__(self, attr_type, b"")
+
+class Nested(Attr):
+ def __init__(self, attr_type, attrs):
+ self.attrs = attrs
+ self.type = attr_type
+
+ def _dump(self):
+ contents = []
+ for attr in self.attrs:
+ contents.append(attr._dump())
+ contents = ''.join(contents)
+ length = len(contents)
+ hdr = struct.pack("HH", length+4, self.type)
+ return hdr + contents
+
+NETLINK_ROUTE = 0
+NETLINK_UNUSED = 1
+NETLINK_USERSOCK = 2
+NETLINK_FIREWALL = 3
+NETLINK_INET_DIAG = 4
+NETLINK_NFLOG = 5
+NETLINK_XFRM = 6
+NETLINK_SELINUX = 7
+NETLINK_ISCSI = 8
+NETLINK_AUDIT = 9
+NETLINK_FIB_LOOKUP = 10
+NETLINK_CONNECTOR = 11
+NETLINK_NETFILTER = 12
+NETLINK_IP6_FW = 13
+NETLINK_DNRTMSG = 14
+NETLINK_KOBJECT_UEVENT = 15
+NETLINK_GENERIC = 16
+
+class Message(object):
+ def __init__(self, msg_type, flags=0, seq=-1, payload=None):
+ self.type = msg_type
+ self.flags = flags
+ self.seq = seq
+ self.pid = -1
+ payload = payload or []
+ if isinstance(payload, list):
+ self.payload = bytes()
+ for attr in payload:
+ self.payload += attr._dump()
+ else:
+ self.payload = payload
+
+ def send(self, conn):
+ if self.seq == -1:
+ self.seq = conn.seq()
+
+ self.pid = conn.pid
+ length = len(self.payload)
+
+ hdr = struct.pack("IHHII", length + 4*4, self.type,
+ self.flags, self.seq, self.pid)
+ conn.send(hdr + self.payload)
+
+ def __repr__(self):
+ return '<netlink.Message type=%d, pid=%d, seq=%d, flags=0x%x "%s">' % (
+ self.type, self.pid, self.seq, self.flags, repr(self.payload))
+
+ @property
+ def ret(self):
+ assert self.type == NLMSG_ERROR
+ return struct.unpack("i", self.payload[:4])[0]
+
+ def send_and_recv(self, conn):
+ self.send(conn)
+ while True:
+ m = conn.recv()
+ if m.seq == self.seq:
+ return m
+
+class Connection(object):
+ def __init__(self, nltype, groups=0, unexpected_msg_handler=None):
+ self.descriptor = socket.socket(socket.AF_NETLINK,
+ socket.SOCK_RAW, nltype)
+ self.descriptor.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 65536)
+ self.descriptor.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 65536)
+ self.descriptor.bind((0, groups))
+ self.pid, self.groups = self.descriptor.getsockname()
+ self._seq = 0
+ self.unexpected = unexpected_msg_handler
+ def send(self, msg):
+ self.descriptor.send(msg)
+ def recv(self):
+ contents = self.descriptor.recv(16384)
+ # XXX: python doesn't give us message flags, check
+ # len(contents) vs. msglen for TRUNC
+ msglen, msg_type, flags, seq, pid = struct.unpack("IHHII",
+ contents[:16])
+ msg = Message(msg_type, flags, seq, contents[16:])
+ msg.pid = pid
+ if msg.type == NLMSG_ERROR:
+ import os
+ errno = msg.ret
+ if errno < 0:
+ err = OSError("Netlink error: %s (%d)" % (
+ os.strerror(-errno), -errno))
+ err.errno = -errno
+ raise err
+ return msg
+ def seq(self):
+ self._seq += 1
+ return self._seq
+
+def parse_attributes(data):
+ attrs = {}
+ while len(data):
+ attr_len, attr_type = struct.unpack("HH", data[:4])
+ attrs[attr_type] = Attr(attr_type, data[4:attr_len])
+ attr_len = ((attr_len + 4 - 1) & ~3)
+ data = data[attr_len:]
+ return attrs
+
+
+
+CTRL_CMD_UNSPEC = 0
+CTRL_CMD_NEWFAMILY = 1
+CTRL_CMD_DELFAMILY = 2
+CTRL_CMD_GETFAMILY = 3
+CTRL_CMD_NEWOPS = 4
+CTRL_CMD_DELOPS = 5
+CTRL_CMD_GETOPS = 6
+
+CTRL_ATTR_UNSPEC = 0
+CTRL_ATTR_FAMILY_ID = 1
+CTRL_ATTR_FAMILY_NAME = 2
+CTRL_ATTR_VERSION = 3
+CTRL_ATTR_HDRSIZE = 4
+CTRL_ATTR_MAXATTR = 5
+CTRL_ATTR_OPS = 6
+
+class GenlHdr(object):
+ def __init__(self, cmd, version=0):
+ self.cmd = cmd
+ self.version = version
+ def _dump(self):
+ return struct.pack("BBxx", self.cmd, self.version)
+
+def _genl_hdr_parse(data):
+ return GenlHdr(*struct.unpack("BBxx", data))
+
+GENL_ID_CTRL = NLMSG_MIN_TYPE
+
+class GenlMessage(Message):
+ def __init__(self, family, cmd, attrs=[], flags=0):
+ Message.__init__(self, family, flags=flags, payload=[GenlHdr(cmd)] + attrs)
+
+class GenlController(object):
+ def __init__(self, conn):
+ self.conn = conn
+ def get_family_id(self, family):
+ a = NulStrAttr(CTRL_ATTR_FAMILY_NAME, family)
+ m = GenlMessage(GENL_ID_CTRL, CTRL_CMD_GETFAMILY, flags=NLM_F_REQUEST, attrs=[a])
+ m.send(self.conn)
+ m = self.conn.recv()
+ gh = _genl_hdr_parse(m.payload[:4])
+ attrs = parse_attributes(m.payload[4:])
+ return attrs[CTRL_ATTR_FAMILY_ID].u16()
+
+genl_controller = GenlController(Connection(NETLINK_GENERIC))
diff --git a/contrib/wpa/tests/hwsim/nl80211.py b/contrib/wpa/tests/hwsim/nl80211.py
new file mode 100644
index 000000000000..55642c022b38
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/nl80211.py
@@ -0,0 +1,357 @@
+# nl80211 definitions
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import struct
+
+nl80211_cmd = {
+ 'GET_WIPHY': 1,
+ 'SET_WIPHY': 2,
+ 'NEW_WIPHY': 3,
+ 'DEL_WIPHY': 4,
+ 'GET_INTERFACE': 5,
+ 'SET_INTERFACE': 6,
+ 'NEW_INTERFACE': 7,
+ 'DEL_INTERFACE': 8,
+ 'GET_KEY': 9,
+ 'SET_KEY': 10,
+ 'NEW_KEY': 11,
+ 'DEL_KEY': 12,
+ 'GET_BEACON': 13,
+ 'SET_BEACON': 14,
+ 'START_AP': 15,
+ 'STOP_AP': 16,
+ 'GET_STATION': 17,
+ 'SET_STATION': 18,
+ 'NEW_STATION': 19,
+ 'DEL_STATION': 20,
+ 'GET_MPATH': 21,
+ 'SET_MPATH': 22,
+ 'NEW_MPATH': 23,
+ 'DEL_MPATH': 24,
+ 'SET_BSS': 25,
+ 'SET_REG': 26,
+ 'REQ_SET_REG': 27,
+ 'GET_MESH_CONFIG': 28,
+ 'SET_MESH_CONFIG': 29,
+ 'SET_MGMT_EXTRA_IE[RESERVED]': 30,
+ 'GET_REG': 31,
+ 'GET_SCAN': 32,
+ 'TRIGGER_SCAN': 33,
+ 'NEW_SCAN_RESULTS': 34,
+ 'SCAN_ABORTED': 35,
+ 'REG_CHANGE': 36,
+ 'AUTHENTICATE': 37,
+ 'ASSOCIATE': 38,
+ 'DEAUTHENTICATE': 39,
+ 'DISASSOCIATE': 40,
+ 'MICHAEL_MIC_FAILURE': 41,
+ 'REG_BEACON_HINT': 42,
+ 'JOIN_IBSS': 43,
+ 'LEAVE_IBSS': 44,
+ 'TESTMODE': 45,
+ 'CONNECT': 46,
+ 'ROAM': 47,
+ 'DISCONNECT': 48,
+ 'SET_WIPHY_NETNS': 49,
+ 'GET_SURVEY': 50,
+ 'NEW_SURVEY_RESULTS': 51,
+ 'SET_PMKSA': 52,
+ 'DEL_PMKSA': 53,
+ 'FLUSH_PMKSA': 54,
+ 'REMAIN_ON_CHANNEL': 55,
+ 'CANCEL_REMAIN_ON_CHANNEL': 56,
+ 'SET_TX_BITRATE_MASK': 57,
+ 'REGISTER_FRAME': 58,
+ 'FRAME': 59,
+ 'FRAME_TX_STATUS': 60,
+ 'SET_POWER_SAVE': 61,
+ 'GET_POWER_SAVE': 62,
+ 'SET_CQM': 63,
+ 'NOTIFY_CQM': 64,
+ 'SET_CHANNEL': 65,
+ 'SET_WDS_PEER': 66,
+ 'FRAME_WAIT_CANCEL': 67,
+ 'JOIN_MESH': 68,
+ 'LEAVE_MESH': 69,
+ 'UNPROT_DEAUTHENTICATE': 70,
+ 'UNPROT_DISASSOCIATE': 71,
+ 'NEW_PEER_CANDIDATE': 72,
+ 'GET_WOWLAN': 73,
+ 'SET_WOWLAN': 74,
+ 'START_SCHED_SCAN': 75,
+ 'STOP_SCHED_SCAN': 76,
+ 'SCHED_SCAN_RESULTS': 77,
+ 'SCHED_SCAN_STOPPED': 78,
+ 'SET_REKEY_OFFLOAD': 79,
+ 'PMKSA_CANDIDATE': 80,
+ 'TDLS_OPER': 81,
+ 'TDLS_MGMT': 82,
+ 'UNEXPECTED_FRAME': 83,
+ 'PROBE_CLIENT': 84,
+ 'REGISTER_BEACONS': 85,
+ 'UNEXPECTED_4ADDR_FRAME': 86,
+ 'SET_NOACK_MAP': 87,
+ 'CH_SWITCH_NOTIFY': 88,
+ 'START_P2P_DEVICE': 89,
+ 'STOP_P2P_DEVICE': 90,
+ 'CONN_FAILED': 91,
+ 'SET_MCAST_RATE': 92,
+ 'SET_MAC_ACL': 93,
+ 'RADAR_DETECT': 94,
+ 'GET_PROTOCOL_FEATURES': 95,
+ 'UPDATE_FT_IES': 96,
+ 'FT_EVENT': 97,
+ 'CRIT_PROTOCOL_START': 98,
+ 'CRIT_PROTOCOL_STOP': 99,
+ 'GET_COALESCE': 100,
+ 'SET_COALESCE': 101,
+ 'CHANNEL_SWITCH': 102,
+ 'VENDOR': 103,
+ 'SET_QOS_MAP': 104,
+}
+
+nl80211_attr = {
+ 'WIPHY': 1,
+ 'WIPHY_NAME': 2,
+ 'IFINDEX': 3,
+ 'IFNAME': 4,
+ 'IFTYPE': 5,
+ 'MAC': 6,
+ 'KEY_DATA': 7,
+ 'KEY_IDX': 8,
+ 'KEY_CIPHER': 9,
+ 'KEY_SEQ': 10,
+ 'KEY_DEFAULT': 11,
+ 'BEACON_INTERVAL': 12,
+ 'DTIM_PERIOD': 13,
+ 'BEACON_HEAD': 14,
+ 'BEACON_TAIL': 15,
+ 'STA_AID': 16,
+ 'STA_FLAGS': 17,
+ 'STA_LISTEN_INTERVAL': 18,
+ 'STA_SUPPORTED_RATES': 19,
+ 'STA_VLAN': 20,
+ 'STA_INFO': 21,
+ 'WIPHY_BANDS': 22,
+ 'MNTR_FLAGS': 23,
+ 'MESH_ID': 24,
+ 'STA_PLINK_ACTION': 25,
+ 'MPATH_NEXT_HOP': 26,
+ 'MPATH_INFO': 27,
+ 'BSS_CTS_PROT': 28,
+ 'BSS_SHORT_PREAMBLE': 29,
+ 'BSS_SHORT_SLOT_TIME': 30,
+ 'HT_CAPABILITY': 31,
+ 'SUPPORTED_IFTYPES': 32,
+ 'REG_ALPHA2': 33,
+ 'REG_RULES': 34,
+ 'MESH_CONFIG': 35,
+ 'BSS_BASIC_RATES': 36,
+ 'WIPHY_TXQ_PARAMS': 37,
+ 'WIPHY_FREQ': 38,
+ 'WIPHY_CHANNEL_TYPE': 39,
+ 'KEY_DEFAULT_MGMT': 40,
+ 'MGMT_SUBTYPE': 41,
+ 'IE': 42,
+ 'MAX_NUM_SCAN_SSIDS': 43,
+ 'SCAN_FREQUENCIES': 44,
+ 'SCAN_SSIDS': 45,
+ 'GENERATION': 46,
+ 'BSS': 47,
+ 'REG_INITIATOR': 48,
+ 'REG_TYPE': 49,
+ 'SUPPORTED_COMMANDS': 50,
+ 'FRAME': 51,
+ 'SSID': 52,
+ 'AUTH_TYPE': 53,
+ 'REASON_CODE': 54,
+ 'KEY_TYPE': 55,
+ 'MAX_SCAN_IE_LEN': 56,
+ 'CIPHER_SUITES': 57,
+ 'FREQ_BEFORE': 58,
+ 'FREQ_AFTER': 59,
+ 'FREQ_FIXED': 60,
+ 'WIPHY_RETRY_SHORT': 61,
+ 'WIPHY_RETRY_LONG': 62,
+ 'WIPHY_FRAG_THRESHOLD': 63,
+ 'WIPHY_RTS_THRESHOLD': 64,
+ 'TIMED_OUT': 65,
+ 'USE_MFP': 66,
+ 'STA_FLAGS2': 67,
+ 'CONTROL_PORT': 68,
+ 'TESTDATA': 69,
+ 'PRIVACY': 70,
+ 'DISCONNECTED_BY_AP': 71,
+ 'STATUS_CODE': 72,
+ 'CIPHER_SUITES_PAIRWISE': 73,
+ 'CIPHER_SUITE_GROUP': 74,
+ 'WPA_VERSIONS': 75,
+ 'AKM_SUITES': 76,
+ 'REQ_IE': 77,
+ 'RESP_IE': 78,
+ 'PREV_BSSID': 79,
+ 'KEY': 80,
+ 'KEYS': 81,
+ 'PID': 82,
+ '4ADDR': 83,
+ 'SURVEY_INFO': 84,
+ 'PMKID': 85,
+ 'MAX_NUM_PMKIDS': 86,
+ 'DURATION': 87,
+ 'COOKIE': 88,
+ 'WIPHY_COVERAGE_CLASS': 89,
+ 'TX_RATES': 90,
+ 'FRAME_MATCH': 91,
+ 'ACK': 92,
+ 'PS_STATE': 93,
+ 'CQM': 94,
+ 'LOCAL_STATE_CHANGE': 95,
+ 'AP_ISOLATE': 96,
+ 'WIPHY_TX_POWER_SETTING': 97,
+ 'WIPHY_TX_POWER_LEVEL': 98,
+ 'TX_FRAME_TYPES': 99,
+ 'RX_FRAME_TYPES': 100,
+ 'FRAME_TYPE': 101,
+ 'CONTROL_PORT_ETHERTYPE': 102,
+ 'CONTROL_PORT_NO_ENCRYPT': 103,
+ 'SUPPORT_IBSS_RSN': 104,
+ 'WIPHY_ANTENNA_TX': 105,
+ 'WIPHY_ANTENNA_RX': 106,
+ 'MCAST_RATE': 107,
+ 'OFFCHANNEL_TX_OK': 108,
+ 'BSS_HT_OPMODE': 109,
+ 'KEY_DEFAULT_TYPES': 110,
+ 'MAX_REMAIN_ON_CHANNEL_DURATION': 111,
+ 'MESH_SETUP': 112,
+ 'WIPHY_ANTENNA_AVAIL_TX': 113,
+ 'WIPHY_ANTENNA_AVAIL_RX': 114,
+ 'SUPPORT_MESH_AUTH': 115,
+ 'STA_PLINK_STATE': 116,
+ 'WOWLAN_TRIGGERS': 117,
+ 'WOWLAN_TRIGGERS_SUPPORTED': 118,
+ 'SCHED_SCAN_INTERVAL': 119,
+ 'INTERFACE_COMBINATIONS': 120,
+ 'SOFTWARE_IFTYPES': 121,
+ 'REKEY_DATA': 122,
+ 'MAX_NUM_SCHED_SCAN_SSIDS': 123,
+ 'MAX_SCHED_SCAN_IE_LEN': 124,
+ 'SCAN_SUPP_RATES': 125,
+ 'HIDDEN_SSID': 126,
+ 'IE_PROBE_RESP': 127,
+ 'IE_ASSOC_RESP': 128,
+ 'STA_WME': 129,
+ 'SUPPORT_AP_UAPSD': 130,
+ 'ROAM_SUPPORT': 131,
+ 'SCHED_SCAN_MATCH': 132,
+ 'MAX_MATCH_SETS': 133,
+ 'PMKSA_CANDIDATE': 134,
+ 'TX_NO_CCK_RATE': 135,
+ 'TDLS_ACTION': 136,
+ 'TDLS_DIALOG_TOKEN': 137,
+ 'TDLS_OPERATION': 138,
+ 'TDLS_SUPPORT': 139,
+ 'TDLS_EXTERNAL_SETUP': 140,
+ 'DEVICE_AP_SME': 141,
+ 'DONT_WAIT_FOR_ACK': 142,
+ 'FEATURE_FLAGS': 143,
+ 'PROBE_RESP_OFFLOAD': 144,
+ 'PROBE_RESP': 145,
+ 'DFS_REGION': 146,
+ 'DISABLE_HT': 147,
+ 'HT_CAPABILITY_MASK': 148,
+ 'NOACK_MAP': 149,
+ 'INACTIVITY_TIMEOUT': 150,
+ 'RX_SIGNAL_DBM': 151,
+ 'BG_SCAN_PERIOD': 152,
+ 'WDEV': 153,
+ 'USER_REG_HINT_TYPE': 154,
+ 'CONN_FAILED_REASON': 155,
+ 'SAE_DATA': 156,
+ 'VHT_CAPABILITY': 157,
+ 'SCAN_FLAGS': 158,
+ 'CHANNEL_WIDTH': 159,
+ 'CENTER_FREQ1': 160,
+ 'CENTER_FREQ2': 161,
+ 'P2P_CTWINDOW': 162,
+ 'P2P_OPPPS': 163,
+ 'LOCAL_MESH_POWER_MODE': 164,
+ 'ACL_POLICY': 165,
+ 'MAC_ADDRS': 166,
+ 'MAC_ACL_MAX': 167,
+ 'RADAR_EVENT': 168,
+ 'EXT_CAPA': 169,
+ 'EXT_CAPA_MASK': 170,
+ 'STA_CAPABILITY': 171,
+ 'STA_EXT_CAPABILITY': 172,
+ 'PROTOCOL_FEATURES': 173,
+ 'SPLIT_WIPHY_DUMP': 174,
+ 'DISABLE_VHT': 175,
+ 'VHT_CAPABILITY_MASK': 176,
+ 'MDID': 177,
+ 'IE_RIC': 178,
+ 'CRIT_PROT_ID': 179,
+ 'MAX_CRIT_PROT_DURATION': 180,
+ 'PEER_AID': 181,
+ 'COALESCE_RULE': 182,
+ 'CH_SWITCH_COUNT': 183,
+ 'CH_SWITCH_BLOCK_TX': 184,
+ 'CSA_IES': 185,
+ 'CSA_C_OFF_BEACON': 186,
+ 'CSA_C_OFF_PRESP': 187,
+ 'RXMGMT_FLAGS': 188,
+ 'STA_SUPPORTED_CHANNELS': 189,
+ 'STA_SUPPORTED_OPER_CLASSES': 190,
+ 'HANDLE_DFS': 191,
+ 'SUPPORT_5_MHZ': 192,
+ 'SUPPORT_10_MHZ': 193,
+ 'OPMODE_NOTIF': 194,
+ 'VENDOR_ID': 195,
+ 'VENDOR_SUBCMD': 196,
+ 'VENDOR_DATA': 197,
+ 'VENDOR_EVENTS': 198,
+ 'QOS_MAP': 199,
+ 'MAC_HINT': 200,
+ 'WIPHY_FREQ_HINT': 201,
+ 'MAX_AP_ASSOC_STA': 202,
+}
+
+def build_nl80211_attr(id, val):
+ attr = struct.pack("@HH", 4 + len(val), nl80211_attr[id]) + val
+ if len(attr) % 4 != 0:
+ attr += b'\x00' * (4 - (len(attr) % 4))
+ return attr
+
+def build_nl80211_attr_u32(id, val):
+ return build_nl80211_attr(id, struct.pack("@I", val))
+
+def build_nl80211_attr_u16(id, val):
+ return build_nl80211_attr(id, struct.pack("@H", val))
+
+def build_nl80211_attr_u8(id, val):
+ return build_nl80211_attr(id, struct.pack("@B", val))
+
+def build_nl80211_attr_flag(id):
+ return build_nl80211_attr(id, b'')
+
+def build_nl80211_attr_mac(id, val):
+ addr = struct.unpack('6B', binascii.unhexlify(val.replace(':', '')))
+ aval = struct.pack('<6B', *addr)
+ return build_nl80211_attr(id, aval)
+
+def parse_nl80211_attrs(msg):
+ attrs = {}
+ while len(msg) >= 4:
+ alen, attr = struct.unpack("@HH", msg[0:4])
+ if alen < 4:
+ raise Exception("Too short nl80211 attribute")
+ alen -= 4
+ msg = msg[4:]
+ if alen > len(msg):
+ raise Exception("nl80211 attribute underflow")
+ attrs[attr] = msg[0:alen]
+ msg = msg[alen:]
+ return attrs
diff --git a/contrib/wpa/tests/hwsim/owe-bss-1.conf b/contrib/wpa/tests/hwsim/owe-bss-1.conf
new file mode 100644
index 000000000000..40cadc992da8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/owe-bss-1.conf
@@ -0,0 +1,12 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3
+bssid=02:00:00:00:03:00
+ctrl_interface=/var/run/hostapd
+
+ssid=transition-mode-open
+owe_transition_ifname=wlan3-2
diff --git a/contrib/wpa/tests/hwsim/owe-bss-2.conf b/contrib/wpa/tests/hwsim/owe-bss-2.conf
new file mode 100644
index 000000000000..8a5415e9fd01
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/owe-bss-2.conf
@@ -0,0 +1,16 @@
+driver=nl80211
+
+hw_mode=g
+channel=1
+ieee80211n=1
+
+interface=wlan3-2
+bssid=02:00:00:00:03:01
+ctrl_interface=/var/run/hostapd
+
+ssid=transition-mode-owe
+wpa=2
+wpa_key_mgmt=OWE
+rsn_pairwise=CCMP
+owe_transition_ifname=wlan3
+ignore_broadcast_ssid=1
diff --git a/contrib/wpa/tests/hwsim/p2p0.conf b/contrib/wpa/tests/hwsim/p2p0.conf
new file mode 100644
index 000000000000..9482bdca4dc3
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/p2p0.conf
@@ -0,0 +1,3 @@
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
+device_name=Device A
+p2p_no_group_iface=1
diff --git a/contrib/wpa/tests/hwsim/p2p1.conf b/contrib/wpa/tests/hwsim/p2p1.conf
new file mode 100644
index 000000000000..3622b152366e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/p2p1.conf
@@ -0,0 +1,3 @@
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
+device_name=Device B
+p2p_no_group_iface=1
diff --git a/contrib/wpa/tests/hwsim/p2p2.conf b/contrib/wpa/tests/hwsim/p2p2.conf
new file mode 100644
index 000000000000..eda52e13bbf9
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/p2p2.conf
@@ -0,0 +1,3 @@
+ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=admin
+device_name=Device C
+p2p_no_group_iface=1
diff --git a/contrib/wpa/tests/hwsim/p2p_utils.py b/contrib/wpa/tests/hwsim/p2p_utils.py
new file mode 100644
index 000000000000..bfd8e2e44eaa
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/p2p_utils.py
@@ -0,0 +1,394 @@
+# P2P helper functions
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import threading
+import time
+try:
+ from Queue import Queue
+except ImportError:
+ from queue import Queue
+
+import hwsim_utils
+
+MGMT_SUBTYPE_PROBE_REQ = 4
+MGMT_SUBTYPE_ACTION = 13
+ACTION_CATEG_PUBLIC = 4
+
+P2P_GO_NEG_REQ = 0
+P2P_GO_NEG_RESP = 1
+P2P_GO_NEG_CONF = 2
+P2P_INVITATION_REQ = 3
+P2P_INVITATION_RESP = 4
+P2P_DEV_DISC_REQ = 5
+P2P_DEV_DISC_RESP = 6
+P2P_PROV_DISC_REQ = 7
+P2P_PROV_DISC_RESP = 8
+
+P2P_ATTR_STATUS = 0
+P2P_ATTR_MINOR_REASON_CODE = 1
+P2P_ATTR_CAPABILITY = 2
+P2P_ATTR_DEVICE_ID = 3
+P2P_ATTR_GROUP_OWNER_INTENT = 4
+P2P_ATTR_CONFIGURATION_TIMEOUT = 5
+P2P_ATTR_LISTEN_CHANNEL = 6
+P2P_ATTR_GROUP_BSSID = 7
+P2P_ATTR_EXT_LISTEN_TIMING = 8
+P2P_ATTR_INTENDED_INTERFACE_ADDR = 9
+P2P_ATTR_MANAGEABILITY = 10
+P2P_ATTR_CHANNEL_LIST = 11
+P2P_ATTR_NOTICE_OF_ABSENCE = 12
+P2P_ATTR_DEVICE_INFO = 13
+P2P_ATTR_GROUP_INFO = 14
+P2P_ATTR_GROUP_ID = 15
+P2P_ATTR_INTERFACE = 16
+P2P_ATTR_OPERATING_CHANNEL = 17
+P2P_ATTR_INVITATION_FLAGS = 18
+P2P_ATTR_OOB_GO_NEG_CHANNEL = 19
+P2P_ATTR_SERVICE_HASH = 21
+P2P_ATTR_SESSION_INFORMATION_DATA = 22
+P2P_ATTR_CONNECTION_CAPABILITY = 23
+P2P_ATTR_ADVERTISEMENT_ID = 24
+P2P_ATTR_ADVERTISED_SERVICE = 25
+P2P_ATTR_SESSION_ID = 26
+P2P_ATTR_FEATURE_CAPABILITY = 27
+P2P_ATTR_PERSISTENT_GROUP = 28
+P2P_ATTR_VENDOR_SPECIFIC = 221
+
+P2P_SC_SUCCESS = 0
+P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE = 1
+P2P_SC_FAIL_INCOMPATIBLE_PARAMS = 2
+P2P_SC_FAIL_LIMIT_REACHED = 3
+P2P_SC_FAIL_INVALID_PARAMS = 4
+P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE = 5
+P2P_SC_FAIL_PREV_PROTOCOL_ERROR = 6
+P2P_SC_FAIL_NO_COMMON_CHANNELS = 7
+P2P_SC_FAIL_UNKNOWN_GROUP = 8
+P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9
+P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10
+P2P_SC_FAIL_REJECTED_BY_USER = 11
+
+WSC_ATTR_CONFIG_METHODS = 0x1008
+
+WLAN_EID_SSID = 0
+WLAN_EID_SUPP_RATES = 1
+WLAN_EID_VENDOR_SPECIFIC = 221
+
+def go_neg_pin_authorized_persistent(i_dev, r_dev, i_intent=None, r_intent=None,
+ i_method='enter', r_method='display',
+ test_data=True, r_listen=True):
+ if r_listen:
+ r_dev.p2p_listen()
+ i_dev.p2p_listen()
+ pin = r_dev.wps_read_pin()
+ logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
+ r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), pin, r_method,
+ go_intent=r_intent, persistent=True)
+ if r_listen:
+ r_dev.p2p_listen()
+ i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method,
+ timeout=20, go_intent=i_intent,
+ persistent=True)
+ r_res = r_dev.p2p_go_neg_auth_result()
+ logger.debug("i_res: " + str(i_res))
+ logger.debug("r_res: " + str(r_res))
+ r_dev.dump_monitor()
+ i_dev.dump_monitor()
+ logger.info("Group formed")
+ if test_data:
+ hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
+ return [i_res, r_res]
+
+def terminate_group(go, cli):
+ logger.info("Terminate persistent group")
+ cli.close_monitor_group()
+ go.remove_group()
+ cli.wait_go_ending_session()
+
+def invite(inv, resp, extra=None, persistent_reconnect=True, use_listen=True):
+ addr = resp.p2p_dev_addr()
+ if persistent_reconnect:
+ resp.global_request("SET persistent_reconnect 1")
+ else:
+ resp.global_request("SET persistent_reconnect 0")
+ if use_listen:
+ resp.p2p_listen()
+ else:
+ resp.p2p_find(social=True)
+ if not inv.discover_peer(addr, social=True):
+ raise Exception("Peer " + addr + " not found")
+ inv.dump_monitor()
+ peer = inv.get_peer(addr)
+ cmd = "P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr
+ if extra:
+ cmd = cmd + " " + extra
+ inv.global_request(cmd)
+
+def check_result(go, cli):
+ ev = go.wait_global_event(["P2P-GROUP-STARTED",
+ "Failed to start AP functionality"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on group re-invocation (on GO)")
+ if "P2P-GROUP-STARTED" not in ev:
+ raise Exception("GO failed to start the group for re-invocation")
+ if "[PERSISTENT]" not in ev:
+ raise Exception("Re-invoked group not marked persistent")
+ go_res = go.group_form_result(ev)
+ if go_res['role'] != 'GO':
+ raise Exception("Persistent group GO did not become GO")
+ if not go_res['persistent']:
+ raise Exception("Persistent group not re-invoked as persistent (GO)")
+ ev = cli.wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on group re-invocation (on client)")
+ if "[PERSISTENT]" not in ev:
+ raise Exception("Re-invoked group not marked persistent")
+ cli_res = cli.group_form_result(ev)
+ if cli_res['role'] != 'client':
+ raise Exception("Persistent group client did not become client")
+ if not cli_res['persistent']:
+ raise Exception("Persistent group not re-invoked as persistent (cli)")
+ return [go_res, cli_res]
+
+def form(go, cli, test_data=True, reverse_init=False, r_listen=True):
+ logger.info("Form a persistent group")
+ if reverse_init:
+ [i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=cli, i_intent=0,
+ r_dev=go, r_intent=15,
+ test_data=test_data,
+ r_listen=r_listen)
+ else:
+ [i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=go, i_intent=15,
+ r_dev=cli, r_intent=0,
+ test_data=test_data,
+ r_listen=r_listen)
+ if not i_res['persistent'] or not r_res['persistent']:
+ raise Exception("Formed group was not persistent")
+ terminate_group(go, cli)
+ if reverse_init:
+ return r_res
+ else:
+ return i_res
+
+def invite_from_cli(go, cli, terminate=True):
+ logger.info("Re-invoke persistent group from client")
+ invite(cli, go)
+ [go_res, cli_res] = check_result(go, cli)
+ hwsim_utils.test_connectivity_p2p(go, cli)
+ if terminate:
+ terminate_group(go, cli)
+ return [go_res, cli_res]
+
+def invite_from_go(go, cli, terminate=True, extra=None):
+ logger.info("Re-invoke persistent group from GO")
+ invite(go, cli, extra=extra)
+ [go_res, cli_res] = check_result(go, cli)
+ hwsim_utils.test_connectivity_p2p(go, cli)
+ if terminate:
+ terminate_group(go, cli)
+ return [go_res, cli_res]
+
+def autogo(go, freq=None, persistent=None):
+ logger.info("Start autonomous GO " + go.ifname)
+ res = go.p2p_start_go(freq=freq, persistent=persistent)
+ logger.debug("res: " + str(res))
+ return res
+
+def connect_cli(go, client, social=False, freq=None):
+ logger.info("Try to connect the client to the GO")
+ pin = client.wps_read_pin()
+ go.p2p_go_authorize_client(pin)
+ res = client.p2p_connect_group(go.p2p_dev_addr(), pin, timeout=60,
+ social=social, freq=freq)
+ logger.info("Client connected")
+ hwsim_utils.test_connectivity_p2p(go, client)
+ return res
+
+def check_grpform_results(i_res, r_res):
+ if i_res['result'] != 'success' or r_res['result'] != 'success':
+ raise Exception("Failed group formation")
+ if i_res['ssid'] != r_res['ssid']:
+ raise Exception("SSID mismatch")
+ if i_res['freq'] != r_res['freq']:
+ raise Exception("freq mismatch")
+ if 'go_neg_freq' in r_res and i_res['go_neg_freq'] != r_res['go_neg_freq']:
+ raise Exception("go_neg_freq mismatch")
+ if i_res['freq'] != i_res['go_neg_freq']:
+ raise Exception("freq/go_neg_freq mismatch")
+ if i_res['role'] != i_res['go_neg_role']:
+ raise Exception("role/go_neg_role mismatch")
+ if 'go_neg_role' in r_res and r_res['role'] != r_res['go_neg_role']:
+ raise Exception("role/go_neg_role mismatch")
+ if i_res['go_dev_addr'] != r_res['go_dev_addr']:
+ raise Exception("GO Device Address mismatch")
+
+def go_neg_init(i_dev, r_dev, pin, i_method, i_intent, res):
+ logger.debug("Initiate GO Negotiation from i_dev")
+ try:
+ i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method, timeout=20, go_intent=i_intent)
+ logger.debug("i_res: " + str(i_res))
+ except Exception as e:
+ i_res = None
+ logger.info("go_neg_init thread caught an exception from p2p_go_neg_init: " + str(e))
+ res.put(i_res)
+
+def go_neg_pin(i_dev, r_dev, i_intent=None, r_intent=None, i_method='enter', r_method='display'):
+ r_dev.p2p_listen()
+ i_dev.p2p_listen()
+ pin = r_dev.wps_read_pin()
+ logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
+ r_dev.dump_monitor()
+ res = Queue()
+ t = threading.Thread(target=go_neg_init, args=(i_dev, r_dev, pin, i_method, i_intent, res))
+ t.start()
+ logger.debug("Wait for GO Negotiation Request on r_dev")
+ ev = r_dev.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+ if ev is None:
+ t.join()
+ raise Exception("GO Negotiation timed out")
+ r_dev.dump_monitor()
+ logger.debug("Re-initiate GO Negotiation from r_dev")
+ try:
+ r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), pin, r_method,
+ go_intent=r_intent, timeout=20)
+ except Exception as e:
+ logger.info("go_neg_pin - r_dev.p2p_go_neg_init() exception: " + str(e))
+ t.join()
+ raise
+ logger.debug("r_res: " + str(r_res))
+ r_dev.dump_monitor()
+ t.join()
+ i_res = res.get()
+ if i_res is None:
+ raise Exception("go_neg_init thread failed")
+ logger.debug("i_res: " + str(i_res))
+ logger.info("Group formed")
+ hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
+ i_dev.dump_monitor()
+ return [i_res, r_res]
+
+def go_neg_pin_authorized(i_dev, r_dev, i_intent=None, r_intent=None,
+ expect_failure=False, i_go_neg_status=None,
+ i_method='enter', r_method='display', test_data=True,
+ i_freq=None, r_freq=None,
+ i_freq2=None, r_freq2=None,
+ i_max_oper_chwidth=None, r_max_oper_chwidth=None,
+ i_ht40=False, i_vht=False, r_ht40=False, r_vht=False):
+ i_dev.p2p_listen()
+ pin = r_dev.wps_read_pin()
+ logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
+ r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), pin, r_method,
+ go_intent=r_intent, freq=r_freq, freq2=r_freq2,
+ max_oper_chwidth=r_max_oper_chwidth, ht40=r_ht40,
+ vht=r_vht)
+ r_dev.p2p_listen()
+ i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method,
+ timeout=20, go_intent=i_intent,
+ expect_failure=expect_failure, freq=i_freq,
+ freq2=i_freq2,
+ max_oper_chwidth=i_max_oper_chwidth,
+ ht40=i_ht40, vht=i_vht)
+ r_res = r_dev.p2p_go_neg_auth_result(expect_failure=expect_failure)
+ logger.debug("i_res: " + str(i_res))
+ logger.debug("r_res: " + str(r_res))
+ r_dev.dump_monitor()
+ i_dev.dump_monitor()
+ if i_go_neg_status:
+ if i_res['result'] != 'go-neg-failed':
+ raise Exception("Expected GO Negotiation failure not reported")
+ if i_res['status'] != i_go_neg_status:
+ raise Exception("Expected GO Negotiation status not seen")
+ if expect_failure:
+ return
+ logger.info("Group formed")
+ if test_data:
+ hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
+ return [i_res, r_res]
+
+def go_neg_init_pbc(i_dev, r_dev, i_intent, res, freq, provdisc):
+ logger.debug("Initiate GO Negotiation from i_dev")
+ try:
+ i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), None, "pbc",
+ timeout=20, go_intent=i_intent, freq=freq,
+ provdisc=provdisc)
+ logger.debug("i_res: " + str(i_res))
+ except Exception as e:
+ i_res = None
+ logger.info("go_neg_init_pbc thread caught an exception from p2p_go_neg_init: " + str(e))
+ res.put(i_res)
+
+def go_neg_pbc(i_dev, r_dev, i_intent=None, r_intent=None, i_freq=None, r_freq=None, provdisc=False, r_listen=False):
+ if r_listen:
+ r_dev.p2p_listen()
+ else:
+ r_dev.p2p_find(social=True)
+ i_dev.p2p_find(social=True)
+ logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
+ r_dev.dump_monitor()
+ res = Queue()
+ t = threading.Thread(target=go_neg_init_pbc, args=(i_dev, r_dev, i_intent, res, i_freq, provdisc))
+ t.start()
+ logger.debug("Wait for GO Negotiation Request on r_dev")
+ ev = r_dev.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+ if ev is None:
+ t.join()
+ raise Exception("GO Negotiation timed out")
+ r_dev.dump_monitor()
+ # Allow some time for the GO Neg Resp to go out before initializing new
+ # GO Negotiation.
+ time.sleep(0.2)
+ logger.debug("Re-initiate GO Negotiation from r_dev")
+ try:
+ r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), None, "pbc",
+ go_intent=r_intent, timeout=20,
+ freq=r_freq)
+ except Exception as e:
+ logger.info("go_neg_pbc - r_dev.p2p_go_neg_init() exception: " + str(e))
+ t.join()
+ raise
+ logger.debug("r_res: " + str(r_res))
+ r_dev.dump_monitor()
+ t.join()
+ i_res = res.get()
+ if i_res is None:
+ raise Exception("go_neg_init_pbc thread failed")
+ logger.debug("i_res: " + str(i_res))
+ logger.info("Group formed")
+ hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
+ i_dev.dump_monitor()
+ return [i_res, r_res]
+
+def go_neg_pbc_authorized(i_dev, r_dev, i_intent=None, r_intent=None,
+ expect_failure=False, i_freq=None, r_freq=None):
+ i_dev.p2p_listen()
+ logger.info("Start GO negotiation " + i_dev.ifname + " -> " + r_dev.ifname)
+ r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), None, "pbc",
+ go_intent=r_intent, freq=r_freq)
+ r_dev.p2p_listen()
+ i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), None, "pbc", timeout=20,
+ go_intent=i_intent,
+ expect_failure=expect_failure, freq=i_freq)
+ r_res = r_dev.p2p_go_neg_auth_result(expect_failure=expect_failure)
+ logger.debug("i_res: " + str(i_res))
+ logger.debug("r_res: " + str(r_res))
+ r_dev.dump_monitor()
+ i_dev.dump_monitor()
+ if expect_failure:
+ return
+ logger.info("Group formed")
+ return [i_res, r_res]
+
+def remove_group(dev1, dev2, allow_failure=False):
+ try:
+ dev1.remove_group()
+ except:
+ if not allow_failure:
+ raise
+ try:
+ dev2.remove_group()
+ except:
+ pass
diff --git a/contrib/wpa/tests/hwsim/pps-mo-1.xml b/contrib/wpa/tests/hwsim/pps-mo-1.xml
new file mode 100644
index 000000000000..b5f818537230
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/pps-mo-1.xml
@@ -0,0 +1,62 @@
+<PerProviderSubscription>
+ <UpdateIdentifier>1</UpdateIdentifier>
+ <Cred01>
+ <Policy>
+ <PreferredRoamingPartnerList>
+ <RP01>
+ <FQDN_Match>another.example.org,includeSubdomains</FQDN_Match>
+ <Priority>10</Priority>
+ </RP01>
+ <RP02>
+ <FQDN_Match>example.com,exactMatch</FQDN_Match>
+ <Priority>20</Priority>
+ </RP02>
+ </PreferredRoamingPartnerList>
+ <PolicyUpdate>
+ <UpdateInterval>10</UpdateInterval>
+ <UpdateMethod>SPP-ClientInitiated</UpdateMethod>
+ <Restriction>Unrestricted</Restriction>
+ <URI>https://policy.example.com/run</URI>
+ <TrustRoot>
+ <CertURL>http://example.com/policy-root.der</CertURL>
+ <CertSHA256Fingerprint>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</CertSHA256Fingerprint>
+ </TrustRoot>
+ </PolicyUpdate>
+ </Policy>
+ <CredentialPriority>1</CredentialPriority>
+ <AAAServerTrustRoot>
+ <Root1>
+ <CertURL>http://example.com/cacert.der</CertURL>
+ <CertSHA256Fingerprint>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</CertSHA256Fingerprint>
+ </Root1>
+ </AAAServerTrustRoot>
+ <SubscriptionUpdate>
+ <UpdateInterval>4294967295</UpdateInterval>
+ <UpdateMethod>SPP-ClientInitiated</UpdateMethod>
+ <Restriction>HomeSP</Restriction>
+ <URI>https://remediation.example.com/run</URI>
+ <TrustRoot>
+ <CertURL>http://example.com/subscription-root.der</CertURL>
+ <CertSHA256Fingerprint>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</CertSHA256Fingerprint>
+ </TrustRoot>
+ </SubscriptionUpdate>
+ <HomeSP>
+ <FriendlyName>Example Operator</FriendlyName>
+ <FQDN>w1.fi</FQDN>
+ <RoamingConsortiumOI>010203040506</RoamingConsortiumOI>
+ </HomeSP>
+ <Credential>
+ <CreationDate>2012-12-01T12:00:00Z</CreationDate>
+ <UsernamePassword>
+ <Username>hs20-test</Username>
+ <Password>cGFzc3dvcmQ=</Password>
+ <MachineManaged>TRUE</MachineManaged>
+ <EAPMethod>
+ <EAPType>21</EAPType>
+ <InnerMethod>MS-CHAP-V2</InnerMethod>
+ </EAPMethod>
+ </UsernamePassword>
+ <Realm>w1.fi</Realm>
+ </Credential>
+ </Cred01>
+</PerProviderSubscription>
diff --git a/contrib/wpa/tests/hwsim/radius_das.py b/contrib/wpa/tests/hwsim/radius_das.py
new file mode 100644
index 000000000000..4a43da4474f7
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/radius_das.py
@@ -0,0 +1,47 @@
+# RADIUS DAS extensions to pyrad
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hashlib
+import random
+import struct
+import pyrad.packet
+
+class DisconnectPacket(pyrad.packet.Packet):
+ def __init__(self, code=pyrad.packet.DisconnectRequest, id=None,
+ secret=None, authenticator=None, **attributes):
+ pyrad.packet.Packet.__init__(self, code, id, secret, authenticator,
+ **attributes)
+
+ def RequestPacket(self):
+ attr = b''
+ for code, datalst in sorted(self.items()):
+ for data in datalst:
+ attr += self._PktEncodeAttribute(code, data)
+
+ if self.id is None:
+ self.id = random.randrange(0, 256)
+
+ header = struct.pack('!BBH', self.code, self.id, (20 + len(attr)))
+ self.authenticator = hashlib.md5(header[0:4] + 16 * b'\x00' + attr
+ + self.secret).digest()
+ return header + self.authenticator + attr
+
+class CoAPacket(pyrad.packet.Packet):
+ def __init__(self, code=pyrad.packet.CoARequest, id=None,
+ secret=None, authenticator=None, **attributes):
+ pyrad.packet.Packet.__init__(self, code, id, secret, authenticator,
+ **attributes)
+
+ def RequestPacket(self):
+ attr = self._PktEncodeAttributes()
+
+ if self.id is None:
+ self.id = random.randrange(0, 256)
+
+ header = struct.pack('!BBH', self.code, self.id, (20 + len(attr)))
+ self.authenticator = hashlib.md5(header[0:4] + 16 * b'\x00' + attr
+ + self.secret).digest()
+ return header + self.authenticator + attr
diff --git a/contrib/wpa/tests/hwsim/remotehost.py b/contrib/wpa/tests/hwsim/remotehost.py
new file mode 100644
index 000000000000..0799b951f2e7
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/remotehost.py
@@ -0,0 +1,258 @@
+# Host class
+# Copyright (c) 2016, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+import subprocess
+import threading
+import tempfile
+import os
+import traceback
+import select
+
+logger = logging.getLogger()
+
+def remote_compatible(func):
+ func.remote_compatible = True
+ return func
+
+def execute_thread(command, reply):
+ cmd = ' '.join(command)
+ logger.debug("thread run: " + cmd)
+ err = tempfile.TemporaryFile()
+ try:
+ status = 0
+ buf = subprocess.check_output(command, stderr=err).decode()
+ except subprocess.CalledProcessError as e:
+ status = e.returncode
+ err.seek(0)
+ buf = err.read()
+ err.close()
+
+ logger.debug("thread cmd: " + cmd)
+ logger.debug("thread exit status: " + str(status))
+ logger.debug("thread exit buf: " + str(buf))
+ reply.append(status)
+ reply.append(buf)
+
+def gen_reaper_file(conf):
+ fd, filename = tempfile.mkstemp(dir='/tmp', prefix=conf + '-')
+ f = os.fdopen(fd, 'w')
+
+ f.write("#!/bin/sh\n")
+ f.write("name=\"$(basename $0)\"\n")
+ f.write("echo $$ > /tmp/$name.pid\n")
+ f.write("exec \"$@\"\n");
+
+ return filename;
+
+class Host():
+ def __init__(self, host=None, ifname=None, port=None, name="", user="root"):
+ self.host = host
+ self.name = name
+ self.user = user
+ self.monitors = []
+ self.monitor_thread = None
+ self.logs = []
+ self.ifname = ifname
+ self.port = port
+ self.dev = None
+ self.monitor_params = []
+ if self.name == "" and host != None:
+ self.name = host
+
+ def local_execute(self, command):
+ logger.debug("execute: " + str(command))
+ err = tempfile.TemporaryFile()
+ try:
+ status = 0
+ buf = subprocess.check_output(command, stderr=err)
+ except subprocess.CalledProcessError as e:
+ status = e.returncode
+ err.seek(0)
+ buf = err.read()
+ err.close()
+
+ logger.debug("status: " + str(status))
+ logger.debug("buf: " + str(buf))
+ return status, buf.decode()
+
+ def execute(self, command):
+ if self.host is None:
+ return self.local_execute(command)
+
+ cmd = ["ssh", self.user + "@" + self.host, ' '.join(command)]
+ _cmd = self.name + " execute: " + ' '.join(cmd)
+ logger.debug(_cmd)
+ err = tempfile.TemporaryFile()
+ try:
+ status = 0
+ buf = subprocess.check_output(cmd, stderr=err)
+ except subprocess.CalledProcessError as e:
+ status = e.returncode
+ err.seek(0)
+ buf = err.read()
+ err.close()
+
+ logger.debug(self.name + " status: " + str(status))
+ logger.debug(self.name + " buf: " + str(buf))
+ return status, buf.decode()
+
+ # async execute
+ def thread_run(self, command, res, use_reaper=True):
+ if use_reaper:
+ filename = gen_reaper_file("reaper")
+ self.send_file(filename, filename)
+ self.execute(["chmod", "755", filename])
+ _command = [filename] + command
+ else:
+ filename = ""
+ _command = command
+
+ if self.host is None:
+ cmd = _command
+ else:
+ cmd = ["ssh", self.user + "@" + self.host, ' '.join(_command)]
+ _cmd = self.name + " thread_run: " + ' '.join(cmd)
+ logger.debug(_cmd)
+ t = threading.Thread(target=execute_thread, name=filename, args=(cmd, res))
+ t.start()
+ return t
+
+ def thread_stop(self, t):
+ if t.name.find("reaper") == -1:
+ raise Exception("use_reaper required")
+
+ pid_file = t.name + ".pid"
+
+ if t.is_alive():
+ cmd = ["kill `cat " + pid_file + "`"]
+ self.execute(cmd)
+
+ # try again
+ self.thread_wait(t, 5)
+ if t.is_alive():
+ cmd = ["kill `cat " + pid_file + "`"]
+ self.execute(cmd)
+
+ # try with -9
+ self.thread_wait(t, 5)
+ if t.is_alive():
+ cmd = ["kill -9 `cat " + pid_file + "`"]
+ self.execute(cmd)
+
+ self.thread_wait(t, 5)
+ if t.is_alive():
+ raise Exception("thread still alive")
+
+ self.execute(["rm", pid_file])
+ self.execute(["rm", t.name])
+ self.local_execute(["rm", t.name])
+
+ def thread_wait(self, t, wait=None):
+ if wait == None:
+ wait_str = "infinite"
+ else:
+ wait_str = str(wait) + "s"
+
+ logger.debug(self.name + " thread_wait(" + wait_str + "): ")
+ if t.is_alive():
+ t.join(wait)
+
+ def pending(self, s, timeout=0):
+ [r, w, e] = select.select([s], [], [], timeout)
+ if r:
+ return True
+ return False
+
+ def proc_run(self, command):
+ filename = gen_reaper_file("reaper")
+ self.send_file(filename, filename)
+ self.execute(["chmod", "755", filename])
+ _command = [filename] + command
+
+ if self.host:
+ cmd = ["ssh", self.user + "@" + self.host, ' '.join(_command)]
+ else:
+ cmd = _command
+
+ _cmd = self.name + " proc_run: " + ' '.join(cmd)
+ logger.debug(_cmd)
+ err = tempfile.TemporaryFile()
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=err)
+ proc.reaper_file = filename
+ return proc
+
+ def proc_wait_event(self, proc, events, timeout=10):
+ if not isinstance(events, list):
+ raise Exception("proc_wait_event() events not a list")
+
+ logger.debug(self.name + " proc_wait_event: " + ' '.join(events) + " timeout: " + str(timeout))
+ start = os.times()[4]
+ try:
+ while True:
+ while self.pending(proc.stdout):
+ line = proc.stdout.readline()
+ if not line:
+ return None
+ line = line.decode()
+ logger.debug(line.strip('\n'))
+ for event in events:
+ if event in line:
+ return line
+ now = os.times()[4]
+ remaining = start + timeout - now
+ if remaining <= 0:
+ break
+ if not self.pending(proc.stdout, timeout=remaining):
+ break
+ except:
+ logger.debug(traceback.format_exc())
+ pass
+ return None
+
+ def proc_stop(self, proc):
+ if not proc:
+ return
+
+ self.execute(["kill `cat " + proc.reaper_file + ".pid`"])
+ self.execute(["rm", proc.reaper_file + ".pid"])
+ self.execute(["rm", proc.reaper_file])
+ self.local_execute(["rm", proc.reaper_file])
+ proc.kill()
+
+ def proc_dump(self, proc):
+ if not proc:
+ return ""
+ return proc.stdout.read()
+
+ def execute_and_wait_event(self, command, events, timeout=10):
+ proc = None
+ ev = None
+
+ try:
+ proc = self.proc_run(command)
+ ev = self.proc_wait_event(proc, events, timeout)
+ except:
+ pass
+
+ self.proc_stop(proc)
+ return ev
+
+ def add_log(self, log_file):
+ self.logs.append(log_file)
+
+ def get_logs(self, local_log_dir=None):
+ for log in self.logs:
+ if local_log_dir:
+ self.local_execute(["scp", self.user + "@[" + self.host + "]:" + log, local_log_dir])
+ self.execute(["rm", log])
+ del self.logs[:]
+
+ def send_file(self, src, dst):
+ if self.host is None:
+ return
+ self.local_execute(["scp", src,
+ self.user + "@[" + self.host + "]:" + dst])
diff --git a/contrib/wpa/tests/hwsim/rfkill.py b/contrib/wpa/tests/hwsim/rfkill.py
new file mode 100755
index 000000000000..72b2527feaa8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/rfkill.py
@@ -0,0 +1,152 @@
+#!/usr/bin/env python
+#
+# rfkill control code
+#
+# Copyright (c) 2015 Intel Corporation
+#
+# Author: Johannes Berg <johannes.berg@intel.com>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import struct
+import fcntl
+import os
+
+(TYPE_ALL,
+ TYPE_WLAN,
+ TYPE_BLUETOOTH,
+ TYPE_UWB,
+ TYPE_WIMAX,
+ TYPE_WWAN,
+ TYPE_GPS,
+ TYPE_FM,
+ TYPE_NFC) = list(range(9))
+
+(_OP_ADD,
+ _OP_DEL,
+ _OP_CHANGE,
+ _OP_CHANGE_ALL) = list(range(4))
+
+_type_names = {
+ TYPE_ALL: "all",
+ TYPE_WLAN: "Wireless LAN",
+ TYPE_BLUETOOTH: "Bluetooth",
+ TYPE_UWB: "Ultra-Wideband",
+ TYPE_WIMAX: "WiMAX",
+ TYPE_WWAN: "Wireless WAN",
+ TYPE_GPS: "GPS",
+ TYPE_FM: "FM",
+ TYPE_NFC: "NFC",
+}
+
+# idx, type, op, soft, hard
+_event_struct = '@IBBBB'
+_event_sz = struct.calcsize(_event_struct)
+
+class RFKillException(Exception):
+ pass
+
+class RFKill(object):
+ def __init__(self, idx):
+ self._idx = idx
+ self._type = None
+
+ @property
+ def idx(self):
+ return self._idx
+
+ @property
+ def name(self):
+ return open('/sys/class/rfkill/rfkill%d/name' % self._idx, 'r').read().rstrip()
+
+ @property
+ def type(self):
+ if not self._type:
+ for r, s, h in RFKill.list():
+ if r.idx == self.idx:
+ self._type = r._type
+ break
+ return self._type
+
+ @property
+ def type_name(self):
+ return _type_names.get(self._type, "unknown")
+
+ @property
+ def blocked(self):
+ l = RFKill.list()
+ for r, s, h in l:
+ if r.idx == self.idx:
+ return (s, h)
+ raise RFKillException("RFKill instance no longer exists")
+
+ @property
+ def soft_blocked(self):
+ return self.blocked[0]
+
+ @soft_blocked.setter
+ def soft_blocked(self, block):
+ if block:
+ self.block()
+ else:
+ self.unblock()
+
+ @property
+ def hard_blocked(self):
+ return self.blocked[1]
+
+ def block(self):
+ rfk = open('/dev/rfkill', 'wb')
+ s = struct.pack(_event_struct, self.idx, TYPE_ALL, _OP_CHANGE, 1, 0)
+ rfk.write(s)
+ rfk.close()
+
+ def unblock(self):
+ rfk = open('/dev/rfkill', 'wb')
+ s = struct.pack(_event_struct, self.idx, TYPE_ALL, _OP_CHANGE, 0, 0)
+ rfk.write(s)
+ rfk.close()
+
+ @classmethod
+ def block_all(cls, t=TYPE_ALL):
+ rfk = open('/dev/rfkill', 'wb')
+ print(rfk)
+ s = struct.pack(_event_struct, 0, t, _OP_CHANGE_ALL, 1, 0)
+ rfk.write(s)
+ rfk.close()
+
+ @classmethod
+ def unblock_all(cls, t=TYPE_ALL):
+ rfk = open('/dev/rfkill', 'wb')
+ s = struct.pack(_event_struct, 0, t, _OP_CHANGE_ALL, 0, 0)
+ rfk.write(s)
+ rfk.close()
+
+ @classmethod
+ def list(cls):
+ res = []
+ rfk = open('/dev/rfkill', 'rb', buffering=0)
+ fd = rfk.fileno()
+ flgs = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, flgs | os.O_NONBLOCK)
+ while True:
+ try:
+ d = rfk.read(_event_sz)
+ if d == None:
+ break
+ _idx, _t, _op, _s, _h = struct.unpack(_event_struct, d)
+ if _op != _OP_ADD:
+ continue
+ r = RFKill(_idx)
+ r._type = _t
+ res.append((r, _s, _h))
+ except IOError:
+ break
+ return res
+
+if __name__ == "__main__":
+ for r, s, h in RFKill.list():
+ print("%d: %s: %s" % (r.idx, r.name, r.type_name))
+ print("\tSoft blocked: %s" % ("yes" if s else "no"))
+ print("\tHard blocked: %s" % ("yes" if h else "no"))
diff --git a/contrib/wpa/tests/hwsim/run-all.sh b/contrib/wpa/tests/hwsim/run-all.sh
new file mode 100755
index 000000000000..ee48cd0581c6
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/run-all.sh
@@ -0,0 +1,162 @@
+#!/bin/sh
+
+errors=0
+umask 0002
+
+DATE="$(date +%s)"
+unset LOGBASEDIR
+if [ -z "$LOGDIR" ]; then
+ LOGBASEDIR=logs
+ LOGDIR=$LOGBASEDIR/$DATE
+ mkdir -p $LOGDIR
+fi
+export LOGDIR
+
+if [ -z "$DBFILE" ]; then
+ DB=""
+else
+ DB="-S $DBFILE --commit $(git rev-parse HEAD)"
+ if [ -n "$BUILD" ]; then
+ DB="$DB -b $BUILD"
+ fi
+ if [ "$PREFILL_DB" = "y" ] ; then
+ DB="$DB --prefill-tests"
+ fi
+fi
+
+usage()
+{
+ echo "$0 [-v | --valgrind | valgrind] [-t | --trace | trace]"
+ echo "\t[-n <num> | --channels <num>] [-B | --build]"
+ echo "\t[-c | --codecov ] [run-tests.py parameters]"
+ exit 1
+}
+
+unset VALGRIND
+unset TRACE
+unset TRACE_ARGS
+unset RUN_TEST_ARGS
+unset BUILD
+unset BUILD_ARGS
+unset CODECOV
+unset VM
+while [ "$1" != "" ]; do
+ case $1 in
+ -v | --valgrind | valgrind)
+ shift
+ echo "$0: using valgrind"
+ VALGRIND=valgrind
+ ;;
+ -t | --trace | trace)
+ shift
+ echo "$0: using Trace"
+ TRACE=trace
+ ;;
+ -n | --channels)
+ shift
+ NUM_CH=$1
+ shift
+ echo "$0: using channels=$NUM_CH"
+ ;;
+ -B | --build)
+ shift
+ echo "$0: build before running tests"
+ BUILD=build
+ ;;
+ -c | --codecov)
+ shift
+ echo "$0: using code coverage"
+ CODECOV=lcov
+ BUILD_ARGS=-c
+ ;;
+ -h | --help)
+ usage
+ ;;
+ -V | --vm)
+ shift
+ echo "$0: running inside a VM"
+ VM=VM
+ ;;
+
+ *)
+ RUN_TEST_ARGS="$RUN_TEST_ARGS$1 "
+ shift
+ ;;
+ esac
+done
+
+if [ ! -z "$RUN_TEST_ARGS" ]; then
+ echo "$0: passing the following args to run-tests.py: $RUN_TEST_ARGS"
+fi
+
+unset SUFFIX
+if [ ! -z "$BUILD" ]; then
+ SUFFIX=-build
+fi
+
+if [ ! -z "$VALGRIND" ]; then
+ SUFFIX=$SUFFIX-valgrind
+fi
+
+if [ ! -z "$TRACE" ]; then
+ SUFFIX=$SUFFIX-trace
+ TRACE_ARGS="-T"
+fi
+
+if [ ! -z "$CODECOV" ]; then
+ SUFFIX=$SUFFIX-codecov
+fi
+
+if [ ! -z "$BUILD" ]; then
+ echo "Building with args=$BUILD_ARGS"
+ if ! ./build.sh $BUILD_ARGS; then
+ echo "Failed building components"
+ exit 1
+ fi
+fi
+
+if ! ./start.sh $VM $VALGRIND $TRACE channels=$NUM_CH; then
+ if ! [ -z "$LOGBASEDIR" ] ; then
+ echo "Could not start test environment" > $LOGDIR/run
+ fi
+ exit 1
+fi
+
+# Only use sudo if not already root.
+if [ "$(id -u)" != 0 ]; then
+ SUDO=sudo
+else
+ SUDO=
+fi
+${SUDO} ./run-tests.py -D --logdir "$LOGDIR" $TRACE_ARGS -q $DB $RUN_TEST_ARGS || errors=1
+
+./stop.sh
+
+if [ ! -z "$VALGRIND" ] ; then
+ failures=`grep "ERROR SUMMARY" $LOGDIR/valgrind-* | grep -v " 0 errors" | wc -l`
+ if [ $failures -gt 0 ]; then
+ echo "Mark as failed due to valgrind errors"
+ errors=1
+ fi
+fi
+
+if tail -100 $LOGDIR/auth_serv | grep -q MEMLEAK; then
+ echo "Mark as failed due to authentication server memory leak"
+ errors=1
+fi
+
+if [ ! -z "$CODECOV" ] ; then
+ lcov -q --capture --directory ../../wpa_supplicant --output-file $LOGDIR/wpas_lcov.info
+ genhtml -q $LOGDIR/wpas_lcov.info --output-directory $LOGDIR/wpas_lcov
+ lcov -q --capture --directory ../../hostapd --output-file $LOGDIR/hostapd_lcov.info
+ genhtml -q $LOGDIR/hostapd_lcov.info --output-directory $LOGDIR/hostapd_lcov
+fi
+
+if [ $errors -gt 0 ]; then
+ if [ -z $VM ]; then
+ tar czf /tmp/hwsim-tests-$DATE-FAILED$SUFFIX.tar.gz $LOGDIR/
+ fi
+ exit 1
+fi
+
+echo "ALL-PASSED"
diff --git a/contrib/wpa/tests/hwsim/run-tests.py b/contrib/wpa/tests/hwsim/run-tests.py
new file mode 100755
index 000000000000..019533f54423
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/run-tests.py
@@ -0,0 +1,692 @@
+#!/usr/bin/env python3
+#
+# Test case executor
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import re
+import sys
+import time
+from datetime import datetime
+import argparse
+import subprocess
+import termios
+
+import logging
+logger = logging.getLogger()
+
+try:
+ import sqlite3
+ sqlite3_imported = True
+except ImportError:
+ sqlite3_imported = False
+
+scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
+sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
+
+from wpasupplicant import WpaSupplicant
+from hostapd import HostapdGlobal
+from check_kernel import check_kernel
+from wlantest import Wlantest
+from utils import HwsimSkip
+
+def set_term_echo(fd, enabled):
+ [iflag, oflag, cflag, lflag, ispeed, ospeed, cc] = termios.tcgetattr(fd)
+ if enabled:
+ lflag |= termios.ECHO
+ else:
+ lflag &= ~termios.ECHO
+ termios.tcsetattr(fd, termios.TCSANOW,
+ [iflag, oflag, cflag, lflag, ispeed, ospeed, cc])
+
+def reset_devs(dev, apdev):
+ ok = True
+ for d in dev:
+ try:
+ d.reset()
+ except Exception as e:
+ logger.info("Failed to reset device " + d.ifname)
+ print(str(e))
+ ok = False
+
+ wpas = None
+ try:
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5', monitor=False)
+ ifaces = wpas.global_request("INTERFACES").splitlines()
+ for iface in ifaces:
+ if iface.startswith("wlan"):
+ wpas.interface_remove(iface)
+ except Exception as e:
+ pass
+ if wpas:
+ wpas.close_ctrl()
+ del wpas
+
+ try:
+ hapd = HostapdGlobal()
+ hapd.flush()
+ hapd.remove('wlan3-6')
+ hapd.remove('wlan3-5')
+ hapd.remove('wlan3-4')
+ hapd.remove('wlan3-3')
+ hapd.remove('wlan3-2')
+ for ap in apdev:
+ hapd.remove(ap['ifname'])
+ hapd.remove('as-erp')
+ except Exception as e:
+ logger.info("Failed to remove hostapd interface")
+ print(str(e))
+ ok = False
+ return ok
+
+def add_log_file(conn, test, run, type, path):
+ if not os.path.exists(path):
+ return
+ contents = None
+ with open(path, 'rb') as f:
+ contents = f.read()
+ if contents is None:
+ return
+ sql = "INSERT INTO logs(test,run,type,contents) VALUES(?, ?, ?, ?)"
+ params = (test, run, type, sqlite3.Binary(contents))
+ try:
+ conn.execute(sql, params)
+ conn.commit()
+ except Exception as e:
+ print("sqlite: " + str(e))
+ print("sql: %r" % (params, ))
+
+def report(conn, prefill, build, commit, run, test, result, duration, logdir,
+ sql_commit=True):
+ if conn:
+ if not build:
+ build = ''
+ if not commit:
+ commit = ''
+ if prefill:
+ conn.execute('DELETE FROM results WHERE test=? AND run=? AND result=?', (test, run, 'NOTRUN'))
+ sql = "INSERT INTO results(test,result,run,time,duration,build,commitid) VALUES(?, ?, ?, ?, ?, ?, ?)"
+ params = (test, result, run, time.time(), duration, build, commit)
+ try:
+ conn.execute(sql, params)
+ if sql_commit:
+ conn.commit()
+ except Exception as e:
+ print("sqlite: " + str(e))
+ print("sql: %r" % (params, ))
+
+ if result == "FAIL":
+ for log in ["log", "log0", "log1", "log2", "log3", "log5",
+ "hostapd", "dmesg", "hwsim0", "hwsim0.pcapng"]:
+ add_log_file(conn, test, run, log,
+ logdir + "/" + test + "." + log)
+
+class DataCollector(object):
+ def __init__(self, logdir, testname, args):
+ self._logdir = logdir
+ self._testname = testname
+ self._tracing = args.tracing
+ self._dmesg = args.dmesg
+ self._dbus = args.dbus
+ def __enter__(self):
+ if self._tracing:
+ output = os.path.abspath(os.path.join(self._logdir, '%s.dat' % (self._testname, )))
+ self._trace_cmd = subprocess.Popen(['trace-cmd', 'record', '-o', output, '-e', 'mac80211', '-e', 'cfg80211', '-e', 'printk', 'sh', '-c', 'echo STARTED ; read l'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=open('/dev/null', 'w'),
+ cwd=self._logdir)
+ l = self._trace_cmd.stdout.read(7)
+ while self._trace_cmd.poll() is None and b'STARTED' not in l:
+ l += self._trace_cmd.stdout.read(1)
+ res = self._trace_cmd.returncode
+ if res:
+ print("Failed calling trace-cmd: returned exit status %d" % res)
+ sys.exit(1)
+ if self._dbus:
+ output = os.path.abspath(os.path.join(self._logdir, '%s.dbus' % (self._testname, )))
+ self._dbus_cmd = subprocess.Popen(['dbus-monitor', '--system'],
+ stdout=open(output, 'w'),
+ stderr=open('/dev/null', 'w'),
+ cwd=self._logdir)
+ res = self._dbus_cmd.returncode
+ if res:
+ print("Failed calling dbus-monitor: returned exit status %d" % res)
+ sys.exit(1)
+ def __exit__(self, type, value, traceback):
+ if self._tracing:
+ self._trace_cmd.stdin.write(b'DONE\n')
+ self._trace_cmd.stdin.flush()
+ self._trace_cmd.wait()
+ if self._dmesg:
+ output = os.path.join(self._logdir, '%s.dmesg' % (self._testname, ))
+ num = 0
+ while os.path.exists(output):
+ output = os.path.join(self._logdir, '%s.dmesg-%d' % (self._testname, num))
+ num += 1
+ subprocess.call(['dmesg', '-c'], stdout=open(output, 'w'))
+
+def rename_log(logdir, basename, testname, dev):
+ try:
+ import getpass
+ srcname = os.path.join(logdir, basename)
+ dstname = os.path.join(logdir, testname + '.' + basename)
+ num = 0
+ while os.path.exists(dstname):
+ dstname = os.path.join(logdir,
+ testname + '.' + basename + '-' + str(num))
+ num = num + 1
+ os.rename(srcname, dstname)
+ if dev:
+ dev.relog()
+ subprocess.call(['chown', '-f', getpass.getuser(), srcname])
+ except Exception as e:
+ logger.info("Failed to rename log files")
+ logger.info(e)
+
+def is_long_duration_test(t):
+ return hasattr(t, "long_duration_test") and t.long_duration_test
+
+def get_test_description(t):
+ if t.__doc__ is None:
+ desc = "MISSING DESCRIPTION"
+ else:
+ desc = t.__doc__
+ if is_long_duration_test(t):
+ desc += " [long]"
+ return desc
+
+def main():
+ tests = []
+ test_modules = []
+ files = os.listdir(scriptsdir)
+ for t in files:
+ m = re.match(r'(test_.*)\.py$', t)
+ if m:
+ logger.debug("Import test cases from " + t)
+ mod = __import__(m.group(1))
+ test_modules.append(mod.__name__.replace('test_', '', 1))
+ for key, val in mod.__dict__.items():
+ if key.startswith("test_"):
+ tests.append(val)
+ test_names = list(set([t.__name__.replace('test_', '', 1) for t in tests]))
+
+ run = None
+
+ parser = argparse.ArgumentParser(description='hwsim test runner')
+ parser.add_argument('--logdir', metavar='<directory>',
+ help='log output directory for all other options, ' +
+ 'must be given if other log options are used')
+ group = parser.add_mutually_exclusive_group()
+ group.add_argument('-d', const=logging.DEBUG, action='store_const',
+ dest='loglevel', default=logging.INFO,
+ help="verbose debug output")
+ group.add_argument('-q', const=logging.WARNING, action='store_const',
+ dest='loglevel', help="be quiet")
+
+ parser.add_argument('-S', metavar='<sqlite3 db>', dest='database',
+ help='database to write results to')
+ parser.add_argument('--prefill-tests', action='store_true', dest='prefill',
+ help='prefill test database with NOTRUN before all tests')
+ parser.add_argument('--commit', metavar='<commit id>',
+ help='commit ID, only for database')
+ parser.add_argument('-b', metavar='<build>', dest='build', help='build ID')
+ parser.add_argument('-L', action='store_true', dest='update_tests_db',
+ help='List tests (and update descriptions in DB)')
+ parser.add_argument('-T', action='store_true', dest='tracing',
+ help='collect tracing per test case (in log directory)')
+ parser.add_argument('-D', action='store_true', dest='dmesg',
+ help='collect dmesg per test case (in log directory)')
+ parser.add_argument('--dbus', action='store_true', dest='dbus',
+ help='collect dbus per test case (in log directory)')
+ parser.add_argument('--shuffle-tests', action='store_true',
+ dest='shuffle_tests',
+ help='Shuffle test cases to randomize order')
+ parser.add_argument('--split', help='split tests for parallel execution (<server number>/<total servers>)')
+ parser.add_argument('--no-reset', action='store_true', dest='no_reset',
+ help='Do not reset devices at the end of the test')
+ parser.add_argument('--long', action='store_true',
+ help='Include test cases that take long time')
+ parser.add_argument('-f', dest='testmodules', metavar='<test module>',
+ help='execute only tests from these test modules',
+ type=str, choices=[[]] + test_modules, nargs='+')
+ parser.add_argument('-l', metavar='<modules file>', dest='mfile',
+ help='test modules file name')
+ parser.add_argument('-i', action='store_true', dest='stdin_ctrl',
+ help='stdin-controlled test case execution')
+ parser.add_argument('tests', metavar='<test>', nargs='*', type=str,
+ help='tests to run (only valid without -f)')
+
+ args = parser.parse_args()
+
+ if (args.tests and args.testmodules) or (args.tests and args.mfile) or (args.testmodules and args.mfile):
+ print('Invalid arguments - only one of (test, test modules, modules file) can be given.')
+ sys.exit(2)
+
+ if args.tests:
+ fail = False
+ for t in args.tests:
+ if t.endswith('*'):
+ prefix = t.rstrip('*')
+ found = False
+ for tn in test_names:
+ if tn.startswith(prefix):
+ found = True
+ break
+ if not found:
+ print('Invalid arguments - test "%s" wildcard did not match' % t)
+ fail = True
+ elif t not in test_names:
+ print('Invalid arguments - test "%s" not known' % t)
+ fail = True
+ if fail:
+ sys.exit(2)
+
+ if args.database:
+ if not sqlite3_imported:
+ print("No sqlite3 module found")
+ sys.exit(2)
+ conn = sqlite3.connect(args.database)
+ conn.execute('CREATE TABLE IF NOT EXISTS results (test,result,run,time,duration,build,commitid)')
+ conn.execute('CREATE TABLE IF NOT EXISTS tests (test,description)')
+ conn.execute('CREATE TABLE IF NOT EXISTS logs (test,run,type,contents)')
+ else:
+ conn = None
+
+ if conn:
+ run = int(time.time())
+
+ # read the modules from the modules file
+ if args.mfile:
+ args.testmodules = []
+ with open(args.mfile) as f:
+ for line in f.readlines():
+ line = line.strip()
+ if not line or line.startswith('#'):
+ continue
+ args.testmodules.append(line)
+
+ tests_to_run = []
+ if args.tests:
+ for selected in args.tests:
+ for t in tests:
+ name = t.__name__.replace('test_', '', 1)
+ if selected.endswith('*'):
+ prefix = selected.rstrip('*')
+ if name.startswith(prefix):
+ tests_to_run.append(t)
+ elif name == selected:
+ tests_to_run.append(t)
+ else:
+ for t in tests:
+ name = t.__name__.replace('test_', '', 1)
+ if args.testmodules:
+ if t.__module__.replace('test_', '', 1) not in args.testmodules:
+ continue
+ tests_to_run.append(t)
+
+ if args.update_tests_db:
+ for t in tests_to_run:
+ name = t.__name__.replace('test_', '', 1)
+ print(name + " - " + get_test_description(t))
+ if conn:
+ sql = 'INSERT OR REPLACE INTO tests(test,description) VALUES (?, ?)'
+ params = (name, get_test_description(t))
+ try:
+ conn.execute(sql, params)
+ except Exception as e:
+ print("sqlite: " + str(e))
+ print("sql: %r" % (params,))
+ if conn:
+ conn.commit()
+ conn.close()
+ sys.exit(0)
+
+ if not args.logdir:
+ if os.path.exists('logs/current'):
+ args.logdir = 'logs/current'
+ else:
+ args.logdir = 'logs'
+
+ # Write debug level log to a file and configurable verbosity to stdout
+ logger.setLevel(logging.DEBUG)
+
+ stdout_handler = logging.StreamHandler()
+ stdout_handler.setLevel(args.loglevel)
+ logger.addHandler(stdout_handler)
+
+ file_name = os.path.join(args.logdir, 'run-tests.log')
+ log_handler = logging.FileHandler(file_name, encoding='utf-8')
+ log_handler.setLevel(logging.DEBUG)
+ fmt = "%(asctime)s %(levelname)s %(message)s"
+ log_formatter = logging.Formatter(fmt)
+ log_handler.setFormatter(log_formatter)
+ logger.addHandler(log_handler)
+
+ dev0 = WpaSupplicant('wlan0', '/tmp/wpas-wlan0')
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev2 = WpaSupplicant('wlan2', '/tmp/wpas-wlan2')
+ dev = [dev0, dev1, dev2]
+ apdev = []
+ apdev.append({"ifname": 'wlan3', "bssid": "02:00:00:00:03:00"})
+ apdev.append({"ifname": 'wlan4', "bssid": "02:00:00:00:04:00"})
+
+ for d in dev:
+ if not d.ping():
+ logger.info(d.ifname + ": No response from wpa_supplicant")
+ return
+ logger.info("DEV: " + d.ifname + ": " + d.p2p_dev_addr())
+ for ap in apdev:
+ logger.info("APDEV: " + ap['ifname'])
+
+ passed = []
+ skipped = []
+ failed = []
+
+ # make sure nothing is left over from previous runs
+ # (if there were any other manual runs or we crashed)
+ if not reset_devs(dev, apdev):
+ if conn:
+ conn.close()
+ conn = None
+ sys.exit(1)
+
+ if args.dmesg:
+ subprocess.call(['dmesg', '-c'], stdout=open('/dev/null', 'w'))
+
+ if conn and args.prefill:
+ for t in tests_to_run:
+ name = t.__name__.replace('test_', '', 1)
+ report(conn, False, args.build, args.commit, run, name, 'NOTRUN', 0,
+ args.logdir, sql_commit=False)
+ conn.commit()
+
+ if args.split:
+ vals = args.split.split('/')
+ split_server = int(vals[0])
+ split_total = int(vals[1])
+ logger.info("Parallel execution - %d/%d" % (split_server, split_total))
+ split_server -= 1
+ tests_to_run.sort(key=lambda t: t.__name__)
+ tests_to_run = [x for i, x in enumerate(tests_to_run) if i % split_total == split_server]
+
+ if args.shuffle_tests:
+ from random import shuffle
+ shuffle(tests_to_run)
+
+ count = 0
+ if args.stdin_ctrl:
+ print("READY")
+ sys.stdout.flush()
+ num_tests = 0
+ else:
+ num_tests = len(tests_to_run)
+ if args.stdin_ctrl:
+ set_term_echo(sys.stdin.fileno(), False)
+
+ check_country_00 = True
+ for d in dev:
+ if d.get_driver_status_field("country") != "00":
+ check_country_00 = False
+
+ while True:
+ if args.stdin_ctrl:
+ test = sys.stdin.readline()
+ if not test:
+ break
+ test = test.splitlines()[0]
+ if test == '':
+ break
+ t = None
+ for tt in tests:
+ name = tt.__name__.replace('test_', '', 1)
+ if name == test:
+ t = tt
+ break
+ if not t:
+ print("NOT-FOUND")
+ sys.stdout.flush()
+ continue
+ else:
+ if len(tests_to_run) == 0:
+ break
+ t = tests_to_run.pop(0)
+
+ if dev[0].get_driver_status_field("country") == "98":
+ # Work around cfg80211 regulatory issues in clearing intersected
+ # country code 98. Need to make station disconnect without any
+ # other wiphy being active in the system.
+ logger.info("country=98 workaround - try to clear state")
+ id = dev[1].add_network()
+ dev[1].set_network(id, "mode", "2")
+ dev[1].set_network_quoted(id, "ssid", "country98")
+ dev[1].set_network(id, "key_mgmt", "NONE")
+ dev[1].set_network(id, "frequency", "2412")
+ dev[1].set_network(id, "scan_freq", "2412")
+ dev[1].select_network(id)
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"])
+ if ev:
+ dev[0].connect("country98", key_mgmt="NONE", scan_freq="2412")
+ dev[1].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].disconnect_and_stop_scan()
+ dev[0].reset()
+ dev[1].reset()
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ name = t.__name__.replace('test_', '', 1)
+ open('/dev/kmsg', 'w').write('running hwsim test case %s\n' % name)
+ if log_handler:
+ log_handler.stream.close()
+ logger.removeHandler(log_handler)
+ file_name = os.path.join(args.logdir, name + '.log')
+ log_handler = logging.FileHandler(file_name, encoding='utf-8')
+ log_handler.setLevel(logging.DEBUG)
+ log_handler.setFormatter(log_formatter)
+ logger.addHandler(log_handler)
+
+ reset_ok = True
+ with DataCollector(args.logdir, name, args):
+ count = count + 1
+ msg = "START {} {}/{}".format(name, count, num_tests)
+ logger.info(msg)
+ if args.loglevel == logging.WARNING:
+ print(msg)
+ sys.stdout.flush()
+ if t.__doc__:
+ logger.info("Test: " + t.__doc__)
+ start = datetime.now()
+ open('/dev/kmsg', 'w').write('TEST-START %s @%.6f\n' % (name, time.time()))
+ for d in dev:
+ try:
+ d.dump_monitor()
+ if not d.ping():
+ raise Exception("PING failed for {}".format(d.ifname))
+ if not d.global_ping():
+ raise Exception("Global PING failed for {}".format(d.ifname))
+ d.request("NOTE TEST-START " + name)
+ except Exception as e:
+ logger.info("Failed to issue TEST-START before " + name + " for " + d.ifname)
+ logger.info(e)
+ print("FAIL " + name + " - could not start test")
+ if conn:
+ conn.close()
+ conn = None
+ if args.stdin_ctrl:
+ set_term_echo(sys.stdin.fileno(), True)
+ sys.exit(1)
+ skip_reason = None
+ try:
+ if is_long_duration_test(t) and not args.long:
+ raise HwsimSkip("Skip test case with long duration due to --long not specified")
+ if t.__code__.co_argcount > 2:
+ params = {}
+ params['logdir'] = args.logdir
+ params['name'] = name
+ params['prefix'] = os.path.join(args.logdir, name)
+ t(dev, apdev, params)
+ elif t.__code__.co_argcount > 1:
+ t(dev, apdev)
+ else:
+ t(dev)
+ result = "PASS"
+ if check_country_00:
+ for d in dev:
+ country = d.get_driver_status_field("country")
+ if country is None:
+ logger.info(d.ifname + ": Could not fetch country code after the test case run")
+ elif country != "00":
+ d.dump_monitor()
+ logger.info(d.ifname + ": Country code not reset back to 00: is " + country)
+ print(d.ifname + ": Country code not reset back to 00: is " + country)
+ result = "FAIL"
+
+ # Try to wait for cfg80211 regulatory state to
+ # clear.
+ d.cmd_execute(['iw', 'reg', 'set', '00'])
+ for i in range(5):
+ time.sleep(1)
+ country = d.get_driver_status_field("country")
+ if country == "00":
+ break
+ if country == "00":
+ print(d.ifname + ": Country code cleared back to 00")
+ logger.info(d.ifname + ": Country code cleared back to 00")
+ else:
+ print("Country code remains set - expect following test cases to fail")
+ logger.info("Country code remains set - expect following test cases to fail")
+ break
+ except HwsimSkip as e:
+ logger.info("Skip test case: %s" % e)
+ skip_reason = e
+ result = "SKIP"
+ except NameError as e:
+ import traceback
+ logger.info(e)
+ traceback.print_exc()
+ result = "FAIL"
+ except Exception as e:
+ import traceback
+ logger.info(e)
+ traceback.print_exc()
+ if args.loglevel == logging.WARNING:
+ print("Exception: " + str(e))
+ result = "FAIL"
+ open('/dev/kmsg', 'w').write('TEST-STOP %s @%.6f\n' % (name, time.time()))
+ for d in dev:
+ try:
+ d.dump_monitor()
+ d.request("NOTE TEST-STOP " + name)
+ except Exception as e:
+ logger.info("Failed to issue TEST-STOP after {} for {}".format(name, d.ifname))
+ logger.info(e)
+ result = "FAIL"
+ if args.no_reset:
+ print("Leaving devices in current state")
+ else:
+ reset_ok = reset_devs(dev, apdev)
+ wpas = None
+ try:
+ wpas = WpaSupplicant(global_iface="/tmp/wpas-wlan5",
+ monitor=False)
+ rename_log(args.logdir, 'log5', name, wpas)
+ if not args.no_reset:
+ wpas.remove_ifname()
+ except Exception as e:
+ pass
+ if wpas:
+ wpas.close_ctrl()
+ del wpas
+
+ for i in range(0, 3):
+ rename_log(args.logdir, 'log' + str(i), name, dev[i])
+ try:
+ hapd = HostapdGlobal()
+ except Exception as e:
+ print("Failed to connect to hostapd interface")
+ print(str(e))
+ reset_ok = False
+ result = "FAIL"
+ hapd = None
+ rename_log(args.logdir, 'hostapd', name, hapd)
+ if hapd:
+ del hapd
+ hapd = None
+
+ # Use None here since this instance of Wlantest() will never be
+ # used for remote host hwsim tests on real hardware.
+ Wlantest.setup(None)
+ wt = Wlantest()
+ rename_log(args.logdir, 'hwsim0.pcapng', name, wt)
+ rename_log(args.logdir, 'hwsim0', name, wt)
+ if os.path.exists(os.path.join(args.logdir, 'fst-wpa_supplicant')):
+ rename_log(args.logdir, 'fst-wpa_supplicant', name, None)
+ if os.path.exists(os.path.join(args.logdir, 'fst-hostapd')):
+ rename_log(args.logdir, 'fst-hostapd', name, None)
+ if os.path.exists(os.path.join(args.logdir, 'wmediumd.log')):
+ rename_log(args.logdir, 'wmediumd.log', name, None)
+
+ end = datetime.now()
+ diff = end - start
+
+ if result == 'PASS' and args.dmesg:
+ if not check_kernel(os.path.join(args.logdir, name + '.dmesg')):
+ logger.info("Kernel issue found in dmesg - mark test failed")
+ result = 'FAIL'
+
+ if result == 'PASS':
+ passed.append(name)
+ elif result == 'SKIP':
+ skipped.append(name)
+ else:
+ failed.append(name)
+
+ report(conn, args.prefill, args.build, args.commit, run, name, result,
+ diff.total_seconds(), args.logdir)
+ result = "{} {} {} {}".format(result, name, diff.total_seconds(), end)
+ logger.info(result)
+ if args.loglevel == logging.WARNING:
+ print(result)
+ if skip_reason:
+ print("REASON", skip_reason)
+ sys.stdout.flush()
+
+ if not reset_ok:
+ print("Terminating early due to device reset failure")
+ break
+ if args.stdin_ctrl:
+ set_term_echo(sys.stdin.fileno(), True)
+
+ if log_handler:
+ log_handler.stream.close()
+ logger.removeHandler(log_handler)
+ file_name = os.path.join(args.logdir, 'run-tests.log')
+ log_handler = logging.FileHandler(file_name, encoding='utf-8')
+ log_handler.setLevel(logging.DEBUG)
+ log_handler.setFormatter(log_formatter)
+ logger.addHandler(log_handler)
+
+ if conn:
+ conn.close()
+
+ if len(failed):
+ logger.info("passed {} test case(s)".format(len(passed)))
+ logger.info("skipped {} test case(s)".format(len(skipped)))
+ logger.info("failed tests: " + ' '.join(failed))
+ if args.loglevel == logging.WARNING:
+ print("failed tests: " + ' '.join(failed))
+ sys.exit(1)
+ logger.info("passed all {} test case(s)".format(len(passed)))
+ if len(skipped):
+ logger.info("skipped {} test case(s)".format(len(skipped)))
+ if args.loglevel == logging.WARNING:
+ print("passed all {} test case(s)".format(len(passed)))
+ if len(skipped):
+ print("skipped {} test case(s)".format(len(skipped)))
+
+if __name__ == "__main__":
+ main()
diff --git a/contrib/wpa/tests/hwsim/start.sh b/contrib/wpa/tests/hwsim/start.sh
new file mode 100755
index 000000000000..ac43d10afad8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/start.sh
@@ -0,0 +1,213 @@
+#!/bin/sh
+
+DIR="$( cd "$( dirname "$0" )" && pwd )"
+WPAS=$DIR/../../wpa_supplicant/wpa_supplicant
+WPACLI=$DIR/../../wpa_supplicant/wpa_cli
+HAPD=$DIR/../../hostapd/hostapd
+HAPD_AS=$DIR/../../hostapd/hostapd
+HAPDCLI=$DIR/../../hostapd/hostapd_cli
+WLANTEST=$DIR/../../wlantest/wlantest
+HLR_AUC_GW=$DIR/../../hostapd/hlr_auc_gw
+
+if [ -z "$LOGDIR" ] ; then
+ DATE="$(date +%s)"
+ LOGDIR="$DIR/logs/$DATE"
+ mkdir -p $LOGDIR
+else
+ if [ -e $LOGDIR/alt-wpa_supplicant/wpa_supplicant/wpa_supplicant ]; then
+ WPAS=$LOGDIR/alt-wpa_supplicant/wpa_supplicant/wpa_supplicant
+ WPACLI=$LOGDIR/alt-wpa_supplicant/wpa_supplicant/wpa_cli
+ # extra code coverage
+ $WPAS > /dev/null 2>&1
+ $WPAS -efoo -Ifoo -mfoo -ofoo -Ofoo -pfoo -Pfoo -h > /dev/null 2>&1
+ $WPAS -bfoo -B -Cfoo -q -W -N -L > /dev/null 2>&1
+ $WPAS -T -v > /dev/null 2>&1
+ $WPAS -u -z > /dev/null 2>&1
+ fi
+ if [ -e $LOGDIR/alt-hostapd/hostapd/hostapd ]; then
+ HAPD=$LOGDIR/alt-hostapd/hostapd/hostapd
+ HAPDCLI=$LOGDIR/alt-hostapd/hostapd/hostapd_cli
+ # extra code coverage
+ $HAPD > /dev/null 2>&1
+ $HAPD -v > /dev/null 2>&1
+ $HAPD -B -efoo -Pfoo -T -bfoo -h > /dev/null 2>&1
+ $HAPD -ufoo > /dev/null 2>&1
+ $HAPD -u00:11:22:33:44:55 > /dev/null 2>&1
+ $HAPD -gfoo > /dev/null 2>&1
+ $HAPD -Gfoo-not-exists > /dev/null 2>&1
+ $HAPD -z > /dev/null 2>&1
+ $HAPD -i foo1,foo2,foo3 > /dev/null 2>&1
+ fi
+ if [ -e $LOGDIR/alt-hostapd-as/hostapd/hostapd ]; then
+ HAPD_AS=$LOGDIR/alt-hostapd-as/hostapd/hostapd
+ fi
+ if [ -e $LOGDIR/alt-hlr_auc_gw/hostapd/hlr_auc_gw ]; then
+ HLR_AUC_GW=$LOGDIR/alt-hlr_auc_gw/hostapd/hlr_auc_gw
+ # extra code coverage
+ $HLR_AUC_GW > /dev/null 2>&1
+ $HLR_AUC_GW -Dfoo -i7 -sfoo -h > /dev/null 2>&1
+ $HLR_AUC_GW -i100 > /dev/null 2>&1
+ $HLR_AUC_GW -z > /dev/null 2>&1
+ fi
+fi
+
+LOGBASEDIR="$( cd "$(dirname "$LOGDIR")" && pwd )"
+if test "$LOGBASEDIR" = "$DIR/logs" -a -w "$LOGBASEDIR" ; then
+ rm -rf "$LOGBASEDIR/current"
+ ln -sf "$(basename "$LOGDIR")" "$LOGBASEDIR/current"
+fi
+
+if groups | tr ' ' "\n" | grep -q ^admin$; then
+ GROUP=admin
+elif groups | tr ' ' "\n" | grep -q ^wheel$; then
+ GROUP=wheel
+else
+ GROUP=adm
+fi
+
+for i in 0 1 2; do
+ sed "s/ GROUP=.*$/ GROUP=$GROUP/" "$DIR/p2p$i.conf" > "$LOGDIR/p2p$i.conf"
+done
+
+sed "s/group=admin/group=$GROUP/;s%LOGDIR%$LOGDIR%g" "$DIR/auth_serv/as.conf" > "$LOGDIR/as.conf"
+sed "s/group=admin/group=$GROUP/;s%LOGDIR%$LOGDIR%g" "$DIR/auth_serv/as2.conf" > "$LOGDIR/as2.conf"
+
+unset VM
+if [ "$1" = "VM" ]; then
+ VM="y"
+ shift
+fi
+
+if [ "$1" = "valgrind" ]; then
+ VALGRIND=y
+ VALGRIND_WPAS="valgrind --log-file=$LOGDIR/valgrind-wlan%d --leak-check=full"
+ VALGRIND_HAPD="valgrind --log-file=$LOGDIR/valgrind-hostapd --leak-check=full"
+ chmod -f a+rx $WPAS
+ chmod -f a+rx $HAPD
+ chmod -f a+rx $HAPD_AS
+ HAPD_AS="valgrind --log-file=$LOGDIR/valgrind-auth-serv --leak-check=full $HAPD_AS"
+ shift
+else
+ unset VALGRIND
+ VALGRIND_WPAS=
+ VALGRIND_HAPD=
+fi
+
+if [ "$1" = "trace" ]; then
+ TRACE="T"
+ shift
+else
+ TRACE=""
+fi
+
+$DIR/stop.sh
+
+TMP=$1
+if [ x${TMP%=[0-9]*} = "xchannels" ]; then
+ NUM_CH=${TMP#channels=}
+ shift
+else
+ NUM_CH=1
+fi
+
+test -d /sys/module/mac80211_hwsim || sudo modprobe mac80211_hwsim radios=7 channels=$NUM_CH support_p2p_device=0 dyndbg=+p
+
+sudo ifconfig hwsim0 up
+sudo $WLANTEST -i hwsim0 -n $LOGDIR/hwsim0.pcapng -c -dtN -L $LOGDIR/hwsim0 &
+for i in 0 1 2; do
+ DBUSARG=""
+ if [ $i = "0" ] && ([ -r /var/run/dbus/pid ] || [ -r /var/run/dbus/system_bus_socket ]); then
+ if $WPAS | grep -q -- -u; then
+ DBUSARG="-u"
+ fi
+ fi
+ sudo $(printf -- "$VALGRIND_WPAS" $i) $WPAS -g /tmp/wpas-wlan$i -G$GROUP -Dnl80211 -iwlan$i -c $LOGDIR/p2p$i.conf \
+ -ddKt$TRACE -f $LOGDIR/log$i $DBUSARG &
+done
+sudo $(printf -- "$VALGRIND_WPAS" 5) $WPAS -g /tmp/wpas-wlan5 -G$GROUP \
+ -ddKt$TRACE -f $LOGDIR/log5 &
+sudo $VALGRIND_HAPD $HAPD -ddKt$TRACE -g /var/run/hostapd-global -G $GROUP -f $LOGDIR/hostapd &
+HPID=$!
+
+if [ -z "$VM" ]; then
+ # Sleep a bit, otherwise pgrep may run before the child is forked
+ sleep 0.1
+ pgrep -P $HPID > $LOGDIR/hostapd-test.pid
+else
+ echo $HPID > $LOGDIR/hostapd-test.pid
+fi
+
+if [ -x $HLR_AUC_GW ]; then
+ cp $DIR/auth_serv/hlr_auc_gw.milenage_db $LOGDIR/hlr_auc_gw.milenage_db
+ sudo $HLR_AUC_GW -u -m $LOGDIR/hlr_auc_gw.milenage_db -g $DIR/auth_serv/hlr_auc_gw.gsm > $LOGDIR/hlr_auc_gw &
+fi
+
+openssl ocsp -index $DIR/auth_serv/index.txt \
+ -rsigner $DIR/auth_serv/ocsp-responder.pem \
+ -rkey $DIR/auth_serv/ocsp-responder.key \
+ -CA $DIR/auth_serv/ca.pem \
+ -issuer $DIR/auth_serv/ca.pem \
+ -verify_other $DIR/auth_serv/ca.pem -trust_other \
+ -ndays 7 \
+ -reqin $DIR/auth_serv/ocsp-req.der \
+ -respout $LOGDIR/ocsp-server-cache.der > $LOGDIR/ocsp.log 2>&1
+if [ ! -r $LOGDIR/ocsp-server-cache.der ]; then
+ cp $DIR/auth_serv/ocsp-server-cache.der $LOGDIR/ocsp-server-cache.der
+fi
+
+touch $LOGDIR/hostapd.db
+sudo $HAPD_AS -ddKt $LOGDIR/as.conf $LOGDIR/as2.conf > $LOGDIR/auth_serv &
+
+# wait for programs to be fully initialized
+for i in 0 1 2 3 4 5 6 7 8 9; do
+ if [ -e /tmp/wpas-wlan0 ]; then
+ break
+ fi
+ sleep 0.05
+done
+for i in 0 1 2; do
+ for j in `seq 1 10`; do
+ if $WPACLI -g /tmp/wpas-wlan$i ping | grep -q PONG; then
+ break
+ fi
+ if [ $j = "10" ]; then
+ echo "Could not connect to /tmp/wpas-wlan$i"
+ exit 1
+ fi
+ sleep 1
+ done
+done
+
+for j in `seq 1 10`; do
+ if $WPACLI -g /var/run/hostapd-global ping | grep -q PONG; then
+ break
+ fi
+ if [ $j = "10" ]; then
+ echo "Could not connect to /var/run/hostapd-global"
+ exit 1
+ fi
+ sleep 1
+done
+
+for j in `seq 1 10`; do
+ if $HAPDCLI -i as ping | grep -q PONG; then
+ break
+ fi
+ if [ $j = "10" ]; then
+ echo "Could not connect to hostapd-as-RADIUS-server"
+ exit 1
+ fi
+ sleep 1
+done
+
+if [ $USER = "0" -o $USER = "root" ]; then
+ exit 0
+fi
+
+sleep 0.75
+sudo chown -f $USER $LOGDIR/hwsim0.pcapng $LOGDIR/hwsim0 $LOGDIR/log* $LOGDIR/hostapd
+if [ "x$VALGRIND" = "xy" ]; then
+ sudo chown -f $USER $LOGDIR/*valgrind*
+fi
+
+exit 0
diff --git a/contrib/wpa/tests/hwsim/stop.sh b/contrib/wpa/tests/hwsim/stop.sh
new file mode 100755
index 000000000000..5d23b5bd68bf
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/stop.sh
@@ -0,0 +1,80 @@
+#!/bin/sh
+
+if pidof wpa_supplicant hostapd valgrind.bin hlr_auc_gw > /dev/null; then
+ RUNNING=yes
+else
+ RUNNING=no
+fi
+
+sudo killall -q hostapd
+sudo killall -q wpa_supplicant
+for i in `pidof valgrind.bin`; do
+ if ps $i | grep -q -E "wpa_supplicant|hostapd"; then
+ sudo kill $i
+ fi
+done
+sudo killall -q wlantest
+if grep -q hwsim0 /proc/net/dev; then
+ sudo ifconfig hwsim0 down
+fi
+
+sudo killall -q hlr_auc_gw
+
+if [ "$RUNNING" = "yes" ]; then
+ # give some time for hostapd and wpa_supplicant to complete deinit
+ for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
+ if ! pidof wpa_supplicant hostapd valgrind.bin hlr_auc_gw > /dev/null; then
+ break
+ fi
+ if [ $i -gt 10 ]; then
+ echo "Waiting for processes to exit (1)"
+ sleep 1
+ else
+ sleep 0.06
+ fi
+ done
+fi
+
+if pidof wpa_supplicant hostapd hlr_auc_gw > /dev/null; then
+ echo "wpa_supplicant/hostapd/hlr_auc_gw did not exit - try to force them to die"
+ sudo killall -9 -q hostapd
+ sudo killall -9 -q wpa_supplicant
+ sudo killall -9 -q hlr_auc_gw
+ for i in `seq 1 5`; do
+ if pidof wpa_supplicant hostapd hlr_auc_gw > /dev/null; then
+ echo "Waiting for processes to exit (2)"
+ sleep 1
+ else
+ break
+ fi
+ done
+fi
+
+for i in `pidof valgrind.bin`; do
+ if ps $i | grep -q -E "wpa_supplicant|hostapd"; then
+ echo "wpa_supplicant/hostapd(valgrind) did not exit - try to force it to die"
+ sudo kill -9 $i
+ fi
+done
+
+count=0
+for i in /tmp/wpas-wlan0 /tmp/wpas-wlan1 /tmp/wpas-wlan2 /tmp/wpas-wlan5 /var/run/hostapd-global /tmp/hlr_auc_gw.sock /tmp/wpa_ctrl_* /tmp/eap_sim_db_*; do
+ count=$(($count + 1))
+ if [ $count -lt 7 -a -e $i ]; then
+ echo "Waiting for ctrl_iface $i to disappear"
+ sleep 1
+ fi
+ if [ -e $i ]; then
+ echo "Control interface file $i exists - remove it"
+ sudo rm $i
+ fi
+done
+
+if grep -q mac80211_hwsim /proc/modules 2>/dev/null ; then
+ sudo rmmod mac80211_hwsim
+ sudo rmmod mac80211
+ sudo rmmod cfg80211
+ # wait at the end to avoid issues starting something new immediately after
+ # this script returns
+ sleep 1
+fi
diff --git a/contrib/wpa/tests/hwsim/test_ap_acs.py b/contrib/wpa/tests/hwsim/test_ap_acs.py
new file mode 100644
index 000000000000..8fc5ec4f3a69
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_acs.py
@@ -0,0 +1,688 @@
+# Test cases for automatic channel selection with hostapd
+# Copyright (c) 2013-2018, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+
+import hostapd
+from utils import *
+from test_dfs import wait_dfs_event
+
+def force_prev_ap_on_24g(ap):
+ # For now, make sure the last operating channel was on 2.4 GHz band to get
+ # sufficient survey data from mac80211_hwsim.
+ hostapd.add_ap(ap, {"ssid": "open"})
+ time.sleep(0.1)
+ hostapd.remove_bss(ap)
+
+def force_prev_ap_on_5g(ap):
+ # For now, make sure the last operating channel was on 5 GHz band to get
+ # sufficient survey data from mac80211_hwsim.
+ hostapd.add_ap(ap, {"ssid": "open", "hw_mode": "a",
+ "channel": "36", "country_code": "US"})
+ time.sleep(0.1)
+ hostapd.remove_bss(ap)
+
+def wait_acs(hapd, return_after_acs=False):
+ ev = hapd.wait_event(["ACS-STARTED", "ACS-COMPLETED", "ACS-FAILED",
+ "AP-ENABLED", "AP-DISABLED"], timeout=5)
+ if not ev:
+ raise Exception("ACS start timed out")
+ if "ACS-STARTED" not in ev:
+ raise Exception("Unexpected ACS event: " + ev)
+
+ state = hapd.get_status_field("state")
+ if state != "ACS":
+ raise Exception("Unexpected interface state %s (expected ACS)" % state)
+
+ ev = hapd.wait_event(["ACS-COMPLETED", "ACS-FAILED", "AP-ENABLED",
+ "AP-DISABLED"], timeout=20)
+ if not ev:
+ raise Exception("ACS timed out")
+ if "ACS-COMPLETED" not in ev:
+ raise Exception("Unexpected ACS event: " + ev)
+
+ if return_after_acs:
+ return
+
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+ if "AP-ENABLED" not in ev:
+ raise Exception("Unexpected ACS event: " + ev)
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state %s (expected ENABLED)" % state)
+
+def test_ap_acs(dev, apdev):
+ """Automatic channel selection"""
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 2400:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+def test_ap_acs_chanlist(dev, apdev):
+ """Automatic channel selection with chanlist set"""
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['chanlist'] = '1 6 11'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 2400:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+def test_ap_acs_freqlist(dev, apdev):
+ """Automatic channel selection with freqlist set"""
+ run_ap_acs_freqlist(dev, apdev, [2412, 2437, 2462])
+
+def test_ap_acs_freqlist2(dev, apdev):
+ """Automatic channel selection with freqlist set"""
+ run_ap_acs_freqlist(dev, apdev, [2417, 2432, 2457])
+
+def run_ap_acs_freqlist(dev, apdev, freqlist):
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['freqlist'] = ','.join([str(x) for x in freqlist])
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ freq = int(hapd.get_status_field("freq"))
+ if freq not in freqlist:
+ raise Exception("Unexpected frequency: %d" % freq)
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=str(freq))
+
+def test_ap_acs_invalid_chanlist(dev, apdev):
+ """Automatic channel selection with invalid chanlist"""
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['chanlist'] = '15-18'
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ res = hapd.request("ENABLE")
+ if "OK" in res:
+ raise Exception("ENABLE command succeeded unexpectedly")
+
+def test_ap_multi_bss_acs(dev, apdev):
+ """hostapd start with a multi-BSS configuration file using ACS"""
+ skip_with_fips(dev[0])
+ check_sae_capab(dev[2])
+ force_prev_ap_on_24g(apdev[0])
+
+ # start the actual test
+ hapd = hostapd.add_iface(apdev[0], 'multi-bss-acs.conf')
+ hapd.enable()
+ wait_acs(hapd)
+
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 2400:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("bss-1", key_mgmt="NONE", scan_freq=freq)
+ dev[1].connect("bss-2", psk="12345678", scan_freq=freq)
+ dev[2].set("sae_groups", "")
+ dev[2].connect("bss-3", key_mgmt="SAE", psk="qwertyuiop", scan_freq=freq)
+
+def test_ap_acs_40mhz(dev, apdev):
+ """Automatic channel selection for 40 MHz channel"""
+ run_ap_acs_40mhz(dev, apdev, '[HT40+]')
+
+def test_ap_acs_40mhz_plus_or_minus(dev, apdev):
+ """Automatic channel selection for 40 MHz channel (plus or minus)"""
+ run_ap_acs_40mhz(dev, apdev, '[HT40+][HT40-]')
+
+def run_ap_acs_40mhz(dev, apdev, ht_capab):
+ clear_scan_cache(apdev[0])
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['ht_capab'] = ht_capab
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 2400:
+ raise Exception("Unexpected frequency")
+ sec = hapd.get_status_field("secondary_channel")
+ if int(sec) == 0:
+ raise Exception("Secondary channel not set")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+def test_ap_acs_40mhz_minus(dev, apdev):
+ """Automatic channel selection for HT40- channel"""
+ clear_scan_cache(apdev[0])
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['ht_capab'] = '[HT40-]'
+ params['acs_num_scans'] = '1'
+ params['chanlist'] = '1 11'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+ if not ev:
+ raise Exception("ACS start timed out")
+ # HT40- is not currently supported in hostapd ACS, so do not try to connect
+ # or verify that this operation succeeded.
+
+def test_ap_acs_5ghz(dev, apdev):
+ """Automatic channel selection on 5 GHz"""
+ try:
+ hapd = None
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'a'
+ params['channel'] = '0'
+ params['country_code'] = 'US'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 5000:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_acs_5ghz_40mhz(dev, apdev):
+ """Automatic channel selection on 5 GHz for 40 MHz channel"""
+ try:
+ hapd = None
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'a'
+ params['channel'] = '0'
+ params['ht_capab'] = '[HT40+]'
+ params['country_code'] = 'US'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 5000:
+ raise Exception("Unexpected frequency")
+
+ sec = hapd.get_status_field("secondary_channel")
+ if int(sec) == 0:
+ raise Exception("Secondary channel not set")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_acs_vht(dev, apdev):
+ """Automatic channel selection for VHT"""
+ try:
+ hapd = None
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'a'
+ params['channel'] = '0'
+ params['ht_capab'] = '[HT40+]'
+ params['country_code'] = 'US'
+ params['ieee80211ac'] = '1'
+ params['vht_oper_chwidth'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 5000:
+ raise Exception("Unexpected frequency")
+
+ sec = hapd.get_status_field("secondary_channel")
+ if int(sec) == 0:
+ raise Exception("Secondary channel not set")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_acs_vht40(dev, apdev):
+ """Automatic channel selection for VHT40"""
+ try:
+ hapd = None
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'a'
+ params['channel'] = '0'
+ params['ht_capab'] = '[HT40+]'
+ params['country_code'] = 'US'
+ params['ieee80211ac'] = '1'
+ params['vht_oper_chwidth'] = '0'
+ params['acs_num_scans'] = '1'
+ params['chanlist'] = '36 149'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 5000:
+ raise Exception("Unexpected frequency")
+
+ sec = hapd.get_status_field("secondary_channel")
+ if int(sec) == 0:
+ raise Exception("Secondary channel not set")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_acs_vht80p80(dev, apdev):
+ """Automatic channel selection for VHT 80+80"""
+ try:
+ hapd = None
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'a'
+ params['channel'] = '0'
+ params['ht_capab'] = '[HT40+]'
+ params['country_code'] = 'US'
+ params['ieee80211ac'] = '1'
+ params['vht_oper_chwidth'] = '3'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["ACS-COMPLETED"], timeout=20)
+ if ev is None:
+ raise Exception("ACS did not complete")
+ # ACS for 80+80 is not yet supported, so the AP setup itself will fail.
+ # Do not try to connection before this gets fully supported.
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("AP enabled/disabled not reported")
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_acs_vht160(dev, apdev):
+ """Automatic channel selection for VHT160"""
+ try:
+ hapd = None
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'a'
+ params['channel'] = '0'
+ params['ht_capab'] = '[HT40+]'
+ params['country_code'] = 'ZA'
+ params['ieee80211ac'] = '1'
+ params['vht_oper_chwidth'] = '2'
+ params['ieee80211d'] = '1'
+ params['ieee80211h'] = '1'
+ params['chanlist'] = '100'
+ params['acs_num_scans'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+ if not ev:
+ raise Exception("ACS start timed out")
+ # VHT160 is not currently supported in hostapd ACS, so do not try to
+ # enforce successful AP start.
+ if "AP-ENABLED" in ev:
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 5000:
+ raise Exception("Unexpected frequency")
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_acs_vht160_scan_disable(dev, apdev):
+ """Automatic channel selection for VHT160 and DISABLE during scan"""
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'a'
+ params['channel'] = '0'
+ params['ht_capab'] = '[HT40+]'
+ params['country_code'] = 'ZA'
+ params['ieee80211ac'] = '1'
+ params['vht_oper_chwidth'] = '2'
+ params["vht_oper_centr_freq_seg0_idx"] = "114"
+ params['ieee80211d'] = '1'
+ params['ieee80211h'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ time.sleep(3)
+ clear_regdom(hapd, dev)
+
+def test_ap_acs_bias(dev, apdev):
+ """Automatic channel selection with bias values"""
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['acs_chan_bias'] = '1:0.8 3:1.2 6:0.7 11:0.8'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 2400:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+def test_ap_acs_survey(dev, apdev):
+ """Automatic channel selection using acs_survey parameter"""
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = 'acs_survey'
+ params['acs_num_scans'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 2400:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+def test_ap_acs_errors(dev, apdev):
+ """Automatic channel selection failures"""
+ clear_scan_cache(apdev[0])
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['acs_num_scans'] = '2'
+ params['chanlist'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+
+ with alloc_fail(hapd, 1, "acs_request_scan"):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected success for ENABLE")
+
+ hapd.dump_monitor()
+ with fail_test(hapd, 1, "acs_request_scan"):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected success for ENABLE")
+
+ hapd.dump_monitor()
+ with fail_test(hapd, 1, "acs_scan_complete"):
+ hapd.enable()
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+ if not ev:
+ raise Exception("ACS start timed out")
+
+ hapd.dump_monitor()
+ with fail_test(hapd, 1, "acs_request_scan;acs_scan_complete"):
+ hapd.enable()
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+ if not ev:
+ raise Exception("ACS start timed out")
+
+@long_duration_test
+def test_ap_acs_dfs(dev, apdev):
+ """Automatic channel selection, HT scan, and DFS"""
+ try:
+ hapd = None
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'a'
+ params['channel'] = '0'
+ params['ht_capab'] = '[HT40+]'
+ params['country_code'] = 'US'
+ params['ieee80211d'] = '1'
+ params['ieee80211h'] = '1'
+ params['acs_num_scans'] = '1'
+ params['chanlist'] = '52 56 60 64'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd, return_after_acs=True)
+
+ wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state")
+
+ freq = int(hapd.get_status_field("freq"))
+ if freq not in [5260, 5280, 5300, 5320]:
+ raise Exception("Unexpected frequency: %d" % freq)
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=str(freq))
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ hostapd.cmd_execute(apdev[0], ['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def test_ap_acs_exclude_dfs(dev, apdev, params):
+ """Automatic channel selection, exclude DFS"""
+ try:
+ hapd = None
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'a'
+ params['channel'] = '0'
+ params['ht_capab'] = '[HT40+]'
+ params['country_code'] = 'US'
+ params['ieee80211d'] = '1'
+ params['ieee80211h'] = '1'
+ params['acs_num_scans'] = '1'
+ params['acs_exclude_dfs'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state")
+
+ freq = int(hapd.get_status_field("freq"))
+ if freq in [5260, 5280, 5300, 5320,
+ 5500, 5520, 5540, 5560, 5580, 5600, 5620, 5640, 5660, 5680]:
+ raise Exception("Unexpected frequency: %d" % freq)
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=str(freq))
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ hostapd.cmd_execute(apdev[0], ['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+@long_duration_test
+def test_ap_acs_vht160_dfs(dev, apdev):
+ """Automatic channel selection 160 MHz, HT scan, and DFS"""
+ try:
+ hapd = None
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'a'
+ params['channel'] = '0'
+ params['ht_capab'] = '[HT40+]'
+ params['country_code'] = 'US'
+ params['ieee80211ac'] = '1'
+ params['vht_oper_chwidth'] = '2'
+ params['ieee80211d'] = '1'
+ params['ieee80211h'] = '1'
+ params['acs_num_scans'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd, return_after_acs=True)
+
+ wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state")
+
+ freq = int(hapd.get_status_field("freq"))
+ if freq not in [5180, 5500]:
+ raise Exception("Unexpected frequency: %d" % freq)
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=str(freq))
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ hostapd.cmd_execute(apdev[0], ['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def test_ap_acs_hw_mode_any(dev, apdev):
+ """Automatic channel selection with hw_mode=any"""
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['hw_mode'] = 'any'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 2400:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+def test_ap_acs_hw_mode_any_5ghz(dev, apdev):
+ """Automatic channel selection with hw_mode=any and 5 GHz"""
+ try:
+ hapd = None
+ force_prev_ap_on_5g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['hw_mode'] = 'any'
+ params['channel'] = '0'
+ params['country_code'] = 'US'
+ params['acs_chan_bias'] = '36:0.7 40:0.7 44:0.7 48:0.7'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 5000:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_acs_with_fallback_to_20(dev, apdev):
+ """Automatic channel selection with fallback to 20 MHz"""
+ force_prev_ap_on_24g(apdev[0])
+ params = {"ssid": "legacy-20",
+ "channel": "7", "ieee80211n": "0"}
+ hostapd.add_ap(apdev[1], params)
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['acs_chan_bias'] = '6:0.1'
+ params['ht_capab'] = '[HT40+]'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 2400:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ logger.info("SIGNAL_POLL: " + str(sig))
+ if "WIDTH=20 MHz" not in sig:
+ raise Exception("Station did not report 20 MHz bandwidth")
+
+def test_ap_acs_rx_during(dev, apdev):
+ """Automatic channel selection and RX during ACS"""
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['chanlist'] = '1 6 11'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ time.sleep(0.1)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ bssid = hapd.own_addr().replace(':', '')
+ addr = "020304050607"
+ broadcast = 6*"ff"
+
+ probereq = "40000000" + broadcast + addr + broadcast + "1000"
+ probereq += "0000" + "010802040b160c121824" + "32043048606c" + "030100"
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % probereq):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ probereq = "40000000" + broadcast + addr + broadcast + "1000"
+ probereq += "0000" + "010102"
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2437 datarate=0 ssi_signal=-30 frame=%s" % probereq):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ auth = "b0003a01" + bssid + addr + bssid + '1000000001000000'
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % auth):
+ raise Exception("MGMT_RX_PROCESS failed")
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+ time.sleep(0.2)
+ try:
+ for i in range(3):
+ dev[i].request("SCAN_INTERVAL 1")
+ dev[i].connect("test-acs", psk="12345678",
+ scan_freq="2412 2437 2462", wait_connect=False)
+ wait_acs(hapd)
+ for i in range(3):
+ dev[i].wait_connected()
+ finally:
+ for i in range(3):
+ dev[i].request("SCAN_INTERVAL 5")
+
+def test_ap_acs_he_24g(dev, apdev):
+ """Automatic channel selection on 2.4 GHz with HE"""
+ clear_scan_cache(apdev[0])
+ force_prev_ap_on_24g(apdev[0])
+
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['ieee80211ax'] = '1'
+ params['ht_capab'] = '[HT40+]'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 2400:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
+
+def test_ap_acs_he_24g_overlap(dev, apdev):
+ """Automatic channel selection on 2.4 GHz with HE (overlap)"""
+ clear_scan_cache(apdev[0])
+ force_prev_ap_on_24g(apdev[0])
+
+ params = {"ssid": "overlapping",
+ "channel": "6", "ieee80211n": "1"}
+ hostapd.add_ap(apdev[1], params)
+
+ params = hostapd.wpa2_params(ssid="test-acs", passphrase="12345678")
+ params['channel'] = '0'
+ params['ieee80211ax'] = '1'
+ params['ht_capab'] = '[HT40+]'
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ wait_acs(hapd)
+
+ freq = hapd.get_status_field("freq")
+ if int(freq) < 2400:
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("test-acs", psk="12345678", scan_freq=freq)
diff --git a/contrib/wpa/tests/hwsim/test_ap_ciphers.py b/contrib/wpa/tests/hwsim/test_ap_ciphers.py
new file mode 100644
index 000000000000..72dcfa54211e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_ciphers.py
@@ -0,0 +1,1200 @@
+# Cipher suite tests
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import logging
+logger = logging.getLogger()
+import os
+import subprocess
+
+import hwsim_utils
+import hostapd
+from utils import *
+from wlantest import Wlantest
+from wpasupplicant import WpaSupplicant
+
+KT_PTK, KT_GTK, KT_IGTK, KT_BIGTK = range(4)
+
+def check_cipher(dev, ap, cipher, group_cipher=None):
+ if cipher not in dev.get_capability("pairwise"):
+ raise HwsimSkip("Cipher %s not supported" % cipher)
+ if group_cipher and group_cipher not in dev.get_capability("group"):
+ raise HwsimSkip("Cipher %s not supported" % group_cipher)
+ params = {"ssid": "test-wpa2-psk",
+ "wpa_passphrase": "12345678",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": cipher}
+ if group_cipher:
+ params["group_cipher"] = group_cipher
+ else:
+ group_cipher = cipher
+ hapd = hostapd.add_ap(ap, params)
+ dev.connect("test-wpa2-psk", psk="12345678",
+ pairwise=cipher, group=group_cipher, scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev, hapd)
+
+def check_group_mgmt_cipher(dev, ap, cipher, sta_req_cipher=None):
+ if cipher not in dev.get_capability("group_mgmt"):
+ raise HwsimSkip("Cipher %s not supported" % cipher)
+ params = {"ssid": "test-wpa2-psk-pmf",
+ "wpa_passphrase": "12345678",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "WPA-PSK-SHA256",
+ "rsn_pairwise": "CCMP",
+ "group_mgmt_cipher": cipher}
+ hapd = hostapd.add_ap(ap, params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev.connect("test-wpa2-psk-pmf", psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", group_mgmt=sta_req_cipher,
+ pairwise="CCMP", group="CCMP", scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev, hapd)
+ hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff")
+ dev.wait_disconnected()
+ if wt.get_bss_counter('valid_bip_mmie', ap['bssid']) < 1:
+ raise Exception("No valid BIP MMIE seen")
+ if wt.get_bss_counter('bip_deauth', ap['bssid']) < 1:
+ raise Exception("No valid BIP deauth seen")
+
+ if cipher == "AES-128-CMAC":
+ group_mgmt = "BIP"
+ else:
+ group_mgmt = cipher
+ res = wt.info_bss('group_mgmt', ap['bssid']).strip()
+ if res != group_mgmt:
+ raise Exception("Unexpected group mgmt cipher: " + res)
+
+@remote_compatible
+def test_ap_cipher_tkip(dev, apdev):
+ """WPA2-PSK/TKIP connection"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ check_cipher(dev[0], apdev[0], "TKIP")
+
+@remote_compatible
+def test_ap_cipher_tkip_countermeasures_ap(dev, apdev):
+ """WPA-PSK/TKIP countermeasures (detected by AP)"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ testfile = "/sys/kernel/debug/ieee80211/%s/netdev:%s/tkip_mic_test" % (dev[0].get_driver_status_field("phyname"), dev[0].ifname)
+ if dev[0].cmd_execute(["ls", testfile])[0] != 0:
+ raise HwsimSkip("tkip_mic_test not supported in mac80211")
+
+ params = {"ssid": "tkip-countermeasures",
+ "wpa_passphrase": "12345678",
+ "wpa": "1",
+ "wpa_key_mgmt": "WPA-PSK",
+ "wpa_pairwise": "TKIP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("tkip-countermeasures", psk="12345678",
+ pairwise="TKIP", group="TKIP", scan_freq="2412")
+
+ dev[0].dump_monitor()
+ dev[0].cmd_execute(["echo", "-n", apdev[0]['bssid'], ">", testfile],
+ shell=True)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection on first Michael MIC failure")
+
+ dev[0].cmd_execute(["echo", "-n", "ff:ff:ff:ff:ff:ff", ">", testfile],
+ shell=True)
+ ev = dev[0].wait_disconnected(timeout=10,
+ error="No disconnection after two Michael MIC failures")
+ if "reason=14" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection during TKIP countermeasures")
+
+def test_ap_cipher_tkip_countermeasures_ap_mixed_mode(dev, apdev):
+ """WPA+WPA2-PSK/TKIP countermeasures (detected by mixed mode AP)"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ testfile = "/sys/kernel/debug/ieee80211/%s/netdev:%s/tkip_mic_test" % (dev[0].get_driver_status_field("phyname"), dev[0].ifname)
+ if dev[0].cmd_execute(["ls", testfile])[0] != 0:
+ raise HwsimSkip("tkip_mic_test not supported in mac80211")
+
+ params = {"ssid": "tkip-countermeasures",
+ "wpa_passphrase": "12345678",
+ "wpa": "3",
+ "wpa_key_mgmt": "WPA-PSK",
+ "wpa_pairwise": "TKIP",
+ "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("tkip-countermeasures", psk="12345678",
+ pairwise="TKIP", group="TKIP", scan_freq="2412")
+ dev[1].connect("tkip-countermeasures", psk="12345678",
+ pairwise="CCMP", scan_freq="2412")
+
+ dev[0].dump_monitor()
+ dev[0].cmd_execute(["echo", "-n", apdev[0]['bssid'], ">", testfile],
+ shell=True)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection on first Michael MIC failure")
+
+ dev[0].cmd_execute(["echo", "-n", "ff:ff:ff:ff:ff:ff", ">", testfile],
+ shell=True)
+
+ ev = dev[0].wait_disconnected(timeout=10,
+ error="No disconnection after two Michael MIC failures")
+ if "reason=14" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+ ev = dev[1].wait_disconnected(timeout=10,
+ error="No disconnection after two Michael MIC failures (2)")
+ if "reason=14" not in ev:
+ raise Exception("Unexpected disconnection reason (2): " + ev)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection during TKIP countermeasures (1)")
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection during TKIP countermeasures (2)")
+
+@remote_compatible
+def test_ap_cipher_tkip_countermeasures_sta(dev, apdev):
+ """WPA-PSK/TKIP countermeasures (detected by STA)"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ params = {"ssid": "tkip-countermeasures",
+ "wpa_passphrase": "12345678",
+ "wpa": "1",
+ "wpa_key_mgmt": "WPA-PSK",
+ "wpa_pairwise": "TKIP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ testfile = "/sys/kernel/debug/ieee80211/%s/netdev:%s/tkip_mic_test" % (hapd.get_driver_status_field("phyname"), apdev[0]['ifname'])
+ if hapd.cmd_execute(["ls", testfile])[0] != 0:
+ raise HwsimSkip("tkip_mic_test not supported in mac80211")
+
+ dev[0].connect("tkip-countermeasures", psk="12345678",
+ pairwise="TKIP", group="TKIP", scan_freq="2412")
+
+ dev[0].dump_monitor()
+ hapd.cmd_execute(["echo", "-n", dev[0].own_addr(), ">", testfile],
+ shell=True)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection on first Michael MIC failure")
+
+ hapd.cmd_execute(["echo", "-n", "ff:ff:ff:ff:ff:ff", ">", testfile],
+ shell=True)
+ ev = dev[0].wait_disconnected(timeout=10,
+ error="No disconnection after two Michael MIC failures")
+ if "reason=14 locally_generated=1" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection during TKIP countermeasures")
+
+@long_duration_test
+def test_ap_cipher_tkip_countermeasures_sta2(dev, apdev):
+ """WPA-PSK/TKIP countermeasures (detected by two STAs)"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ params = {"ssid": "tkip-countermeasures",
+ "wpa_passphrase": "12345678",
+ "wpa": "1",
+ "wpa_key_mgmt": "WPA-PSK",
+ "wpa_pairwise": "TKIP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ testfile = "/sys/kernel/debug/ieee80211/%s/netdev:%s/tkip_mic_test" % (hapd.get_driver_status_field("phyname"), apdev[0]['ifname'])
+ if hapd.cmd_execute(["ls", testfile])[0] != 0:
+ raise HwsimSkip("tkip_mic_test not supported in mac80211")
+
+ dev[0].connect("tkip-countermeasures", psk="12345678",
+ pairwise="TKIP", group="TKIP", scan_freq="2412")
+ dev[0].dump_monitor()
+ id = dev[1].connect("tkip-countermeasures", psk="12345678",
+ pairwise="TKIP", group="TKIP", scan_freq="2412")
+ dev[1].dump_monitor()
+
+ hapd.cmd_execute(["echo", "-n", "ff:ff:ff:ff:ff:ff", ">", testfile],
+ shell=True)
+ ev = dev[0].wait_disconnected(timeout=10,
+ error="No disconnection after two Michael MIC failure")
+ if "reason=14" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+ ev = dev[1].wait_disconnected(timeout=5,
+ error="No disconnection after two Michael MIC failure")
+ if "reason=14" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection during TKIP countermeasures")
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection during TKIP countermeasures")
+
+ dev[0].request("REMOVE_NETWORK all")
+ logger.info("Waiting for TKIP countermeasures to end")
+ connected = False
+ start = os.times()[4]
+ while True:
+ now = os.times()[4]
+ if start + 70 < now:
+ break
+ dev[0].connect("tkip-countermeasures", psk="12345678",
+ pairwise="TKIP", group="TKIP", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("No connection result")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ connected = True
+ break
+ if "status_code=1" not in ev:
+ raise Exception("Unexpected connection failure reason during TKIP countermeasures: " + ev)
+ dev[0].request("REMOVE_NETWORK all")
+ time.sleep(1)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ if not connected:
+ raise Exception("No connection after TKIP countermeasures terminated")
+
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is None:
+ dev[1].request("DISCONNECT")
+ dev[1].select_network(id)
+ dev[1].wait_connected()
+
+@remote_compatible
+def test_ap_cipher_ccmp(dev, apdev):
+ """WPA2-PSK/CCMP connection"""
+ check_cipher(dev[0], apdev[0], "CCMP")
+
+def test_ap_cipher_gcmp(dev, apdev):
+ """WPA2-PSK/GCMP connection"""
+ check_cipher(dev[0], apdev[0], "GCMP")
+
+def test_ap_cipher_ccmp_256(dev, apdev):
+ """WPA2-PSK/CCMP-256 connection"""
+ check_cipher(dev[0], apdev[0], "CCMP-256")
+
+def test_ap_cipher_gcmp_256(dev, apdev):
+ """WPA2-PSK/GCMP-256 connection"""
+ check_cipher(dev[0], apdev[0], "GCMP-256")
+
+def test_ap_cipher_gcmp_256_group_gcmp_256(dev, apdev):
+ """WPA2-PSK/GCMP-256 connection with group cipher override GCMP-256"""
+ check_cipher(dev[0], apdev[0], "GCMP-256", "GCMP-256")
+
+def test_ap_cipher_gcmp_256_group_gcmp(dev, apdev):
+ """WPA2-PSK/GCMP-256 connection with group cipher override GCMP"""
+ check_cipher(dev[0], apdev[0], "GCMP-256", "GCMP")
+
+def test_ap_cipher_gcmp_256_group_ccmp_256(dev, apdev):
+ """WPA2-PSK/GCMP-256 connection with group cipher override CCMP-256"""
+ check_cipher(dev[0], apdev[0], "GCMP-256", "CCMP-256")
+
+def test_ap_cipher_gcmp_256_group_ccmp(dev, apdev):
+ """WPA2-PSK/GCMP-256 connection with group cipher override CCMP"""
+ check_cipher(dev[0], apdev[0], "GCMP-256", "CCMP")
+
+def test_ap_cipher_gcmp_ccmp(dev, apdev, params):
+ """WPA2-PSK/GCMP/CCMP ciphers"""
+ config = os.path.join(params['logdir'], 'ap_cipher_gcmp_ccmp.conf')
+
+ for cipher in ["CCMP", "GCMP", "CCMP-256", "GCMP-256"]:
+ if cipher not in dev[0].get_capability("pairwise"):
+ raise HwsimSkip("Cipher %s not supported" % cipher)
+ if cipher not in dev[0].get_capability("group"):
+ raise HwsimSkip("Group cipher %s not supported" % cipher)
+
+ params = {"ssid": "test-wpa2-psk",
+ "wpa_passphrase": "12345678",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP GCMP CCMP-256 GCMP-256"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+
+ for cipher in ["CCMP", "GCMP", "CCMP-256", "GCMP-256"]:
+ dev[0].connect("test-wpa2-psk", psk="12345678",
+ pairwise=cipher, group="CCMP", scan_freq="2412")
+ if dev[0].get_status_field("group_cipher") != "CCMP":
+ raise Exception("Unexpected group_cipher")
+ if dev[0].get_status_field("pairwise_cipher") != cipher:
+ raise Exception("Unexpected pairwise_cipher")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].connect("test-wpa2-psk", psk="12345678",
+ pairwise="CCMP CCMP-256 GCMP GCMP-256",
+ group="CCMP CCMP-256 GCMP GCMP-256", scan_freq="2412")
+ if dev[0].get_status_field("group_cipher") != "CCMP":
+ raise Exception("Unexpected group_cipher")
+ res = dev[0].get_status_field("pairwise_cipher")
+ if res != "CCMP-256" and res != "GCMP-256":
+ raise Exception("Unexpected pairwise_cipher")
+
+ try:
+ with open(config, "w") as f:
+ f.write("network={\n" +
+ "\tssid=\"test-wpa2-psk\"\n" +
+ "\tkey_mgmt=WPA-PSK\n" +
+ "\tpsk=\"12345678\"\n" +
+ "\tpairwise=GCMP\n" +
+ "\tgroup=CCMP\n" +
+ "\tscan_freq=2412\n" +
+ "}\n")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", config=config)
+ wpas.wait_connected()
+ if wpas.get_status_field("group_cipher") != "CCMP":
+ raise Exception("Unexpected group_cipher")
+ if wpas.get_status_field("pairwise_cipher") != "GCMP":
+ raise Exception("Unexpected pairwise_cipher")
+ finally:
+ os.remove(config)
+
+@remote_compatible
+def test_ap_cipher_mixed_wpa_wpa2(dev, apdev):
+ """WPA2-PSK/CCMP/ and WPA-PSK/TKIP mixed configuration"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ ssid = "test-wpa-wpa2-psk"
+ passphrase = "12345678"
+ params = {"ssid": ssid,
+ "wpa_passphrase": passphrase,
+ "wpa": "3",
+ "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP",
+ "wpa_pairwise": "TKIP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+ dev[0].connect(ssid, psk=passphrase, proto="WPA2",
+ pairwise="CCMP", group="TKIP", scan_freq="2412")
+ status = dev[0].get_status()
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Incorrect key_mgmt reported")
+ if status['pairwise_cipher'] != 'CCMP':
+ raise Exception("Incorrect pairwise_cipher reported")
+ if status['group_cipher'] != 'TKIP':
+ raise Exception("Incorrect group_cipher reported")
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if bss['ssid'] != ssid:
+ raise Exception("Unexpected SSID in the BSS entry")
+ if "[WPA-PSK-TKIP]" not in bss['flags']:
+ raise Exception("Missing BSS flag WPA-PSK-TKIP")
+ if "[WPA2-PSK-CCMP]" not in bss['flags']:
+ raise Exception("Missing BSS flag WPA2-PSK-CCMP")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[1].connect(ssid, psk=passphrase, proto="WPA",
+ pairwise="TKIP", group="TKIP", scan_freq="2412")
+ status = dev[1].get_status()
+ if status['key_mgmt'] != 'WPA-PSK':
+ raise Exception("Incorrect key_mgmt reported")
+ if status['pairwise_cipher'] != 'TKIP':
+ raise Exception("Incorrect pairwise_cipher reported")
+ if status['group_cipher'] != 'TKIP':
+ raise Exception("Incorrect group_cipher reported")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[1], hapd)
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+@remote_compatible
+def test_ap_cipher_wpa_sae(dev, apdev):
+ """WPA-PSK/TKIP and SAE mixed AP - WPA IE and RSNXE coexistence"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ check_sae_capab(dev[0])
+ ssid = "test-wpa-sae"
+ passphrase = "12345678"
+ params = {"ssid": ssid,
+ "wpa_passphrase": passphrase,
+ "wpa": "3",
+ "wpa_key_mgmt": "WPA-PSK SAE",
+ "rsn_pairwise": "CCMP",
+ "wpa_pairwise": "TKIP",
+ "sae_pwe": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+
+ dev[0].connect(ssid, psk=passphrase, proto="WPA",
+ pairwise="TKIP", group="TKIP", scan_freq="2412")
+ status = dev[0].get_status()
+ if status['key_mgmt'] != 'WPA-PSK':
+ raise Exception("Incorrect key_mgmt reported")
+ if status['pairwise_cipher'] != 'TKIP':
+ raise Exception("Incorrect pairwise_cipher reported")
+ if status['group_cipher'] != 'TKIP':
+ raise Exception("Incorrect group_cipher reported")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_cipher_bip(dev, apdev):
+ """WPA2-PSK with BIP"""
+ check_group_mgmt_cipher(dev[0], apdev[0], "AES-128-CMAC")
+
+def test_ap_cipher_bip_req(dev, apdev):
+ """WPA2-PSK with BIP required"""
+ check_group_mgmt_cipher(dev[0], apdev[0], "AES-128-CMAC", "AES-128-CMAC")
+
+def test_ap_cipher_bip_req2(dev, apdev):
+ """WPA2-PSK with BIP required (2)"""
+ check_group_mgmt_cipher(dev[0], apdev[0], "AES-128-CMAC",
+ "AES-128-CMAC BIP-GMAC-128 BIP-GMAC-256 BIP-CMAC-256")
+
+def test_ap_cipher_bip_gmac_128(dev, apdev):
+ """WPA2-PSK with BIP-GMAC-128"""
+ check_group_mgmt_cipher(dev[0], apdev[0], "BIP-GMAC-128")
+
+def test_ap_cipher_bip_gmac_128_req(dev, apdev):
+ """WPA2-PSK with BIP-GMAC-128 required"""
+ check_group_mgmt_cipher(dev[0], apdev[0], "BIP-GMAC-128", "BIP-GMAC-128")
+
+def test_ap_cipher_bip_gmac_256(dev, apdev):
+ """WPA2-PSK with BIP-GMAC-256"""
+ check_group_mgmt_cipher(dev[0], apdev[0], "BIP-GMAC-256")
+
+def test_ap_cipher_bip_gmac_256_req(dev, apdev):
+ """WPA2-PSK with BIP-GMAC-256 required"""
+ check_group_mgmt_cipher(dev[0], apdev[0], "BIP-GMAC-256", "BIP-GMAC-256")
+
+def test_ap_cipher_bip_cmac_256(dev, apdev):
+ """WPA2-PSK with BIP-CMAC-256"""
+ check_group_mgmt_cipher(dev[0], apdev[0], "BIP-CMAC-256")
+
+def test_ap_cipher_bip_cmac_256_req(dev, apdev):
+ """WPA2-PSK with BIP-CMAC-256 required"""
+ check_group_mgmt_cipher(dev[0], apdev[0], "BIP-CMAC-256", "BIP-CMAC-256")
+
+def test_ap_cipher_bip_req_mismatch(dev, apdev):
+ """WPA2-PSK with BIP cipher mismatch"""
+ group_mgmt = dev[0].get_capability("group_mgmt")
+ for cipher in ["AES-128-CMAC", "BIP-GMAC-256"]:
+ if cipher not in group_mgmt:
+ raise HwsimSkip("Cipher %s not supported" % cipher)
+
+ params = {"ssid": "test-wpa2-psk-pmf",
+ "wpa_passphrase": "12345678",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "WPA-PSK-SHA256",
+ "rsn_pairwise": "CCMP",
+ "group_mgmt_cipher": "AES-128-CMAC"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(hapd.own_addr(), 2412)
+ id = dev[0].connect("test-wpa2-psk-pmf", psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", group_mgmt="BIP-GMAC-256",
+ pairwise="CCMP", group="CCMP", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Network selection result not indicated")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+
+ dev[0].request("DISCONNECT")
+ dev[0].set_network(id, "group_mgmt", "AES-128-CMAC")
+ dev[0].select_network(id)
+ dev[0].wait_connected()
+
+def get_rx_spec(phy, keytype=KT_PTK):
+ keys = "/sys/kernel/debug/ieee80211/%s/keys" % (phy)
+ try:
+ for key in os.listdir(keys):
+ keydir = keys + "/" + key
+ with open(keydir + '/keyidx') as f:
+ keyid = int(f.read())
+ if keytype in (KT_PTK, KT_GTK) and keyid not in (0, 1, 2, 3):
+ continue
+ if keytype == KT_IGTK and keyid not in (4, 5):
+ continue
+ if keytype == KT_BIGTK and keyid not in (6, 7):
+ continue
+ files = os.listdir(keydir)
+ if keytype == KT_PTK and "station" not in files:
+ continue
+ if keytype != KT_PTK and "station" in files:
+ continue
+ with open(keydir + "/rx_spec") as f:
+ return f.read()
+ except OSError as e:
+ raise HwsimSkip("debugfs not supported in mac80211")
+ return None
+
+def get_tk_replay_counter(phy, keytype=KT_PTK):
+ keys = "/sys/kernel/debug/ieee80211/%s/keys" % (phy)
+ try:
+ for key in os.listdir(keys):
+ keydir = keys + "/" + key
+ with open(keydir + '/keyidx') as f:
+ keyid = int(f.read())
+ if keytype in (KT_PTK, KT_GTK) and keyid not in (0, 1, 2, 3):
+ continue
+ if keytype == KT_IGTK and keyid not in (4, 5):
+ continue
+ if keytype == KT_BIGTK and keyid not in (6, 7):
+ continue
+ files = os.listdir(keydir)
+ if keytype == KT_PTK and "station" not in files:
+ continue
+ if keytype != KT_PTK and "station" in files:
+ continue
+ with open(keydir + "/replays") as f:
+ return int(f.read())
+ except OSError as e:
+ raise HwsimSkip("debugfs not supported in mac80211")
+ return None
+
+def test_ap_cipher_replay_protection_ap_ccmp(dev, apdev):
+ """CCMP replay protection on AP"""
+ run_ap_cipher_replay_protection_ap(dev, apdev, "CCMP")
+
+def test_ap_cipher_replay_protection_ap_tkip(dev, apdev):
+ """TKIP replay protection on AP"""
+ skip_without_tkip(dev[0])
+ run_ap_cipher_replay_protection_ap(dev, apdev, "TKIP")
+
+def test_ap_cipher_replay_protection_ap_gcmp(dev, apdev):
+ """GCMP replay protection on AP"""
+ if "GCMP" not in dev[0].get_capability("pairwise"):
+ raise HwsimSkip("GCMP not supported")
+ run_ap_cipher_replay_protection_ap(dev, apdev, "GCMP")
+
+def run_ap_cipher_replay_protection_ap(dev, apdev, cipher):
+ params = {"ssid": "test-wpa2-psk",
+ "wpa_passphrase": "12345678",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": cipher}
+ hapd = hostapd.add_ap(apdev[0], params)
+ phy = hapd.get_driver_status_field("phyname")
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect("test-wpa2-psk", psk="12345678",
+ pairwise=cipher, group=cipher, scan_freq="2412")
+ hapd.wait_sta()
+
+ if cipher != "TKIP":
+ replays = get_tk_replay_counter(phy)
+ if replays != 0:
+ raise Exception("Unexpected replay reported (1)")
+
+ for i in range(5):
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ if cipher != "TKIP":
+ replays = get_tk_replay_counter(phy)
+ if replays != 0:
+ raise Exception("Unexpected replay reported (2)")
+
+ if "OK" not in dev[0].request("RESET_PN"):
+ raise Exception("RESET_PN failed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd, timeout=1,
+ success_expected=False)
+
+ if cipher != "TKIP":
+ replays = get_tk_replay_counter(phy)
+ if replays < 1:
+ raise Exception("Replays not reported")
+
+def test_ap_cipher_replay_protection_sta_ccmp(dev, apdev):
+ """CCMP replay protection on STA (TK)"""
+ run_ap_cipher_replay_protection_sta(dev, apdev, "CCMP")
+
+def test_ap_cipher_replay_protection_sta_tkip(dev, apdev):
+ """TKIP replay protection on STA (TK)"""
+ skip_without_tkip(dev[0])
+ run_ap_cipher_replay_protection_sta(dev, apdev, "TKIP")
+
+def test_ap_cipher_replay_protection_sta_gcmp(dev, apdev):
+ """GCMP replay protection on STA (TK)"""
+ if "GCMP" not in dev[0].get_capability("pairwise"):
+ raise HwsimSkip("GCMP not supported")
+ run_ap_cipher_replay_protection_sta(dev, apdev, "GCMP")
+
+def test_ap_cipher_replay_protection_sta_gtk_ccmp(dev, apdev):
+ """CCMP replay protection on STA (GTK)"""
+ run_ap_cipher_replay_protection_sta(dev, apdev, "CCMP", keytype=KT_GTK)
+
+def test_ap_cipher_replay_protection_sta_gtk_tkip(dev, apdev):
+ """TKIP replay protection on STA (GTK)"""
+ skip_without_tkip(dev[0])
+ run_ap_cipher_replay_protection_sta(dev, apdev, "TKIP", keytype=KT_GTK)
+
+def test_ap_cipher_replay_protection_sta_gtk_gcmp(dev, apdev):
+ """GCMP replay protection on STA (GTK)"""
+ if "GCMP" not in dev[0].get_capability("pairwise"):
+ raise HwsimSkip("GCMP not supported")
+ run_ap_cipher_replay_protection_sta(dev, apdev, "GCMP", keytype=KT_GTK)
+
+def test_ap_cipher_replay_protection_sta_igtk(dev, apdev):
+ """CCMP replay protection on STA (IGTK)"""
+ run_ap_cipher_replay_protection_sta(dev, apdev, "CCMP", keytype=KT_IGTK)
+
+def test_ap_cipher_replay_protection_sta_bigtk(dev, apdev):
+ """CCMP replay protection on STA (BIGTK)"""
+ run_ap_cipher_replay_protection_sta(dev, apdev, "CCMP", keytype=KT_BIGTK)
+
+def run_ap_cipher_replay_protection_sta(dev, apdev, cipher, keytype=KT_PTK):
+ params = {"ssid": "test-wpa2-psk",
+ "wpa_passphrase": "12345678",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": cipher}
+ if keytype == KT_IGTK or keytype == KT_BIGTK:
+ params['ieee80211w'] = '2'
+ if keytype == KT_BIGTK:
+ params['beacon_prot'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ phy = dev[0].get_driver_status_field("phyname")
+ dev[0].connect("test-wpa2-psk", psk="12345678", ieee80211w='1',
+ beacon_prot='1',
+ pairwise=cipher, group=cipher, scan_freq="2412")
+ hapd.wait_sta()
+
+ if keytype == KT_BIGTK:
+ time.sleep(1)
+
+ if cipher != "TKIP":
+ replays = get_tk_replay_counter(phy, keytype)
+ if replays != 0:
+ raise Exception("Unexpected replay reported (1)")
+
+ for i in range(5):
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ if cipher != "TKIP":
+ replays = get_tk_replay_counter(phy, keytype)
+ if replays != 0:
+ raise Exception("Unexpected replay reported (2)")
+
+ if keytype == KT_IGTK:
+ hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff test=1")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev:
+ dev[0].wait_connected()
+
+ addr = "ff:ff:ff:ff:ff:ff" if keytype != KT_PTK else dev[0].own_addr()
+ cmd = "RESET_PN " + addr
+ if keytype == KT_IGTK:
+ cmd += " IGTK"
+ if keytype == KT_BIGTK:
+ cmd += " BIGTK"
+ if "OK" not in hapd.request(cmd):
+ raise Exception("RESET_PN failed")
+ time.sleep(0.1)
+ if keytype == KT_IGTK:
+ hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff test=1")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ elif keytype == KT_BIGTK:
+ time.sleep(1)
+ else:
+ hwsim_utils.test_connectivity(dev[0], hapd, timeout=1,
+ success_expected=False)
+
+ if cipher != "TKIP":
+ replays = get_tk_replay_counter(phy, keytype)
+ if replays < 1:
+ raise Exception("Replays not reported")
+
+@disable_ipv6
+def test_ap_wpa2_delayed_m3_retransmission(dev, apdev):
+ """Delayed M3 retransmission"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ phy = dev[0].get_driver_status_field("phyname")
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+ hapd.wait_sta()
+
+ for i in range(5):
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ time.sleep(0.1)
+ before_tk = get_rx_spec(phy, keytype=KT_PTK).splitlines()
+ before_gtk = get_rx_spec(phy, keytype=KT_GTK).splitlines()
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("RESEND_M3 " + addr):
+ raise Exception("RESEND_M3 failed")
+ time.sleep(0.1)
+ after_tk = get_rx_spec(phy, keytype=KT_PTK).splitlines()
+ after_gtk = get_rx_spec(phy, keytype=KT_GTK).splitlines()
+
+ if "OK" not in hapd.request("RESET_PN " + addr):
+ raise Exception("RESET_PN failed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd, timeout=1,
+ success_expected=False)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ for i in range(len(before_tk)):
+ b = int(before_tk[i], 16)
+ a = int(after_tk[i], 16)
+ if a < b:
+ raise Exception("TK RX counter decreased: idx=%d before=%d after=%d" % (i, b, a))
+
+ for i in range(len(before_gtk)):
+ b = int(before_gtk[i], 16)
+ a = int(after_gtk[i], 16)
+ if a < b:
+ raise Exception("GTK RX counter decreased: idx=%d before=%d after=%d" % (i, b, a))
+
+@disable_ipv6
+def test_ap_wpa2_delayed_m1_m3_retransmission(dev, apdev):
+ """Delayed M1+M3 retransmission"""
+ run_ap_wpa2_delayed_m1_m3_retransmission(dev, apdev, False)
+
+@disable_ipv6
+def test_ap_wpa2_delayed_m1_m3_retransmission2(dev, apdev):
+ """Delayed M1+M3 retransmission (change M1 ANonce)"""
+ run_ap_wpa2_delayed_m1_m3_retransmission(dev, apdev, True)
+
+def run_ap_wpa2_delayed_m1_m3_retransmission(dev, apdev,
+ change_m1_anonce=False):
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ phy = dev[0].get_driver_status_field("phyname")
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+ hapd.wait_sta()
+
+ for i in range(5):
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ time.sleep(0.1)
+ before_tk = get_rx_spec(phy, keytype=KT_PTK).splitlines()
+ before_gtk = get_rx_spec(phy, keytype=KT_GTK).splitlines()
+ addr = dev[0].own_addr()
+ if change_m1_anonce:
+ if "OK" not in hapd.request("RESEND_M1 " + addr + " change-anonce"):
+ raise Exception("RESEND_M1 failed")
+ if "OK" not in hapd.request("RESEND_M1 " + addr):
+ raise Exception("RESEND_M1 failed")
+ if "OK" not in hapd.request("RESEND_M3 " + addr):
+ raise Exception("RESEND_M3 failed")
+ time.sleep(0.1)
+ after_tk = get_rx_spec(phy, keytype=KT_PTK).splitlines()
+ after_gtk = get_rx_spec(phy, keytype=KT_GTK).splitlines()
+
+ if "OK" not in hapd.request("RESET_PN " + addr):
+ raise Exception("RESET_PN failed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd, timeout=1,
+ success_expected=False)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ for i in range(len(before_tk)):
+ b = int(before_tk[i], 16)
+ a = int(after_tk[i], 16)
+ if a < b:
+ raise Exception("TK RX counter decreased: idx=%d before=%d after=%d" % (i, b, a))
+
+ for i in range(len(before_gtk)):
+ b = int(before_gtk[i], 16)
+ a = int(after_gtk[i], 16)
+ if a < b:
+ raise Exception("GTK RX counter decreased: idx=%d before=%d after=%d" % (i, b, a))
+
+@disable_ipv6
+def test_ap_wpa2_delayed_group_m1_retransmission(dev, apdev):
+ """Delayed group M1 retransmission"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ phy = dev[0].get_driver_status_field("phyname")
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+ hapd.wait_sta()
+
+ for i in range(5):
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ time.sleep(0.1)
+ before = get_rx_spec(phy, keytype=KT_GTK).splitlines()
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("RESEND_GROUP_M1 " + addr):
+ raise Exception("RESEND_GROUP_M1 failed")
+ time.sleep(0.1)
+ after = get_rx_spec(phy, keytype=KT_GTK).splitlines()
+
+ if "OK" not in hapd.request("RESET_PN ff:ff:ff:ff:ff:ff"):
+ raise Exception("RESET_PN failed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd, timeout=1,
+ success_expected=False)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ for i in range(len(before)):
+ b = int(before[i], 16)
+ a = int(after[i], 16)
+ if a < b:
+ raise Exception("RX counter decreased: idx=%d before=%d after=%d" % (i, b, a))
+
+@disable_ipv6
+def test_ap_wpa2_delayed_group_m1_retransmission_igtk(dev, apdev):
+ """Delayed group M1 retransmission (check IGTK protection)"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678",
+ ieee80211w="2")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ phy = dev[0].get_driver_status_field("phyname")
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412",
+ ieee80211w="1")
+ hapd.wait_sta()
+
+ hwsim_utils.test_connectivity(dev[0], hapd, timeout=1)
+
+ # deauth once to see that works OK
+ addr = dev[0].own_addr()
+ hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff")
+ dev[0].wait_disconnected(timeout=10)
+
+ # now to check the protection
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+ hapd.wait_sta()
+
+ hwsim_utils.test_connectivity(dev[0], hapd, timeout=1)
+
+ if "OK" not in hapd.request("RESEND_GROUP_M1 " + addr):
+ raise Exception("RESEND_GROUP_M1 failed")
+ if "OK" not in hapd.request("RESET_PN ff:ff:ff:ff:ff:ff IGTK"):
+ raise Exception("RESET_PN failed")
+
+ time.sleep(0.1)
+ hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff test=1")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+ hwsim_utils.test_connectivity(dev[0], hapd, timeout=1)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_delayed_m1_m3_zero_tk(dev, apdev):
+ """Delayed M1+M3 retransmission and zero TK"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+ hapd.wait_sta()
+
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("RESEND_M1 " + addr + " change-anonce"):
+ raise Exception("RESEND_M1 failed")
+ if "OK" not in hapd.request("RESEND_M1 " + addr):
+ raise Exception("RESEND_M1 failed")
+ if "OK" not in hapd.request("RESEND_M3 " + addr):
+ raise Exception("RESEND_M3 failed")
+
+ KEY_FLAG_RX = 0x04
+ KEY_FLAG_TX = 0x08
+ KEY_FLAG_PAIRWISE = 0x20
+ KEY_FLAG_RX_TX = KEY_FLAG_RX | KEY_FLAG_TX
+ KEY_FLAG_PAIRWISE_RX_TX = KEY_FLAG_PAIRWISE | KEY_FLAG_RX_TX
+ if "OK" not in hapd.request("SET_KEY 3 %s %d %d %s %s %d" % (addr, 0, 1, 6*"00", 16*"00", KEY_FLAG_PAIRWISE_RX_TX)):
+ raise Exception("SET_KEY failed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd, timeout=1, broadcast=False,
+ success_expected=False)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_plaintext_m1_m3(dev, apdev):
+ """Plaintext M1/M3 during PTK rekey"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+
+ time.sleep(0.1)
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("RESEND_M1 " + addr + " plaintext"):
+ raise Exception("RESEND_M1 failed")
+ time.sleep(0.1)
+ if "OK" not in hapd.request("RESEND_M3 " + addr + " plaintext"):
+ raise Exception("RESEND_M3 failed")
+ time.sleep(0.1)
+
+def test_ap_wpa2_plaintext_m1_m3_pmf(dev, apdev):
+ """Plaintext M1/M3 during PTK rekey (PMF)"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect("test-wpa2-psk", psk="12345678", ieee80211w="2",
+ scan_freq="2412")
+
+ time.sleep(0.1)
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("RESEND_M1 " + addr + " plaintext"):
+ raise Exception("RESEND_M1 failed")
+ time.sleep(0.1)
+ if "OK" not in hapd.request("RESEND_M3 " + addr + " plaintext"):
+ raise Exception("RESEND_M3 failed")
+ time.sleep(0.1)
+
+def test_ap_wpa2_plaintext_m3(dev, apdev):
+ """Plaintext M3 during PTK rekey"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+
+ time.sleep(0.1)
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("RESEND_M1 " + addr):
+ raise Exception("RESEND_M1 failed")
+ time.sleep(0.1)
+ if "OK" not in hapd.request("RESEND_M3 " + addr + " plaintext"):
+ raise Exception("RESEND_M3 failed")
+ time.sleep(0.1)
+
+def test_ap_wpa2_plaintext_group_m1(dev, apdev):
+ """Plaintext group M1"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+
+ time.sleep(0.1)
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("RESEND_GROUP_M1 " + addr + " plaintext"):
+ raise Exception("RESEND_GROUP_M1 failed")
+ time.sleep(0.2)
+ if "OK" not in hapd.request("RESEND_GROUP_M1 " + addr):
+ raise Exception("RESEND_GROUP_M1 failed")
+ time.sleep(0.1)
+
+def test_ap_wpa2_plaintext_group_m1_pmf(dev, apdev):
+ """Plaintext group M1 (PMF)"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect("test-wpa2-psk", psk="12345678", ieee80211w="2",
+ scan_freq="2412")
+
+ time.sleep(0.1)
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("RESEND_GROUP_M1 " + addr + " plaintext"):
+ raise Exception("RESEND_GROUP_M1 failed")
+ time.sleep(0.2)
+ if "OK" not in hapd.request("RESEND_GROUP_M1 " + addr):
+ raise Exception("RESEND_GROUP_M1 failed")
+ time.sleep(0.1)
+
+def test_ap_wpa2_test_command_failures(dev, apdev):
+ """EAPOL/key config test command failures"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+ tests = ["RESEND_M1 foo",
+ "RESEND_M1 22:22:22:22:22:22",
+ "RESEND_M3 foo",
+ "RESEND_M3 22:22:22:22:22:22",
+ "RESEND_GROUP_M1 foo",
+ "RESEND_GROUP_M1 22:22:22:22:22:22",
+ "SET_KEY foo",
+ "SET_KEY 3 foo",
+ "SET_KEY 3 22:22:22:22:22:22",
+ "SET_KEY 3 22:22:22:22:22:22 1",
+ "SET_KEY 3 22:22:22:22:22:22 1 1",
+ "SET_KEY 3 22:22:22:22:22:22 1 1 q",
+ "SET_KEY 3 22:22:22:22:22:22 1 1 112233445566",
+ "SET_KEY 3 22:22:22:22:22:22 1 1 112233445566 1",
+ "SET_KEY 3 22:22:22:22:22:22 1 1 112233445566 12",
+ "SET_KEY 3 22:22:22:22:22:22 1 1 112233445566 12 1",
+ "SET_KEY 3 22:22:22:22:22:22 1 1 112233445566 12 1 ",
+ "RESET_PN ff:ff:ff:ff:ff:ff BIGTK",
+ "RESET_PN ff:ff:ff:ff:ff:ff IGTK",
+ "RESET_PN 22:22:22:22:22:22",
+ "RESET_PN foo"]
+ for t in tests:
+ if "FAIL" not in hapd.request(t):
+ raise Exception("Invalid command accepted: " + t)
+
+def test_ap_wpa2_gtk_initial_rsc_tkip(dev, apdev):
+ """Initial group cipher RSC (TKIP)"""
+ skip_without_tkip(dev[0])
+ run_ap_wpa2_gtk_initial_rsc(dev, apdev, "TKIP")
+
+def test_ap_wpa2_gtk_initial_rsc_ccmp(dev, apdev):
+ """Initial group cipher RSC (CCMP)"""
+ run_ap_wpa2_gtk_initial_rsc(dev, apdev, "CCMP")
+
+def test_ap_wpa2_gtk_initial_rsc_ccmp_256(dev, apdev):
+ """Initial group cipher RSC (CCMP-256)"""
+ run_ap_wpa2_gtk_initial_rsc(dev, apdev, "CCMP-256")
+
+def test_ap_wpa2_gtk_initial_rsc_gcmp(dev, apdev):
+ """Initial group cipher RSC (GCMP)"""
+ run_ap_wpa2_gtk_initial_rsc(dev, apdev, "GCMP")
+
+def test_ap_wpa2_gtk_initial_rsc_gcmp_256(dev, apdev):
+ """Initial group cipher RSC (GCMP-256)"""
+ run_ap_wpa2_gtk_initial_rsc(dev, apdev, "GCMP-256")
+
+def run_ap_wpa2_gtk_initial_rsc(dev, apdev, cipher):
+ if cipher not in dev[0].get_capability("pairwise") or \
+ cipher not in dev[0].get_capability("group"):
+ raise HwsimSkip("Cipher %s not supported" % cipher)
+
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ params["rsn_pairwise"] = cipher
+ params["group_cipher"] = cipher
+ params["gtk_rsc_override"] = "341200000000"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect("test-wpa2-psk", psk="12345678", proto="WPA2",
+ pairwise=cipher, group=cipher, scan_freq="2412")
+ hapd.wait_sta()
+ # Verify that unicast traffic works, but broadcast traffic does not.
+ hwsim_utils.test_connectivity(dev[0], hapd, broadcast=False)
+ hwsim_utils.test_connectivity(dev[0], hapd, success_expected=False)
+ hwsim_utils.test_connectivity(dev[0], hapd, success_expected=False)
+
+def test_ap_wpa2_igtk_initial_rsc_aes_128_cmac(dev, apdev):
+ """Initial management group cipher RSC (AES-128-CMAC)"""
+ run_ap_wpa2_igtk_initial_rsc(dev, apdev, "AES-128-CMAC")
+
+def test_ap_wpa2_igtk_initial_rsc_bip_gmac_128(dev, apdev):
+ """Initial management group cipher RSC (BIP-GMAC-128)"""
+ run_ap_wpa2_igtk_initial_rsc(dev, apdev, "BIP-GMAC-128")
+
+def test_ap_wpa2_igtk_initial_rsc_bip_gmac_256(dev, apdev):
+ """Initial management group cipher RSC (BIP-GMAC-256)"""
+ run_ap_wpa2_igtk_initial_rsc(dev, apdev, "BIP-GMAC-256")
+
+def test_ap_wpa2_igtk_initial_rsc_bip_cmac_256(dev, apdev):
+ """Initial management group cipher RSC (BIP-CMAC-256)"""
+ run_ap_wpa2_igtk_initial_rsc(dev, apdev, "BIP-CMAC-256")
+
+def run_ap_wpa2_igtk_initial_rsc(dev, apdev, cipher):
+ if cipher not in dev[0].get_capability("group_mgmt"):
+ raise HwsimSkip("Cipher %s not supported" % cipher)
+
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ params["ieee80211w"] = "2"
+ params["rsn_pairwise"] = "CCMP"
+ params["group_cipher"] = "CCMP"
+ params["group_mgmt_cipher"] = cipher
+ params["igtk_rsc_override"] = "341200000000"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect("test-wpa2-psk", psk="12345678", proto="WPA2",
+ ieee80211w="2", pairwise="CCMP", group="CCMP",
+ group_mgmt=cipher,
+ scan_freq="2412")
+ hapd.wait_sta()
+ # Verify that broadcast robust management frames are dropped.
+ dev[0].note("Sending broadcast Deauthentication and Disassociation frames with too small IPN")
+ hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff test=1")
+ hapd.request("DISASSOCIATE ff:ff:ff:ff:ff:ff test=1")
+ hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff test=1")
+ hapd.request("DISASSOCIATE ff:ff:ff:ff:ff:ff test=1")
+ dev[0].note("Done sending broadcast Deauthentication and Disassociation frames with too small IPN")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+ # Verify thar unicast robust management frames go through.
+ hapd.request("DEAUTHENTICATE " + dev[0].own_addr() + " reason=123 test=1")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is None:
+ raise Exception("Disconnection not reported")
+ if "reason=123" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
diff --git a/contrib/wpa/tests/hwsim/test_ap_config.py b/contrib/wpa/tests/hwsim/test_ap_config.py
new file mode 100644
index 000000000000..b1d9d2133188
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_config.py
@@ -0,0 +1,581 @@
+# hostapd configuration tests
+# Copyright (c) 2014-2016, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import signal
+import time
+import logging
+logger = logging.getLogger(__name__)
+import subprocess
+
+from remotehost import remote_compatible
+import hostapd
+from utils import alloc_fail, fail_test
+
+@remote_compatible
+def test_ap_config_errors(dev, apdev):
+ """Various hostapd configuration errors"""
+
+ # IEEE 802.11d without country code
+ params = {"ssid": "foo", "ieee80211d": "1"}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success (ieee80211d without country_code)")
+ hostapd.remove_bss(apdev[0])
+
+ # IEEE 802.11h without IEEE 802.11d
+ params = {"ssid": "foo", "ieee80211h": "1"}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success (ieee80211h without ieee80211d")
+ hostapd.remove_bss(apdev[0])
+
+ # Power Constraint without IEEE 802.11d
+ params = {"ssid": "foo", "local_pwr_constraint": "1"}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success (local_pwr_constraint without ieee80211d)")
+ hostapd.remove_bss(apdev[0])
+
+ # Spectrum management without Power Constraint
+ params = {"ssid": "foo", "spectrum_mgmt_required": "1"}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success (spectrum_mgmt_required without local_pwr_constraint)")
+ hostapd.remove_bss(apdev[0])
+
+ # IEEE 802.1X without authentication server
+ params = {"ssid": "foo", "ieee8021x": "1"}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success (ieee8021x)")
+ hostapd.remove_bss(apdev[0])
+
+ # RADIUS-PSK without macaddr_acl=2
+ params = hostapd.wpa2_params(ssid="foo", passphrase="12345678")
+ params["wpa_psk_radius"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success (wpa_psk_radius)")
+ hostapd.remove_bss(apdev[0])
+
+ # FT without NAS-Identifier
+ params = {"wpa": "2",
+ "wpa_key_mgmt": "FT-PSK",
+ "rsn_pairwise": "CCMP",
+ "wpa_passphrase": "12345678"}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success (FT without nas_identifier)")
+ hostapd.remove_bss(apdev[0])
+
+ # Hotspot 2.0 without WPA2/CCMP
+ params = hostapd.wpa2_params(ssid="foo")
+ params['wpa_key_mgmt'] = "WPA-EAP"
+ params['ieee8021x'] = "1"
+ params['auth_server_addr'] = "127.0.0.1"
+ params['auth_server_port'] = "1812"
+ params['auth_server_shared_secret'] = "radius"
+ params['interworking'] = "1"
+ params['hs20'] = "1"
+ params['wpa'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success (HS 2.0 without WPA2/CCMP)")
+ hostapd.remove_bss(apdev[0])
+
+def test_ap_config_reload(dev, apdev, params):
+ """hostapd configuration reload"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "foo"})
+ hapd.set("ssid", "foobar")
+ with open(os.path.join(params['logdir'], 'hostapd-test.pid'), "r") as f:
+ pid = int(f.read())
+ os.kill(pid, signal.SIGHUP)
+ time.sleep(0.1)
+ dev[0].connect("foobar", key_mgmt="NONE", scan_freq="2412")
+ hapd.set("ssid", "foo")
+ os.kill(pid, signal.SIGHUP)
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+
+def test_ap_config_reload_file(dev, apdev, params):
+ """hostapd configuration reload from file"""
+ hapd = hostapd.add_iface(apdev[0], "bss-1.conf")
+ hapd.enable()
+ hapd.set("ssid", "foobar")
+ with open(os.path.join(params['logdir'], 'hostapd-test.pid'), "r") as f:
+ pid = int(f.read())
+ os.kill(pid, signal.SIGHUP)
+ time.sleep(0.1)
+ dev[0].connect("foobar", key_mgmt="NONE", scan_freq="2412")
+ hapd.set("ssid", "foo")
+ os.kill(pid, signal.SIGHUP)
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+
+def test_ap_config_reload_file_while_disabled(dev, apdev, params):
+ """hostapd configuration reload from file when disabled"""
+ hapd = hostapd.add_iface(apdev[0], "bss-1.conf")
+ hapd.enable()
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=3)
+ if ev is None:
+ raise Exception("AP-ENABLED event not reported")
+ hapd.set("ssid", "foobar")
+ with open(os.path.join(params['logdir'], 'hostapd-test.pid'), "r") as f:
+ pid = int(f.read())
+ hapd.disable()
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=3)
+ if ev is None:
+ raise Exception("AP-DISABLED event not reported")
+ hapd.dump_monitor()
+ os.kill(pid, signal.SIGHUP)
+ time.sleep(0.1)
+ hapd.enable()
+ dev[0].connect("foobar", key_mgmt="NONE", scan_freq="2412")
+
+def write_hostapd_config(conffile, ifname, ssid, ht=True, bss2=False):
+ with open(conffile, "w") as f:
+ f.write("driver=nl80211\n")
+ f.write("hw_mode=g\n")
+ f.write("channel=1\n")
+ if ht:
+ f.write("ieee80211n=1\n")
+ f.write("interface=" + ifname + "\n")
+ f.write("ssid=" + ssid + "\n")
+ if bss2:
+ f.write("bss=" + ifname + "_2\n")
+ f.write("ssid=" + ssid + "-2\n")
+
+def test_ap_config_reload_on_sighup(dev, apdev, params):
+ """hostapd configuration reload modification from file on SIGHUP"""
+ run_ap_config_reload_on_sighup(dev, apdev, params)
+
+def test_ap_config_reload_on_sighup_no_ht(dev, apdev, params):
+ """hostapd configuration reload modification from file on SIGHUP (no HT)"""
+ run_ap_config_reload_on_sighup(dev, apdev, params, ht=False)
+
+def run_ap_config_reload_on_sighup(dev, apdev, params, ht=True):
+ name = "ap_config_reload_on_sighup"
+ if not ht:
+ name += "_no_ht"
+ pidfile = params['prefix'] + ".hostapd.pid"
+ logfile = params['prefix'] + ".hostapd.log"
+ conffile = params['prefix'] + ".hostapd.conf"
+ prg = os.path.join(params['logdir'], 'alt-hostapd/hostapd/hostapd')
+ if not os.path.exists(prg):
+ prg = '../../hostapd/hostapd'
+ write_hostapd_config(conffile, apdev[0]['ifname'], "test-1", ht=ht)
+ cmd = [prg, '-B', '-dddt', '-P', pidfile, '-f', logfile, conffile]
+ res = subprocess.check_call(cmd)
+ if res != 0:
+ raise Exception("Could not start hostapd: %s" % str(res))
+
+ dev[0].connect("test-1", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ write_hostapd_config(conffile, apdev[0]['ifname'], "test-2", ht=ht)
+ with open(pidfile, "r") as f:
+ pid = int(f.read())
+ os.kill(pid, signal.SIGHUP)
+
+ time.sleep(0.1)
+ dev[0].flush_scan_cache()
+
+ dev[0].connect("test-2", key_mgmt="NONE", scan_freq="2412")
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ os.kill(pid, signal.SIGTERM)
+ removed = False
+ for i in range(20):
+ time.sleep(0.1)
+ if not os.path.exists(pidfile):
+ removed = True
+ break
+ if not removed:
+ raise Exception("hostapd PID file not removed on SIGTERM")
+
+ if ht and "dd180050f202" not in bss['ie']:
+ raise Exception("Missing WMM IE after reload")
+ if not ht and "dd180050f202" in bss['ie']:
+ raise Exception("Unexpected WMM IE after reload")
+
+def test_ap_config_reload_on_sighup_bss_changes(dev, apdev, params):
+ """hostapd configuration reload modification from file on SIGHUP with bss remove/add"""
+ pidfile = params['prefix'] + ".hostapd.pid"
+ logfile = params['prefix'] + ".hostapd-log"
+ conffile = params['prefix'] + ".hostapd.conf"
+ prg = os.path.join(params['logdir'], 'alt-hostapd/hostapd/hostapd')
+ if not os.path.exists(prg):
+ prg = '../../hostapd/hostapd'
+ write_hostapd_config(conffile, apdev[0]['ifname'], "test", bss2=True)
+ cmd = [prg, '-B', '-dddt', '-P', pidfile, '-f', logfile, conffile]
+ res = subprocess.check_call(cmd)
+ if res != 0:
+ raise Exception("Could not start hostapd: %s" % str(res))
+
+ dev[0].connect("test", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ dev[1].connect("test-2", key_mgmt="NONE", scan_freq="2412")
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[1].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ write_hostapd_config(conffile, apdev[0]['ifname'], "test-a", bss2=False)
+ with open(pidfile, "r") as f:
+ pid = int(f.read())
+ os.kill(pid, signal.SIGHUP)
+
+ time.sleep(0.5)
+ dev[0].flush_scan_cache()
+
+ dev[0].connect("test-a", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ write_hostapd_config(conffile, apdev[0]['ifname'], "test-b", bss2=True)
+ os.kill(pid, signal.SIGHUP)
+
+ time.sleep(0.5)
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+ dev[0].connect("test-b", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ dev[1].connect("test-b-2", key_mgmt="NONE", scan_freq="2412")
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[1].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ os.kill(pid, signal.SIGTERM)
+
+def test_ap_config_reload_before_enable(dev, apdev, params):
+ """hostapd configuration reload before enable"""
+ hapd = hostapd.add_iface(apdev[0], "bss-1.conf")
+ with open(os.path.join(params['logdir'], 'hostapd-test.pid'), "r") as f:
+ pid = int(f.read())
+ os.kill(pid, signal.SIGHUP)
+ hapd.ping()
+
+def test_ap_config_sigusr1(dev, apdev, params):
+ """hostapd SIGUSR1"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "foobar"})
+ with open(os.path.join(params['logdir'], 'hostapd-test.pid'), "r") as f:
+ pid = int(f.read())
+ os.kill(pid, signal.SIGUSR1)
+ dev[0].connect("foobar", key_mgmt="NONE", scan_freq="2412")
+ os.kill(pid, signal.SIGUSR1)
+
+def test_ap_config_invalid_value(dev, apdev, params):
+ """Ignoring invalid hostapd configuration parameter updates"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test"}, no_enable=True)
+ not_exist = "/tmp/hostapd-test/does-not-exist"
+ tests = [("driver", "foobar"),
+ ("ssid2", "Q"),
+ ("macaddr_acl", "255"),
+ ("accept_mac_file", not_exist),
+ ("deny_mac_file", not_exist),
+ ("eapol_version", "255"),
+ ("eap_user_file", not_exist),
+ ("wep_key_len_broadcast", "-1"),
+ ("wep_key_len_unicast", "-1"),
+ ("wep_rekey_period", "-1"),
+ ("eap_rekey_period", "-1"),
+ ("radius_client_addr", "foo"),
+ ("acs_chan_bias", "-1:0.8"),
+ ("acs_chan_bias", "1"),
+ ("acs_chan_bias", "1:p"),
+ ("acs_chan_bias", "1:-0.8"),
+ ("acs_chan_bias", "1:0.8p"),
+ ("dtim_period", "0"),
+ ("bss_load_update_period", "-1"),
+ ("send_probe_response", "255"),
+ ("beacon_rate", "ht:-1"),
+ ("beacon_rate", "ht:32"),
+ ("beacon_rate", "vht:-1"),
+ ("beacon_rate", "vht:10"),
+ ("beacon_rate", "9"),
+ ("beacon_rate", "10001"),
+ ("vlan_file", not_exist),
+ ("bss", ""),
+ ("bssid", "foo"),
+ ("extra_cred", not_exist),
+ ("anqp_elem", "265"),
+ ("anqp_elem", "265"),
+ ("anqp_elem", "265:1"),
+ ("anqp_elem", "265:1q"),
+ ("fst_priority", ""),
+ ("fils_cache_id", "q"),
+ ("venue_url", "foo"),
+ ("venue_url", "1:" + 255*"a"),
+ ("sae_password", "secret|mac=qq"),
+ ("dpp_controller", "ipaddr=1"),
+ ("dpp_controller", "ipaddr=127.0.0.1 pkhash=q"),
+ ("dpp_controller", "ipaddr=127.0.0.1 pkhash=" + 32*"qq"),
+ ("dpp_controller", "pkhash=" + 32*"aa"),
+ ("check_cert_subject", ""),
+ ("eap_teap_auth", "-1"),
+ ("eap_teap_auth", "100"),
+ ("group_cipher", "foo"),
+ ("group_cipher", "NONE"),
+ ("chan_util_avg_period", "-1"),
+ ("multi_ap_backhaul_ssid", ""),
+ ("multi_ap_backhaul_ssid", '""'),
+ ("multi_ap_backhaul_ssid", "1"),
+ ("multi_ap_backhaul_ssid", '"' + 33*"A" + '"'),
+ ("multi_ap_backhaul_wpa_passphrase", ""),
+ ("multi_ap_backhaul_wpa_passphrase", 64*"q"),
+ ("multi_ap_backhaul_wpa_psk", "q"),
+ ("multi_ap_backhaul_wpa_psk", 63*"aa"),
+ ("hs20_release", "0"),
+ ("hs20_release", "255"),
+ ("dhcp_server", "::::::"),
+ ("dpp_netaccesskey", "q"),
+ ("dpp_csign", "q"),
+ ("owe_transition_bssid", "q"),
+ ("owe_transition_ssid", ""),
+ ("owe_transition_ssid", '""'),
+ ("owe_transition_ssid", '"' + 33*"a" + '"'),
+ ("multi_ap", "-1"),
+ ("multi_ap", "255"),
+ ("unknown-item", "foo")]
+ for field, val in tests:
+ if "FAIL" not in hapd.request("SET %s %s" % (field, val)):
+ raise Exception("Invalid %s accepted" % field)
+ hapd.enable()
+ dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+
+def test_ap_config_eap_user_file_parsing(dev, apdev, params):
+ """hostapd eap_user_file parsing"""
+ tmp = params['prefix'] + '.tmp'
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "foobar"})
+
+ for i in range(2):
+ if "OK" not in hapd.request("SET eap_user_file auth_serv/eap_user.conf"):
+ raise Exception("eap_user_file rejected")
+
+ tests = ["#\n\n*\tTLS\nradius_accept_attr=:",
+ "foo\n",
+ "\"foo\n",
+ "\"foo\"\n",
+ "\"foo\" FOOBAR\n",
+ "\"foo\" " + 10*"TLS," + "TLS \"\n",
+ "\"foo\" TLS \nfoo\n",
+ "\"foo\" PEAP hash:foo\n",
+ "\"foo\" PEAP hash:8846f7eaee8fb117ad06bdd830b7586q\n",
+ "\"foo\" PEAP 01020\n",
+ "\"foo\" PEAP 010q\n"
+ '"pwd" PWD ssha1:\n',
+ '"pwd" PWD ssha1:' + 20*'00' + '\n',
+ '"pwd" PWD ssha256:\n',
+ '"pwd" PWD ssha512:\n',
+ '"pwd" PWD ssha1:' + 20*'00' + 'qq\n',
+ '"pwd" PWD ssha1:' + 19*'00' + 'qq00\n',
+ "\"foo\" TLS\nradius_accept_attr=123:x:012\n",
+ "\"foo\" TLS\nradius_accept_attr=123:x:012q\n",
+ "\"foo\" TLS\nradius_accept_attr=123:Q:01\n",
+ "\"foo\" TLS\nradius_accept_attr=123\nfoo\n"]
+ for t in tests:
+ with open(tmp, "w") as f:
+ f.write(t)
+ if "FAIL" not in hapd.request("SET eap_user_file " + tmp):
+ raise Exception("Invalid eap_user_file accepted")
+
+ tests = [("\"foo\" TLS\n", 2, "hostapd_config_read_eap_user"),
+ ("\"foo\" PEAP \"foo\"\n", 3, "hostapd_config_read_eap_user"),
+ ("\"foo\" PEAP hash:8846f7eaee8fb117ad06bdd830b75861\n", 3,
+ "hostapd_config_read_eap_user"),
+ ("\"foo\" PEAP 0102\n", 3, "hostapd_config_read_eap_user"),
+ ("\"foo\" TLS\nradius_accept_attr=123\n", 1,
+ "=hostapd_parse_radius_attr"),
+ ("\"foo\" TLS\nradius_accept_attr=123\n", 1,
+ "wpabuf_alloc;hostapd_parse_radius_attr"),
+ ("\"foo\" TLS\nradius_accept_attr=123:s:foo\n", 2,
+ "hostapd_parse_radius_attr"),
+ ("\"foo\" TLS\nradius_accept_attr=123:x:0102\n", 2,
+ "hostapd_parse_radius_attr"),
+ ("\"foo\" TLS\nradius_accept_attr=123:d:1\n", 2,
+ "hostapd_parse_radius_attr"),
+ ('"pwd" PWD ssha1:046239e0660a59015231082a071c803e9f5848ae42eaccb4c08c97ae397bc879c4b071b9088ee715\n', 1, "hostapd_config_eap_user_salted"),
+ ('"pwd" PWD ssha1:046239e0660a59015231082a071c803e9f5848ae42eaccb4c08c97ae397bc879c4b071b9088ee715\n', 2, "hostapd_config_eap_user_salted"),
+ ("* TLS\n", 1, "hostapd_config_read_eap_user")]
+ for t, count, func in tests:
+ with alloc_fail(hapd, count, func):
+ with open(tmp, "w") as f:
+ f.write(t)
+ if "FAIL" not in hapd.request("SET eap_user_file " + tmp):
+ raise Exception("eap_user_file accepted during OOM")
+
+def test_ap_config_set_oom(dev, apdev):
+ """hostapd configuration parsing OOM"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "foobar"})
+
+ tests = [(1, "hostapd_parse_das_client",
+ "SET radius_das_client 192.168.1.123 pw"),
+ (1, "hostapd_parse_chanlist", "SET chanlist 1 6 11-13"),
+ (1, "hostapd_config_bss", "SET bss foo"),
+ (2, "hostapd_config_bss", "SET bss foo"),
+ (3, "hostapd_config_bss", "SET bss foo"),
+ (1, "add_r0kh",
+ "SET r0kh 02:01:02:03:04:05 r0kh-1.example.com 000102030405060708090a0b0c0d0e0f"),
+ (1, "add_r1kh",
+ "SET r1kh 02:01:02:03:04:05 02:11:22:33:44:55 000102030405060708090a0b0c0d0e0f"),
+ (1, "parse_roaming_consortium", "SET roaming_consortium 021122"),
+ (1, "parse_lang_string", "SET venue_name eng:Example venue"),
+ (1, "parse_3gpp_cell_net",
+ "SET anqp_3gpp_cell_net 244,91;310,026;234,56"),
+ (1, "parse_nai_realm", "SET nai_realm 0,example.com;example.net"),
+ (2, "parse_nai_realm", "SET nai_realm 0,example.com;example.net"),
+ (1, "parse_anqp_elem", "SET anqp_elem 265:0000"),
+ (2, "parse_anqp_elem", "SET anqp_elem 266:000000"),
+ (1, "parse_venue_url", "SET venue_url 1:http://example.com/"),
+ (1, "hs20_parse_operator_icon", "SET operator_icon icon"),
+ (2, "hs20_parse_operator_icon", "SET operator_icon icon"),
+ (1, "hs20_parse_conn_capab", "SET hs20_conn_capab 1:0:2"),
+ (1, "hs20_parse_wan_metrics",
+ "SET hs20_wan_metrics 01:8000:1000:80:240:3000"),
+ (1, "hs20_parse_icon",
+ "SET hs20_icon 32:32:eng:image/png:icon32:/tmp/icon32.png"),
+ (1, "hs20_parse_osu_server_uri",
+ "SET osu_server_uri https://example.com/osu/"),
+ (1, "hostapd_config_parse_acs_chan_bias",
+ "SET acs_chan_bias 1:0.8 6:0.8 11:0.8"),
+ (2, "hostapd_config_parse_acs_chan_bias",
+ "SET acs_chan_bias 1:0.8 6:0.8 11:0.8"),
+ (1, "parse_wpabuf_hex", "SET vendor_elements 01020304"),
+ (1, "parse_fils_realm", "SET fils_realm example.com"),
+ (1, "parse_sae_password", "SET sae_password secret"),
+ (2, "parse_sae_password", "SET sae_password secret"),
+ (2, "parse_sae_password", "SET sae_password secret|id=pw"),
+ (3, "parse_sae_password", "SET sae_password secret|id=pw"),
+ (1, "hostapd_dpp_controller_parse", "SET dpp_controller ipaddr=127.0.0.1 pkhash=" + 32*"11"),
+ (1, "hostapd_config_fill", "SET check_cert_subject foo"),
+ (1, "hostapd_config_fill", "SET multi_ap_backhaul_wpa_psk " + 64*"00"),
+ (1, "hostapd_parse_intlist;hostapd_config_fill",
+ "SET owe_groups 19"),
+ (1, "hostapd_config_fill",
+ "SET pac_opaque_encr_key 000102030405060708090a0b0c0d0e0f"),
+ (1, "hostapd_config_fill", "SET eap_message hello"),
+ (1, "hostapd_config_fill",
+ "SET wpa_psk 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"),
+ (1, "hostapd_config_fill", "SET time_zone EST5"),
+ (1, "hostapd_config_fill",
+ "SET network_auth_type 02http://www.example.com/redirect/"),
+ (1, "hostapd_config_fill", "SET domain_name example.com"),
+ (1, "hostapd_config_fill", "SET hs20_operating_class 5173"),
+ (1, "hostapd_config_fill", "SET own_ie_override 11223344"),
+ (1, "hostapd_parse_intlist", "SET sae_groups 19 25"),
+ (1, "hostapd_parse_intlist", "SET basic_rates 10 20 55 110"),
+ (1, "hostapd_parse_intlist", "SET supported_rates 10 20 55 110")]
+ if "WEP40" in dev[0].get_capability("group"):
+ tests += [(1, "hostapd_config_read_wep", "SET wep_key0 \"hello\""),
+ (1, "hostapd_config_read_wep", "SET wep_key0 0102030405")]
+ for count, func, cmd in tests:
+ with alloc_fail(hapd, count, func):
+ if "FAIL" not in hapd.request(cmd):
+ raise Exception("Command accepted during OOM: " + cmd)
+
+ hapd.set("hs20_icon", "32:32:eng:image/png:icon32:/tmp/icon32.png")
+ hapd.set("hs20_conn_capab", "1:0:2")
+ hapd.set("nai_realm", "0,example.com;example.net")
+ hapd.set("venue_name", "eng:Example venue")
+ hapd.set("roaming_consortium", "021122")
+ hapd.set("osu_server_uri", "https://example.com/osu/")
+ hapd.set("vendor_elements", "01020304")
+ hapd.set("vendor_elements", "01020304")
+ hapd.set("vendor_elements", "")
+ hapd.set("lci", "11223344")
+ hapd.set("civic", "11223344")
+ hapd.set("lci", "")
+ hapd.set("civic", "")
+
+ tests = [(1, "hs20_parse_icon",
+ "SET hs20_icon 32:32:eng:image/png:icon32:/tmp/icon32.png"),
+ (1, "parse_roaming_consortium", "SET roaming_consortium 021122"),
+ (2, "parse_nai_realm", "SET nai_realm 0,example.com;example.net"),
+ (1, "parse_lang_string", "SET venue_name eng:Example venue"),
+ (1, "hs20_parse_osu_server_uri",
+ "SET osu_server_uri https://example.com/osu/"),
+ (1, "hs20_parse_osu_nai", "SET osu_nai anonymous@example.com"),
+ (1, "hs20_parse_osu_nai2", "SET osu_nai2 anonymous@example.com"),
+ (1, "hostapd_parse_intlist", "SET osu_method_list 1 0"),
+ (1, "hs20_parse_osu_icon", "SET osu_icon icon32"),
+ (2, "hs20_parse_osu_icon", "SET osu_icon icon32"),
+ (2, "hs20_parse_osu_icon", "SET osu_icon icon32"),
+ (1, "hs20_parse_conn_capab", "SET hs20_conn_capab 1:0:2")]
+ for count, func, cmd in tests:
+ with alloc_fail(hapd, count, func):
+ if "FAIL" not in hapd.request(cmd):
+ raise Exception("Command accepted during OOM (2): " + cmd)
+
+ tests = [(1, "parse_fils_realm", "SET fils_realm example.com")]
+ for count, func, cmd in tests:
+ with fail_test(hapd, count, func):
+ if "FAIL" not in hapd.request(cmd):
+ raise Exception("Command accepted during FAIL_TEST: " + cmd)
+
+def test_ap_config_set_errors(dev, apdev):
+ """hostapd configuration parsing errors"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "foobar"})
+ if "WEP40" in dev[0].get_capability("group"):
+ hapd.set("wep_key0", '"hello"')
+ hapd.set("wep_key1", '"hello"')
+ hapd.set("wep_key0", '')
+ hapd.set("wep_key0", '"hello"')
+ if "FAIL" not in hapd.request("SET wep_key1 \"hello\""):
+ raise Exception("SET wep_key1 allowed to override existing key")
+ hapd.set("wep_key1", '')
+ hapd.set("wep_key1", '"hello"')
+
+ hapd.set("auth_server_addr", "127.0.0.1")
+ hapd.set("acct_server_addr", "127.0.0.1")
+
+ hapd.set("fst_group_id", "hello")
+ if "FAIL" not in hapd.request("SET fst_group_id hello2"):
+ raise Exception("Duplicate fst_group_id accepted")
+
+ tests = ["SET eap_reauth_period -1",
+ "SET fst_llt ",
+ "SET auth_server_addr_replace foo",
+ "SET acct_server_addr_replace foo"]
+ for t in tests:
+ if "FAIL" not in hapd.request(t):
+ raise Exception("Invalid command accepted: " + t)
+
+ # Deprecated entries
+ hapd.set("tx_queue_after_beacon_aifs", '2')
+ hapd.set("tx_queue_beacon_aifs", '2')
+ hapd.set("tx_queue_data9_aifs", '2')
+ hapd.set("debug", '1')
+ hapd.set("dump_file", '/tmp/hostapd-test-dump')
+ hapd.set("eap_authenticator", '0')
+ hapd.set("radio_measurements", '0')
+ hapd.set("radio_measurements", '1')
+ hapd.set("peerkey", "0")
+
+ # Various extra coverage (not really errors)
+ hapd.set("logger_syslog_level", '1')
+ hapd.set("logger_syslog", '0')
+ hapd.set("ctrl_interface_group", '4')
+ hapd.set("tls_flags", "[ALLOW-SIGN-RSA-MD5][DISABLE-TIME-CHECKS][DISABLE-TLSv1.0]")
+
+ for i in range(50000):
+ if "OK" not in hapd.request("SET hs20_conn_capab 17:5060:0"):
+ logger.info("hs20_conn_capab limit at %d" % i)
+ break
+ if i < 1000 or i >= 49999:
+ raise Exception("hs20_conn_capab limit not seen")
diff --git a/contrib/wpa/tests/hwsim/test_ap_csa.py b/contrib/wpa/tests/hwsim/test_ap_csa.py
new file mode 100644
index 000000000000..744d1e1f23ef
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_csa.py
@@ -0,0 +1,189 @@
+# AP CSA tests
+# Copyright (c) 2013, Luciano Coelho <luciano.coelho@intel.com>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import *
+
+def connect(dev, apdev, scan_freq="2412", **kwargs):
+ params = {"ssid": "ap-csa",
+ "channel": "1"}
+ params.update(kwargs)
+ ap = hostapd.add_ap(apdev[0], params)
+ dev.connect("ap-csa", key_mgmt="NONE", scan_freq=scan_freq)
+ return ap
+
+def switch_channel(ap, count, freq):
+ ap.request("CHAN_SWITCH " + str(count) + " " + str(freq))
+
+ ev = ap.wait_event(["CTRL-EVENT-STARTED-CHANNEL-SWITCH"], timeout=10)
+ if ev is None:
+ raise Exception("Channel switch start event not seen")
+ if "freq=" + str(freq) not in ev:
+ raise Exception("Unexpected channel in CS started event")
+
+ ev = ap.wait_event(["CTRL-EVENT-CHANNEL-SWITCH"], timeout=10)
+ if ev is None:
+ raise Exception("Channel switch completed event not seen")
+ if "freq=" + str(freq) not in ev:
+ raise Exception("Unexpected channel in CS completed event")
+
+ ev = ap.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=" + str(freq) not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+
+def wait_channel_switch(dev, freq):
+ ev = dev.wait_event(["CTRL-EVENT-STARTED-CHANNEL-SWITCH"], timeout=5)
+ if ev is None:
+ raise Exception("Channel switch start not reported")
+ if "freq=%d" % freq not in ev:
+ raise Exception("Unexpected frequency in channel switch started: " + ev)
+
+ ev = dev.wait_event(["CTRL-EVENT-CHANNEL-SWITCH"], timeout=5)
+ if ev is None:
+ raise Exception("Channel switch not reported")
+ if "freq=%d" % freq not in ev:
+ raise Exception("Unexpected frequency: " + ev)
+
+@remote_compatible
+def test_ap_csa_1_switch(dev, apdev):
+ """AP Channel Switch, one switch"""
+ csa_supported(dev[0])
+ freq = int(dev[0].get_driver_status_field("freq"))
+ if freq != 0:
+ raise Exception("Unexpected driver freq=%d in beginning" % freq)
+ ap = connect(dev[0], apdev)
+ freq = int(dev[0].get_driver_status_field("freq"))
+ if freq != 2412:
+ raise Exception("Unexpected driver freq=%d after association" % freq)
+
+ hwsim_utils.test_connectivity(dev[0], ap)
+ switch_channel(ap, 10, 2462)
+ wait_channel_switch(dev[0], 2462)
+ hwsim_utils.test_connectivity(dev[0], ap)
+ freq = int(dev[0].get_driver_status_field("freq"))
+ if freq != 2462:
+ raise Exception("Unexpected driver freq=%d after channel switch" % freq)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ freq = int(dev[0].get_driver_status_field("freq"))
+ if freq != 0:
+ raise Exception("Unexpected driver freq=%d after disconnection" % freq)
+
+@remote_compatible
+def test_ap_csa_2_switches(dev, apdev):
+ """AP Channel Switch, two switches"""
+ csa_supported(dev[0])
+ ap = connect(dev[0], apdev)
+
+ hwsim_utils.test_connectivity(dev[0], ap)
+ switch_channel(ap, 10, 2462)
+ wait_channel_switch(dev[0], 2462)
+ hwsim_utils.test_connectivity(dev[0], ap)
+ switch_channel(ap, 10, 2412)
+ wait_channel_switch(dev[0], 2412)
+ hwsim_utils.test_connectivity(dev[0], ap)
+
+@remote_compatible
+def test_ap_csa_1_switch_count_0(dev, apdev):
+ """AP Channel Switch, one switch with count 0"""
+ csa_supported(dev[0])
+ ap = connect(dev[0], apdev)
+
+ hwsim_utils.test_connectivity(dev[0], ap)
+ switch_channel(ap, 0, 2462)
+ # this does not result in CSA currently, so do not bother checking
+ # connectivity
+
+@remote_compatible
+def test_ap_csa_2_switches_count_0(dev, apdev):
+ """AP Channel Switch, two switches with count 0"""
+ csa_supported(dev[0])
+ ap = connect(dev[0], apdev)
+
+ hwsim_utils.test_connectivity(dev[0], ap)
+ switch_channel(ap, 0, 2462)
+ # this does not result in CSA currently, so do not bother checking
+ # connectivity
+ switch_channel(ap, 0, 2412)
+ # this does not result in CSA currently, so do not bother checking
+ # connectivity
+
+@remote_compatible
+def test_ap_csa_1_switch_count_1(dev, apdev):
+ """AP Channel Switch, one switch with count 1"""
+ csa_supported(dev[0])
+ ap = connect(dev[0], apdev)
+
+ hwsim_utils.test_connectivity(dev[0], ap)
+ switch_channel(ap, 1, 2462)
+ # this does not result in CSA currently, so do not bother checking
+ # connectivity
+
+@remote_compatible
+def test_ap_csa_2_switches_count_1(dev, apdev):
+ """AP Channel Switch, two switches with count 1"""
+ csa_supported(dev[0])
+ ap = connect(dev[0], apdev)
+
+ hwsim_utils.test_connectivity(dev[0], ap)
+ switch_channel(ap, 1, 2462)
+ # this does not result in CSA currently, so do not bother checking
+ # connectivity
+ switch_channel(ap, 1, 2412)
+ # this does not result in CSA currently, so do not bother checking
+ # connectivity
+
+@remote_compatible
+def test_ap_csa_1_switch_count_2(dev, apdev):
+ """AP Channel Switch, one switch with count 2"""
+ csa_supported(dev[0])
+ ap = connect(dev[0], apdev)
+
+ hwsim_utils.test_connectivity(dev[0], ap)
+ switch_channel(ap, 2, 2462)
+ wait_channel_switch(dev[0], 2462)
+ hwsim_utils.test_connectivity(dev[0], ap)
+
+@remote_compatible
+def test_ap_csa_ecsa_only(dev, apdev):
+ """AP Channel Switch, one switch with only ECSA IE"""
+ csa_supported(dev[0])
+ ap = connect(dev[0], apdev, ecsa_ie_only="1")
+
+ hwsim_utils.test_connectivity(dev[0], ap)
+ switch_channel(ap, 10, 2462)
+ wait_channel_switch(dev[0], 2462)
+ hwsim_utils.test_connectivity(dev[0], ap)
+
+@remote_compatible
+def test_ap_csa_invalid(dev, apdev):
+ """AP Channel Switch - invalid channel"""
+ csa_supported(dev[0])
+ ap = connect(dev[0], apdev)
+
+ vals = [2461, 4900, 4901, 5181, 5746, 5699, 5895, 5899]
+ for val in vals:
+ if "FAIL" not in ap.request("CHAN_SWITCH 1 %d" % val):
+ raise Exception("Invalid channel accepted: %d" % val)
+
+def test_ap_csa_disable(dev, apdev):
+ """AP Channel Switch and DISABLE command before completion"""
+ csa_supported(dev[0])
+ ap = connect(dev[0], apdev, scan_freq="2412 2462")
+ if "OK" not in ap.request("CHAN_SWITCH 10 2462"):
+ raise Exception("CHAN_SWITCH failed")
+ ap.disable()
+ ap.enable()
+ dev[0].wait_disconnected()
+ dev[0].wait_connected()
diff --git a/contrib/wpa/tests/hwsim/test_ap_dynamic.py b/contrib/wpa/tests/hwsim/test_ap_dynamic.py
new file mode 100644
index 000000000000..ad29eb71eb76
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_dynamic.py
@@ -0,0 +1,586 @@
+# Test cases for dynamic BSS changes with hostapd
+# Copyright (c) 2013, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+import os
+
+import hwsim_utils
+import hostapd
+from utils import *
+from test_ap_acs import force_prev_ap_on_24g
+
+@remote_compatible
+def test_ap_change_ssid(dev, apdev):
+ """Dynamic SSID change with hostapd and WPA2-PSK"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk-start",
+ passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = dev[0].connect("test-wpa2-psk-start", psk="12345678",
+ scan_freq="2412")
+ dev[0].request("DISCONNECT")
+
+ logger.info("Change SSID dynamically")
+ res = hapd.request("SET ssid test-wpa2-psk-new")
+ if "OK" not in res:
+ raise Exception("SET command failed")
+ res = hapd.request("RELOAD")
+ if "OK" not in res:
+ raise Exception("RELOAD command failed")
+
+ dev[0].set_network_quoted(id, "ssid", "test-wpa2-psk-new")
+ dev[0].connect_network(id)
+
+def test_ap_change_ssid_wps(dev, apdev):
+ """Dynamic SSID change with hostapd and WPA2-PSK using WPS"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk-start",
+ passphrase="12345678")
+ # Use a PSK and not the passphrase, because the PSK will have to be computed
+ # again if we use a passphrase.
+ del params["wpa_passphrase"]
+ params["wpa_psk"] = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+
+ params.update({"wps_state": "2", "eap_server": "1"})
+ bssid = apdev[0]['bssid']
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ new_ssid = "test-wpa2-psk-new"
+ logger.info("Change SSID dynamically (WPS)")
+ res = hapd.request("SET ssid " + new_ssid)
+ if "OK" not in res:
+ raise Exception("SET command failed")
+ res = hapd.request("RELOAD")
+ if "OK" not in res:
+ raise Exception("RELOAD command failed")
+
+ # Connect to the new ssid using wps:
+ hapd.request("WPS_PBC")
+ if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[0].request("WPS_PBC")
+ dev[0].wait_connected(timeout=20)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != bssid:
+ raise Exception("Not fully connected")
+ if status['ssid'] != new_ssid:
+ raise Exception("Unexpected SSID %s != %s" % (status['ssid'], new_ssid))
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_ap_reload_invalid(dev, apdev):
+ """hostapd RELOAD with invalid configuration"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk-start",
+ passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+ # Enable IEEE 802.11d without specifying country code
+ hapd.set("ieee80211d", "1")
+ if "FAIL" not in hapd.request("RELOAD"):
+ raise Exception("RELOAD command succeeded")
+ dev[0].connect("test-wpa2-psk-start", psk="12345678", scan_freq="2412")
+
+def multi_check(apdev, dev, check, scan_opt=True):
+ id = []
+ num_bss = len(check)
+ for i in range(0, num_bss):
+ dev[i].request("BSS_FLUSH 0")
+ dev[i].dump_monitor()
+ for i in range(0, num_bss):
+ if check[i]:
+ continue
+ id.append(dev[i].connect("bss-" + str(i + 1), key_mgmt="NONE",
+ scan_freq="2412", wait_connect=False))
+ for i in range(num_bss):
+ if not check[i]:
+ continue
+ bssid = hostapd.bssid_inc(apdev, i)
+ if scan_opt:
+ dev[i].scan_for_bss(bssid, freq=2412)
+ id.append(dev[i].connect("bss-" + str(i + 1), key_mgmt="NONE",
+ scan_freq="2412", wait_connect=True))
+ first = True
+ for i in range(num_bss):
+ if not check[i]:
+ timeout = 0.2 if first else 0.01
+ first = False
+ ev = dev[i].wait_event(["CTRL-EVENT-CONNECTED"], timeout=timeout)
+ if ev:
+ raise Exception("Unexpected connection")
+
+ for i in range(0, num_bss):
+ dev[i].remove_network(id[i])
+ for i in range(num_bss):
+ if check[i]:
+ dev[i].wait_disconnected(timeout=5)
+
+ res = ''
+ for i in range(0, num_bss):
+ res = res + dev[i].request("BSS RANGE=ALL MASK=0x2")
+
+ for i in range(0, num_bss):
+ if not check[i]:
+ bssid = '02:00:00:00:03:0' + str(i)
+ if bssid in res:
+ raise Exception("Unexpected BSS" + str(i) + " in scan results")
+
+def test_ap_bss_add_remove(dev, apdev):
+ """Dynamic BSS add/remove operations with hostapd"""
+ try:
+ _test_ap_bss_add_remove(dev, apdev)
+ finally:
+ for i in range(3):
+ dev[i].request("SCAN_INTERVAL 5")
+
+def _test_ap_bss_add_remove(dev, apdev):
+ for i in range(3):
+ dev[i].flush_scan_cache()
+ dev[i].request("SCAN_INTERVAL 1")
+ ifname1 = apdev[0]['ifname']
+ ifname2 = apdev[0]['ifname'] + '-2'
+ ifname3 = apdev[0]['ifname'] + '-3'
+ logger.info("Set up three BSSes one by one")
+ hostapd.add_bss(apdev[0], ifname1, 'bss-1.conf')
+ multi_check(apdev[0], dev, [True, False, False])
+ hostapd.add_bss(apdev[0], ifname2, 'bss-2.conf')
+ multi_check(apdev[0], dev, [True, True, False])
+ hostapd.add_bss(apdev[0], ifname3, 'bss-3.conf')
+ multi_check(apdev[0], dev, [True, True, True])
+
+ logger.info("Remove the last BSS and re-add it")
+ hostapd.remove_bss(apdev[0], ifname3)
+ multi_check(apdev[0], dev, [True, True, False])
+ hostapd.add_bss(apdev[0], ifname3, 'bss-3.conf')
+ multi_check(apdev[0], dev, [True, True, True])
+
+ logger.info("Remove the middle BSS and re-add it")
+ hostapd.remove_bss(apdev[0], ifname2)
+ multi_check(apdev[0], dev, [True, False, True])
+ hostapd.add_bss(apdev[0], ifname2, 'bss-2.conf')
+ multi_check(apdev[0], dev, [True, True, True])
+
+ logger.info("Remove the first BSS and re-add it and other BSSs")
+ hostapd.remove_bss(apdev[0], ifname1)
+ multi_check(apdev[0], dev, [False, False, False])
+ hostapd.add_bss(apdev[0], ifname1, 'bss-1.conf')
+ hostapd.add_bss(apdev[0], ifname2, 'bss-2.conf')
+ hostapd.add_bss(apdev[0], ifname3, 'bss-3.conf')
+ multi_check(apdev[0], dev, [True, True, True])
+
+ logger.info("Remove two BSSes and re-add them")
+ hostapd.remove_bss(apdev[0], ifname2)
+ multi_check(apdev[0], dev, [True, False, True])
+ hostapd.remove_bss(apdev[0], ifname3)
+ multi_check(apdev[0], dev, [True, False, False])
+ hostapd.add_bss(apdev[0], ifname2, 'bss-2.conf')
+ multi_check(apdev[0], dev, [True, True, False])
+ hostapd.add_bss(apdev[0], ifname3, 'bss-3.conf')
+ multi_check(apdev[0], dev, [True, True, True])
+
+ logger.info("Remove three BSSes in and re-add them")
+ hostapd.remove_bss(apdev[0], ifname3)
+ multi_check(apdev[0], dev, [True, True, False])
+ hostapd.remove_bss(apdev[0], ifname2)
+ multi_check(apdev[0], dev, [True, False, False])
+ hostapd.remove_bss(apdev[0], ifname1)
+ multi_check(apdev[0], dev, [False, False, False])
+ hostapd.add_bss(apdev[0], ifname1, 'bss-1.conf')
+ multi_check(apdev[0], dev, [True, False, False])
+ hostapd.add_bss(apdev[0], ifname2, 'bss-2.conf')
+ multi_check(apdev[0], dev, [True, True, False])
+ hostapd.add_bss(apdev[0], ifname3, 'bss-3.conf')
+ multi_check(apdev[0], dev, [True, True, True])
+
+ logger.info("Test error handling if a duplicate ifname is tried")
+ hostapd.add_bss(apdev[0], ifname3, 'bss-3.conf', ignore_error=True)
+ multi_check(apdev[0], dev, [True, True, True])
+
+def test_ap_bss_add_remove_during_ht_scan(dev, apdev):
+ """Dynamic BSS add during HT40 co-ex scan"""
+ for i in range(3):
+ dev[i].flush_scan_cache()
+ ifname1 = apdev[0]['ifname']
+ ifname2 = apdev[0]['ifname'] + '-2'
+ confname1 = hostapd.cfg_file(apdev[0], "bss-ht40-1.conf")
+ confname2 = hostapd.cfg_file(apdev[0], "bss-ht40-2.conf")
+ hapd_global = hostapd.HostapdGlobal(apdev)
+ hapd_global.send_file(confname1, confname1)
+ hapd_global.send_file(confname2, confname2)
+ hostapd.add_bss(apdev[0], ifname1, confname1)
+ hostapd.add_bss(apdev[0], ifname2, confname2)
+ multi_check(apdev[0], dev, [True, True], scan_opt=False)
+ hostapd.remove_bss(apdev[0], ifname2)
+ hostapd.remove_bss(apdev[0], ifname1)
+
+ hostapd.add_bss(apdev[0], ifname1, confname1)
+ hostapd.add_bss(apdev[0], ifname2, confname2)
+ hostapd.remove_bss(apdev[0], ifname2)
+ multi_check(apdev[0], dev, [True, False], scan_opt=False)
+ hostapd.remove_bss(apdev[0], ifname1)
+
+ hostapd.add_bss(apdev[0], ifname1, confname1)
+ hostapd.add_bss(apdev[0], ifname2, confname2)
+ hostapd.remove_bss(apdev[0], ifname1)
+ multi_check(apdev[0], dev, [False, False])
+
+def test_ap_multi_bss_config(dev, apdev):
+ """hostapd start with a multi-BSS configuration file"""
+ for i in range(3):
+ dev[i].flush_scan_cache()
+ ifname1 = apdev[0]['ifname']
+ ifname2 = apdev[0]['ifname'] + '-2'
+ ifname3 = apdev[0]['ifname'] + '-3'
+ logger.info("Set up three BSSes with one configuration file")
+ hapd = hostapd.add_iface(apdev[0], 'multi-bss.conf')
+ hapd.enable()
+ multi_check(apdev[0], dev, [True, True, True])
+ hostapd.remove_bss(apdev[0], ifname2)
+ multi_check(apdev[0], dev, [True, False, True])
+ hostapd.remove_bss(apdev[0], ifname3)
+ multi_check(apdev[0], dev, [True, False, False])
+ hostapd.remove_bss(apdev[0], ifname1)
+ multi_check(apdev[0], dev, [False, False, False])
+
+ hapd = hostapd.add_iface(apdev[0], 'multi-bss.conf')
+ hapd.enable()
+ hostapd.remove_bss(apdev[0], ifname1)
+ multi_check(apdev[0], dev, [False, False, False])
+
+def invalid_ap(ap):
+ logger.info("Trying to start AP " + ap['ifname'] + " with invalid configuration")
+ hapd = hostapd.add_ap(ap, {}, no_enable=True)
+ hapd.set("ssid", "invalid-config")
+ hapd.set("channel", "12345")
+ try:
+ hapd.enable()
+ started = True
+ except Exception as e:
+ started = False
+ if started:
+ raise Exception("ENABLE command succeeded unexpectedly")
+ return hapd
+
+@remote_compatible
+def test_ap_invalid_config(dev, apdev):
+ """Try to start AP with invalid configuration and fix configuration"""
+ hapd = invalid_ap(apdev[0])
+
+ logger.info("Fix configuration and start AP again")
+ hapd.set("channel", "1")
+ hapd.enable()
+ dev[0].connect("invalid-config", key_mgmt="NONE", scan_freq="2412")
+
+@remote_compatible
+def test_ap_invalid_config2(dev, apdev):
+ """Try to start AP with invalid configuration and remove interface"""
+ hapd = invalid_ap(apdev[0])
+ logger.info("Remove interface with failed configuration")
+ hostapd.remove_bss(apdev[0])
+
+def test_ap_remove_during_acs(dev, apdev):
+ """Remove interface during ACS"""
+ force_prev_ap_on_24g(apdev[0])
+ params = hostapd.wpa2_params(ssid="test-acs-remove", passphrase="12345678")
+ params['channel'] = '0'
+ hostapd.add_ap(apdev[0], params)
+ hostapd.remove_bss(apdev[0])
+
+def test_ap_remove_during_acs2(dev, apdev):
+ """Remove BSS during ACS in multi-BSS configuration"""
+ force_prev_ap_on_24g(apdev[0])
+ ifname = apdev[0]['ifname']
+ ifname2 = ifname + "-2"
+ hapd = hostapd.add_ap(apdev[0], {}, no_enable=True)
+ hapd.set("ssid", "test-acs-remove")
+ hapd.set("channel", "0")
+ hapd.set("bss", ifname2)
+ hapd.set("ssid", "test-acs-remove2")
+ hapd.enable()
+ hostapd.remove_bss(apdev[0])
+
+def test_ap_remove_during_acs3(dev, apdev):
+ """Remove second BSS during ACS in multi-BSS configuration"""
+ force_prev_ap_on_24g(apdev[0])
+ ifname = apdev[0]['ifname']
+ ifname2 = ifname + "-2"
+ hapd = hostapd.add_ap(apdev[0], {}, no_enable=True)
+ hapd.set("ssid", "test-acs-remove")
+ hapd.set("channel", "0")
+ hapd.set("bss", ifname2)
+ hapd.set("ssid", "test-acs-remove2")
+ hapd.enable()
+ hostapd.remove_bss(apdev[0], ifname2)
+
+@remote_compatible
+def test_ap_remove_during_ht_coex_scan(dev, apdev):
+ """Remove interface during HT co-ex scan"""
+ params = hostapd.wpa2_params(ssid="test-ht-remove", passphrase="12345678")
+ params['channel'] = '1'
+ params['ht_capab'] = "[HT40+]"
+ ifname = apdev[0]['ifname']
+ hostapd.add_ap(apdev[0], params)
+ hostapd.remove_bss(apdev[0])
+
+def test_ap_remove_during_ht_coex_scan2(dev, apdev):
+ """Remove BSS during HT co-ex scan in multi-BSS configuration"""
+ ifname = apdev[0]['ifname']
+ ifname2 = ifname + "-2"
+ hapd = hostapd.add_ap(apdev[0], {}, no_enable=True)
+ hapd.set("ssid", "test-ht-remove")
+ hapd.set("channel", "1")
+ hapd.set("ht_capab", "[HT40+]")
+ hapd.set("bss", ifname2)
+ hapd.set("ssid", "test-ht-remove2")
+ hapd.enable()
+ hostapd.remove_bss(apdev[0])
+
+def test_ap_remove_during_ht_coex_scan3(dev, apdev):
+ """Remove second BSS during HT co-ex scan in multi-BSS configuration"""
+ ifname = apdev[0]['ifname']
+ ifname2 = ifname + "-2"
+ hapd = hostapd.add_ap(apdev[0], {}, no_enable=True)
+ hapd.set("ssid", "test-ht-remove")
+ hapd.set("channel", "1")
+ hapd.set("ht_capab", "[HT40+]")
+ hapd.set("bss", ifname2)
+ hapd.set("ssid", "test-ht-remove2")
+ hapd.enable()
+ hostapd.remove_bss(apdev[0], ifname2)
+
+@remote_compatible
+def test_ap_enable_disable_reenable(dev, apdev):
+ """Enable, disable, re-enable AP"""
+ hapd = hostapd.add_ap(apdev[0], {}, no_enable=True)
+ hapd.set("ssid", "dynamic")
+ hapd.enable()
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=30)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ dev[0].connect("dynamic", key_mgmt="NONE", scan_freq="2412")
+ hapd.disable()
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=30)
+ if ev is None:
+ raise Exception("AP disabling timed out")
+ dev[0].wait_disconnected(timeout=10)
+ hapd.enable()
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=30)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ dev[1].connect("dynamic", key_mgmt="NONE", scan_freq="2412")
+ dev[0].wait_connected(timeout=10)
+
+def test_ap_double_disable(dev, apdev):
+ """Double DISABLE regression test"""
+ hapd = hostapd.add_bss(apdev[0], apdev[0]['ifname'], 'bss-1.conf')
+ hostapd.add_bss(apdev[0], apdev[0]['ifname'] + '-2', 'bss-2.conf')
+ hapd.disable()
+ if "FAIL" not in hapd.request("DISABLE"):
+ raise Exception("Second DISABLE accepted unexpectedly")
+ hapd.enable()
+ hapd.disable()
+ if "FAIL" not in hapd.request("DISABLE"):
+ raise Exception("Second DISABLE accepted unexpectedly")
+
+def test_ap_bss_add_many(dev, apdev):
+ """Large number of BSS add operations with hostapd"""
+ try:
+ _test_ap_bss_add_many(dev, apdev)
+ finally:
+ dev[0].request("SCAN_INTERVAL 5")
+ ifname = apdev[0]['ifname']
+ hapd = hostapd.HostapdGlobal(apdev[0])
+ hapd.flush()
+ for i in range(16):
+ ifname2 = ifname + '-' + str(i)
+ hapd.remove(ifname2)
+ try:
+ os.remove('/tmp/hwsim-bss.conf')
+ except:
+ pass
+
+def _test_ap_bss_add_many(dev, apdev):
+ ifname = apdev[0]['ifname']
+ hostapd.add_bss(apdev[0], ifname, 'bss-1.conf')
+ fname = '/tmp/hwsim-bss.conf'
+ for i in range(16):
+ ifname2 = ifname + '-' + str(i)
+ with open(fname, 'w') as f:
+ f.write("driver=nl80211\n")
+ f.write("hw_mode=g\n")
+ f.write("channel=1\n")
+ f.write("ieee80211n=1\n")
+ f.write("interface=%s\n" % ifname2)
+ f.write("bssid=02:00:00:00:03:%02x\n" % (i + 1))
+ f.write("ctrl_interface=/var/run/hostapd\n")
+ f.write("ssid=test-%d\n" % i)
+ hostapd.add_bss(apdev[0], ifname2, fname)
+ os.remove(fname)
+
+ dev[0].request("SCAN_INTERVAL 1")
+ dev[0].connect("bss-1", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=5)
+ for i in range(16):
+ dev[0].connect("test-%d" % i, key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=5)
+ ifname2 = ifname + '-' + str(i)
+ hostapd.remove_bss(apdev[0], ifname2)
+
+def test_ap_bss_add_reuse_existing(dev, apdev):
+ """Dynamic BSS add operation reusing existing interface"""
+ ifname1 = apdev[0]['ifname']
+ ifname2 = apdev[0]['ifname'] + '-2'
+ hostapd.add_bss(apdev[0], ifname1, 'bss-1.conf')
+ subprocess.check_call(["iw", "dev", ifname1, "interface", "add", ifname2,
+ "type", "__ap"])
+ hostapd.add_bss(apdev[0], ifname2, 'bss-2.conf')
+ hostapd.remove_bss(apdev[0], ifname2)
+ subprocess.check_call(["iw", "dev", ifname2, "del"])
+
+def hapd_bss_out_of_mem(hapd, phy, confname, count, func):
+ with alloc_fail(hapd, count, func):
+ hapd_global = hostapd.HostapdGlobal()
+ res = hapd_global.ctrl.request("ADD bss_config=" + phy + ":" + confname)
+ if "OK" in res:
+ raise Exception("add_bss succeeded")
+
+def test_ap_bss_add_out_of_memory(dev, apdev):
+ """Running out of memory while adding a BSS"""
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "open"})
+
+ ifname1 = apdev[0]['ifname']
+ ifname2 = apdev[0]['ifname'] + '-2'
+
+ confname1 = hostapd.cfg_file(apdev[0], "bss-1.conf")
+ confname2 = hostapd.cfg_file(apdev[0], "bss-2.conf")
+ hapd_bss_out_of_mem(hapd2, 'phy3', confname1, 1, 'hostapd_add_iface')
+ for i in range(1, 3):
+ hapd_bss_out_of_mem(hapd2, 'phy3', confname1,
+ i, 'hostapd_interface_init_bss')
+ hapd_bss_out_of_mem(hapd2, 'phy3', confname1,
+ 1, 'ieee802_11_build_ap_params')
+
+ hostapd.add_bss(apdev[0], ifname1, confname1)
+
+ hapd_bss_out_of_mem(hapd2, 'phy3', confname2,
+ 1, 'hostapd_interface_init_bss')
+ hapd_bss_out_of_mem(hapd2, 'phy3', confname2,
+ 1, 'ieee802_11_build_ap_params')
+
+ hostapd.add_bss(apdev[0], ifname2, confname2)
+ hostapd.remove_bss(apdev[0], ifname2)
+ hostapd.remove_bss(apdev[0], ifname1)
+
+def test_ap_multi_bss(dev, apdev):
+ """Multiple BSSes with hostapd"""
+ ifname1 = apdev[0]['ifname']
+ ifname2 = apdev[0]['ifname'] + '-2'
+ hapd1 = hostapd.add_bss(apdev[0], ifname1, 'bss-1.conf')
+ hapd2 = hostapd.add_bss(apdev[0], ifname2, 'bss-2.conf')
+ dev[0].connect("bss-1", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("bss-2", key_mgmt="NONE", scan_freq="2412")
+
+ hwsim_utils.test_connectivity(dev[0], hapd1)
+ hwsim_utils.test_connectivity(dev[1], hapd2)
+
+ sta0 = hapd1.get_sta(dev[0].own_addr())
+ sta1 = hapd2.get_sta(dev[1].own_addr())
+ if 'rx_packets' not in sta0 or int(sta0['rx_packets']) < 1:
+ raise Exception("sta0 did not report receiving packets")
+ if 'rx_packets' not in sta1 or int(sta1['rx_packets']) < 1:
+ raise Exception("sta1 did not report receiving packets")
+
+@remote_compatible
+def test_ap_add_with_driver(dev, apdev):
+ """Add hostapd interface with driver specified"""
+ ifname = apdev[0]['ifname']
+ try:
+ hostname = apdev[0]['hostname']
+ except:
+ hostname = None
+ hapd_global = hostapd.HostapdGlobal(apdev[0])
+ hapd_global.add(ifname, driver="nl80211")
+ port = hapd_global.get_ctrl_iface_port(ifname)
+ hapd = hostapd.Hostapd(ifname, hostname, port)
+ hapd.set_defaults()
+ hapd.set("ssid", "dynamic")
+ hapd.enable()
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=30)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ dev[0].connect("dynamic", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.disable()
+
+def test_ap_duplicate_bssid(dev, apdev):
+ """Duplicate BSSID"""
+ params = {"ssid": "test"}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ hapd.enable()
+ ifname2 = apdev[0]['ifname'] + '-2'
+ ifname3 = apdev[0]['ifname'] + '-3'
+ # "BSS 'wlan3-2' may not have BSSID set to the MAC address of the radio"
+ try:
+ hostapd.add_bss(apdev[0], ifname2, 'bss-2-dup.conf')
+ raise Exception("BSS add succeeded unexpectedly")
+ except Exception as e:
+ if "Could not add hostapd BSS" in str(e):
+ pass
+ else:
+ raise
+
+ hostapd.add_bss(apdev[0], ifname3, 'bss-3.conf')
+
+ dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ hapd.set("bssid", "02:00:00:00:03:02")
+ hapd.disable()
+ # "Duplicate BSSID 02:00:00:00:03:02 on interface 'wlan3-3' and 'wlan3'."
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE with duplicate BSSID succeeded unexpectedly")
+
+def test_ap_bss_config_file(dev, apdev, params):
+ """hostapd BSS config file"""
+ pidfile = params['prefix'] + ".hostapd.pid"
+ logfile = params['prefix'] + ".hostapd-log"
+ prg = os.path.join(params['logdir'], 'alt-hostapd/hostapd/hostapd')
+ if not os.path.exists(prg):
+ prg = '../../hostapd/hostapd'
+ phy = get_phy(apdev[0])
+ confname1 = hostapd.cfg_file(apdev[0], "bss-1.conf")
+ confname2 = hostapd.cfg_file(apdev[0], "bss-2.conf")
+ confname3 = hostapd.cfg_file(apdev[0], "bss-3.conf")
+
+ cmd = [prg, '-B', '-dddt', '-P', pidfile, '-f', logfile, '-S', '-T',
+ '-b', phy + ':' + confname1, '-b', phy + ':' + confname2,
+ '-b', phy + ':' + confname3]
+ res = subprocess.check_call(cmd)
+ if res != 0:
+ raise Exception("Could not start hostapd: %s" % str(res))
+ multi_check(apdev[0], dev, [True, True, True])
+ for i in range(0, 3):
+ dev[i].request("DISCONNECT")
+
+ hapd = hostapd.Hostapd(apdev[0]['ifname'])
+ hapd.ping()
+ if "OK" not in hapd.request("TERMINATE"):
+ raise Exception("Failed to terminate hostapd process")
+ ev = hapd.wait_event(["CTRL-EVENT-TERMINATING"], timeout=15)
+ if ev is None:
+ raise Exception("CTRL-EVENT-TERMINATING not seen")
+ for i in range(30):
+ time.sleep(0.1)
+ if not os.path.exists(pidfile):
+ break
+ if os.path.exists(pidfile):
+ raise Exception("PID file exits after process termination")
diff --git a/contrib/wpa/tests/hwsim/test_ap_eap.py b/contrib/wpa/tests/hwsim/test_ap_eap.py
new file mode 100644
index 000000000000..d5e1d995b81e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_eap.py
@@ -0,0 +1,7492 @@
+# -*- coding: utf-8 -*-
+# WPA2-Enterprise tests
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import base64
+import binascii
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+import os
+import signal
+import socket
+try:
+ import SocketServer
+except ImportError:
+ import socketserver as SocketServer
+import struct
+import tempfile
+
+import hwsim_utils
+from hwsim import HWSimRadio
+import hostapd
+from utils import *
+from wpasupplicant import WpaSupplicant
+from test_ap_psk import check_mib, find_wpas_process, read_process_memory, verify_not_present, get_key_locations, set_test_assoc_ie
+
+try:
+ import OpenSSL
+ openssl_imported = True
+except ImportError:
+ openssl_imported = False
+
+def check_hlr_auc_gw_support():
+ if not os.path.exists("/tmp/hlr_auc_gw.sock"):
+ raise HwsimSkip("No hlr_auc_gw available")
+
+def check_eap_capa(dev, method):
+ res = dev.get_capability("eap")
+ if method not in res:
+ raise HwsimSkip("EAP method %s not supported in the build" % method)
+
+def check_subject_match_support(dev):
+ tls = dev.request("GET tls_library")
+ if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
+ raise HwsimSkip("subject_match not supported with this TLS library: " + tls)
+
+def check_check_cert_subject_support(dev):
+ tls = dev.request("GET tls_library")
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("check_cert_subject not supported with this TLS library: " + tls)
+
+def check_altsubject_match_support(dev):
+ tls = dev.request("GET tls_library")
+ if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
+ raise HwsimSkip("altsubject_match not supported with this TLS library: " + tls)
+
+def check_domain_match(dev):
+ tls = dev.request("GET tls_library")
+ if tls.startswith("internal"):
+ raise HwsimSkip("domain_match not supported with this TLS library: " + tls)
+
+def check_domain_suffix_match(dev):
+ tls = dev.request("GET tls_library")
+ if tls.startswith("internal"):
+ raise HwsimSkip("domain_suffix_match not supported with this TLS library: " + tls)
+
+def check_domain_match_full(dev):
+ tls = dev.request("GET tls_library")
+ if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
+ raise HwsimSkip("domain_suffix_match requires full match with this TLS library: " + tls)
+
+def check_cert_probe_support(dev):
+ tls = dev.request("GET tls_library")
+ if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
+ raise HwsimSkip("Certificate probing not supported with this TLS library: " + tls)
+
+def check_ext_cert_check_support(dev):
+ tls = dev.request("GET tls_library")
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("ext_cert_check not supported with this TLS library: " + tls)
+
+def check_ocsp_support(dev):
+ tls = dev.request("GET tls_library")
+ #if tls.startswith("internal"):
+ # raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+ #if "BoringSSL" in tls:
+ # raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+ if tls.startswith("wolfSSL"):
+ raise HwsimSkip("OCSP not supported with this TLS library: " + tls)
+
+def check_pkcs5_v15_support(dev):
+ tls = dev.request("GET tls_library")
+ if "BoringSSL" in tls or "GnuTLS" in tls:
+ raise HwsimSkip("PKCS#5 v1.5 not supported with this TLS library: " + tls)
+
+def check_ocsp_multi_support(dev):
+ tls = dev.request("GET tls_library")
+ if not tls.startswith("internal"):
+ raise HwsimSkip("OCSP-multi not supported with this TLS library: " + tls)
+ as_hapd = hostapd.Hostapd("as")
+ res = as_hapd.request("GET tls_library")
+ del as_hapd
+ if not res.startswith("internal"):
+ raise HwsimSkip("Authentication server does not support ocsp_multi")
+
+def check_pkcs12_support(dev):
+ tls = dev.request("GET tls_library")
+ #if tls.startswith("internal"):
+ # raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
+ if tls.startswith("wolfSSL"):
+ raise HwsimSkip("PKCS#12 not supported with this TLS library: " + tls)
+
+def check_dh_dsa_support(dev):
+ tls = dev.request("GET tls_library")
+ if tls.startswith("internal"):
+ raise HwsimSkip("DH DSA not supported with this TLS library: " + tls)
+
+def check_ec_support(dev):
+ tls = dev.request("GET tls_library")
+ if tls.startswith("internal"):
+ raise HwsimSkip("EC not supported with this TLS library: " + tls)
+
+def read_pem(fname):
+ with open(fname, "r") as f:
+ lines = f.readlines()
+ copy = False
+ cert = ""
+ for l in lines:
+ if "-----END" in l:
+ break
+ if copy:
+ cert = cert + l
+ if "-----BEGIN" in l:
+ copy = True
+ return base64.b64decode(cert)
+
+def eap_connect(dev, hapd, method, identity,
+ sha256=False, expect_failure=False, local_error_report=False,
+ maybe_local_error=False, report_failure=False,
+ expect_cert_error=None, **kwargs):
+ id = dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap=method, identity=identity,
+ wait_connect=False, scan_freq="2412", ieee80211w="1",
+ **kwargs)
+ eap_check_auth(dev, method, True, sha256=sha256,
+ expect_failure=expect_failure,
+ local_error_report=local_error_report,
+ maybe_local_error=maybe_local_error,
+ report_failure=report_failure,
+ expect_cert_error=expect_cert_error)
+ if expect_failure:
+ return id
+ if hapd:
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ return id
+
+def eap_check_auth(dev, method, initial, rsn=True, sha256=False,
+ expect_failure=False, local_error_report=False,
+ maybe_local_error=False, report_failure=False,
+ expect_cert_error=None):
+ ev = dev.wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+ ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD",
+ "CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP method selection timed out")
+ if "CTRL-EVENT-EAP-FAILURE" in ev:
+ if maybe_local_error:
+ return
+ raise Exception("Could not select EAP method")
+ if method not in ev:
+ raise Exception("Unexpected EAP method")
+ if expect_cert_error is not None:
+ ev = dev.wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None or "reason=%d " % expect_cert_error not in ev:
+ raise Exception("Expected certificate error not reported")
+ if expect_failure:
+ ev = dev.wait_event(["CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("EAP failure timed out")
+ if "CTRL-EVENT-EAP-SUCCESS" in ev:
+ raise Exception("Unexpected EAP success")
+ ev = dev.wait_disconnected(timeout=10)
+ if maybe_local_error and "locally_generated=1" in ev:
+ return
+ if not local_error_report:
+ if "reason=23" not in ev:
+ raise Exception("Proper reason code for disconnection not reported")
+ return
+ if report_failure:
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ if "CTRL-EVENT-EAP-SUCCESS" not in ev:
+ raise Exception("EAP failed")
+ else:
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+
+ if initial:
+ ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+ else:
+ ev = dev.wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Association with the AP timed out")
+ status = dev.get_status()
+ if status["wpa_state"] != "COMPLETED":
+ raise Exception("Connection not completed")
+
+ if status["suppPortStatus"] != "Authorized":
+ raise Exception("Port not authorized")
+ if "selectedMethod" not in status:
+ logger.info("Status: " + str(status))
+ raise Exception("No selectedMethod in status")
+ if method not in status["selectedMethod"]:
+ raise Exception("Incorrect EAP method status")
+ if sha256:
+ e = "WPA2-EAP-SHA256"
+ elif rsn:
+ e = "WPA2/IEEE 802.1X/EAP"
+ else:
+ e = "WPA/IEEE 802.1X/EAP"
+ if status["key_mgmt"] != e:
+ raise Exception("Unexpected key_mgmt status: " + status["key_mgmt"])
+ return status
+
+def eap_reauth(dev, method, rsn=True, sha256=False, expect_failure=False):
+ dev.request("REAUTHENTICATE")
+ return eap_check_auth(dev, method, False, rsn=rsn, sha256=sha256,
+ expect_failure=expect_failure)
+
+def test_ap_wpa2_eap_sim(dev, apdev):
+ """WPA2-Enterprise connection using EAP-SIM"""
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "SIM")
+
+ eap_connect(dev[1], hapd, "SIM", "1232010000000001",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+ eap_connect(dev[2], hapd, "SIM", "1232010000000002",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ expect_failure=True)
+
+ logger.info("Negative test with incorrect key")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ expect_failure=True)
+
+ logger.info("Invalid GSM-Milenage key")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a",
+ expect_failure=True)
+
+ logger.info("Invalid GSM-Milenage key(2)")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a8q:cb9cccc4b9258e6dca4760379fb82581",
+ expect_failure=True)
+
+ logger.info("Invalid GSM-Milenage key(3)")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb8258q",
+ expect_failure=True)
+
+ logger.info("Invalid GSM-Milenage key(4)")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a89qcb9cccc4b9258e6dca4760379fb82581",
+ expect_failure=True)
+
+ logger.info("Missing key configuration")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_sim_sql(dev, apdev, params):
+ """WPA2-Enterprise connection using EAP-SIM (SQL)"""
+ check_hlr_auc_gw_support()
+ try:
+ import sqlite3
+ except ImportError:
+ raise HwsimSkip("No sqlite3 module available")
+ con = sqlite3.connect(os.path.join(params['logdir'], "hostapd.db"))
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "1814"
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+
+ logger.info("SIM fast re-authentication")
+ eap_reauth(dev[0], "SIM")
+
+ logger.info("SIM full auth with pseudonym")
+ with con:
+ cur = con.cursor()
+ cur.execute("DELETE FROM reauth WHERE permanent='1232010000000000'")
+ eap_reauth(dev[0], "SIM")
+
+ logger.info("SIM full auth with permanent identity")
+ with con:
+ cur = con.cursor()
+ cur.execute("DELETE FROM reauth WHERE permanent='1232010000000000'")
+ cur.execute("DELETE FROM pseudonyms WHERE permanent='1232010000000000'")
+ eap_reauth(dev[0], "SIM")
+
+ logger.info("SIM reauth with mismatching MK")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET mk='0000000000000000000000000000000000000000' WHERE permanent='1232010000000000'")
+ eap_reauth(dev[0], "SIM", expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET counter='10' WHERE permanent='1232010000000000'")
+ eap_reauth(dev[0], "SIM")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET counter='10' WHERE permanent='1232010000000000'")
+ logger.info("SIM reauth with mismatching counter")
+ eap_reauth(dev[0], "SIM")
+ dev[0].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET counter='1001' WHERE permanent='1232010000000000'")
+ logger.info("SIM reauth with max reauth count reached")
+ eap_reauth(dev[0], "SIM")
+
+def test_ap_wpa2_eap_sim_config(dev, apdev):
+ """EAP-SIM configuration options"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
+ identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ phase1="sim_min_num_chal=1",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method: vendor 0 method 18 (SIM)"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP error message seen")
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
+ identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ phase1="sim_min_num_chal=4",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method: vendor 0 method 18 (SIM)"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP error message seen (2)")
+ dev[0].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ phase1="sim_min_num_chal=2")
+ eap_connect(dev[1], hapd, "SIM", "1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ anonymous_identity="345678")
+
+def test_ap_wpa2_eap_sim_id_0(dev, apdev):
+ """WPA2-Enterprise connection using EAP-SIM (no pseudonym or reauth)"""
+ run_ap_wpa2_eap_sim_id(dev, apdev, 0)
+
+def test_ap_wpa2_eap_sim_id_1(dev, apdev):
+ """WPA2-Enterprise connection using EAP-SIM (pseudonym, no reauth)"""
+ run_ap_wpa2_eap_sim_id(dev, apdev, 1)
+
+def test_ap_wpa2_eap_sim_id_2(dev, apdev):
+ """WPA2-Enterprise connection using EAP-SIM (no pseudonym, reauth)"""
+ run_ap_wpa2_eap_sim_id(dev, apdev, 2)
+
+def test_ap_wpa2_eap_sim_id_3(dev, apdev):
+ """WPA2-Enterprise connection using EAP-SIM (pseudonym and reauth)"""
+ run_ap_wpa2_eap_sim_id(dev, apdev, 3)
+
+def run_ap_wpa2_eap_sim_id(dev, apdev, eap_sim_id):
+ check_hlr_auc_gw_support()
+ params = int_eap_server_params()
+ params['eap_sim_id'] = str(eap_sim_id)
+ params['eap_sim_db'] = 'unix:/tmp/hlr_auc_gw.sock'
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+ eap_reauth(dev[0], "SIM")
+
+def test_ap_wpa2_eap_sim_ext(dev, apdev):
+ """WPA2-Enterprise connection using EAP-SIM and external GSM auth"""
+ try:
+ _test_ap_wpa2_eap_sim_ext(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_sim_ext(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
+ identity="1232010000000000",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Network connected timed out")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+
+ # IK:CK:RES
+ resp = "00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:0011223344"
+ # This will fail during processing, but the ctrl_iface command succeeds
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTH:" + resp)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ time.sleep(0.1)
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ # This will fail during GSM auth validation
+ if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:q"):
+ raise Exception("CTRL-RSP-SIM failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ time.sleep(0.1)
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ # This will fail during GSM auth validation
+ if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:34"):
+ raise Exception("CTRL-RSP-SIM failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ time.sleep(0.1)
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ # This will fail during GSM auth validation
+ if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677"):
+ raise Exception("CTRL-RSP-SIM failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ time.sleep(0.1)
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ # This will fail during GSM auth validation
+ if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677:q"):
+ raise Exception("CTRL-RSP-SIM failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ time.sleep(0.1)
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ # This will fail during GSM auth validation
+ if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677:00112233"):
+ raise Exception("CTRL-RSP-SIM failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ time.sleep(0.1)
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ # This will fail during GSM auth validation
+ if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:0011223344556677:00112233:q"):
+ raise Exception("CTRL-RSP-SIM failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+
+def test_ap_wpa2_eap_sim_ext_replace_sim(dev, apdev):
+ """EAP-SIM with external GSM auth and replacing SIM without clearing pseudonym id"""
+ try:
+ _test_ap_wpa2_eap_sim_ext_replace_sim(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_sim_ext_replace_sim(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
+ identity="1232010000000000",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000000 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ dev[0].wait_connected(timeout=15)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # Replace SIM, but forget to drop the previous pseudonym identity
+ dev[0].set_network_quoted(id, "identity", "1232010000000009")
+ dev[0].select_network(id, freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000009 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP-Failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_sim_ext_replace_sim2(dev, apdev):
+ """EAP-SIM with external GSM auth and replacing SIM and clearing pseudonym identity"""
+ try:
+ _test_ap_wpa2_eap_sim_ext_replace_sim2(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_sim_ext_replace_sim2(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
+ identity="1232010000000000",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000000 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ dev[0].wait_connected(timeout=15)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # Replace SIM and drop the previous pseudonym identity
+ dev[0].set_network_quoted(id, "identity", "1232010000000009")
+ dev[0].set_network(id, "anonymous_identity", "NULL")
+ dev[0].select_network(id, freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000009 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_sim_ext_replace_sim3(dev, apdev):
+ """EAP-SIM with external GSM auth, replacing SIM, and no identity in config"""
+ try:
+ _test_ap_wpa2_eap_sim_ext_replace_sim3(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_sim_ext_replace_sim3(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"])
+ if ev is None:
+ raise Exception("Request for identity timed out")
+ rid = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-IDENTITY-" + rid + ":1232010000000000")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000000 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ dev[0].wait_connected(timeout=15)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # Replace SIM and drop the previous permanent and pseudonym identities
+ dev[0].set_network(id, "identity", "NULL")
+ dev[0].set_network(id, "anonymous_identity", "NULL")
+ dev[0].select_network(id, freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"])
+ if ev is None:
+ raise Exception("Request for identity timed out")
+ rid = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-IDENTITY-" + rid + ":1232010000000009")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000009 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_sim_ext_auth_fail(dev, apdev):
+ """EAP-SIM with external GSM auth and auth failing"""
+ try:
+ _test_ap_wpa2_eap_sim_ext_auth_fail(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_sim_ext_auth_fail(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
+ identity="1232010000000000",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ rid = p[0].split('-')[3]
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-FAIL")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_sim_change_bssid(dev, apdev):
+ """EAP-SIM and external GSM auth to check fast reauth with bssid change"""
+ try:
+ _test_ap_wpa2_eap_sim_change_bssid(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_sim_change_bssid(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
+ identity="1232010000000000",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000000 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ dev[0].wait_connected(timeout=15)
+ hapd.wait_sta()
+
+ # Verify that EAP-SIM Reauthentication can be used after a profile change
+ # that does not affect EAP parameters.
+ dev[0].set_network(id, "bssid", "any")
+ eap_reauth(dev[0], "SIM")
+
+def test_ap_wpa2_eap_sim_no_change_set(dev, apdev):
+ """EAP-SIM and external GSM auth to check fast reauth with no-change SET_NETWORK"""
+ try:
+ _test_ap_wpa2_eap_sim_no_change_set(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_sim_no_change_set(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
+ identity="1232010000000000",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000000 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ dev[0].wait_connected(timeout=15)
+ hapd.wait_sta()
+
+ # Verify that EAP-SIM Reauthentication can be used after network profile
+ # SET_NETWORK commands that do not actually change previously set
+ # parameter values.
+ dev[0].set_network(id, "key_mgmt", "WPA-EAP")
+ dev[0].set_network(id, "eap", "SIM")
+ dev[0].set_network_quoted(id, "identity", "1232010000000000")
+ dev[0].set_network_quoted(id, "ssid", "test-wpa2-eap")
+ eap_reauth(dev[0], "SIM")
+
+def test_ap_wpa2_eap_sim_ext_anonymous(dev, apdev):
+ """EAP-SIM with external GSM auth and anonymous identity"""
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ try:
+ run_ap_wpa2_eap_sim_ext_anonymous(dev, "anonymous@example.org")
+ run_ap_wpa2_eap_sim_ext_anonymous(dev, "@example.org")
+ run_ap_wpa2_eap_sim_ext_anonymous(dev, "example.org!anonymous@otherexample.org")
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def test_ap_wpa2_eap_sim_ext_anonymous_no_pseudonym(dev, apdev):
+ """EAP-SIM with external GSM auth and anonymous identity without pseudonym update"""
+ check_hlr_auc_gw_support()
+ params = int_eap_server_params()
+ params['eap_sim_id'] = '0'
+ params['eap_sim_db'] = 'unix:/tmp/hlr_auc_gw.sock'
+ hostapd.add_ap(apdev[0], params)
+ try:
+ run_ap_wpa2_eap_sim_ext_anonymous(dev, "anonymous@example.org",
+ anon_id_change=False)
+ run_ap_wpa2_eap_sim_ext_anonymous(dev, "@example.org",
+ anon_id_change=False)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def run_ap_wpa2_eap_sim_ext_anonymous(dev, anon, anon_id_change=True):
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="SIM", key_mgmt="WPA-EAP",
+ identity="1232010000000000",
+ anonymous_identity=anon,
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000000 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ dev[0].wait_connected(timeout=5)
+ anon_id = dev[0].get_network(id, "anonymous_identity").strip('"')
+ if anon_id_change and anon == anon_id:
+ raise Exception("anonymous_identity did not change")
+ if not anon_id_change and anon != anon_id:
+ raise Exception("anonymous_identity changed")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def test_ap_wpa2_eap_sim_oom(dev, apdev):
+ """EAP-SIM and OOM"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ tests = [(1, "milenage_f2345"),
+ (2, "milenage_f2345"),
+ (3, "milenage_f2345"),
+ (4, "milenage_f2345"),
+ (5, "milenage_f2345"),
+ (6, "milenage_f2345"),
+ (7, "milenage_f2345"),
+ (8, "milenage_f2345"),
+ (9, "milenage_f2345"),
+ (10, "milenage_f2345"),
+ (11, "milenage_f2345"),
+ (12, "milenage_f2345")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
+ identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("EAP method not selected")
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_aka(dev, apdev):
+ """WPA2-Enterprise connection using EAP-AKA"""
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "AKA")
+
+ logger.info("Negative test with incorrect key")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ expect_failure=True)
+
+ logger.info("Invalid Milenage key")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a",
+ expect_failure=True)
+
+ logger.info("Invalid Milenage key(2)")
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a8q:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ expect_failure=True)
+
+ logger.info("Invalid Milenage key(3)")
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb8258q:000000000123",
+ expect_failure=True)
+
+ logger.info("Invalid Milenage key(4)")
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:00000000012q",
+ expect_failure=True)
+
+ logger.info("Invalid Milenage key(5)")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581q000000000123",
+ expect_failure=True)
+
+ logger.info("Invalid Milenage key(6)")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="ffdca4eda45b53cf0f12d7c9c3bc6a89qcb9cccc4b9258e6dca4760379fb82581q000000000123",
+ expect_failure=True)
+
+ logger.info("Missing key configuration")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_aka_sql(dev, apdev, params):
+ """WPA2-Enterprise connection using EAP-AKA (SQL)"""
+ check_hlr_auc_gw_support()
+ try:
+ import sqlite3
+ except ImportError:
+ raise HwsimSkip("No sqlite3 module available")
+ con = sqlite3.connect(os.path.join(params['logdir'], "hostapd.db"))
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "1814"
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+
+ logger.info("AKA fast re-authentication")
+ eap_reauth(dev[0], "AKA")
+
+ logger.info("AKA full auth with pseudonym")
+ with con:
+ cur = con.cursor()
+ cur.execute("DELETE FROM reauth WHERE permanent='0232010000000000'")
+ eap_reauth(dev[0], "AKA")
+
+ logger.info("AKA full auth with permanent identity")
+ with con:
+ cur = con.cursor()
+ cur.execute("DELETE FROM reauth WHERE permanent='0232010000000000'")
+ cur.execute("DELETE FROM pseudonyms WHERE permanent='0232010000000000'")
+ eap_reauth(dev[0], "AKA")
+
+ logger.info("AKA reauth with mismatching MK")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET mk='0000000000000000000000000000000000000000' WHERE permanent='0232010000000000'")
+ eap_reauth(dev[0], "AKA", expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET counter='10' WHERE permanent='0232010000000000'")
+ eap_reauth(dev[0], "AKA")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET counter='10' WHERE permanent='0232010000000000'")
+ logger.info("AKA reauth with mismatching counter")
+ eap_reauth(dev[0], "AKA")
+ dev[0].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET counter='1001' WHERE permanent='0232010000000000'")
+ logger.info("AKA reauth with max reauth count reached")
+ eap_reauth(dev[0], "AKA")
+
+def test_ap_wpa2_eap_aka_config(dev, apdev):
+ """EAP-AKA configuration options"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ anonymous_identity="2345678")
+
+def test_ap_wpa2_eap_aka_ext(dev, apdev):
+ """WPA2-Enterprise connection using EAP-AKA and external UMTS auth"""
+ try:
+ _test_ap_wpa2_eap_aka_ext(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_aka_ext(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="AKA", key_mgmt="WPA-EAP",
+ identity="0232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Network connected timed out")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "UMTS-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+
+ # IK:CK:RES
+ resp = "00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:0011223344"
+ # This will fail during processing, but the ctrl_iface command succeeds
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "UMTS-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ # This will fail during UMTS auth validation
+ if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTS:112233445566778899aabbccddee"):
+ raise Exception("CTRL-RSP-SIM failed")
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "UMTS-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ # This will fail during UMTS auth validation
+ if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTS:12"):
+ raise Exception("CTRL-RSP-SIM failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+
+ tests = [":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:0011223344",
+ ":UMTS-AUTH:34",
+ ":UMTS-AUTH:00112233445566778899aabbccddeeff.00112233445566778899aabbccddeeff:0011223344",
+ ":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddee:0011223344",
+ ":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff.0011223344",
+ ":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff0011223344",
+ ":UMTS-AUTH:00112233445566778899aabbccddeeff:00112233445566778899aabbccddeeff:001122334q"]
+ for t in tests:
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "UMTS-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ # This will fail during UMTS auth validation
+ if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + t):
+ raise Exception("CTRL-RSP-SIM failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+
+def test_ap_wpa2_eap_aka_ext_auth_fail(dev, apdev):
+ """EAP-AKA with external UMTS auth and auth failing"""
+ try:
+ _test_ap_wpa2_eap_aka_ext_auth_fail(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_aka_ext_auth_fail(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="AKA", key_mgmt="WPA-EAP",
+ identity="0232010000000000",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ rid = p[0].split('-')[3]
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-FAIL")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_aka_prime(dev, apdev):
+ """WPA2-Enterprise connection using EAP-AKA'"""
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "AKA'")
+
+ logger.info("EAP-AKA' bidding protection when EAP-AKA enabled as well")
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="AKA' AKA",
+ identity="6555444333222111@both",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+ wait_connect=False, scan_freq="2412")
+ dev[1].wait_connected(timeout=15)
+
+ logger.info("Negative test with incorrect key")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
+ password="ff22250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_aka_prime_sql(dev, apdev, params):
+ """WPA2-Enterprise connection using EAP-AKA' (SQL)"""
+ check_hlr_auc_gw_support()
+ try:
+ import sqlite3
+ except ImportError:
+ raise HwsimSkip("No sqlite3 module available")
+ con = sqlite3.connect(os.path.join(params['logdir'], "hostapd.db"))
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "1814"
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+
+ logger.info("AKA' fast re-authentication")
+ eap_reauth(dev[0], "AKA'")
+
+ logger.info("AKA' full auth with pseudonym")
+ with con:
+ cur = con.cursor()
+ cur.execute("DELETE FROM reauth WHERE permanent='6555444333222111'")
+ eap_reauth(dev[0], "AKA'")
+
+ logger.info("AKA' full auth with permanent identity")
+ with con:
+ cur = con.cursor()
+ cur.execute("DELETE FROM reauth WHERE permanent='6555444333222111'")
+ cur.execute("DELETE FROM pseudonyms WHERE permanent='6555444333222111'")
+ eap_reauth(dev[0], "AKA'")
+
+ logger.info("AKA' reauth with mismatching k_aut")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET k_aut='0000000000000000000000000000000000000000000000000000000000000000' WHERE permanent='6555444333222111'")
+ eap_reauth(dev[0], "AKA'", expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET counter='10' WHERE permanent='6555444333222111'")
+ eap_reauth(dev[0], "AKA'")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET counter='10' WHERE permanent='6555444333222111'")
+ logger.info("AKA' reauth with mismatching counter")
+ eap_reauth(dev[0], "AKA'")
+ dev[0].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE reauth SET counter='1001' WHERE permanent='6555444333222111'")
+ logger.info("AKA' reauth with max reauth count reached")
+ eap_reauth(dev[0], "AKA'")
+
+def test_ap_wpa2_eap_aka_prime_ext_auth_fail(dev, apdev):
+ """EAP-AKA' with external UMTS auth and auth failing"""
+ try:
+ _test_ap_wpa2_eap_aka_prime_ext_auth_fail(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_aka_prime_ext_auth_fail(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="AKA'", key_mgmt="WPA-EAP",
+ identity="6555444333222111",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ rid = p[0].split('-')[3]
+ dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-FAIL")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_aka_prime_ext(dev, apdev):
+ """EAP-AKA' with external UMTS auth to hit Synchronization-Failure"""
+ try:
+ _test_ap_wpa2_eap_aka_prime_ext(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_wpa2_eap_aka_prime_ext(dev, apdev):
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ id = dev[0].connect("test-wpa2-eap", eap="AKA'", key_mgmt="WPA-EAP",
+ identity="6555444333222111",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Network connected timed out")
+
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "UMTS-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ # This will fail during UMTS auth validation
+ if "OK" not in dev[0].request("CTRL-RSP-SIM-" + rid + ":UMTS-AUTS:112233445566778899aabbccddee"):
+ raise Exception("CTRL-RSP-SIM failed")
+ ev = dev[0].wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+
+def test_ap_wpa2_eap_ttls_pap(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/PAP"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "WPA-EAP":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "TTLS")
+ check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-1"),
+ ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-1")])
+
+def test_ap_wpa2_eap_ttls_pap_subject_match(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/PAP and (alt)subject_match"""
+ check_subject_match_support(dev[0])
+ check_altsubject_match_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
+ altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/")
+ eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_pap_check_cert_subject(dev, apdev):
+ """EAP-TTLS/PAP and check_cert_subject"""
+ check_check_cert_subject_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ tests = ["C=FI/O=w1.fi/CN=server.w1.fi",
+ "C=FI/O=w1.fi",
+ "C=FI/CN=server.w1.fi",
+ "O=w1.fi/CN=server.w1.fi",
+ "C=FI",
+ "O=w1.fi",
+ "O=w1.*",
+ "CN=server.w1.fi",
+ "*"]
+ for test in tests:
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ check_cert_subject=test)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def test_ap_wpa2_eap_ttls_pap_check_cert_subject_neg(dev, apdev):
+ """EAP-TTLS/PAP and check_cert_subject (negative)"""
+ check_check_cert_subject_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ tests = ["C=US",
+ "C",
+ "C=FI1*",
+ "O=w1.f",
+ "O=w1.fi1",
+ "O=w1.fi/O=foo",
+ "O=foo/O=w1.fi",
+ "O=w1.fi/O=w1.fi"]
+ for test in tests:
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ expect_failure=True, expect_cert_error=12,
+ check_cert_subject=test)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def test_ap_wpa2_eap_ttls_pap_incorrect_password(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/PAP - incorrect password"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="wrong",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ expect_failure=True)
+ eap_connect(dev[1], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_chap(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/CHAP"""
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "chap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.der", phase2="auth=CHAP")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_chap_altsubject_match(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/CHAP"""
+ skip_with_fips(dev[0])
+ check_altsubject_match_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "chap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.der", phase2="auth=CHAP",
+ altsubject_match="EMAIL:noone@example.com;URI:http://example.com/;DNS:server.w1.fi")
+ eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_chap_incorrect_password(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/CHAP - incorrect password"""
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "chap user",
+ anonymous_identity="ttls", password="wrong",
+ ca_cert="auth_serv/ca.pem", phase2="auth=CHAP",
+ expect_failure=True)
+ eap_connect(dev[1], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=CHAP",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_mschap(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/MSCHAP"""
+ skip_with_fips(dev[0])
+ check_domain_suffix_match(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "mschap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ domain_suffix_match="server.w1.fi")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "TTLS")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "TTLS", "mschap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ fragment_size="200")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ eap_connect(dev[0], hapd, "TTLS", "mschap user",
+ anonymous_identity="ttls",
+ password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP")
+
+def test_ap_wpa2_eap_ttls_mschap_incorrect_password(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/MSCHAP - incorrect password"""
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "mschap user",
+ anonymous_identity="ttls", password="wrong",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ expect_failure=True)
+ eap_connect(dev[1], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ expect_failure=True)
+ eap_connect(dev[2], hapd, "TTLS", "no such user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_mschapv2(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2"""
+ check_domain_suffix_match(dev[0])
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ domain_suffix_match="server.w1.fi")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sta1 = hapd.get_sta(dev[0].p2p_interface_addr())
+ eapol1 = hapd.get_sta(dev[0].p2p_interface_addr(), info="eapol")
+ eap_reauth(dev[0], "TTLS")
+ sta2 = hapd.get_sta(dev[0].p2p_interface_addr())
+ eapol2 = hapd.get_sta(dev[0].p2p_interface_addr(), info="eapol")
+ if int(sta2['dot1xAuthEapolFramesRx']) <= int(sta1['dot1xAuthEapolFramesRx']):
+ raise Exception("dot1xAuthEapolFramesRx did not increase")
+ if int(eapol2['authAuthEapStartsWhileAuthenticated']) < 1:
+ raise Exception("authAuthEapStartsWhileAuthenticated did not increase")
+ if int(eapol2['backendAuthSuccesses']) <= int(eapol1['backendAuthSuccesses']):
+ raise Exception("backendAuthSuccesses did not increase")
+
+ logger.info("Password as hash value")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
+ anonymous_identity="ttls",
+ password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+
+def test_ap_wpa2_eap_ttls_invalid_phase2(dev, apdev):
+ """EAP-TTLS with invalid phase2 parameter values"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ tests = ["auth=MSCHAPv2", "auth=MSCHAPV2 autheap=MD5",
+ "autheap=MD5 auth=MSCHAPV2", "auth=PAP auth=CHAP",
+ "autheap=MD5 autheap=FOO autheap=MSCHAPV2"]
+ for t in tests:
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2=t,
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=10)
+ if ev is None or "method=21" not in ev:
+ raise Exception("EAP-TTLS not started")
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method",
+ "CTRL-EVENT-CONNECTED"], timeout=5)
+ if ev is None or "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("No EAP-TTLS failure reported for phase2=" + t)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def test_ap_wpa2_eap_ttls_mschapv2_suffix_match(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2"""
+ check_domain_match_full(dev[0])
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ domain_suffix_match="w1.fi")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_mschapv2_domain_match(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2 (domain_match)"""
+ check_domain_match(dev[0])
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ domain_match="Server.w1.fi")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_mschapv2_incorrect_password(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2 - incorrect password"""
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
+ anonymous_identity="ttls", password="password1",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ expect_failure=True)
+ eap_connect(dev[1], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_mschapv2_utf8(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/MSCHAPv2 and UTF-8 password"""
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "utf8-user-hash",
+ anonymous_identity="ttls", password="secret-åäö-€-password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+ eap_connect(dev[1], hapd, "TTLS", "utf8-user",
+ anonymous_identity="ttls",
+ password_hex="hash:bd5844fad2489992da7fe8c5a01559cf",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+ for p in ["80", "41c041e04141e041", 257*"41"]:
+ dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="utf8-user-hash",
+ anonymous_identity="ttls", password_hex=p,
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[2].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=1)
+ if ev is None:
+ raise Exception("No failure reported")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[2].wait_disconnected()
+
+def test_ap_wpa2_eap_ttls_eap_gtc(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-GTC"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_eap_gtc_incorrect_password(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-GTC - incorrect password"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="wrong",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_gtc_no_password(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-GTC - no password"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "user-no-passwd",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_gtc_server_oom(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-GTC - server OOM"""
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ with alloc_fail(hapd, 1, "eap_gtc_init"):
+ eap_connect(dev[0], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+
+ with alloc_fail(hapd, 1, "eap_gtc_buildReq"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ # This would eventually time out, but we can stop after having reached
+ # the allocation failure.
+ for i in range(20):
+ time.sleep(0.1)
+ if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+ break
+
+def test_ap_wpa2_eap_ttls_eap_gtc_oom(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-GTC (OOM)"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tests = ["eap_gtc_init",
+ "eap_msg_alloc;eap_gtc_process"]
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_ttls_eap_md5(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-MD5"""
+ check_eap_capa(dev[0], "MD5")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MD5")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_ttls_eap_md5_incorrect_password(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-MD5 - incorrect password"""
+ check_eap_capa(dev[0], "MD5")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="wrong",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_md5_no_password(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-MD5 - no password"""
+ check_eap_capa(dev[0], "MD5")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "user-no-passwd",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_md5_server_oom(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-MD5 - server OOM"""
+ check_eap_capa(dev[0], "MD5")
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ with alloc_fail(hapd, 1, "eap_md5_init"):
+ eap_connect(dev[0], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
+ expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+
+ with alloc_fail(hapd, 1, "eap_md5_buildReq"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MD5",
+ wait_connect=False, scan_freq="2412")
+ # This would eventually time out, but we can stop after having reached
+ # the allocation failure.
+ for i in range(20):
+ time.sleep(0.1)
+ if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+ break
+
+def test_ap_wpa2_eap_ttls_eap_mschapv2(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-MSCHAPv2"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "TTLS")
+
+ logger.info("Negative test with incorrect password")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password1",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_mschapv2_no_password(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-MSCHAPv2 - no password"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "user-no-passwd",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_ttls_eap_mschapv2_server_oom(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-MSCHAPv2 - server OOM"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ with alloc_fail(hapd, 1, "eap_mschapv2_init"):
+ eap_connect(dev[0], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+ expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+
+ with alloc_fail(hapd, 1, "eap_mschapv2_build_challenge"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+ wait_connect=False, scan_freq="2412")
+ # This would eventually time out, but we can stop after having reached
+ # the allocation failure.
+ for i in range(20):
+ time.sleep(0.1)
+ if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+ break
+ dev[0].request("REMOVE_NETWORK all")
+
+ with alloc_fail(hapd, 1, "eap_mschapv2_build_success_req"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+ wait_connect=False, scan_freq="2412")
+ # This would eventually time out, but we can stop after having reached
+ # the allocation failure.
+ for i in range(20):
+ time.sleep(0.1)
+ if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+ break
+ dev[0].request("REMOVE_NETWORK all")
+
+ with alloc_fail(hapd, 1, "eap_mschapv2_build_failure_req"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="wrong",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=MSCHAPV2",
+ wait_connect=False, scan_freq="2412")
+ # This would eventually time out, but we can stop after having reached
+ # the allocation failure.
+ for i in range(20):
+ time.sleep(0.1)
+ if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+ break
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_ttls_eap_sim(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-SIM"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "1232010000000000",
+ anonymous_identity="1232010000000000@ttls",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=SIM")
+ eap_reauth(dev[0], "TTLS")
+
+def run_ext_sim_auth(hapd, dev):
+ ev = dev.wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ rid = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000000 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev.request("CTRL-RSP-SIM-" + rid + ":GSM-AUTH:" + resp)
+ dev.wait_connected(timeout=15)
+ hapd.wait_sta()
+
+ dev.dump_monitor()
+ dev.request("REAUTHENTICATE")
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("EAP reauthentication did not succeed")
+ ev = dev.wait_event(["WPA: Key negotiation completed"], timeout=5)
+ if ev is None:
+ raise Exception("Key negotiation did not complete")
+ dev.dump_monitor()
+
+def test_ap_wpa2_eap_ttls_eap_sim_ext(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-SIM and external GSM auth"""
+ check_hlr_auc_gw_support()
+ try:
+ run_ap_wpa2_eap_ttls_eap_sim_ext(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def run_ap_wpa2_eap_ttls_eap_sim_ext(dev, apdev):
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ dev[0].connect("test-wpa2-eap", eap="TTLS", key_mgmt="WPA-EAP",
+ identity="1232010000000000",
+ anonymous_identity="1232010000000000@ttls",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=SIM",
+ wait_connect=False, scan_freq="2412")
+ run_ext_sim_auth(hapd, dev[0])
+
+def test_ap_wpa2_eap_ttls_eap_vendor(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-vendor"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "vendor-test-2",
+ anonymous_identity="ttls",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=VENDOR-TEST")
+
+def test_ap_wpa2_eap_peap_eap_sim(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAP/EAP-SIM"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PEAP", "1232010000000000",
+ anonymous_identity="1232010000000000@peap",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ ca_cert="auth_serv/ca.pem", phase2="auth=SIM")
+ eap_reauth(dev[0], "PEAP")
+
+def test_ap_wpa2_eap_peap_eap_sim_ext(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAP/EAP-SIM and external GSM auth"""
+ check_hlr_auc_gw_support()
+ try:
+ run_ap_wpa2_eap_peap_eap_sim_ext(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def run_ap_wpa2_eap_peap_eap_sim_ext(dev, apdev):
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ dev[0].connect("test-wpa2-eap", eap="PEAP", key_mgmt="WPA-EAP",
+ identity="1232010000000000",
+ anonymous_identity="1232010000000000@peap",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ ca_cert="auth_serv/ca.pem", phase2="auth=SIM",
+ wait_connect=False, scan_freq="2412")
+ run_ext_sim_auth(hapd, dev[0])
+
+def test_ap_wpa2_eap_fast_eap_sim(dev, apdev):
+ """WPA2-Enterprise connection using EAP-FAST/EAP-SIM"""
+ check_eap_capa(dev[0], "FAST")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "FAST", "1232010000000000",
+ anonymous_identity="1232010000000000@fast",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_auth_sim",
+ ca_cert="auth_serv/ca.pem", phase2="auth=SIM")
+ eap_reauth(dev[0], "FAST")
+
+def test_ap_wpa2_eap_fast_eap_sim_ext(dev, apdev):
+ """WPA2-Enterprise connection using EAP-FAST/EAP-SIM and external GSM auth"""
+ check_hlr_auc_gw_support()
+ try:
+ run_ap_wpa2_eap_fast_eap_sim_ext(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def run_ap_wpa2_eap_fast_eap_sim_ext(dev, apdev):
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET external_sim 1")
+ dev[0].connect("test-wpa2-eap", eap="PEAP", key_mgmt="WPA-EAP",
+ identity="1232010000000000",
+ anonymous_identity="1232010000000000@peap",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_auth_sim",
+ ca_cert="auth_serv/ca.pem", phase2="auth=SIM",
+ wait_connect=False, scan_freq="2412")
+ run_ext_sim_auth(hapd, dev[0])
+
+def test_ap_wpa2_eap_ttls_eap_aka(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/EAP-AKA"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "0232010000000000",
+ anonymous_identity="0232010000000000@ttls",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=AKA")
+ eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_peap_eap_aka(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAP/EAP-AKA"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PEAP", "0232010000000000",
+ anonymous_identity="0232010000000000@peap",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ ca_cert="auth_serv/ca.pem", phase2="auth=AKA")
+ eap_reauth(dev[0], "PEAP")
+
+def test_ap_wpa2_eap_fast_eap_aka(dev, apdev):
+ """WPA2-Enterprise connection using EAP-FAST/EAP-AKA"""
+ check_eap_capa(dev[0], "FAST")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "FAST", "0232010000000000",
+ anonymous_identity="0232010000000000@fast",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_auth_aka",
+ ca_cert="auth_serv/ca.pem", phase2="auth=AKA")
+ eap_reauth(dev[0], "FAST")
+
+def test_ap_wpa2_eap_peap_eap_mschapv2(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PEAP", "user",
+ anonymous_identity="peap", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "PEAP")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "PEAP", "user",
+ anonymous_identity="peap", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ fragment_size="200")
+
+ logger.info("Password as hash value")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "PEAP", "user",
+ anonymous_identity="peap",
+ password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+
+ logger.info("Negative test with incorrect password")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "PEAP", "user",
+ anonymous_identity="peap", password="password1",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_peap_eap_mschapv2_domain(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2 with domain"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PEAP", r"DOMAIN\user3",
+ anonymous_identity="peap", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "PEAP")
+
+def test_ap_wpa2_eap_peap_eap_mschapv2_incorrect_password(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2 - incorrect password"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PEAP", "user",
+ anonymous_identity="peap", password="wrong",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_peap_crypto_binding(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAPv0/EAP-MSCHAPv2 and crypto binding"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PEAP", "user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peapver=0 crypto_binding=2",
+ phase2="auth=MSCHAPV2")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "PEAP")
+
+ eap_connect(dev[1], hapd, "PEAP", "user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peapver=0 crypto_binding=1",
+ phase2="auth=MSCHAPV2")
+ eap_connect(dev[2], hapd, "PEAP", "user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peapver=0 crypto_binding=0",
+ phase2="auth=MSCHAPV2")
+
+def test_ap_wpa2_eap_peap_crypto_binding_server_oom(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAPv0/EAP-MSCHAPv2 and crypto binding with server OOM"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ with alloc_fail(hapd, 1, "eap_mschapv2_getKey"):
+ eap_connect(dev[0], hapd, "PEAP", "user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peapver=0 crypto_binding=2",
+ phase2="auth=MSCHAPV2",
+ expect_failure=True, local_error_report=True)
+
+def test_ap_wpa2_eap_peap_params(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAPv0/EAP-MSCHAPv2 and various parameters"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PEAP", "user",
+ anonymous_identity="peap", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="peapver=0 peaplabel=1",
+ expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+ identity="user",
+ anonymous_identity="peap", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="peap_outer_success=0",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("No EAP success seen")
+ # This won't succeed to connect with peap_outer_success=0, so stop here.
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ eap_connect(dev[1], hapd, "PEAP", "user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peap_outer_success=1",
+ phase2="auth=MSCHAPV2")
+ eap_connect(dev[2], hapd, "PEAP", "user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peap_outer_success=2",
+ phase2="auth=MSCHAPV2")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+ identity="user",
+ anonymous_identity="peap", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="peapver=1 peaplabel=1",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("No EAP success seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev and "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].disconnect_and_stop_scan()
+
+ tests = [("peap-ver0", ""),
+ ("peap-ver1", ""),
+ ("peap-ver0", "peapver=0"),
+ ("peap-ver1", "peapver=1")]
+ for anon, phase1 in tests:
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+ identity="user", anonymous_identity=anon,
+ password="password", phase1=phase1,
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [("peap-ver0", "peapver=1"),
+ ("peap-ver1", "peapver=0")]
+ for anon, phase1 in tests:
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+ identity="user", anonymous_identity=anon,
+ password="password", phase1=phase1,
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("No EAP-Failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ eap_connect(dev[0], hapd, "PEAP", "user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="tls_allow_md5=1 tls_disable_session_ticket=1 tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=0 tls_ext_cert_check=0",
+ phase2="auth=MSCHAPV2")
+
+def test_ap_wpa2_eap_peap_eap_gtc(dev, apdev, params):
+ """WPA2-Enterprise connection using EAP-PEAP/EAP-GTC"""
+ p = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], p)
+ eap_connect(dev[0], hapd, "PEAP", "user", phase1="peapver=1",
+ anonymous_identity="peap", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC")
+
+def test_ap_wpa2_eap_peap_eap_tls(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAP/EAP-TLS"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PEAP", "cert user",
+ ca_cert="auth_serv/ca.pem", phase2="auth=TLS",
+ ca_cert2="auth_serv/ca.pem",
+ client_cert2="auth_serv/user.pem",
+ private_key2="auth_serv/user.key")
+ eap_reauth(dev[0], "PEAP")
+
+def test_ap_wpa2_eap_peap_eap_vendor(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PEAP/EAP-vendor"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PEAP", "vendor-test-2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=VENDOR-TEST")
+
+def test_ap_wpa2_eap_tls(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ eap_reauth(dev[0], "TLS")
+
+def test_eap_tls_pkcs8_pkcs5_v2_des3(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and PKCS #8, PKCS #5 v2 DES3 key"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key.pkcs8",
+ private_key_passwd="whatever")
+
+def test_eap_tls_pkcs8_pkcs5_v15(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and PKCS #8, PKCS #5 v1.5 key"""
+ check_pkcs5_v15_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key.pkcs8.pkcs5v15",
+ private_key_passwd="whatever")
+
+def test_ap_wpa2_eap_tls_blob(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and config blobs"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ cert = read_pem("auth_serv/ca.pem")
+ if "OK" not in dev[0].request("SET blob cacert " + binascii.hexlify(cert).decode()):
+ raise Exception("Could not set cacert blob")
+ cert = read_pem("auth_serv/user.pem")
+ if "OK" not in dev[0].request("SET blob usercert " + binascii.hexlify(cert).decode()):
+ raise Exception("Could not set usercert blob")
+ key = read_pem("auth_serv/user.rsa-key")
+ if "OK" not in dev[0].request("SET blob userkey " + binascii.hexlify(key).decode()):
+ raise Exception("Could not set cacert blob")
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="blob://cacert",
+ client_cert="blob://usercert",
+ private_key="blob://userkey")
+
+def test_ap_wpa2_eap_tls_blob_missing(dev, apdev):
+ """EAP-TLS and config blob missing"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user",
+ ca_cert="blob://testing-blob-does-not-exist",
+ client_cert="blob://testing-blob-does-not-exist",
+ private_key="blob://testing-blob-does-not-exist",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"], timeout=10)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_tls_with_tls_len(dev, apdev):
+ """EAP-TLS and TLS Message Length in unfragmented packets"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ phase1="include_tls_length=1",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+
+def test_ap_wpa2_eap_tls_pkcs12(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and PKCS#12"""
+ check_pkcs12_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user",
+ ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-REQ-PASSPHRASE"])
+ if ev is None:
+ raise Exception("Request for private key passphrase timed out")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-PASSPHRASE-" + id + ":whatever")
+ dev[0].wait_connected(timeout=10)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ # Run this twice to verify certificate chain handling with OpenSSL. Use two
+ # different files to cover both cases of the extra certificate being the
+ # one that signed the client certificate and it being unrelated to the
+ # client certificate.
+ for pkcs12 in "auth_serv/user2.pkcs12", "auth_serv/user3.pkcs12":
+ for i in range(2):
+ eap_connect(dev[0], hapd, "TLS", "tls user",
+ ca_cert="auth_serv/ca.pem",
+ private_key=pkcs12,
+ private_key_passwd="whatever")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_tls_pkcs12_blob(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and PKCS#12 from configuration blob"""
+ cert = read_pem("auth_serv/ca.pem")
+ cacert = binascii.hexlify(cert).decode()
+ run_ap_wpa2_eap_tls_pkcs12_blob(dev, apdev, cacert)
+
+def test_ap_wpa2_eap_tls_pkcs12_blob_pem(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and PKCS#12 from configuration blob and PEM ca_cert blob"""
+ with open("auth_serv/ca.pem", "r") as f:
+ lines = f.readlines()
+ copy = False
+ cert = ""
+ for l in lines:
+ if "-----BEGIN" in l:
+ copy = True
+ if copy:
+ cert += l
+ if "-----END" in l:
+ copy = False
+ break
+ cacert = binascii.hexlify(cert.encode()).decode()
+ run_ap_wpa2_eap_tls_pkcs12_blob(dev, apdev, cacert)
+
+def run_ap_wpa2_eap_tls_pkcs12_blob(dev, apdev, cacert):
+ check_pkcs12_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "OK" not in dev[0].request("SET blob cacert " + cacert):
+ raise Exception("Could not set cacert blob")
+ with open("auth_serv/user.pkcs12", "rb") as f:
+ if "OK" not in dev[0].request("SET blob pkcs12 " + binascii.hexlify(f.read()).decode()):
+ raise Exception("Could not set pkcs12 blob")
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="blob://cacert",
+ private_key="blob://pkcs12",
+ private_key_passwd="whatever")
+
+def test_ap_wpa2_eap_tls_neg_incorrect_trust_root(dev, apdev):
+ """WPA2-Enterprise negative test - incorrect trust root"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ cert = read_pem("auth_serv/ca-incorrect.pem")
+ if "OK" not in dev[0].request("SET blob cacert " + binascii.hexlify(cert).decode()):
+ raise Exception("Could not set cacert blob")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="blob://cacert",
+ wait_connect=False, scan_freq="2412")
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca-incorrect.pem",
+ wait_connect=False, scan_freq="2412")
+
+ for dev in (dev[0], dev[1]):
+ ev = dev.wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+
+ ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
+ if ev is None:
+ raise Exception("EAP method selection timed out")
+ if "TTLS" not in ev:
+ raise Exception("Unexpected EAP method")
+
+ ev = dev.wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ "CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result timed out")
+ if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
+ raise Exception("TLS certificate error not reported")
+
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result(2) timed out")
+ if "CTRL-EVENT-EAP-FAILURE" not in ev:
+ raise Exception("EAP failure not reported")
+
+ ev = dev.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result(3) timed out")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Disconnection not reported")
+
+ ev = dev.wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("Network block disabling not reported")
+
+def test_ap_wpa2_eap_tls_diff_ca_trust(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/PAP and different CA trust"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password", phase2="auth=PAP",
+ ca_cert="auth_serv/ca.pem",
+ wait_connect=True, scan_freq="2412")
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password", phase2="auth=PAP",
+ ca_cert="auth_serv/ca-incorrect.pem",
+ only_add_network=True, scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21"], timeout=15)
+ if ev is None:
+ raise Exception("EAP-TTLS not re-started")
+
+ ev = dev[0].wait_disconnected(timeout=15)
+ if "reason=23" not in ev:
+ raise Exception("Proper reason code for disconnection not reported")
+
+def test_ap_wpa2_eap_tls_diff_ca_trust2(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/PAP and different CA trust"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password", phase2="auth=PAP",
+ wait_connect=True, scan_freq="2412")
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password", phase2="auth=PAP",
+ ca_cert="auth_serv/ca-incorrect.pem",
+ only_add_network=True, scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21"], timeout=15)
+ if ev is None:
+ raise Exception("EAP-TTLS not re-started")
+
+ ev = dev[0].wait_disconnected(timeout=15)
+ if "reason=23" not in ev:
+ raise Exception("Proper reason code for disconnection not reported")
+
+def test_ap_wpa2_eap_tls_diff_ca_trust3(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/PAP and different CA trust"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password", phase2="auth=PAP",
+ ca_cert="auth_serv/ca.pem",
+ wait_connect=True, scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[0].set_network_quoted(id, "ca_cert", "auth_serv/ca-incorrect.pem")
+ dev[0].select_network(id, freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=21"], timeout=15)
+ if ev is None:
+ raise Exception("EAP-TTLS not re-started")
+
+ ev = dev[0].wait_disconnected(timeout=15)
+ if "reason=23" not in ev:
+ raise Exception("Proper reason code for disconnection not reported")
+
+def test_ap_wpa2_eap_tls_neg_suffix_match(dev, apdev):
+ """WPA2-Enterprise negative test - domain suffix mismatch"""
+ check_domain_suffix_match(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem",
+ domain_suffix_match="incorrect.example.com",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
+ if ev is None:
+ raise Exception("EAP method selection timed out")
+ if "TTLS" not in ev:
+ raise Exception("Unexpected EAP method")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ "CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result timed out")
+ if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
+ raise Exception("TLS certificate error not reported")
+ if "Domain suffix mismatch" not in ev:
+ raise Exception("Domain suffix mismatch not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result(2) timed out")
+ if "CTRL-EVENT-EAP-FAILURE" not in ev:
+ raise Exception("EAP failure not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result(3) timed out")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Disconnection not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("Network block disabling not reported")
+
+def test_ap_wpa2_eap_tls_neg_domain_match(dev, apdev):
+ """WPA2-Enterprise negative test - domain mismatch"""
+ check_domain_match(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem",
+ domain_match="w1.fi",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
+ if ev is None:
+ raise Exception("EAP method selection timed out")
+ if "TTLS" not in ev:
+ raise Exception("Unexpected EAP method")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ "CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result timed out")
+ if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
+ raise Exception("TLS certificate error not reported")
+ if "Domain mismatch" not in ev:
+ raise Exception("Domain mismatch not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result(2) timed out")
+ if "CTRL-EVENT-EAP-FAILURE" not in ev:
+ raise Exception("EAP failure not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result(3) timed out")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Disconnection not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("Network block disabling not reported")
+
+def test_ap_wpa2_eap_tls_neg_subject_match(dev, apdev):
+ """WPA2-Enterprise negative test - subject mismatch"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem",
+ subject_match="/C=FI/O=w1.fi/CN=example.com",
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD",
+ "EAP: Failed to initialize EAP method"], timeout=10)
+ if ev is None:
+ raise Exception("EAP method selection timed out")
+ if "EAP: Failed to initialize EAP method" in ev:
+ tls = dev[0].request("GET tls_library")
+ if tls.startswith("OpenSSL"):
+ raise Exception("Failed to select EAP method")
+ logger.info("subject_match not supported - connection failed, so test succeeded")
+ return
+ if "TTLS" not in ev:
+ raise Exception("Unexpected EAP method")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ "CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result timed out")
+ if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
+ raise Exception("TLS certificate error not reported")
+ if "Subject mismatch" not in ev:
+ raise Exception("Subject mismatch not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result(2) timed out")
+ if "CTRL-EVENT-EAP-FAILURE" not in ev:
+ raise Exception("EAP failure not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result(3) timed out")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Disconnection not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("Network block disabling not reported")
+
+def test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev):
+ """WPA2-Enterprise negative test - altsubject mismatch"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+
+ tests = ["incorrect.example.com",
+ "DNS:incorrect.example.com",
+ "DNS:w1.fi",
+ "DNS:erver.w1.fi"]
+ for match in tests:
+ _test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev, match)
+
+def _test_ap_wpa2_eap_tls_neg_altsubject_match(dev, apdev, match):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem",
+ altsubject_match=match,
+ wait_connect=False, scan_freq="2412")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD",
+ "EAP: Failed to initialize EAP method"], timeout=10)
+ if ev is None:
+ raise Exception("EAP method selection timed out")
+ if "EAP: Failed to initialize EAP method" in ev:
+ tls = dev[0].request("GET tls_library")
+ if tls.startswith("OpenSSL"):
+ raise Exception("Failed to select EAP method")
+ logger.info("altsubject_match not supported - connection failed, so test succeeded")
+ return
+ if "TTLS" not in ev:
+ raise Exception("Unexpected EAP method")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ "CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result timed out")
+ if "CTRL-EVENT-EAP-TLS-CERT-ERROR" not in ev:
+ raise Exception("TLS certificate error not reported")
+ if "AltSubject mismatch" not in ev:
+ raise Exception("altsubject mismatch not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result(2) timed out")
+ if "CTRL-EVENT-EAP-FAILURE" not in ev:
+ raise Exception("EAP failure not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result(3) timed out")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Disconnection not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("Network block disabling not reported")
+
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_unauth_tls(dev, apdev):
+ """WPA2-Enterprise connection using UNAUTH-TLS"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "UNAUTH-TLS", "unauth-tls",
+ ca_cert="auth_serv/ca.pem")
+ eap_reauth(dev[0], "UNAUTH-TLS")
+
+def test_ap_wpa2_eap_ttls_server_cert_hash(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS and server certificate hash"""
+ check_cert_probe_support(dev[0])
+ skip_with_fips(dev[0])
+ srv_cert_hash = "f75a953c1aa9967926525d4d860d1ff7e872f7088782f060768d12aecbd5f25e"
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="probe", ca_cert="probe://",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PEER-CERT depth=0"], timeout=10)
+ if ev is None:
+ raise Exception("No peer server certificate event seen")
+ if "hash=" + srv_cert_hash not in ev:
+ raise Exception("Expected server certificate hash not reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result timed out")
+ if "Server certificate chain probe" not in ev:
+ raise Exception("Server certificate probe not reported")
+ dev[0].wait_disconnected(timeout=10)
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="hash://server/sha256/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
+ if ev is None:
+ raise Exception("EAP result timed out")
+ if "Server certificate mismatch" not in ev:
+ raise Exception("Server certificate mismatch not reported")
+ dev[0].wait_disconnected(timeout=10)
+ dev[0].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="hash://server/sha256/" + srv_cert_hash,
+ phase2="auth=MSCHAPV2")
+
+def test_ap_wpa2_eap_ttls_server_cert_hash_invalid(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS and server certificate hash (invalid config)"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="hash://server/md5/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a",
+ wait_connect=False, scan_freq="2412")
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="hash://server/sha256/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca",
+ wait_connect=False, scan_freq="2412")
+ dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="hash://server/sha256/5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6Q",
+ wait_connect=False, scan_freq="2412")
+ for i in range(0, 3):
+ ev = dev[i].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+ ev = dev[i].wait_event(["EAP: Failed to initialize EAP method: vendor 0 method 21 (TTLS)"], timeout=5)
+ if ev is None:
+ raise Exception("Did not report EAP method initialization failure")
+
+def test_ap_wpa2_eap_pwd(dev, apdev):
+ """WPA2-Enterprise connection using EAP-pwd"""
+ check_eap_capa(dev[0], "PWD")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PWD", "pwd user", password="secret password")
+ eap_reauth(dev[0], "PWD")
+ dev[0].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[1], hapd, "PWD",
+ "pwd.user@test123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.example.com",
+ password="secret password",
+ fragment_size="90")
+
+ logger.info("Negative test with incorrect password")
+ eap_connect(dev[2], hapd, "PWD", "pwd user", password="secret-password",
+ expect_failure=True, local_error_report=True)
+
+ eap_connect(dev[0], hapd, "PWD",
+ "pwd.user@test123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.example.com",
+ password="secret password",
+ fragment_size="31")
+
+def test_ap_wpa2_eap_pwd_nthash(dev, apdev):
+ """WPA2-Enterprise connection using EAP-pwd and NTHash"""
+ check_eap_capa(dev[0], "PWD")
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PWD", "pwd-hash", password="secret password")
+ eap_connect(dev[1], hapd, "PWD", "pwd-hash",
+ password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a")
+ eap_connect(dev[2], hapd, "PWD", "pwd user",
+ password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a",
+ expect_failure=True, local_error_report=True)
+
+def test_ap_wpa2_eap_pwd_salt_sha1(dev, apdev):
+ """WPA2-Enterprise connection using EAP-pwd and salted password SHA-1"""
+ check_eap_capa(dev[0], "PWD")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PWD", "pwd-hash-sha1",
+ password="secret password")
+
+def test_ap_wpa2_eap_pwd_salt_sha256(dev, apdev):
+ """WPA2-Enterprise connection using EAP-pwd and salted password SHA256"""
+ check_eap_capa(dev[0], "PWD")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PWD", "pwd-hash-sha256",
+ password="secret password")
+
+def test_ap_wpa2_eap_pwd_salt_sha512(dev, apdev):
+ """WPA2-Enterprise connection using EAP-pwd and salted password SHA512"""
+ check_eap_capa(dev[0], "PWD")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PWD", "pwd-hash-sha512",
+ password="secret password")
+
+def test_ap_wpa2_eap_pwd_groups(dev, apdev):
+ """WPA2-Enterprise connection using various EAP-pwd groups"""
+ check_eap_capa(dev[0], "PWD")
+ tls = dev[0].request("GET tls_library")
+ params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP", "ieee8021x": "1",
+ "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf"}
+ groups = [19, 20, 21]
+ for i in groups:
+ logger.info("Group %d" % i)
+ params['pwd_group'] = str(i)
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PWD", "pwd user",
+ password="secret password",
+ phase1="eap_pwd_groups=0-65535")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ hapd.disable()
+
+def test_ap_wpa2_eap_pwd_invalid_group(dev, apdev):
+ """WPA2-Enterprise connection using invalid EAP-pwd group"""
+ check_eap_capa(dev[0], "PWD")
+ params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP", "ieee8021x": "1",
+ "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf"}
+ for i in [0, 25, 26, 27]:
+ logger.info("Group %d" % i)
+ params['pwd_group'] = str(i)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PWD",
+ identity="pwd user", password="secret password",
+ phase1="eap_pwd_groups=0-65535",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report (group %d)" % i)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ hapd.disable()
+
+def test_ap_wpa2_eap_pwd_disabled_group(dev, apdev):
+ """WPA2-Enterprise connection using disabled EAP-pwd group"""
+ check_eap_capa(dev[0], "PWD")
+ params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP", "ieee8021x": "1",
+ "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf"}
+ for i in [19, 21]:
+ logger.info("Group %d" % i)
+ params['pwd_group'] = str(i)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PWD",
+ identity="pwd user", password="secret password",
+ phase1="eap_pwd_groups=20",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report (group %d)" % i)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ hapd.disable()
+
+ params['pwd_group'] = "20"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PWD",
+ identity="pwd user", password="secret password",
+ phase1="eap_pwd_groups=20",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_pwd_as_frag(dev, apdev):
+ """WPA2-Enterprise connection using EAP-pwd with server fragmentation"""
+ check_eap_capa(dev[0], "PWD")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP", "ieee8021x": "1",
+ "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
+ "pwd_group": "19", "fragment_size": "40"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PWD", "pwd user", password="secret password")
+
+def test_ap_wpa2_eap_gpsk(dev, apdev):
+ """WPA2-Enterprise connection using EAP-GPSK"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = eap_connect(dev[0], hapd, "GPSK", "gpsk user",
+ password="abcdefghijklmnop0123456789abcdef")
+ eap_reauth(dev[0], "GPSK")
+
+ logger.info("Test forced algorithm selection")
+ for phase1 in ["cipher=1", "cipher=2"]:
+ dev[0].set_network_quoted(id, "phase1", phase1)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ dev[0].wait_connected(timeout=10)
+
+ logger.info("Test failed algorithm negotiation")
+ dev[0].set_network_quoted(id, "phase1", "cipher=9")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP failure timed out")
+
+ logger.info("Negative test with incorrect password")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "GPSK", "gpsk user",
+ password="ffcdefghijklmnop0123456789abcdef",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_sake(dev, apdev):
+ """WPA2-Enterprise connection using EAP-SAKE"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "SAKE", "sake user",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+ eap_reauth(dev[0], "SAKE")
+
+ logger.info("Negative test with incorrect password")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "SAKE", "sake user",
+ password_hex="ff23456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_eke(dev, apdev):
+ """WPA2-Enterprise connection using EAP-EKE"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = eap_connect(dev[0], hapd, "EKE", "eke user", password="hello")
+ eap_reauth(dev[0], "EKE")
+
+ logger.info("Test forced algorithm selection")
+ for phase1 in ["dhgroup=5 encr=1 prf=2 mac=2",
+ "dhgroup=4 encr=1 prf=2 mac=2",
+ "dhgroup=3 encr=1 prf=2 mac=2",
+ "dhgroup=3 encr=1 prf=1 mac=1"]:
+ dev[0].set_network_quoted(id, "phase1", phase1)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ dev[0].wait_connected(timeout=10)
+ dev[0].dump_monitor()
+
+ logger.info("Test failed algorithm negotiation")
+ dev[0].set_network_quoted(id, "phase1", "dhgroup=9 encr=9 prf=9 mac=9")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP failure timed out")
+ dev[0].dump_monitor()
+
+ logger.info("Test unsupported algorithm proposals")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ eap_connect(dev[0], hapd, "EKE", "eke user", password="hello",
+ phase1="dhgroup=2 encr=1 prf=1 mac=1", expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ eap_connect(dev[0], hapd, "EKE", "eke user", password="hello",
+ phase1="dhgroup=1 encr=1 prf=1 mac=1", expect_failure=True)
+
+ logger.info("Negative test with incorrect password")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "EKE", "eke user", password="hello1",
+ expect_failure=True)
+
+@long_duration_test
+def test_ap_wpa2_eap_eke_many(dev, apdev):
+ """WPA2-Enterprise connection using EAP-EKE (many connections)"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ success = 0
+ fail = 0
+ for i in range(100):
+ for j in range(3):
+ dev[j].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="EKE",
+ identity="eke user", password="hello",
+ phase1="dhgroup=3 encr=1 prf=1 mac=1",
+ scan_freq="2412", wait_connect=False)
+ for j in range(3):
+ ev = dev[j].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("No connected/disconnected event")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ fail += 1
+ # The RADIUS server limits on active sessions can be hit when
+ # going through this test case, so try to give some more time
+ # for the server to remove sessions.
+ logger.info("Failed to connect i=%d j=%d" % (i, j))
+ dev[j].request("REMOVE_NETWORK all")
+ time.sleep(1)
+ else:
+ success += 1
+ dev[j].request("REMOVE_NETWORK all")
+ dev[j].wait_disconnected()
+ dev[j].dump_monitor()
+ logger.info("Total success=%d failure=%d" % (success, fail))
+
+def test_ap_wpa2_eap_eke_serverid_nai(dev, apdev):
+ """WPA2-Enterprise connection using EAP-EKE with serverid NAI"""
+ params = int_eap_server_params()
+ params['server_id'] = 'example.server@w1.fi'
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "EKE", "eke user", password="hello")
+
+def test_ap_wpa2_eap_eke_server_oom(dev, apdev):
+ """WPA2-Enterprise connection using EAP-EKE with server OOM"""
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+
+ for count, func in [(1, "eap_eke_build_commit"),
+ (2, "eap_eke_build_commit"),
+ (3, "eap_eke_build_commit"),
+ (1, "eap_eke_build_confirm"),
+ (2, "eap_eke_build_confirm"),
+ (1, "eap_eke_process_commit"),
+ (2, "eap_eke_process_commit"),
+ (1, "eap_eke_process_confirm"),
+ (1, "eap_eke_process_identity"),
+ (2, "eap_eke_process_identity"),
+ (3, "eap_eke_process_identity"),
+ (4, "eap_eke_process_identity")]:
+ with alloc_fail(hapd, count, func):
+ eap_connect(dev[0], hapd, "EKE", "eke user", password="hello",
+ expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+
+ for count, func, pw in [(1, "eap_eke_init", "hello"),
+ (1, "eap_eke_get_session_id", "hello"),
+ (1, "eap_eke_getKey", "hello"),
+ (1, "eap_eke_build_msg", "hello"),
+ (1, "eap_eke_build_failure", "wrong"),
+ (1, "eap_eke_build_identity", "hello"),
+ (2, "eap_eke_build_identity", "hello")]:
+ with alloc_fail(hapd, count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="EKE", identity="eke user", password=pw,
+ wait_connect=False, scan_freq="2412")
+ # This would eventually time out, but we can stop after having
+ # reached the allocation failure.
+ for i in range(20):
+ time.sleep(0.1)
+ if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+ break
+ dev[0].request("REMOVE_NETWORK all")
+
+ for count in range(1, 1000):
+ try:
+ with alloc_fail(hapd, count, "eap_server_sm_step"):
+ dev[0].connect("test-wpa2-eap",
+ key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="EKE", identity="eke user", password=pw,
+ wait_connect=False, scan_freq="2412")
+ # This would eventually time out, but we can stop after having
+ # reached the allocation failure.
+ for i in range(10):
+ time.sleep(0.1)
+ if hapd.request("GET_ALLOC_FAIL").startswith('0'):
+ break
+ dev[0].request("REMOVE_NETWORK all")
+ except Exception as e:
+ if str(e) == "Allocation failure did not trigger":
+ if count < 30:
+ raise Exception("Too few allocation failures")
+ logger.info("%d allocation failures tested" % (count - 1))
+ break
+ raise e
+
+def test_ap_wpa2_eap_ikev2(dev, apdev):
+ """WPA2-Enterprise connection using EAP-IKEv2"""
+ check_eap_capa(dev[0], "IKEV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "IKEV2", "ikev2 user",
+ password="ike password")
+ eap_reauth(dev[0], "IKEV2")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "IKEV2", "ikev2 user",
+ password="ike password", fragment_size="50")
+
+ logger.info("Negative test with incorrect password")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "IKEV2", "ikev2 user",
+ password="ike-password", expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "IKEV2", "ikev2 user",
+ password="ike password", fragment_size="0")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_ikev2_as_frag(dev, apdev):
+ """WPA2-Enterprise connection using EAP-IKEv2 with server fragmentation"""
+ check_eap_capa(dev[0], "IKEV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP", "ieee8021x": "1",
+ "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
+ "fragment_size": "50"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "IKEV2", "ikev2 user",
+ password="ike password")
+ eap_reauth(dev[0], "IKEV2")
+
+def test_ap_wpa2_eap_ikev2_oom(dev, apdev):
+ """WPA2-Enterprise connection using EAP-IKEv2 and OOM"""
+ check_eap_capa(dev[0], "IKEV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+
+ tests = [(1, "dh_init"),
+ (2, "dh_init"),
+ (1, "dh_derive_shared")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="IKEV2",
+ identity="ikev2 user", password="ike password",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("EAP method not selected")
+ for i in range(10):
+ if "0:" in dev[0].request("GET_ALLOC_FAIL"):
+ break
+ time.sleep(0.02)
+ dev[0].request("REMOVE_NETWORK all")
+
+ tls = dev[0].request("GET tls_library")
+ if not tls.startswith("wolfSSL"):
+ tests = [(1, "os_get_random;dh_init")]
+ else:
+ tests = [(1, "crypto_dh_init;dh_init")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="IKEV2",
+ identity="ikev2 user", password="ike password",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("EAP method not selected")
+ for i in range(10):
+ if "0:" in dev[0].request("GET_FAIL"):
+ break
+ time.sleep(0.02)
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_pax(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PAX"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+ eap_reauth(dev[0], "PAX")
+
+ logger.info("Negative test with incorrect password")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="ff23456789abcdef0123456789abcdef",
+ expect_failure=True)
+
+def test_ap_wpa2_eap_psk(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PSK"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params["wpa_key_mgmt"] = "WPA-EAP-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PSK", "psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef", sha256=True)
+ eap_reauth(dev[0], "PSK", sha256=True)
+ check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-5"),
+ ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-5")])
+
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if 'flags' not in bss:
+ raise Exception("Could not get BSS flags from BSS table")
+ if "[WPA2-EAP-SHA256-CCMP]" not in bss['flags']:
+ raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+ logger.info("Negative test with incorrect password")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "PSK", "psk.user@example.com",
+ password_hex="ff23456789abcdef0123456789abcdef", sha256=True,
+ expect_failure=True)
+
+def test_ap_wpa2_eap_psk_oom(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PSK and OOM"""
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ tests = [(1, "=aes_128_eax_encrypt"),
+ (1, "=aes_128_eax_decrypt")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PSK",
+ identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("EAP method not selected")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL",
+ note="Failure not triggered: %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "aes_ctr_encrypt;aes_128_eax_encrypt"),
+ (1, "omac1_aes_128;aes_128_eax_encrypt"),
+ (2, "omac1_aes_128;aes_128_eax_encrypt"),
+ (3, "omac1_aes_128;aes_128_eax_encrypt"),
+ (1, "omac1_aes_vector"),
+ (1, "omac1_aes_128;aes_128_eax_decrypt"),
+ (2, "omac1_aes_128;aes_128_eax_decrypt"),
+ (3, "omac1_aes_128;aes_128_eax_decrypt"),
+ (1, "aes_ctr_encrypt;aes_128_eax_decrypt")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PSK",
+ identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("EAP method not selected")
+ wait_fail_trigger(dev[0], "GET_FAIL",
+ note="Failure not triggered: %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1, "aes_128_encrypt_block"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PSK",
+ identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP method failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa_eap_peap_eap_mschapv2(dev, apdev):
+ """WPA-Enterprise connection using EAP-PEAP/EAP-MSCHAPv2"""
+ skip_without_tkip(dev[0])
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa_eap_params(ssid="test-wpa-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="PEAP",
+ identity="user", password="password", phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem", wait_connect=False,
+ scan_freq="2412")
+ eap_check_auth(dev[0], "PEAP", True, rsn=False)
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ eap_reauth(dev[0], "PEAP", rsn=False)
+ check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-50-f2-1"),
+ ("dot11RSNAAuthenticationSuiteSelected", "00-50-f2-1")])
+ status = dev[0].get_status(extra="VERBOSE")
+ if 'portControl' not in status:
+ raise Exception("portControl missing from STATUS-VERBOSE")
+ if status['portControl'] != 'Auto':
+ raise Exception("Unexpected portControl value: " + status['portControl'])
+ if 'eap_session_id' not in status:
+ raise Exception("eap_session_id missing from STATUS-VERBOSE")
+ if not status['eap_session_id'].startswith("19"):
+ raise Exception("Unexpected eap_session_id value: " + status['eap_session_id'])
+
+def test_ap_wpa2_eap_interactive(dev, apdev):
+ """WPA2-Enterprise connection using interactive identity/password entry"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tests = [("Connection with dynamic TTLS/MSCHAPv2 password entry",
+ "TTLS", "ttls", "DOMAIN\mschapv2 user", "auth=MSCHAPV2",
+ None, "password"),
+ ("Connection with dynamic TTLS/MSCHAPv2 identity and password entry",
+ "TTLS", "ttls", None, "auth=MSCHAPV2",
+ "DOMAIN\mschapv2 user", "password"),
+ ("Connection with dynamic TTLS/EAP-MSCHAPv2 password entry",
+ "TTLS", "ttls", "user", "autheap=MSCHAPV2", None, "password"),
+ ("Connection with dynamic TTLS/EAP-MD5 password entry",
+ "TTLS", "ttls", "user", "autheap=MD5", None, "password"),
+ ("Connection with dynamic PEAP/EAP-MSCHAPv2 password entry",
+ "PEAP", None, "user", "auth=MSCHAPV2", None, "password"),
+ ("Connection with dynamic PEAP/EAP-GTC password entry",
+ "PEAP", None, "user", "auth=GTC", None, "password")]
+ for [desc, eap, anon, identity, phase2, req_id, req_pw] in tests:
+ logger.info(desc)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap=eap,
+ anonymous_identity=anon, identity=identity,
+ ca_cert="auth_serv/ca.pem", phase2=phase2,
+ wait_connect=False, scan_freq="2412")
+ if req_id:
+ ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"])
+ if ev is None:
+ raise Exception("Request for identity timed out")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-IDENTITY-" + id + ":" + req_id)
+ ev = dev[0].wait_event(["CTRL-REQ-PASSWORD", "CTRL-REQ-OTP"])
+ if ev is None:
+ raise Exception("Request for password timed out")
+ id = ev.split(':')[0].split('-')[-1]
+ type = "OTP" if "CTRL-REQ-OTP" in ev else "PASSWORD"
+ dev[0].request("CTRL-RSP-" + type + "-" + id + ":" + req_pw)
+ dev[0].wait_connected(timeout=10)
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_ext_enable_network_while_connected(dev, apdev):
+ """WPA2-Enterprise interactive identity entry and ENABLE_NETWORK"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ id_other = dev[0].connect("other", key_mgmt="NONE", scan_freq="2412",
+ only_add_network=True)
+
+ req_id = "DOMAIN\mschapv2 user"
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ anonymous_identity="ttls", identity=None,
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"])
+ if ev is None:
+ raise Exception("Request for identity timed out")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-IDENTITY-" + id + ":" + req_id)
+ dev[0].wait_connected(timeout=10)
+
+ if "OK" not in dev[0].request("ENABLE_NETWORK " + str(id_other)):
+ raise Exception("Failed to enable network")
+ ev = dev[0].wait_event(["SME: Trying to authenticate"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected reconnection attempt on ENABLE_NETWORK")
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_vendor_test(dev, apdev):
+ """WPA2-Enterprise connection using EAP vendor test"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "VENDOR-TEST", "vendor-test")
+ eap_reauth(dev[0], "VENDOR-TEST")
+ eap_connect(dev[1], hapd, "VENDOR-TEST", "vendor-test",
+ password="pending")
+
+def test_ap_wpa2_eap_vendor_test_oom(dev, apdev):
+ """WPA2-Enterprise connection using EAP vendor test (OOM)"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+
+ tests = ["eap_vendor_test_init",
+ "eap_msg_alloc;eap_vendor_test_process",
+ "eap_vendor_test_getKey"]
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="VENDOR-TEST", identity="vendor-test",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_fast_mschapv2_unauth_prov(dev, apdev):
+ """WPA2-Enterprise connection using EAP-FAST/MSCHAPv2 and unauthenticated provisioning"""
+ check_eap_capa(dev[0], "FAST")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1", pac_file="blob://fast_pac")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ res = eap_reauth(dev[0], "FAST")
+ if res['tls_session_reused'] != '1':
+ raise Exception("EAP-FAST could not use PAC session ticket")
+
+def test_ap_wpa2_eap_fast_pac_file(dev, apdev, params):
+ """WPA2-Enterprise connection using EAP-FAST/MSCHAPv2 and PAC file"""
+ check_eap_capa(dev[0], "FAST")
+ pac_file = os.path.join(params['logdir'], "fast.pac")
+ pac_file2 = os.path.join(params['logdir'], "fast-bin.pac")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1", pac_file=pac_file)
+ with open(pac_file, "r") as f:
+ data = f.read()
+ if "wpa_supplicant EAP-FAST PAC file - version 1" not in data:
+ raise Exception("PAC file header missing")
+ if "PAC-Key=" not in data:
+ raise Exception("PAC-Key missing from PAC file")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file=pac_file)
+
+ eap_connect(dev[1], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1 fast_pac_format=binary",
+ pac_file=pac_file2)
+ dev[1].request("REMOVE_NETWORK all")
+ eap_connect(dev[1], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_pac_format=binary",
+ pac_file=pac_file2)
+ finally:
+ try:
+ os.remove(pac_file)
+ except:
+ pass
+ try:
+ os.remove(pac_file2)
+ except:
+ pass
+
+def test_ap_wpa2_eap_fast_binary_pac(dev, apdev):
+ """WPA2-Enterprise connection using EAP-FAST and binary PAC format"""
+ check_eap_capa(dev[0], "FAST")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1 fast_max_pac_list_len=1 fast_pac_format=binary",
+ pac_file="blob://fast_pac_bin")
+ res = eap_reauth(dev[0], "FAST")
+ if res['tls_session_reused'] != '1':
+ raise Exception("EAP-FAST could not use PAC session ticket")
+
+ # Verify fast_max_pac_list_len=0 special case
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1 fast_max_pac_list_len=0 fast_pac_format=binary",
+ pac_file="blob://fast_pac_bin")
+
+def test_ap_wpa2_eap_fast_missing_pac_config(dev, apdev):
+ """WPA2-Enterprise connection using EAP-FAST and missing PAC config"""
+ check_eap_capa(dev[0], "FAST")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://fast_pac_not_in_use",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_fast_binary_pac_errors(dev, apdev):
+ """EAP-FAST and binary PAC errors"""
+ check_eap_capa(dev[0], "FAST")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tests = [(1, "=eap_fast_save_pac_bin"),
+ (1, "eap_fast_write_pac"),
+ (2, "eap_fast_write_pac"),]
+ for count, func in tests:
+ if "OK" not in dev[0].request("SET blob fast_pac_bin_errors "):
+ raise Exception("Could not set blob")
+
+ with alloc_fail(dev[0], count, func):
+ eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1 fast_pac_format=binary",
+ pac_file="blob://fast_pac_bin_errors")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = ["00", "000000000000", "6ae4920c0001",
+ "6ae4920c000000",
+ "6ae4920c0000" + "0000" + 32*"00" + "ffff" + "0000",
+ "6ae4920c0000" + "0000" + 32*"00" + "0001" + "0000",
+ "6ae4920c0000" + "0000" + 32*"00" + "0000" + "0001",
+ "6ae4920c0000" + "0000" + 32*"00" + "0000" + "0008" + "00040000" + "0007000100"]
+ for t in tests:
+ if "OK" not in dev[0].request("SET blob fast_pac_bin_errors " + t):
+ raise Exception("Could not set blob")
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1 fast_pac_format=binary",
+ pac_file="blob://fast_pac_bin_errors",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=5)
+ if ev is None:
+ raise Exception("Failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ pac = "6ae4920c0000" + "0000" + 32*"00" + "0000" + "0000"
+ tests = [(1, "eap_fast_load_pac_bin"),
+ (2, "eap_fast_load_pac_bin"),
+ (3, "eap_fast_load_pac_bin")]
+ for count, func in tests:
+ if "OK" not in dev[0].request("SET blob fast_pac_bin_errors " + pac):
+ raise Exception("Could not set blob")
+
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1 fast_pac_format=binary",
+ pac_file="blob://fast_pac_bin_errors",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=5)
+ if ev is None:
+ raise Exception("Failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ pac = "6ae4920c0000" + "0000" + 32*"00" + "0000" + "0005" + "0011223344"
+ if "OK" not in dev[0].request("SET blob fast_pac_bin_errors " + pac):
+ raise Exception("Could not set blob")
+
+ eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1 fast_pac_format=binary",
+ pac_file="blob://fast_pac_bin_errors")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ pac = "6ae4920c0000" + "0000" + 32*"00" + "0000" + "0009" + "00040000" + "0007000100"
+ tests = [(1, "eap_fast_pac_get_a_id"),
+ (2, "eap_fast_pac_get_a_id")]
+ for count, func in tests:
+ if "OK" not in dev[0].request("SET blob fast_pac_bin_errors " + pac):
+ raise Exception("Could not set blob")
+ with alloc_fail(dev[0], count, func):
+ eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1 fast_pac_format=binary",
+ pac_file="blob://fast_pac_bin_errors")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_fast_text_pac_errors(dev, apdev):
+ """EAP-FAST and text PAC errors"""
+ check_eap_capa(dev[0], "FAST")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+
+ tests = [(1, "eap_fast_parse_hex;eap_fast_parse_pac_key"),
+ (1, "eap_fast_parse_hex;eap_fast_parse_pac_opaque"),
+ (1, "eap_fast_parse_hex;eap_fast_parse_a_id"),
+ (1, "eap_fast_parse_start"),
+ (1, "eap_fast_save_pac")]
+ for count, func in tests:
+ dev[0].request("FLUSH")
+ if "OK" not in dev[0].request("SET blob fast_pac_text_errors "):
+ raise Exception("Could not set blob")
+
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac_text_errors",
+ scan_freq="2412", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ pac = "wpa_supplicant EAP-FAST PAC file - version 1\n"
+ pac += "START\n"
+ pac += "PAC-Type\n"
+ pac += "END\n"
+ if "OK" not in dev[0].request("SET blob fast_pac_text_errors " + binascii.hexlify(pac.encode()).decode()):
+ raise Exception("Could not set blob")
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac_text_errors",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"], timeout=5)
+ if ev is None:
+ raise Exception("Failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].request("FLUSH")
+ if "OK" not in dev[0].request("SET blob fast_pac_text_errors "):
+ raise Exception("Could not set blob")
+
+ with alloc_fail(dev[0], 1, "eap_fast_add_pac_data"):
+ for i in range(3):
+ params = int_eap_server_params()
+ params['ssid'] = "test-wpa2-eap-2"
+ params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
+ params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
+ params['eap_fast_a_id_info'] = "test server %d" % i
+
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].connect("test-wpa2-eap-2", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac_text_errors",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ hapd2.disable()
+
+def test_ap_wpa2_eap_fast_pac_truncate(dev, apdev):
+ """EAP-FAST and PAC list truncation"""
+ check_eap_capa(dev[0], "FAST")
+ if "OK" not in dev[0].request("SET blob fast_pac_truncate "):
+ raise Exception("Could not set blob")
+ for i in range(5):
+ params = int_eap_server_params()
+ params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
+ params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
+ params['eap_fast_a_id_info'] = "test server %d" % i
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1 fast_max_pac_list_len=2",
+ pac_file="blob://fast_pac_truncate",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ hapd.disable()
+
+def test_ap_wpa2_eap_fast_pac_refresh(dev, apdev):
+ """EAP-FAST and PAC refresh"""
+ check_eap_capa(dev[0], "FAST")
+ if "OK" not in dev[0].request("SET blob fast_pac_refresh "):
+ raise Exception("Could not set blob")
+ for i in range(2):
+ params = int_eap_server_params()
+ params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
+ params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
+ params['eap_fast_a_id_info'] = "test server %d" % i
+ params['pac_key_refresh_time'] = "1"
+ params['pac_key_lifetime'] = "10"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac_refresh",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ hapd.disable()
+
+ for i in range(2):
+ params = int_eap_server_params()
+ params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
+ params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
+ params['eap_fast_a_id_info'] = "test server %d" % i
+ params['pac_key_refresh_time'] = "10"
+ params['pac_key_lifetime'] = "10"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac_refresh",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ hapd.disable()
+
+def test_ap_wpa2_eap_fast_pac_lifetime(dev, apdev):
+ """EAP-FAST and PAC lifetime"""
+ check_eap_capa(dev[0], "FAST")
+ if "OK" not in dev[0].request("SET blob fast_pac_refresh "):
+ raise Exception("Could not set blob")
+
+ i = 0
+ params = int_eap_server_params()
+ params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
+ params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
+ params['eap_fast_a_id_info'] = "test server %d" % i
+ params['pac_key_refresh_time'] = "0"
+ params['pac_key_lifetime'] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_refresh",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ time.sleep(3)
+ dev[0].request("PMKSA_FLUSH")
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP-Failure seen after expired PAC")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].select_network(id)
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_fast_gtc_auth_prov(dev, apdev):
+ """WPA2-Enterprise connection using EAP-FAST/GTC and authenticated provisioning"""
+ check_eap_capa(dev[0], "FAST")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+ phase1="fast_provisioning=2", pac_file="blob://fast_pac_auth")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ res = eap_reauth(dev[0], "FAST")
+ if res['tls_session_reused'] != '1':
+ raise Exception("EAP-FAST could not use PAC session ticket")
+
+def test_ap_wpa2_eap_fast_gtc_identity_change(dev, apdev):
+ """WPA2-Enterprise connection using EAP-FAST/GTC and identity changing"""
+ check_eap_capa(dev[0], "FAST")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_auth")
+ dev[0].set_network_quoted(id, "identity", "user2")
+ dev[0].wait_disconnected()
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("EAP-FAST not started")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_fast_prf_oom(dev, apdev):
+ """WPA2-Enterprise connection using EAP-FAST and OOM in PRF"""
+ check_eap_capa(dev[0], "FAST")
+ tls = dev[0].request("GET tls_library")
+ if tls.startswith("OpenSSL"):
+ func = "tls_connection_get_eap_fast_key"
+ count = 2
+ elif tls.startswith("internal"):
+ func = "tls_connection_prf"
+ count = 1
+ else:
+ raise HwsimSkip("Unsupported TLS library")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password", ca_cert="auth_serv/ca.pem",
+ phase2="auth=GTC",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_auth",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("DISCONNECT")
+
+def test_ap_wpa2_eap_fast_server_oom(dev, apdev):
+ """EAP-FAST/MSCHAPv2 and server OOM"""
+ check_eap_capa(dev[0], "FAST")
+
+ params = int_eap_server_params()
+ params['dh_file'] = 'auth_serv/dh.conf'
+ params['pac_opaque_encr_key'] = '000102030405060708090a0b0c0d0e0f'
+ params['eap_fast_a_id'] = '1011'
+ params['eap_fast_a_id_info'] = 'another test server'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with alloc_fail(hapd, 1, "tls_session_ticket_ext_cb"):
+ id = eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac",
+ expect_failure=True)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP failure reported")
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+
+ dev[0].select_network(id, freq="2412")
+
+def test_ap_wpa2_eap_fast_cipher_suites(dev, apdev):
+ """EAP-FAST and different TLS cipher suites"""
+ check_eap_capa(dev[0], "FAST")
+ tls = dev[0].request("GET tls_library")
+ if not tls.startswith("OpenSSL") and not tls.startswith("wolfSSL"):
+ raise HwsimSkip("TLS library is not OpenSSL or wolfSSL: " + tls)
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET blob fast_pac_ciphers ")
+ eap_connect(dev[0], hapd, "FAST", "user",
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_ciphers")
+ res = dev[0].get_status_field('EAP TLS cipher')
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ if res != "DHE-RSA-AES256-SHA":
+ raise Exception("Unexpected cipher suite for provisioning: " + res)
+
+ tests = ["DHE-RSA-AES128-SHA",
+ "RC4-SHA",
+ "AES128-SHA",
+ "AES256-SHA",
+ "DHE-RSA-AES256-SHA"]
+ for cipher in tests:
+ dev[0].dump_monitor()
+ logger.info("Testing " + cipher)
+ try:
+ eap_connect(dev[0], hapd, "FAST", "user",
+ openssl_ciphers=cipher,
+ anonymous_identity="FAST", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+ pac_file="blob://fast_pac_ciphers",
+ report_failure=True)
+ except Exception as e:
+ if cipher == "RC4-SHA" and \
+ ("Could not select EAP method" in str(e) or \
+ "EAP failed" in str(e)):
+ if "run=OpenSSL 1.1" in tls:
+ logger.info("Allow failure due to missing TLS library support")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ continue
+ raise
+ res = dev[0].get_status_field('EAP TLS cipher')
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ if res != cipher:
+ raise Exception("Unexpected TLS cipher info (configured %s): %s" % (cipher, res))
+
+def test_ap_wpa2_eap_fast_prov(dev, apdev):
+ """EAP-FAST and provisioning options"""
+ check_eap_capa(dev[0], "FAST")
+ if "OK" not in dev[0].request("SET blob fast_pac_prov "):
+ raise Exception("Could not set blob")
+
+ i = 100
+ params = int_eap_server_params()
+ params['disable_pmksa_caching'] = '1'
+ params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff%02x" % i
+ params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff%02x" % i
+ params['eap_fast_a_id_info'] = "test server %d" % i
+ params['eap_fast_prov'] = "0"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ logger.info("Provisioning attempt while server has provisioning disabled")
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_prov",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
+ timeout=15)
+ if ev is None:
+ raise Exception("EAP result not reported")
+ if "parameter='failure'" not in ev:
+ raise Exception("Unexpected EAP result: " + ev)
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+
+ hapd.disable()
+ logger.info("Authenticated provisioning")
+ hapd.set("eap_fast_prov", "2")
+ hapd.enable()
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
+ timeout=15)
+ if ev is None:
+ raise Exception("EAP result not reported")
+ if "parameter='success'" not in ev:
+ raise Exception("Unexpected EAP result: " + ev)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ hapd.disable()
+ logger.info("Provisioning disabled - using previously provisioned PAC")
+ hapd.set("eap_fast_prov", "0")
+ hapd.enable()
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
+ timeout=15)
+ if ev is None:
+ raise Exception("EAP result not reported")
+ if "parameter='success'" not in ev:
+ raise Exception("Unexpected EAP result: " + ev)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ logger.info("Drop PAC and verify connection failure")
+ if "OK" not in dev[0].request("SET blob fast_pac_prov "):
+ raise Exception("Could not set blob")
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
+ timeout=15)
+ if ev is None:
+ raise Exception("EAP result not reported")
+ if "parameter='failure'" not in ev:
+ raise Exception("Unexpected EAP result: " + ev)
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+
+ hapd.disable()
+ logger.info("Anonymous provisioning")
+ hapd.set("eap_fast_prov", "1")
+ hapd.enable()
+ dev[0].set_network_quoted(id, "phase1", "fast_provisioning=1")
+ dev[0].select_network(id, freq="2412")
+ # Anonymous provisioning results in EAP-Failure first
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
+ timeout=15)
+ if ev is None:
+ raise Exception("EAP result not reported")
+ if "parameter='failure'" not in ev:
+ raise Exception("Unexpected EAP result: " + ev)
+ dev[0].wait_disconnected()
+ # And then the actual data connection
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
+ timeout=15)
+ if ev is None:
+ raise Exception("EAP result not reported")
+ if "parameter='success'" not in ev:
+ raise Exception("Unexpected EAP result: " + ev)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ hapd.disable()
+ logger.info("Provisioning disabled - using previously provisioned PAC")
+ hapd.set("eap_fast_prov", "0")
+ hapd.enable()
+
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='completion'"],
+ timeout=15)
+ if ev is None:
+ raise Exception("EAP result not reported")
+ if "parameter='success'" not in ev:
+ raise Exception("Unexpected EAP result: " + ev)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def test_ap_wpa2_eap_fast_eap_vendor(dev, apdev):
+ """WPA2-Enterprise connection using EAP-FAST/EAP-vendor"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "FAST", "vendor-test-2",
+ anonymous_identity="FAST",
+ phase1="fast_provisioning=2", pac_file="blob://fast_pac",
+ ca_cert="auth_serv/ca.pem", phase2="auth=VENDOR-TEST")
+
+def test_ap_wpa2_eap_tls_ocsp(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and verifying OCSP"""
+ check_ocsp_support(dev[0])
+ check_pkcs12_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=2)
+
+def test_ap_wpa2_eap_tls_ocsp_multi(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and verifying OCSP-multi"""
+ check_ocsp_multi_support(dev[0])
+ check_pkcs12_support(dev[0])
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=2)
+
+def int_eap_server_params():
+ params = {"ssid": "test-wpa2-eap", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP", "ieee8021x": "1",
+ "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key",
+ "dh_file": "auth_serv/dh.conf"}
+ return params
+
+def run_openssl(arg):
+ logger.info(' '.join(arg))
+ cmd = subprocess.Popen(arg, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ res = cmd.stdout.read().decode() + "\n" + cmd.stderr.read().decode()
+ cmd.stdout.close()
+ cmd.stderr.close()
+ cmd.wait()
+ if cmd.returncode != 0:
+ raise Exception("bad return code from openssl\n\n" + res)
+ logger.info("openssl result:\n" + res)
+
+def ocsp_cache_key_id(outfile):
+ if os.path.exists(outfile):
+ return
+ arg = ["openssl", "ocsp", "-index", "auth_serv/index.txt",
+ '-rsigner', 'auth_serv/ocsp-responder.pem',
+ '-rkey', 'auth_serv/ocsp-responder.key',
+ '-resp_key_id',
+ '-CA', 'auth_serv/ca.pem',
+ '-issuer', 'auth_serv/ca.pem',
+ '-verify_other', 'auth_serv/ca.pem',
+ '-trust_other',
+ '-ndays', '7',
+ '-reqin', 'auth_serv/ocsp-req.der',
+ '-respout', outfile]
+ run_openssl(arg)
+
+def test_ap_wpa2_eap_tls_ocsp_key_id(dev, apdev, params):
+ """EAP-TLS and OCSP certificate signed OCSP response using key ID"""
+ check_ocsp_support(dev[0])
+ check_pkcs12_support(dev[0])
+ ocsp = os.path.join(params['logdir'], "ocsp-server-cache-key-id.der")
+ ocsp_cache_key_id(ocsp)
+ if not os.path.exists(ocsp):
+ raise HwsimSkip("No OCSP response available")
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = ocsp
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=2,
+ scan_freq="2412")
+
+def ocsp_req(outfile):
+ if os.path.exists(outfile):
+ return
+ arg = ["openssl", "ocsp",
+ "-reqout", outfile,
+ '-issuer', 'auth_serv/ca.pem',
+ '-sha256',
+ '-serial', '0xD8D3E3A6CBE3CD5F',
+ '-no_nonce']
+ run_openssl(arg)
+ if not os.path.exists(outfile):
+ raise HwsimSkip("Failed to generate OCSP request")
+
+def ocsp_resp_ca_signed(reqfile, outfile, status):
+ ocsp_req(reqfile)
+ if os.path.exists(outfile):
+ return
+ arg = ["openssl", "ocsp",
+ "-index", "auth_serv/index%s.txt" % status,
+ "-rsigner", "auth_serv/ca.pem",
+ "-rkey", "auth_serv/ca-key.pem",
+ "-CA", "auth_serv/ca.pem",
+ "-ndays", "7",
+ "-reqin", reqfile,
+ "-resp_no_certs",
+ "-respout", outfile]
+ run_openssl(arg)
+ if not os.path.exists(outfile):
+ raise HwsimSkip("No OCSP response available")
+
+def ocsp_resp_server_signed(reqfile, outfile):
+ ocsp_req(reqfile)
+ if os.path.exists(outfile):
+ return
+ arg = ["openssl", "ocsp",
+ "-index", "auth_serv/index.txt",
+ "-rsigner", "auth_serv/server.pem",
+ "-rkey", "auth_serv/server.key",
+ "-CA", "auth_serv/ca.pem",
+ "-ndays", "7",
+ "-reqin", reqfile,
+ "-respout", outfile]
+ run_openssl(arg)
+ if not os.path.exists(outfile):
+ raise HwsimSkip("No OCSP response available")
+
+def test_ap_wpa2_eap_tls_ocsp_ca_signed_good(dev, apdev, params):
+ """EAP-TLS and CA signed OCSP response (good)"""
+ check_ocsp_support(dev[0])
+ check_pkcs12_support(dev[0])
+ req = os.path.join(params['logdir'], "ocsp-req.der")
+ ocsp = os.path.join(params['logdir'], "ocsp-resp-ca-signed.der")
+ ocsp_resp_ca_signed(req, ocsp, "")
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = ocsp
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=2,
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_tls_ocsp_ca_signed_revoked(dev, apdev, params):
+ """EAP-TLS and CA signed OCSP response (revoked)"""
+ check_ocsp_support(dev[0])
+ check_pkcs12_support(dev[0])
+ req = os.path.join(params['logdir'], "ocsp-req.der")
+ ocsp = os.path.join(params['logdir'], "ocsp-resp-ca-signed-revoked.der")
+ ocsp_resp_ca_signed(req, ocsp, "-revoked")
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = ocsp
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=2,
+ wait_connect=False, scan_freq="2412")
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if 'bad certificate status response' in ev:
+ break
+ if 'certificate revoked' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_tls_ocsp_ca_signed_unknown(dev, apdev, params):
+ """EAP-TLS and CA signed OCSP response (unknown)"""
+ check_ocsp_support(dev[0])
+ check_pkcs12_support(dev[0])
+ req = os.path.join(params['logdir'], "ocsp-req.der")
+ ocsp = os.path.join(params['logdir'], "ocsp-resp-ca-signed-unknown.der")
+ ocsp_resp_ca_signed(req, ocsp, "-unknown")
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = ocsp
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=2,
+ wait_connect=False, scan_freq="2412")
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if 'bad certificate status response' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_tls_ocsp_server_signed(dev, apdev, params):
+ """EAP-TLS and server signed OCSP response"""
+ check_ocsp_support(dev[0])
+ check_pkcs12_support(dev[0])
+ req = os.path.join(params['logdir'], "ocsp-req.der")
+ ocsp = os.path.join(params['logdir'], "ocsp-resp-server-signed.der")
+ ocsp_resp_server_signed(req, ocsp)
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = ocsp
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=2,
+ wait_connect=False, scan_freq="2412")
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if 'bad certificate status response' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_tls_ocsp_invalid_data(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and invalid OCSP data"""
+ check_ocsp_support(dev[0])
+ check_pkcs12_support(dev[0])
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = "auth_serv/ocsp-req.der"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=2,
+ wait_connect=False, scan_freq="2412")
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if 'bad certificate status response' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_tls_ocsp_invalid(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and invalid OCSP response"""
+ check_ocsp_support(dev[0])
+ check_pkcs12_support(dev[0])
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = "auth_serv/ocsp-server-cache.der-invalid"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=2,
+ wait_connect=False, scan_freq="2412")
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if 'bad certificate status response' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_tls_ocsp_unknown_sign(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TLS and unknown OCSP signer"""
+ check_ocsp_support(dev[0])
+ check_pkcs12_support(dev[0])
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = "auth_serv/ocsp-server-cache.der-unknown-sign"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=2,
+ wait_connect=False, scan_freq="2412")
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if 'bad certificate status response' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def ocsp_resp_status(outfile, status):
+ if os.path.exists(outfile):
+ return
+ arg = ["openssl", "ocsp", "-index", "auth_serv/index-%s.txt" % status,
+ '-rsigner', 'auth_serv/ocsp-responder.pem',
+ '-rkey', 'auth_serv/ocsp-responder.key',
+ '-CA', 'auth_serv/ca.pem',
+ '-issuer', 'auth_serv/ca.pem',
+ '-verify_other', 'auth_serv/ca.pem',
+ '-trust_other',
+ '-ndays', '7',
+ '-reqin', 'auth_serv/ocsp-req.der',
+ '-respout', outfile]
+ run_openssl(arg)
+
+def test_ap_wpa2_eap_ttls_ocsp_revoked(dev, apdev, params):
+ """WPA2-Enterprise connection using EAP-TTLS and OCSP status revoked"""
+ check_ocsp_support(dev[0])
+ ocsp = os.path.join(params['logdir'], "ocsp-server-cache-revoked.der")
+ ocsp_resp_status(ocsp, "revoked")
+ if not os.path.exists(ocsp):
+ raise HwsimSkip("No OCSP response available")
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = ocsp
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", ca_cert="auth_serv/ca.pem",
+ anonymous_identity="ttls", password="password",
+ phase2="auth=PAP", ocsp=2,
+ wait_connect=False, scan_freq="2412")
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if 'bad certificate status response' in ev:
+ break
+ if 'certificate revoked' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_ttls_ocsp_unknown(dev, apdev, params):
+ """WPA2-Enterprise connection using EAP-TTLS and OCSP status unknown"""
+ check_ocsp_support(dev[0])
+ ocsp = os.path.join(params['logdir'], "ocsp-server-cache-unknown.der")
+ ocsp_resp_status(ocsp, "unknown")
+ if not os.path.exists(ocsp):
+ raise HwsimSkip("No OCSP response available")
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = ocsp
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", ca_cert="auth_serv/ca.pem",
+ anonymous_identity="ttls", password="password",
+ phase2="auth=PAP", ocsp=2,
+ wait_connect=False, scan_freq="2412")
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if 'bad certificate status response' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_ttls_optional_ocsp_unknown(dev, apdev, params):
+ """WPA2-Enterprise connection using EAP-TTLS and OCSP status unknown"""
+ check_ocsp_support(dev[0])
+ ocsp = os.path.join(params['logdir'], "ocsp-server-cache-unknown.der")
+ ocsp_resp_status(ocsp, "unknown")
+ if not os.path.exists(ocsp):
+ raise HwsimSkip("No OCSP response available")
+ params = int_eap_server_params()
+ params["ocsp_stapling_response"] = ocsp
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", ca_cert="auth_serv/ca.pem",
+ anonymous_identity="ttls", password="password",
+ phase2="auth=PAP", ocsp=1, scan_freq="2412")
+
+def test_ap_wpa2_eap_tls_intermediate_ca(dev, apdev, params):
+ """EAP-TLS with intermediate server/user CA"""
+ params = int_eap_server_params()
+ params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
+ params["server_cert"] = "auth_serv/iCA-server/server.pem"
+ params["private_key"] = "auth_serv/iCA-server/server.key"
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+ if "GnuTLS" in tls or "wolfSSL" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user.pem"
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user",
+ ca_cert=ca_cert,
+ client_cert=client_cert,
+ private_key="auth_serv/iCA-user/user.key",
+ scan_freq="2412")
+
+def root_ocsp(cert):
+ ca = "auth_serv/ca.pem"
+
+ fd2, fn2 = tempfile.mkstemp()
+ os.close(fd2)
+
+ arg = ["openssl", "ocsp", "-reqout", fn2, "-issuer", ca, "-sha256",
+ "-cert", cert, "-no_nonce", "-text"]
+ run_openssl(arg)
+
+ fd, fn = tempfile.mkstemp()
+ os.close(fd)
+ arg = ["openssl", "ocsp", "-index", "auth_serv/rootCA/index.txt",
+ "-rsigner", ca, "-rkey", "auth_serv/ca-key.pem",
+ "-CA", ca, "-issuer", ca, "-verify_other", ca, "-trust_other",
+ "-ndays", "7", "-reqin", fn2, "-resp_no_certs", "-respout", fn,
+ "-text"]
+ run_openssl(arg)
+ os.unlink(fn2)
+ return fn
+
+def ica_ocsp(cert, md="-sha256"):
+ prefix = "auth_serv/iCA-server/"
+ ca = prefix + "cacert.pem"
+ cert = prefix + cert
+
+ fd2, fn2 = tempfile.mkstemp()
+ os.close(fd2)
+
+ arg = ["openssl", "ocsp", "-reqout", fn2, "-issuer", ca, md,
+ "-cert", cert, "-no_nonce", "-text"]
+ run_openssl(arg)
+
+ fd, fn = tempfile.mkstemp()
+ os.close(fd)
+ arg = ["openssl", "ocsp", "-index", prefix + "index.txt",
+ "-rsigner", ca, "-rkey", prefix + "private/cakey.pem",
+ "-CA", ca, "-issuer", ca, "-verify_other", ca, "-trust_other",
+ "-ndays", "7", "-reqin", fn2, "-resp_no_certs", "-respout", fn,
+ "-text"]
+ run_openssl(arg)
+ os.unlink(fn2)
+ return fn
+
+def test_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params):
+ """EAP-TLS with intermediate server/user CA and OCSP on server certificate"""
+ run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha256")
+
+def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_sha1(dev, apdev, params):
+ """EAP-TLS with intermediate server/user CA and OCSP on server certificate )SHA1)"""
+ run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, "-sha1")
+
+def run_ap_wpa2_eap_tls_intermediate_ca_ocsp(dev, apdev, params, md):
+ params = int_eap_server_params()
+ params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
+ params["server_cert"] = "auth_serv/iCA-server/server.pem"
+ params["private_key"] = "auth_serv/iCA-server/server.key"
+ fn = ica_ocsp("server.pem", md)
+ params["ocsp_stapling_response"] = fn
+ try:
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+ if "GnuTLS" in tls or "wolfSSL" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user.pem"
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user",
+ ca_cert=ca_cert,
+ client_cert=client_cert,
+ private_key="auth_serv/iCA-user/user.key",
+ scan_freq="2412", ocsp=2)
+ finally:
+ os.unlink(fn)
+
+def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params):
+ """EAP-TLS with intermediate server/user CA and OCSP on revoked server certificate"""
+ run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params,
+ "-sha256")
+
+def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked_sha1(dev, apdev, params):
+ """EAP-TLS with intermediate server/user CA and OCSP on revoked server certificate (SHA1)"""
+ run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params,
+ "-sha1")
+
+def run_ap_wpa2_eap_tls_intermediate_ca_ocsp_revoked(dev, apdev, params, md):
+ check_ocsp_support(dev[0])
+ params = int_eap_server_params()
+ params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
+ params["server_cert"] = "auth_serv/iCA-server/server-revoked.pem"
+ params["private_key"] = "auth_serv/iCA-server/server-revoked.key"
+ fn = ica_ocsp("server-revoked.pem", md)
+ params["ocsp_stapling_response"] = fn
+ try:
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+ if "GnuTLS" in tls or "wolfSSL" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user.pem"
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user",
+ ca_cert=ca_cert,
+ client_cert=client_cert,
+ private_key="auth_serv/iCA-user/user.key",
+ scan_freq="2412", ocsp=1, wait_connect=False)
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS",
+ "CTRL-EVENT-EAP-SUCCESS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if "CTRL-EVENT-EAP-SUCCESS" in ev:
+ raise Exception("Unexpected EAP-Success")
+ if 'bad certificate status response' in ev:
+ break
+ if 'certificate revoked' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ finally:
+ os.unlink(fn)
+
+def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi_missing_resp(dev, apdev, params):
+ """EAP-TLS with intermediate server/user CA and OCSP multi missing response"""
+ check_ocsp_support(dev[0])
+ check_ocsp_multi_support(dev[0])
+
+ params = int_eap_server_params()
+ params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
+ params["server_cert"] = "auth_serv/iCA-server/server.pem"
+ params["private_key"] = "auth_serv/iCA-server/server.key"
+ fn = ica_ocsp("server.pem")
+ params["ocsp_stapling_response"] = fn
+ try:
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+ if "GnuTLS" in tls or "wolfSSL" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user.pem"
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user",
+ ca_cert=ca_cert,
+ client_cert=client_cert,
+ private_key="auth_serv/iCA-user/user.key",
+ scan_freq="2412", ocsp=3, wait_connect=False)
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS",
+ "CTRL-EVENT-EAP-SUCCESS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if "CTRL-EVENT-EAP-SUCCESS" in ev:
+ raise Exception("Unexpected EAP-Success")
+ if 'bad certificate status response' in ev:
+ break
+ if 'certificate revoked' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ finally:
+ os.unlink(fn)
+
+def test_ap_wpa2_eap_tls_intermediate_ca_ocsp_multi(dev, apdev, params):
+ """EAP-TLS with intermediate server/user CA and OCSP multi OK"""
+ check_ocsp_support(dev[0])
+ check_ocsp_multi_support(dev[0])
+
+ params = int_eap_server_params()
+ params["ca_cert"] = "auth_serv/iCA-server/ca-and-root.pem"
+ params["server_cert"] = "auth_serv/iCA-server/server.pem"
+ params["private_key"] = "auth_serv/iCA-server/server.key"
+ fn = ica_ocsp("server.pem")
+ fn2 = root_ocsp("auth_serv/iCA-server/cacert.pem")
+ params["ocsp_stapling_response"] = fn
+
+ with open(fn, "rb") as f:
+ resp_server = f.read()
+ with open(fn2, "rb") as f:
+ resp_ica = f.read()
+
+ fd3, fn3 = tempfile.mkstemp()
+ try:
+ f = os.fdopen(fd3, 'wb')
+ f.write(struct.pack(">L", len(resp_server))[1:4])
+ f.write(resp_server)
+ f.write(struct.pack(">L", len(resp_ica))[1:4])
+ f.write(resp_ica)
+ f.close()
+
+ params["ocsp_stapling_response_multi"] = fn3
+
+ hostapd.add_ap(apdev[0], params)
+ tls = dev[0].request("GET tls_library")
+ if "GnuTLS" in tls or "wolfSSL" in tls:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user_and_ica.pem"
+ else:
+ ca_cert = "auth_serv/iCA-user/ca-and-root.pem"
+ client_cert = "auth_serv/iCA-user/user.pem"
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user",
+ ca_cert=ca_cert,
+ client_cert=client_cert,
+ private_key="auth_serv/iCA-user/user.key",
+ scan_freq="2412", ocsp=3)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ finally:
+ os.unlink(fn)
+ os.unlink(fn2)
+ os.unlink(fn3)
+
+def test_ap_wpa2_eap_tls_ocsp_multi_revoked(dev, apdev, params):
+ """EAP-TLS and CA signed OCSP multi response (revoked)"""
+ check_ocsp_support(dev[0])
+ check_ocsp_multi_support(dev[0])
+ check_pkcs12_support(dev[0])
+
+ req = os.path.join(params['logdir'], "ocsp-req.der")
+ ocsp_revoked = os.path.join(params['logdir'],
+ "ocsp-resp-ca-signed-revoked.der")
+ ocsp_unknown = os.path.join(params['logdir'],
+ "ocsp-resp-ca-signed-unknown.der")
+ ocsp_resp_ca_signed(req, ocsp_revoked, "-revoked")
+ ocsp_resp_ca_signed(req, ocsp_unknown, "-unknown")
+
+ with open(ocsp_revoked, "rb") as f:
+ resp_revoked = f.read()
+ with open(ocsp_unknown, "rb") as f:
+ resp_unknown = f.read()
+
+ fd, fn = tempfile.mkstemp()
+ try:
+ # This is not really a valid order of the OCSPResponse items in the
+ # list, but this works for now to verify parsing and processing of
+ # multiple responses.
+ f = os.fdopen(fd, 'wb')
+ f.write(struct.pack(">L", len(resp_unknown))[1:4])
+ f.write(resp_unknown)
+ f.write(struct.pack(">L", len(resp_revoked))[1:4])
+ f.write(resp_revoked)
+ f.write(struct.pack(">L", 0)[1:4])
+ f.write(struct.pack(">L", len(resp_unknown))[1:4])
+ f.write(resp_unknown)
+ f.close()
+
+ params = int_eap_server_params()
+ params["ocsp_stapling_response_multi"] = fn
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever", ocsp=1,
+ wait_connect=False, scan_freq="2412")
+ count = 0
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS",
+ "CTRL-EVENT-EAP-SUCCESS"])
+ if ev is None:
+ raise Exception("Timeout on EAP status")
+ if "CTRL-EVENT-EAP-SUCCESS" in ev:
+ raise Exception("Unexpected EAP-Success")
+ if 'bad certificate status response' in ev:
+ break
+ if 'certificate revoked' in ev:
+ break
+ count = count + 1
+ if count > 10:
+ raise Exception("Unexpected number of EAP status messages")
+ finally:
+ os.unlink(fn)
+
+def test_ap_wpa2_eap_tls_domain_suffix_match_cn_full(dev, apdev):
+ """WPA2-Enterprise using EAP-TLS and domain suffix match (CN)"""
+ check_domain_match_full(dev[0])
+ check_pkcs12_support(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-no-dnsname.pem"
+ params["private_key"] = "auth_serv/server-no-dnsname.key"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever",
+ domain_suffix_match="server3.w1.fi",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_tls_domain_match_cn(dev, apdev):
+ """WPA2-Enterprise using EAP-TLS and domainmatch (CN)"""
+ check_domain_match(dev[0])
+ check_pkcs12_support(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-no-dnsname.pem"
+ params["private_key"] = "auth_serv/server-no-dnsname.key"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever",
+ domain_match="server3.w1.fi",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_tls_domain_suffix_match_cn(dev, apdev):
+ """WPA2-Enterprise using EAP-TLS and domain suffix match (CN)"""
+ check_domain_match_full(dev[0])
+ check_pkcs12_support(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-no-dnsname.pem"
+ params["private_key"] = "auth_serv/server-no-dnsname.key"
+ hostapd.add_ap(apdev[0], params)
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever",
+ domain_suffix_match="w1.fi",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_tls_domain_suffix_mismatch_cn(dev, apdev):
+ """WPA2-Enterprise using EAP-TLS and domain suffix mismatch (CN)"""
+ check_domain_suffix_match(dev[0])
+ check_pkcs12_support(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-no-dnsname.pem"
+ params["private_key"] = "auth_serv/server-no-dnsname.key"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever",
+ domain_suffix_match="example.com",
+ wait_connect=False,
+ scan_freq="2412")
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever",
+ domain_suffix_match="erver3.w1.fi",
+ wait_connect=False,
+ scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+ ev = dev[1].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report (2)")
+
+def test_ap_wpa2_eap_tls_domain_mismatch_cn(dev, apdev):
+ """WPA2-Enterprise using EAP-TLS and domain mismatch (CN)"""
+ check_domain_match(dev[0])
+ check_pkcs12_support(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-no-dnsname.pem"
+ params["private_key"] = "auth_serv/server-no-dnsname.key"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever",
+ domain_match="example.com",
+ wait_connect=False,
+ scan_freq="2412")
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ private_key="auth_serv/user.pkcs12",
+ private_key_passwd="whatever",
+ domain_match="w1.fi",
+ wait_connect=False,
+ scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+ ev = dev[1].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report (2)")
+
+def test_ap_wpa2_eap_ttls_expired_cert(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and expired certificate"""
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-expired.pem"
+ params["private_key"] = "auth_serv/server-expired.key"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="mschap user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ wait_connect=False,
+ scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"])
+ if ev is None:
+ raise Exception("Timeout on EAP certificate error report")
+ if "reason=4" not in ev or "certificate has expired" not in ev:
+ raise Exception("Unexpected failure reason: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_ttls_ignore_expired_cert(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and ignore certificate expiration"""
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-expired.pem"
+ params["private_key"] = "auth_serv/server-expired.key"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="mschap user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ phase1="tls_disable_time_checks=1",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_ttls_long_duration(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and long certificate duration"""
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-long-duration.pem"
+ params["private_key"] = "auth_serv/server-long-duration.key"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="mschap user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_ttls_server_cert_eku_client(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and server cert with client EKU"""
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-eku-client.pem"
+ params["private_key"] = "auth_serv/server-eku-client.key"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="mschap user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ wait_connect=False,
+ scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("Timeout on EAP failure report")
+
+def test_ap_wpa2_eap_ttls_server_cert_eku_client_server(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and server cert with client and server EKU"""
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-eku-client-server.pem"
+ params["private_key"] = "auth_serv/server-eku-client-server.key"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="mschap user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_ttls_server_pkcs12(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and server PKCS#12 file"""
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ del params["server_cert"]
+ params["private_key"] = "auth_serv/server.pkcs12"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="mschap user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_ttls_server_pkcs12_extra(dev, apdev):
+ """EAP-TTLS and server PKCS#12 file with extra certs"""
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ del params["server_cert"]
+ params["private_key"] = "auth_serv/server-extra.pkcs12"
+ params["private_key_passwd"] = "whatever"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="mschap user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_ttls_dh_params(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/CHAP and setting DH params"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.der", phase2="auth=PAP",
+ dh_file="auth_serv/dh.conf")
+
+def test_ap_wpa2_eap_ttls_dh_params_dsa(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS and setting DH params (DSA)"""
+ check_dh_dsa_support(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.der", phase2="auth=PAP",
+ dh_file="auth_serv/dsaparam.pem")
+
+def test_ap_wpa2_eap_ttls_dh_params_not_found(dev, apdev):
+ """EAP-TTLS and DH params file not found"""
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="mschap user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ dh_file="auth_serv/dh-no-such-file.conf",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("EAP failure timed out")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_ttls_dh_params_invalid(dev, apdev):
+ """EAP-TTLS and invalid DH params file"""
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="mschap user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ dh_file="auth_serv/ca.pem",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("EAP failure timed out")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_ttls_dh_params_blob(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS/CHAP and setting DH params from blob"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dh = read_pem("auth_serv/dh2.conf")
+ if "OK" not in dev[0].request("SET blob dhparams " + binascii.hexlify(dh).decode()):
+ raise Exception("Could not set dhparams blob")
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.der", phase2="auth=PAP",
+ dh_file="blob://dhparams")
+
+def test_ap_wpa2_eap_ttls_dh_params_server(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and alternative server dhparams"""
+ params = int_eap_server_params()
+ params["dh_file"] = "auth_serv/dh2.conf"
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.der", phase2="auth=PAP")
+
+def test_ap_wpa2_eap_ttls_dh_params_dsa_server(dev, apdev):
+ """WPA2-Enterprise using EAP-TTLS and alternative server dhparams (DSA)"""
+ params = int_eap_server_params()
+ params["dh_file"] = "auth_serv/dsaparam.pem"
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.der", phase2="auth=PAP")
+
+def test_ap_wpa2_eap_ttls_dh_params_not_found(dev, apdev):
+ """EAP-TLS server and dhparams file not found"""
+ params = int_eap_server_params()
+ params["dh_file"] = "auth_serv/dh-no-such-file.conf"
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Invalid configuration accepted")
+
+def test_ap_wpa2_eap_ttls_dh_params_invalid(dev, apdev):
+ """EAP-TLS server and invalid dhparams file"""
+ params = int_eap_server_params()
+ params["dh_file"] = "auth_serv/ca.pem"
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Invalid configuration accepted")
+
+def test_ap_wpa2_eap_reauth(dev, apdev):
+ """WPA2-Enterprise and Authenticator forcing reauthentication"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['eap_reauth_period'] = '2'
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+ logger.info("Wait for reauthentication")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on reauthentication")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on reauthentication")
+ for i in range(0, 20):
+ state = dev[0].get_status_field("wpa_state")
+ if state == "COMPLETED":
+ break
+ time.sleep(0.1)
+ if state != "COMPLETED":
+ raise Exception("Reauthentication did not complete")
+
+def test_ap_wpa2_eap_reauth_ptk_rekey_blocked_ap(dev, apdev):
+ """WPA2-Enterprise and Authenticator forcing reauthentication with PTK rekey blocked on AP"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['eap_reauth_period'] = '2'
+ params['wpa_deny_ptk0_rekey'] = '2'
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+ logger.info("Wait for disconnect due to reauth")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on reauthentication")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Reauthentication without disconnect")
+
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=1)
+ if ev is None:
+ raise Exception("Timeout on reconnect")
+
+def test_ap_wpa2_eap_reauth_ptk_rekey_blocked_sta(dev, apdev):
+ """WPA2-Enterprise and Authenticator forcing reauthentication with PTK rekey blocked on station"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['eap_reauth_period'] = '2'
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wpa_deny_ptk0_rekey="2")
+ logger.info("Wait for disconnect due to reauth")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on reauthentication")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Reauthentication without disconnect")
+
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=1)
+ if ev is None:
+ raise Exception("Timeout on reconnect")
+
+def test_ap_wpa2_eap_request_identity_message(dev, apdev):
+ """Optional displayable message in EAP Request-Identity"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['eap_message'] = 'hello\\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com'
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+
+def test_ap_wpa2_eap_sim_aka_result_ind(dev, apdev):
+ """WPA2-Enterprise using EAP-SIM/AKA and protected result indication"""
+ check_hlr_auc_gw_support()
+ params = int_eap_server_params()
+ params['eap_sim_db'] = "unix:/tmp/hlr_auc_gw.sock"
+ params['eap_sim_aka_result_ind'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ eap_connect(dev[0], hapd, "SIM", "1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ phase1="result_ind=1")
+ eap_reauth(dev[0], "SIM")
+ eap_connect(dev[1], hapd, "SIM", "1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "AKA", "0232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ phase1="result_ind=1")
+ eap_reauth(dev[0], "AKA")
+ eap_connect(dev[1], hapd, "AKA", "0232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+
+ eap_connect(dev[0], hapd, "AKA'", "6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+ phase1="result_ind=1")
+ eap_reauth(dev[0], "AKA'")
+ eap_connect(dev[1], hapd, "AKA'", "6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+
+def test_ap_wpa2_eap_sim_zero_db_timeout(dev, apdev):
+ """WPA2-Enterprise using EAP-SIM with zero database timeout"""
+ check_hlr_auc_gw_support()
+ params = int_eap_server_params()
+ params['eap_sim_db'] = "unix:/tmp/hlr_auc_gw.sock"
+ params['eap_sim_db_timeout'] = "0"
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # Run multiple iterations to make it more likely to hit the case where the
+ # DB request times out and response is lost.
+ for i in range(20):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="SIM",
+ identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"],
+ timeout=15)
+ if ev is None:
+ raise Exception("No connection result")
+ dev[0].request("REMOVE_NETWORK all")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ break
+ dev[0].wait_disconnected()
+ hapd.ping()
+
+def test_ap_wpa2_eap_too_many_roundtrips(dev, apdev):
+ """WPA2-Enterprise connection resulting in too many EAP roundtrips"""
+ skip_with_fips(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="TTLS", identity="mschap user",
+ wait_connect=False, scan_freq="2412", ieee80211w="1",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ fragment_size="4")
+ ev = dev[0].wait_event(["EAP: more than",
+ "CTRL-EVENT-EAP-SUCCESS"], timeout=20)
+ if ev is None or "EAP: more than" not in ev:
+ raise Exception("EAP roundtrip limit not reached")
+
+def test_ap_wpa2_eap_too_many_roundtrips_server(dev, apdev):
+ """WPA2-Enterprise connection resulting in too many EAP roundtrips (server)"""
+ run_ap_wpa2_eap_too_many_roundtrips_server(dev, apdev, 10, 10)
+
+def test_ap_wpa2_eap_too_many_roundtrips_server2(dev, apdev):
+ """WPA2-Enterprise connection resulting in too many EAP roundtrips (server)"""
+ run_ap_wpa2_eap_too_many_roundtrips_server(dev, apdev, 10, 1)
+
+def run_ap_wpa2_eap_too_many_roundtrips_server(dev, apdev, max_rounds,
+ max_rounds_short):
+ skip_with_fips(dev[0])
+ params = int_eap_server_params()
+ params["max_auth_rounds"] = str(max_rounds)
+ params["max_auth_rounds_short"] = str(max_rounds_short)
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="TTLS", identity="mschap user",
+ wait_connect=False, scan_freq="2412", ieee80211w="1",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ fragment_size="4")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None or "SUCCESS" in ev:
+ raise Exception("EAP roundtrip limit not reported")
+
+def test_ap_wpa2_eap_expanded_nak(dev, apdev):
+ """WPA2-Enterprise connection with EAP resulting in expanded NAK"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="PSK", identity="vendor-test",
+ password_hex="ff23456789abcdef0123456789abcdef",
+ wait_connect=False)
+
+ found = False
+ for i in range(0, 5):
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+ if "refuse proposed method" in ev:
+ found = True
+ break
+ if not found:
+ raise Exception("Unexpected EAP status: " + ev)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"])
+ if ev is None:
+ raise Exception("EAP failure timed out")
+
+def test_ap_wpa2_eap_sql(dev, apdev, params):
+ """WPA2-Enterprise connection using SQLite for user DB"""
+ skip_with_fips(dev[0])
+ try:
+ import sqlite3
+ except ImportError:
+ raise HwsimSkip("No sqlite3 module available")
+ dbfile = os.path.join(params['logdir'], "eap-user.db")
+ try:
+ os.remove(dbfile)
+ except:
+ pass
+ con = sqlite3.connect(dbfile)
+ with con:
+ cur = con.cursor()
+ cur.execute("CREATE TABLE users(identity TEXT PRIMARY KEY, methods TEXT, password TEXT, remediation TEXT, phase2 INTEGER)")
+ cur.execute("CREATE TABLE wildcards(identity TEXT PRIMARY KEY, methods TEXT)")
+ cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-pap','TTLS-PAP','password',1)")
+ cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-chap','TTLS-CHAP','password',1)")
+ cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-mschap','TTLS-MSCHAP','password',1)")
+ cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-mschapv2','TTLS-MSCHAPV2','password',1)")
+ cur.execute("INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS')")
+ cur.execute("CREATE TABLE authlog(timestamp TEXT, session TEXT, nas_ip TEXT, username TEXT, note TEXT)")
+
+ try:
+ params = int_eap_server_params()
+ params["eap_user_file"] = "sqlite:" + dbfile
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "user-mschapv2",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+ dev[0].request("REMOVE_NETWORK all")
+ eap_connect(dev[1], hapd, "TTLS", "user-mschap",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP")
+ dev[1].request("REMOVE_NETWORK all")
+ eap_connect(dev[0], hapd, "TTLS", "user-chap",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=CHAP")
+ eap_connect(dev[1], hapd, "TTLS", "user-pap",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[1].wait_disconnected()
+ hapd.disable()
+ hapd.enable()
+ eap_connect(dev[0], hapd, "TTLS", "user-mschapv2",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+ finally:
+ os.remove(dbfile)
+
+def test_ap_wpa2_eap_non_ascii_identity(dev, apdev):
+ """WPA2-Enterprise connection attempt using non-ASCII identity"""
+ params = int_eap_server_params()
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="\x80", password="password", wait_connect=False)
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="a\x80", password="password", wait_connect=False)
+ for i in range(0, 2):
+ ev = dev[i].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+ ev = dev[i].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
+ if ev is None:
+ raise Exception("EAP method selection timed out")
+
+def test_ap_wpa2_eap_non_ascii_identity2(dev, apdev):
+ """WPA2-Enterprise connection attempt using non-ASCII identity"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="\x80", password="password", wait_connect=False)
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="a\x80", password="password", wait_connect=False)
+ for i in range(0, 2):
+ ev = dev[i].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=16)
+ if ev is None:
+ raise Exception("Association and EAP start timed out")
+ ev = dev[i].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=10)
+ if ev is None:
+ raise Exception("EAP method selection timed out")
+
+def test_openssl_cipher_suite_config_wpas(dev, apdev):
+ """OpenSSL cipher suite configuration on wpa_supplicant"""
+ tls = dev[0].request("GET tls_library")
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("TLS library is not OpenSSL: " + tls)
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ openssl_ciphers="AES128",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+ eap_connect(dev[1], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ openssl_ciphers="EXPORT",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ expect_failure=True, maybe_local_error=True)
+ dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password",
+ openssl_ciphers="FOO",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ wait_connect=False)
+ ev = dev[2].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP failure after invalid openssl_ciphers not reported")
+ dev[2].request("DISCONNECT")
+
+def test_openssl_cipher_suite_config_hapd(dev, apdev):
+ """OpenSSL cipher suite configuration on hostapd"""
+ tls = dev[0].request("GET tls_library")
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("wpa_supplicant TLS library is not OpenSSL: " + tls)
+ params = int_eap_server_params()
+ params['openssl_ciphers'] = "AES256"
+ hapd = hostapd.add_ap(apdev[0], params)
+ tls = hapd.request("GET tls_library")
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("hostapd TLS library is not OpenSSL: " + tls)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+ eap_connect(dev[1], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ openssl_ciphers="AES128",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ expect_failure=True)
+ eap_connect(dev[2], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ openssl_ciphers="HIGH:!ADH",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+
+ params['openssl_ciphers'] = "FOO"
+ hapd2 = hostapd.add_ap(apdev[1], params, no_enable=True)
+ if "FAIL" not in hapd2.request("ENABLE"):
+ if "run=OpenSSL 1.1.1" in tls:
+ logger.info("Ignore acceptance of an invalid openssl_ciphers value with OpenSSL 1.1.1")
+ else:
+ raise Exception("Invalid openssl_ciphers value accepted")
+
+def test_wpa2_eap_ttls_pap_key_lifetime_in_memory(dev, apdev, params):
+ """Key lifetime in memory with WPA2-Enterprise using EAP-TTLS/PAP"""
+ p = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], p)
+ password = "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25"
+ id = eap_connect(dev[0], hapd, "TTLS", "pap-secret",
+ anonymous_identity="ttls", password=password,
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+ run_eap_key_lifetime_in_memory(dev, params, id, password)
+
+def test_wpa2_eap_peap_gtc_key_lifetime_in_memory(dev, apdev, params):
+ """Key lifetime in memory with WPA2-Enterprise using PEAP/GTC"""
+ p = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], p)
+ password = "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25"
+ id = eap_connect(dev[0], hapd, "PEAP", "user-secret",
+ anonymous_identity="peap", password=password,
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC")
+ run_eap_key_lifetime_in_memory(dev, params, id, password)
+
+def run_eap_key_lifetime_in_memory(dev, params, id, password):
+ pid = find_wpas_process(dev[0])
+
+ # The decrypted copy of GTK is freed only after the CTRL-EVENT-CONNECTED
+ # event has been delivered, so verify that wpa_supplicant has returned to
+ # eloop before reading process memory.
+ time.sleep(1)
+ dev[0].ping()
+ password = password.encode()
+ buf = read_process_memory(pid, password)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].relog()
+ msk = None
+ emsk = None
+ pmk = None
+ ptk = None
+ gtk = None
+ with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+ for l in f.readlines():
+ if "EAP-TTLS: Derived key - hexdump" in l or \
+ "EAP-PEAP: Derived key - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ msk = binascii.unhexlify(val)
+ if "EAP-TTLS: Derived EMSK - hexdump" in l or \
+ "EAP-PEAP: Derived EMSK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ emsk = binascii.unhexlify(val)
+ if "WPA: PMK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ pmk = binascii.unhexlify(val)
+ if "WPA: PTK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ ptk = binascii.unhexlify(val)
+ if "WPA: Group Key - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ gtk = binascii.unhexlify(val)
+ if not msk or not emsk or not pmk or not ptk or not gtk:
+ raise Exception("Could not find keys from debug log")
+ if len(gtk) != 16:
+ raise Exception("Unexpected GTK length")
+
+ kck = ptk[0:16]
+ kek = ptk[16:32]
+ tk = ptk[32:48]
+
+ fname = os.path.join(params['logdir'],
+ 'wpa2_eap_ttls_pap_key_lifetime_in_memory.memctx-')
+
+ logger.info("Checking keys in memory while associated")
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, msk, "MSK")
+ get_key_locations(buf, emsk, "EMSK")
+ if password not in buf:
+ raise HwsimSkip("Password not found while associated")
+ if pmk not in buf:
+ raise HwsimSkip("PMK not found while associated")
+ if kck not in buf:
+ raise Exception("KCK not found while associated")
+ if kek not in buf:
+ raise Exception("KEK not found while associated")
+ #if tk in buf:
+ # raise Exception("TK found from memory")
+
+ logger.info("Checking keys in memory after disassociation")
+ buf = read_process_memory(pid, password)
+
+ # Note: Password is still present in network configuration
+ # Note: PMK is in PMKSA cache and EAP fast re-auth data
+
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, msk, "MSK")
+ get_key_locations(buf, emsk, "EMSK")
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ if gtk in buf:
+ get_key_locations(buf, gtk, "GTK")
+ verify_not_present(buf, gtk, fname, "GTK")
+
+ dev[0].request("PMKSA_FLUSH")
+ dev[0].set_network_quoted(id, "identity", "foo")
+ logger.info("Checking keys in memory after PMKSA cache and EAP fast reauth flush")
+ buf = read_process_memory(pid, password)
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, msk, "MSK")
+ get_key_locations(buf, emsk, "EMSK")
+ verify_not_present(buf, pmk, fname, "PMK")
+
+ dev[0].request("REMOVE_NETWORK all")
+
+ logger.info("Checking keys in memory after network profile removal")
+ buf = read_process_memory(pid, password)
+
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, msk, "MSK")
+ get_key_locations(buf, emsk, "EMSK")
+ verify_not_present(buf, password, fname, "password")
+ verify_not_present(buf, pmk, fname, "PMK")
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ verify_not_present(buf, gtk, fname, "GTK")
+ verify_not_present(buf, msk, fname, "MSK")
+ verify_not_present(buf, emsk, fname, "EMSK")
+
+def test_ap_wpa2_eap_unexpected_wep_eapol_key(dev, apdev):
+ """WPA2-Enterprise connection and unexpected WEP EAPOL-Key"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+
+ # Send unexpected WEP EAPOL-Key; this gets dropped
+ res = dev[0].request("EAPOL_RX " + bssid + " 0203002c0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000")
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+def test_ap_wpa2_eap_in_bridge(dev, apdev):
+ """WPA2-EAP and wpas interface in a bridge"""
+ br_ifname = 'sta-br0'
+ ifname = 'wlan5'
+ try:
+ _test_ap_wpa2_eap_in_bridge(dev, apdev)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
+ subprocess.call(['brctl', 'delif', br_ifname, ifname])
+ subprocess.call(['brctl', 'delbr', br_ifname])
+ subprocess.call(['iw', ifname, 'set', '4addr', 'off'])
+
+def _test_ap_wpa2_eap_in_bridge(dev, apdev):
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ br_ifname = 'sta-br0'
+ ifname = 'wlan5'
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ subprocess.call(['brctl', 'addbr', br_ifname])
+ subprocess.call(['brctl', 'setfd', br_ifname, '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+ subprocess.call(['iw', ifname, 'set', '4addr', 'on'])
+ subprocess.check_call(['brctl', 'addif', br_ifname, ifname])
+ wpas.interface_add(ifname, br_ifname=br_ifname)
+ wpas.dump_monitor()
+
+ id = eap_connect(wpas, hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+ wpas.dump_monitor()
+ eap_reauth(wpas, "PAX")
+ wpas.dump_monitor()
+ # Try again as a regression test for packet socket workaround
+ eap_reauth(wpas, "PAX")
+ wpas.dump_monitor()
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+ wpas.request("RECONNECT")
+ wpas.wait_connected()
+ wpas.dump_monitor()
+
+def test_ap_wpa2_eap_session_ticket(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS and TLS session ticket enabled"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "WPA-EAP":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="tls_disable_session_ticket=0", phase2="auth=PAP")
+ eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_no_workaround(dev, apdev):
+ """WPA2-Enterprise connection using EAP-TTLS and eap_workaround=0"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "WPA-EAP":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", eap_workaround='0',
+ phase2="auth=PAP")
+ eap_reauth(dev[0], "TTLS")
+
+def test_ap_wpa2_eap_tls_check_crl(dev, apdev):
+ """EAP-TLS and server checking CRL"""
+ params = int_eap_server_params()
+ params['check_crl'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # check_crl=1 and no CRL available --> reject connection
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key", expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+
+ hapd.disable()
+ hapd.set("ca_cert", "auth_serv/ca-and-crl.pem")
+ hapd.enable()
+
+ # check_crl=1 and valid CRL --> accept
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ dev[0].request("REMOVE_NETWORK all")
+
+ hapd.disable()
+ hapd.set("check_crl", "2")
+ hapd.enable()
+
+ # check_crl=2 and valid CRL --> accept
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_tls_check_crl_not_strict(dev, apdev):
+ """EAP-TLS and server checking CRL with check_crl_strict=0"""
+ params = int_eap_server_params()
+ params['check_crl'] = '1'
+ params['ca_cert'] = "auth_serv/ca-and-crl-expired.pem"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # check_crl_strict=1 and expired CRL --> reject connection
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key", expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+
+ hapd.disable()
+ hapd.set("check_crl_strict", "0")
+ hapd.enable()
+
+ # check_crl_strict=0 --> accept
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_wpa2_eap_tls_crl_reload(dev, apdev, params):
+ """EAP-TLS and server reloading CRL from ca_cert"""
+ ca_cert = os.path.join(params['logdir'],
+ "ap_wpa2_eap_tls_crl_reload.ca_cert")
+ with open('auth_serv/ca.pem', 'r') as f:
+ only_cert = f.read()
+ with open('auth_serv/ca-and-crl.pem', 'r') as f:
+ cert_and_crl = f.read()
+ with open(ca_cert, 'w') as f:
+ f.write(only_cert)
+ params = int_eap_server_params()
+ params['ca_cert'] = ca_cert
+ params['check_crl'] = '1'
+ params['crl_reload_interval'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # check_crl=1 and no CRL available --> reject connection
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key", expect_failure=True)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ with open(ca_cert, 'w') as f:
+ f.write(cert_and_crl)
+ time.sleep(1)
+
+ # check_crl=1 and valid CRL --> accept
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_tls_check_cert_subject(dev, apdev):
+ """EAP-TLS and server checking client subject name"""
+ params = int_eap_server_params()
+ params['check_cert_subject'] = 'C=FI/O=w1.fi/CN=Test User'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_check_cert_subject_support(hapd)
+
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+
+def test_ap_wpa2_eap_tls_check_cert_subject_neg(dev, apdev):
+ """EAP-TLS and server checking client subject name (negative)"""
+ params = int_eap_server_params()
+ params['check_cert_subject'] = 'C=FI/O=example'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_check_cert_subject_support(hapd)
+
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key", expect_failure=True)
+
+def test_ap_wpa2_eap_tls_oom(dev, apdev):
+ """EAP-TLS and OOM"""
+ check_subject_match_support(dev[0])
+ check_altsubject_match_support(dev[0])
+ check_domain_match(dev[0])
+ check_domain_match_full(dev[0])
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+
+ tests = [(1, "tls_connection_set_subject_match"),
+ (2, "tls_connection_set_subject_match"),
+ (3, "tls_connection_set_subject_match"),
+ (4, "tls_connection_set_subject_match")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ subject_match="/C=FI/O=w1.fi/CN=server.w1.fi",
+ altsubject_match="EMAIL:noone@example.com;DNS:server.w1.fi;URI:http://example.com/",
+ domain_suffix_match="server.w1.fi",
+ domain_match="server.w1.fi",
+ wait_connect=False, scan_freq="2412")
+ # TLS parameter configuration error results in CTRL-REQ-PASSPHRASE
+ ev = dev[0].wait_event(["CTRL-REQ-PASSPHRASE"], timeout=5)
+ if ev is None:
+ raise Exception("No passphrase request")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_tls_macacl(dev, apdev):
+ """WPA2-Enterprise connection using MAC ACL"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params["macaddr_acl"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[1], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+
+def test_ap_wpa2_eap_oom(dev, apdev):
+ """EAP server and OOM"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+
+ with alloc_fail(hapd, 1, "eapol_auth_alloc"):
+ # The first attempt fails, but STA will send EAPOL-Start to retry and
+ # that succeeds.
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ scan_freq="2412")
+
+def check_tls_ver(dev, hapd, phase1, expected):
+ eap_connect(dev, hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ phase1=phase1)
+ ver = dev.get_status_field("eap_tls_version")
+ if ver != expected:
+ raise Exception("Unexpected TLS version (expected %s): %s" % (expected, ver))
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_ap_wpa2_eap_tls_versions(dev, apdev):
+ """EAP-TLS and TLS version configuration"""
+ params = {"ssid": "test-wpa2-eap",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP",
+ "ieee8021x": "1",
+ "eap_server": "1",
+ "tls_flags": "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][ENABLE-TLSv1.3]",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tls = dev[0].request("GET tls_library")
+ if tls.startswith("OpenSSL"):
+ if "build=OpenSSL 1.0.1" not in tls and "run=OpenSSL 1.0.1" not in tls:
+ check_tls_ver(dev[0], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
+ "TLSv1.2")
+ if tls.startswith("wolfSSL"):
+ if ("build=3.10.0" in tls and "run=3.10.0" in tls) or \
+ ("build=3.13.0" in tls and "run=3.13.0" in tls):
+ check_tls_ver(dev[0], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1",
+ "TLSv1.2")
+ elif tls.startswith("internal"):
+ check_tls_ver(dev[0], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1", "TLSv1.2")
+ check_tls_ver(dev[1], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=1", "TLSv1.1")
+ check_tls_ver(dev[2], hapd,
+ "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1", "TLSv1")
+ if "run=OpenSSL 1.1.1" in tls:
+ check_tls_ver(dev[0], hapd,
+ "tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0", "TLSv1.3")
+
+def test_ap_wpa2_eap_tls_versions_server(dev, apdev):
+ """EAP-TLS and TLS version configuration on server side"""
+ params = {"ssid": "test-wpa2-eap",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP",
+ "ieee8021x": "1",
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tests = [("TLSv1", "[ENABLE-TLSv1.0][DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
+ ("TLSv1.1", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"),
+ ("TLSv1.2", "[ENABLE-TLSv1.0][ENABLE-TLSv1.1][ENABLE-TLSv1.2][DISABLE-TLSv1.3]")]
+ for exp, flags in tests:
+ hapd.disable()
+ hapd.set("tls_flags", flags)
+ hapd.enable()
+ check_tls_ver(dev[0], hapd, "tls_disable_tlsv1_0=0 tls_disable_tlsv1_1=0 tls_disable_tlsv1_2=0 tls_disable_tlsv1_3=0", exp)
+
+def test_ap_wpa2_eap_tls_13(dev, apdev):
+ """EAP-TLS and TLS 1.3"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tls = dev[0].request("GET tls_library")
+ if "run=OpenSSL 1.1.1" not in tls:
+ raise HwsimSkip("TLS v1.3 not supported")
+ id = eap_connect(dev[0], hapd, "TLS", "tls user",
+ ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0")
+ ver = dev[0].get_status_field("eap_tls_version")
+ if ver != "TLSv1.3":
+ raise Exception("Unexpected TLS version")
+
+ eap_reauth(dev[0], "TLS")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("PMKSA_FLUSH")
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+def test_ap_wpa2_eap_ttls_13(dev, apdev):
+ """EAP-TTLS and TLS 1.3"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tls = dev[0].request("GET tls_library")
+ if "run=OpenSSL 1.1.1" not in tls:
+ raise HwsimSkip("TLS v1.3 not supported")
+ id = eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0",
+ phase2="auth=PAP")
+ ver = dev[0].get_status_field("eap_tls_version")
+ if ver != "TLSv1.3":
+ raise Exception("Unexpected TLS version")
+
+ eap_reauth(dev[0], "TTLS")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("PMKSA_FLUSH")
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+def test_ap_wpa2_eap_peap_13(dev, apdev):
+ """PEAP and TLS 1.3"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tls = dev[0].request("GET tls_library")
+ if "run=OpenSSL 1.1.1" not in tls:
+ raise HwsimSkip("TLS v1.3 not supported")
+ id = eap_connect(dev[0], hapd, "PEAP", "user",
+ anonymous_identity="peap", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0",
+ phase2="auth=MSCHAPV2")
+ ver = dev[0].get_status_field("eap_tls_version")
+ if ver != "TLSv1.3":
+ raise Exception("Unexpected TLS version")
+
+ eap_reauth(dev[0], "PEAP")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("PMKSA_FLUSH")
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+def test_ap_wpa2_eap_tls_13_ec(dev, apdev):
+ """EAP-TLS and TLS 1.3 (EC certificates)"""
+ params = {"ssid": "test-wpa2-eap",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP",
+ "ieee8021x": "1",
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ec-ca.pem",
+ "server_cert": "auth_serv/ec-server.pem",
+ "private_key": "auth_serv/ec-server.key",
+ "tls_flags": "[ENABLE-TLSv1.3]"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ tls = hapd.request("GET tls_library")
+ if "run=OpenSSL 1.1.1" not in tls:
+ raise HwsimSkip("TLS v1.3 not supported")
+
+ tls = dev[0].request("GET tls_library")
+ if "run=OpenSSL 1.1.1" not in tls:
+ raise HwsimSkip("TLS v1.3 not supported")
+ id = eap_connect(dev[0], hapd, "TLS", "tls user",
+ ca_cert="auth_serv/ec-ca.pem",
+ client_cert="auth_serv/ec-user.pem",
+ private_key="auth_serv/ec-user.key",
+ phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0")
+ ver = dev[0].get_status_field("eap_tls_version")
+ if ver != "TLSv1.3":
+ raise Exception("Unexpected TLS version")
+
+def test_ap_wpa2_eap_tls_rsa_and_ec(dev, apdev, params):
+ """EAP-TLS and both RSA and EC sertificates certificates"""
+ check_ec_support(dev[0])
+ ca = os.path.join(params['logdir'], "ap_wpa2_eap_tls_rsa_and_ec.ca.pem")
+ with open(ca, "w") as f:
+ with open("auth_serv/ca.pem", "r") as f2:
+ f.write(f2.read())
+ with open("auth_serv/ec-ca.pem", "r") as f2:
+ f.write(f2.read())
+ params = {"ssid": "test-wpa2-eap",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP",
+ "ieee8021x": "1",
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": ca,
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key",
+ "server_cert2": "auth_serv/ec-server.pem",
+ "private_key2": "auth_serv/ec-server.key"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ eap_connect(dev[0], hapd, "TLS", "tls user",
+ ca_cert="auth_serv/ec-ca.pem",
+ client_cert="auth_serv/ec-user.pem",
+ private_key="auth_serv/ec-user.key")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ # TODO: Make wpa_supplicant automatically filter out cipher suites that
+ # would require ECDH/ECDSA keys when those are not configured in the
+ # selected client certificate. And for no-client-cert case, deprioritize
+ # those cipher suites based on configured ca_cert value so that the most
+ # likely to work cipher suites are selected by the server. Only do these
+ # when an explicit openssl_ciphers parameter is not set.
+ eap_connect(dev[1], hapd, "TLS", "tls user",
+ openssl_ciphers="DEFAULT:-aECDH:-aECDSA",
+ ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[1].wait_disconnected()
+
+def test_ap_wpa2_eap_tls_ec_and_rsa(dev, apdev, params):
+ """EAP-TLS and both EC and RSA sertificates certificates"""
+ check_ec_support(dev[0])
+ ca = os.path.join(params['logdir'], "ap_wpa2_eap_tls_ec_and_rsa.ca.pem")
+ with open(ca, "w") as f:
+ with open("auth_serv/ca.pem", "r") as f2:
+ f.write(f2.read())
+ with open("auth_serv/ec-ca.pem", "r") as f2:
+ f.write(f2.read())
+ params = {"ssid": "test-wpa2-eap",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP",
+ "ieee8021x": "1",
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": ca,
+ "private_key2": "auth_serv/server-extra.pkcs12",
+ "private_key_passwd2": "whatever",
+ "server_cert": "auth_serv/ec-server.pem",
+ "private_key": "auth_serv/ec-server.key"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ eap_connect(dev[0], hapd, "TLS", "tls user",
+ ca_cert="auth_serv/ec-ca.pem",
+ client_cert="auth_serv/ec-user.pem",
+ private_key="auth_serv/ec-user.key")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ # TODO: Make wpa_supplicant automatically filter out cipher suites that
+ # would require ECDH/ECDSA keys when those are not configured in the
+ # selected client certificate. And for no-client-cert case, deprioritize
+ # those cipher suites based on configured ca_cert value so that the most
+ # likely to work cipher suites are selected by the server. Only do these
+ # when an explicit openssl_ciphers parameter is not set.
+ eap_connect(dev[1], hapd, "TLS", "tls user",
+ openssl_ciphers="DEFAULT:-aECDH:-aECDSA",
+ ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[1].wait_disconnected()
+
+def test_rsn_ie_proto_eap_sta(dev, apdev):
+ """RSN element protocol testing for EAP cases on STA side"""
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ # This is the RSN element used normally by hostapd
+ params['own_ie_override'] = '30140100000fac040100000fac040100000fac010c00'
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ tests = [('No RSN Capabilities field',
+ '30120100000fac040100000fac040100000fac01'),
+ ('No AKM Suite fields',
+ '300c0100000fac040100000fac04'),
+ ('No Pairwise Cipher Suite fields',
+ '30060100000fac04'),
+ ('No Group Data Cipher Suite field',
+ '30020100')]
+ for txt, ie in tests:
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ logger.info(txt)
+ hapd.disable()
+ hapd.set('own_ie_override', ie)
+ hapd.enable()
+ dev[0].request("BSS_FLUSH 0")
+ dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def check_tls_session_resumption_capa(dev, hapd):
+ tls = hapd.request("GET tls_library")
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("hostapd TLS library is not OpenSSL or wolfSSL: " + tls)
+
+ tls = dev.request("GET tls_library")
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("Session resumption not supported with this TLS library: " + tls)
+
+def test_eap_ttls_pap_session_resumption(dev, apdev):
+ """EAP-TTLS/PAP session resumption"""
+ params = int_eap_server_params()
+ params['tls_session_lifetime'] = '60'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_tls_session_resumption_capa(dev[0], hapd)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", eap_workaround='0',
+ phase2="auth=PAP")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '1':
+ raise Exception("Session resumption not used on the second connection")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_eap_ttls_chap_session_resumption(dev, apdev):
+ """EAP-TTLS/CHAP session resumption"""
+ params = int_eap_server_params()
+ params['tls_session_lifetime'] = '60'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_tls_session_resumption_capa(dev[0], hapd)
+ eap_connect(dev[0], hapd, "TTLS", "chap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.der", phase2="auth=CHAP")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '1':
+ raise Exception("Session resumption not used on the second connection")
+
+def test_eap_ttls_mschap_session_resumption(dev, apdev):
+ """EAP-TTLS/MSCHAP session resumption"""
+ check_domain_suffix_match(dev[0])
+ params = int_eap_server_params()
+ params['tls_session_lifetime'] = '60'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_tls_session_resumption_capa(dev[0], hapd)
+ eap_connect(dev[0], hapd, "TTLS", "mschap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ domain_suffix_match="server.w1.fi")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '1':
+ raise Exception("Session resumption not used on the second connection")
+
+def test_eap_ttls_mschapv2_session_resumption(dev, apdev):
+ """EAP-TTLS/MSCHAPv2 session resumption"""
+ check_domain_suffix_match(dev[0])
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_eap_server_params()
+ params['tls_session_lifetime'] = '60'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_tls_session_resumption_capa(dev[0], hapd)
+ eap_connect(dev[0], hapd, "TTLS", "DOMAIN\mschapv2 user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ domain_suffix_match="server.w1.fi")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '1':
+ raise Exception("Session resumption not used on the second connection")
+
+def test_eap_ttls_eap_gtc_session_resumption(dev, apdev):
+ """EAP-TTLS/EAP-GTC session resumption"""
+ params = int_eap_server_params()
+ params['tls_session_lifetime'] = '60'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_tls_session_resumption_capa(dev[0], hapd)
+ eap_connect(dev[0], hapd, "TTLS", "user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '1':
+ raise Exception("Session resumption not used on the second connection")
+
+def test_eap_ttls_no_session_resumption(dev, apdev):
+ """EAP-TTLS session resumption disabled on server"""
+ params = int_eap_server_params()
+ params['tls_session_lifetime'] = '0'
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "pap user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", eap_workaround='0',
+ phase2="auth=PAP")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the second connection")
+
+def test_eap_peap_session_resumption(dev, apdev):
+ """EAP-PEAP session resumption"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_eap_server_params()
+ params['tls_session_lifetime'] = '60'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_tls_session_resumption_capa(dev[0], hapd)
+ eap_connect(dev[0], hapd, "PEAP", "user",
+ anonymous_identity="peap", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '1':
+ raise Exception("Session resumption not used on the second connection")
+
+def test_eap_peap_session_resumption_crypto_binding(dev, apdev):
+ """EAP-PEAP session resumption with crypto binding"""
+ params = int_eap_server_params()
+ params['tls_session_lifetime'] = '60'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_tls_session_resumption_capa(dev[0], hapd)
+ eap_connect(dev[0], hapd, "PEAP", "user",
+ anonymous_identity="peap", password="password",
+ phase1="peapver=0 crypto_binding=2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '1':
+ raise Exception("Session resumption not used on the second connection")
+
+def test_eap_peap_no_session_resumption(dev, apdev):
+ """EAP-PEAP session resumption disabled on server"""
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PEAP", "user",
+ anonymous_identity="peap", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the second connection")
+
+def test_eap_tls_session_resumption(dev, apdev):
+ """EAP-TLS session resumption"""
+ params = int_eap_server_params()
+ params['tls_session_lifetime'] = '60'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_tls_session_resumption_capa(dev[0], hapd)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '1':
+ raise Exception("Session resumption not used on the second connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '1':
+ raise Exception("Session resumption not used on the third connection")
+
+def test_eap_tls_session_resumption_expiration(dev, apdev):
+ """EAP-TLS session resumption"""
+ params = int_eap_server_params()
+ params['tls_session_lifetime'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_tls_session_resumption_capa(dev[0], hapd)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ # Allow multiple attempts since OpenSSL may not expire the cached entry
+ # immediately.
+ for i in range(10):
+ time.sleep(1.2)
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") == '0':
+ break
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Session resumption used after lifetime expiration")
+
+def test_eap_tls_no_session_resumption(dev, apdev):
+ """EAP-TLS session resumption disabled on server"""
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the second connection")
+
+def test_eap_tls_session_resumption_radius(dev, apdev):
+ """EAP-TLS session resumption (RADIUS)"""
+ params = {"ssid": "as", "beacon_int": "2000",
+ "radius_server_clients": "auth_serv/radius_clients.conf",
+ "radius_server_auth_port": '18128',
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key",
+ "tls_session_lifetime": "60"}
+ authsrv = hostapd.add_ap(apdev[1], params)
+ check_tls_session_resumption_capa(dev[0], authsrv)
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18128"
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '1':
+ raise Exception("Session resumption not used on the second connection")
+
+def test_eap_tls_no_session_resumption_radius(dev, apdev):
+ """EAP-TLS session resumption disabled (RADIUS)"""
+ params = {"ssid": "as", "beacon_int": "2000",
+ "radius_server_clients": "auth_serv/radius_clients.conf",
+ "radius_server_auth_port": '18128',
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key",
+ "tls_session_lifetime": "0"}
+ hostapd.add_ap(apdev[1], params)
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18128"
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the first connection")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("Key handshake with the AP timed out")
+ if dev[0].get_status_field("tls_session_reused") != '0':
+ raise Exception("Unexpected session resumption on the second connection")
+
+def test_eap_mschapv2_errors(dev, apdev):
+ """EAP-MSCHAPv2 error cases"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ check_eap_capa(dev[0], "FAST")
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="MSCHAPV2",
+ identity="phase1-user", password="password",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "hash_nt_password_hash;mschapv2_derive_response"),
+ (1, "nt_password_hash;mschapv2_derive_response"),
+ (1, "nt_password_hash;=mschapv2_derive_response"),
+ (1, "generate_nt_response;mschapv2_derive_response"),
+ (1, "generate_authenticator_response;mschapv2_derive_response"),
+ (1, "nt_password_hash;=mschapv2_derive_response"),
+ (1, "get_master_key;mschapv2_derive_response"),
+ (1, "os_get_random;eap_mschapv2_challenge_reply")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="MSCHAPV2",
+ identity="phase1-user", password="password",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "hash_nt_password_hash;mschapv2_derive_response"),
+ (1, "hash_nt_password_hash;=mschapv2_derive_response"),
+ (1, "generate_nt_response_pwhash;mschapv2_derive_response"),
+ (1, "generate_authenticator_response_pwhash;mschapv2_derive_response")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="MSCHAPV2",
+ identity="phase1-user",
+ password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "eap_mschapv2_init"),
+ (1, "eap_msg_alloc;eap_mschapv2_challenge_reply"),
+ (1, "eap_msg_alloc;eap_mschapv2_success"),
+ (1, "eap_mschapv2_getKey")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="MSCHAPV2",
+ identity="phase1-user", password="password",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "eap_msg_alloc;eap_mschapv2_failure")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="MSCHAPV2",
+ identity="phase1-user", password="wrong password",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(2, "eap_mschapv2_init"),
+ (3, "eap_mschapv2_init")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="FAST",
+ anonymous_identity="FAST", identity="user",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_eap_gpsk_errors(dev, apdev):
+ """EAP-GPSK error cases"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "os_get_random;eap_gpsk_send_gpsk_2", None),
+ (1, "eap_gpsk_derive_session_id;eap_gpsk_send_gpsk_2",
+ "cipher=1"),
+ (1, "eap_gpsk_derive_session_id;eap_gpsk_send_gpsk_2",
+ "cipher=2"),
+ (1, "eap_gpsk_derive_keys_helper", None),
+ (2, "eap_gpsk_derive_keys_helper", None),
+ (3, "eap_gpsk_derive_keys_helper", None),
+ (1, "eap_gpsk_compute_mic_aes;eap_gpsk_compute_mic;eap_gpsk_send_gpsk_2",
+ "cipher=1"),
+ (1, "hmac_sha256;eap_gpsk_compute_mic;eap_gpsk_send_gpsk_2",
+ "cipher=2"),
+ (1, "eap_gpsk_compute_mic;eap_gpsk_validate_gpsk_3_mic", None),
+ (1, "eap_gpsk_compute_mic;eap_gpsk_send_gpsk_4", None),
+ (1, "eap_gpsk_derive_mid_helper", None)]
+ for count, func, phase1 in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ phase1=phase1,
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "eap_gpsk_init"),
+ (2, "eap_gpsk_init"),
+ (3, "eap_gpsk_init"),
+ (1, "eap_gpsk_process_id_server"),
+ (1, "eap_msg_alloc;eap_gpsk_send_gpsk_2"),
+ (1, "eap_gpsk_derive_session_id;eap_gpsk_send_gpsk_2"),
+ (1, "eap_gpsk_derive_mid_helper;eap_gpsk_derive_session_id;eap_gpsk_send_gpsk_2"),
+ (1, "eap_gpsk_derive_keys"),
+ (1, "eap_gpsk_derive_keys_helper"),
+ (1, "eap_msg_alloc;eap_gpsk_send_gpsk_4"),
+ (1, "eap_gpsk_getKey"),
+ (1, "eap_gpsk_get_emsk"),
+ (1, "eap_gpsk_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa-eap", key_mgmt="WPA-EAP", eap="GPSK",
+ identity="gpsk user@domain", erp="1",
+ password="abcdefghijklmnop0123456789abcdef",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_sim_db(dev, apdev, params):
+ """EAP-SIM DB error cases"""
+ sockpath = '/tmp/hlr_auc_gw.sock-test'
+ try:
+ os.remove(sockpath)
+ except:
+ pass
+ hparams = int_eap_server_params()
+ hparams['eap_sim_db'] = 'unix:' + sockpath
+ hapd = hostapd.add_ap(apdev[0], hparams)
+
+ # Initial test with hlr_auc_gw socket not available
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="SIM", identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["EAP-ERROR-CODE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP method specific error code not reported")
+ if int(ev.split()[1]) != 16384:
+ raise Exception("Unexpected EAP method specific error code: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP-Failure not reported")
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+
+ # Test with invalid responses and response timeout
+
+ class test_handler(SocketServer.DatagramRequestHandler):
+ def handle(self):
+ data = self.request[0].decode().strip()
+ socket = self.request[1]
+ logger.debug("Received hlr_auc_gw request: " + data)
+ # EAP-SIM DB: Failed to parse response string
+ socket.sendto(b"FOO", self.client_address)
+ # EAP-SIM DB: Failed to parse response string
+ socket.sendto(b"FOO 1", self.client_address)
+ # EAP-SIM DB: Unknown external response
+ socket.sendto(b"FOO 1 2", self.client_address)
+ logger.info("No proper response - wait for pending eap_sim_db request timeout")
+
+ server = SocketServer.UnixDatagramServer(sockpath, test_handler)
+ server.timeout = 1
+
+ dev[0].select_network(id)
+ server.handle_request()
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP-Failure not reported")
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+
+ # Test with a valid response
+
+ class test_handler2(SocketServer.DatagramRequestHandler):
+ def handle(self):
+ data = self.request[0].decode().strip()
+ socket = self.request[1]
+ logger.debug("Received hlr_auc_gw request: " + data)
+ fname = os.path.join(params['logdir'],
+ 'hlr_auc_gw.milenage_db')
+ cmd = subprocess.Popen(['../../hostapd/hlr_auc_gw',
+ '-m', fname, data],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read().decode().strip()
+ cmd.stdout.close()
+ logger.debug("hlr_auc_gw response: " + res)
+ socket.sendto(res.encode(), self.client_address)
+
+ server.RequestHandlerClass = test_handler2
+
+ dev[0].select_network(id)
+ server.handle_request()
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_sim_db_sqlite(dev, apdev, params):
+ """EAP-SIM DB error cases (SQLite)"""
+ sockpath = '/tmp/hlr_auc_gw.sock-test'
+ try:
+ os.remove(sockpath)
+ except:
+ pass
+ hparams = int_eap_server_params()
+ hparams['eap_sim_db'] = 'unix:' + sockpath
+ hapd = hostapd.add_ap(apdev[0], hparams)
+
+ fname = params['prefix'] + ".milenage_db.sqlite"
+ cmd = subprocess.Popen(['../../hostapd/hlr_auc_gw',
+ '-D', fname, "FOO"],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read().decode().strip()
+ cmd.stdout.close()
+ logger.debug("hlr_auc_gw response: " + res)
+
+ try:
+ import sqlite3
+ except ImportError:
+ raise HwsimSkip("No sqlite3 module available")
+ con = sqlite3.connect(fname)
+ with con:
+ cur = con.cursor()
+ try:
+ cur.execute("INSERT INTO milenage(imsi,ki,opc,amf,sqn) VALUES ('232010000000000', '90dca4eda45b53cf0f12d7c9c3bc6a89', 'cb9cccc4b9258e6dca4760379fb82581', '61df', '000000000000')")
+ except sqlite3.IntegrityError as e:
+ pass
+
+ class test_handler3(SocketServer.DatagramRequestHandler):
+ def handle(self):
+ data = self.request[0].decode().strip()
+ socket = self.request[1]
+ logger.debug("Received hlr_auc_gw request: " + data)
+ cmd = subprocess.Popen(['../../hostapd/hlr_auc_gw',
+ '-D', fname, data],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read().decode().strip()
+ cmd.stdout.close()
+ logger.debug("hlr_auc_gw response: " + res)
+ socket.sendto(res.encode(), self.client_address)
+
+ server = SocketServer.UnixDatagramServer(sockpath, test_handler3)
+ server.timeout = 1
+
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="SIM", identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ scan_freq="2412", wait_connect=False)
+ server.handle_request()
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_eap_tls_sha512(dev, apdev, params):
+ """EAP-TLS with SHA512 signature"""
+ params = int_eap_server_params()
+ params["ca_cert"] = "auth_serv/sha512-ca.pem"
+ params["server_cert"] = "auth_serv/sha512-server.pem"
+ params["private_key"] = "auth_serv/sha512-server.key"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user sha512",
+ ca_cert="auth_serv/sha512-ca.pem",
+ client_cert="auth_serv/sha512-user.pem",
+ private_key="auth_serv/sha512-user.key",
+ scan_freq="2412")
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user sha512",
+ ca_cert="auth_serv/sha512-ca.pem",
+ client_cert="auth_serv/sha384-user.pem",
+ private_key="auth_serv/sha384-user.key",
+ scan_freq="2412")
+
+def test_eap_tls_sha384(dev, apdev, params):
+ """EAP-TLS with SHA384 signature"""
+ params = int_eap_server_params()
+ params["ca_cert"] = "auth_serv/sha512-ca.pem"
+ params["server_cert"] = "auth_serv/sha384-server.pem"
+ params["private_key"] = "auth_serv/sha384-server.key"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user sha512",
+ ca_cert="auth_serv/sha512-ca.pem",
+ client_cert="auth_serv/sha512-user.pem",
+ private_key="auth_serv/sha512-user.key",
+ scan_freq="2412")
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user sha512",
+ ca_cert="auth_serv/sha512-ca.pem",
+ client_cert="auth_serv/sha384-user.pem",
+ private_key="auth_serv/sha384-user.key",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_assoc_rsn(dev, apdev):
+ """WPA2-Enterprise AP and association request RSN IE differences"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap-11w")
+ params["ieee80211w"] = "2"
+ hostapd.add_ap(apdev[1], params)
+
+ # Success cases with optional RSN IE fields removed one by one
+ tests = [("Normal wpa_supplicant assoc req RSN IE",
+ "30140100000fac040100000fac040100000fac010000"),
+ ("Extra PMKIDCount field in RSN IE",
+ "30160100000fac040100000fac040100000fac0100000000"),
+ ("Extra Group Management Cipher Suite in RSN IE",
+ "301a0100000fac040100000fac040100000fac0100000000000fac06"),
+ ("Extra undefined extension field in RSN IE",
+ "301c0100000fac040100000fac040100000fac0100000000000fac061122"),
+ ("RSN IE without RSN Capabilities",
+ "30120100000fac040100000fac040100000fac01"),
+ ("RSN IE without AKM", "300c0100000fac040100000fac04"),
+ ("RSN IE without pairwise", "30060100000fac04"),
+ ("RSN IE without group", "30020100")]
+ for title, ie in tests:
+ logger.info(title)
+ set_test_assoc_ie(dev[0], ie)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [("Normal wpa_supplicant assoc req RSN IE",
+ "30140100000fac040100000fac040100000fac01cc00"),
+ ("Group management cipher included in assoc req RSN IE",
+ "301a0100000fac040100000fac040100000fac01cc000000000fac06")]
+ for title, ie in tests:
+ logger.info(title)
+ set_test_assoc_ie(dev[0], ie)
+ dev[0].connect("test-wpa2-eap-11w", key_mgmt="WPA-EAP", ieee80211w="1",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [("Invalid group cipher", "30060100000fac02", [40, 41]),
+ ("Invalid pairwise cipher", "300c0100000fac040100000fac02", 42)]
+ for title, ie, status in tests:
+ logger.info(title)
+ set_test_assoc_ie(dev[0], ie)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+ if ev is None:
+ raise Exception("Association rejection not reported")
+ ok = False
+ if isinstance(status, list):
+ for i in status:
+ ok = "status_code=" + str(i) in ev
+ if ok:
+ break
+ else:
+ ok = "status_code=" + str(status) in ev
+ if not ok:
+ raise Exception("Unexpected status code: " + ev)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = [("Management frame protection not enabled",
+ "30140100000fac040100000fac040100000fac010000", 31),
+ ("Unsupported management group cipher",
+ "301a0100000fac040100000fac040100000fac01cc000000000fac0b", 46)]
+ for title, ie, status in tests:
+ logger.info(title)
+ set_test_assoc_ie(dev[0], ie)
+ dev[0].connect("test-wpa2-eap-11w", key_mgmt="WPA-EAP", ieee80211w="1",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+ if ev is None:
+ raise Exception("Association rejection not reported")
+ if "status_code=" + str(status) not in ev:
+ raise Exception("Unexpected status code: " + ev)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def test_eap_tls_ext_cert_check(dev, apdev):
+ """EAP-TLS and external server certification validation"""
+ # With internal server certificate chain validation
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user",
+ ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ phase1="tls_ext_cert_check=1", scan_freq="2412",
+ only_add_network=True)
+ run_ext_cert_check(dev, apdev, id)
+
+def test_eap_ttls_ext_cert_check(dev, apdev):
+ """EAP-TTLS and external server certification validation"""
+ # Without internal server certificate chain validation
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password", phase2="auth=PAP",
+ phase1="tls_ext_cert_check=1", scan_freq="2412",
+ only_add_network=True)
+ run_ext_cert_check(dev, apdev, id)
+
+def test_eap_peap_ext_cert_check(dev, apdev):
+ """EAP-PEAP and external server certification validation"""
+ # With internal server certificate chain validation
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+ identity="user", anonymous_identity="peap",
+ ca_cert="auth_serv/ca.pem",
+ password="password", phase2="auth=MSCHAPV2",
+ phase1="tls_ext_cert_check=1", scan_freq="2412",
+ only_add_network=True)
+ run_ext_cert_check(dev, apdev, id)
+
+def test_eap_fast_ext_cert_check(dev, apdev):
+ """EAP-FAST and external server certification validation"""
+ check_eap_capa(dev[0], "FAST")
+ # With internal server certificate chain validation
+ dev[0].request("SET blob fast_pac_auth_ext ")
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="FAST",
+ identity="user", anonymous_identity="FAST",
+ ca_cert="auth_serv/ca.pem",
+ password="password", phase2="auth=GTC",
+ phase1="tls_ext_cert_check=1 fast_provisioning=2",
+ pac_file="blob://fast_pac_auth_ext",
+ scan_freq="2412",
+ only_add_network=True)
+ run_ext_cert_check(dev, apdev, id)
+
+def run_ext_cert_check(dev, apdev, net_id):
+ check_ext_cert_check_support(dev[0])
+ if not openssl_imported:
+ raise HwsimSkip("OpenSSL python method not available")
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].select_network(net_id)
+ certs = {}
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PEER-CERT",
+ "CTRL-REQ-EXT_CERT_CHECK",
+ "CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("No peer server certificate event seen")
+ if "CTRL-EVENT-EAP-PEER-CERT" in ev:
+ depth = None
+ cert = None
+ vals = ev.split(' ')
+ for v in vals:
+ if v.startswith("depth="):
+ depth = int(v.split('=')[1])
+ elif v.startswith("cert="):
+ cert = v.split('=')[1]
+ if depth is not None and cert:
+ certs[depth] = binascii.unhexlify(cert)
+ elif "CTRL-EVENT-EAP-SUCCESS" in ev:
+ raise Exception("Unexpected EAP-Success")
+ elif "CTRL-REQ-EXT_CERT_CHECK" in ev:
+ id = ev.split(':')[0].split('-')[-1]
+ break
+ if 0 not in certs:
+ raise Exception("Server certificate not received")
+ if 1 not in certs:
+ raise Exception("Server certificate issuer not received")
+
+ cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1,
+ certs[0])
+ cn = cert.get_subject().commonName
+ logger.info("Server certificate CN=" + cn)
+
+ issuer = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_ASN1,
+ certs[1])
+ icn = issuer.get_subject().commonName
+ logger.info("Issuer certificate CN=" + icn)
+
+ if cn != "server.w1.fi":
+ raise Exception("Unexpected server certificate CN: " + cn)
+ if icn != "Root CA":
+ raise Exception("Unexpected server certificate issuer CN: " + icn)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=0.1)
+ if ev:
+ raise Exception("Unexpected EAP-Success before external check result indication")
+
+ dev[0].request("CTRL-RSP-EXT_CERT_CHECK-" + id + ":good")
+ dev[0].wait_connected()
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+ dev[0].request("SET blob fast_pac_auth_ext ")
+ dev[0].request("RECONNECT")
+
+ ev = dev[0].wait_event(["CTRL-REQ-EXT_CERT_CHECK"], timeout=10)
+ if ev is None:
+ raise Exception("No peer server certificate event seen (2)")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-EXT_CERT_CHECK-" + id + ":bad")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("EAP-Failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_eap_tls_errors(dev, apdev):
+ """EAP-TLS error cases"""
+ params = int_eap_server_params()
+ params['fragment_size'] = '100'
+ hostapd.add_ap(apdev[0], params)
+ with alloc_fail(dev[0], 1,
+ "eap_peer_tls_reassemble_fragment"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_tls_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_peer_tls_ssl_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ engine="1",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ ev = dev[0].wait_event(["CTRL-REQ-PIN"], timeout=5)
+ if ev is None:
+ raise Exception("No CTRL-REQ-PIN seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = ["eap_peer_tls_derive_key;eap_tls_success",
+ "eap_peer_tls_derive_session_id;eap_tls_success",
+ "eap_tls_getKey",
+ "eap_tls_get_emsk",
+ "eap_tls_get_session_id"]
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TLS",
+ identity="tls user@domain",
+ ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ erp="1",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_unauth_tls_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="UNAUTH-TLS",
+ identity="unauth-tls", ca_cert="auth_serv/ca.pem",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_peer_tls_ssl_init;eap_unauth_tls_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="UNAUTH-TLS",
+ identity="unauth-tls", ca_cert="auth_serv/ca.pem",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_wfa_unauth_tls_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="WFA-UNAUTH-TLS",
+ identity="osen@example.com", ca_cert="auth_serv/ca.pem",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_peer_tls_ssl_init;eap_wfa_unauth_tls_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="WFA-UNAUTH-TLS",
+ identity="osen@example.com", ca_cert="auth_serv/ca.pem",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_status(dev, apdev):
+ """EAP state machine status information"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="PEAP",
+ identity="cert user",
+ ca_cert="auth_serv/ca.pem", phase2="auth=TLS",
+ ca_cert2="auth_serv/ca.pem",
+ client_cert2="auth_serv/user.pem",
+ private_key2="auth_serv/user.key",
+ scan_freq="2412", wait_connect=False)
+ success = False
+ states = []
+ method_states = []
+ decisions = []
+ req_methods = []
+ selected_methods = []
+ connected = False
+ for i in range(100000):
+ if not connected and i % 10 == 9:
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.0001)
+ if ev:
+ connected = True
+ s = dev[0].get_status(extra="VERBOSE")
+ if 'EAP state' in s:
+ state = s['EAP state']
+ if state:
+ if state not in states:
+ states.append(state)
+ if state == "SUCCESS":
+ success = True
+ break
+ if 'methodState' in s:
+ val = s['methodState']
+ if val not in method_states:
+ method_states.append(val)
+ if 'decision' in s:
+ val = s['decision']
+ if val not in decisions:
+ decisions.append(val)
+ if 'reqMethod' in s:
+ val = s['reqMethod']
+ if val not in req_methods:
+ req_methods.append(val)
+ if 'selectedMethod' in s:
+ val = s['selectedMethod']
+ if val not in selected_methods:
+ selected_methods.append(val)
+ logger.info("Iterations: %d" % i)
+ logger.info("EAP states: " + str(states))
+ logger.info("methodStates: " + str(method_states))
+ logger.info("decisions: " + str(decisions))
+ logger.info("reqMethods: " + str(req_methods))
+ logger.info("selectedMethods: " + str(selected_methods))
+ if not success:
+ raise Exception("EAP did not succeed")
+ if not connected:
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_eap_gpsk_ptk_rekey_ap(dev, apdev):
+ """WPA2-Enterprise with EAP-GPSK and PTK rekey enforced by AP"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['wpa_ptk_rekey'] = '2'
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = eap_connect(dev[0], hapd, "GPSK", "gpsk user",
+ password="abcdefghijklmnop0123456789abcdef")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_eap_wildcard_ssid(dev, apdev):
+ """WPA2-Enterprise connection using EAP-GPSK and wildcard SSID"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(bssid=apdev[0]['bssid'], key_mgmt="WPA-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+def test_ap_wpa2_eap_psk_mac_addr_change(dev, apdev):
+ """WPA2-Enterprise connection using EAP-PSK after MAC address change"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ cmd = subprocess.Popen(['ps', '-eo', 'pid,command'], stdout=subprocess.PIPE)
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ pid = 0
+ for p in res.splitlines():
+ if "wpa_supplicant" not in p:
+ continue
+ if dev[0].ifname not in p:
+ continue
+ pid = int(p.strip().split(' ')[0])
+ if pid == 0:
+ logger.info("Could not find wpa_supplicant PID")
+ else:
+ logger.info("wpa_supplicant PID %d" % pid)
+
+ addr = dev[0].get_status_field("address")
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'down'])
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'address',
+ '02:11:22:33:44:55'])
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'up'])
+ addr1 = dev[0].get_status_field("address")
+ if addr1 != '02:11:22:33:44:55':
+ raise Exception("Failed to change MAC address")
+
+ # Scan using the externally set MAC address, stop the wpa_supplicant
+ # process to avoid it from processing the ifdown event before the interface
+ # is already UP, change the MAC address back, allow the wpa_supplicant
+ # process to continue. This will result in the ifdown + ifup sequence of
+ # RTM_NEWLINK events to be processed while the interface is already UP.
+ try:
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ os.kill(pid, signal.SIGSTOP)
+ time.sleep(0.1)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'down'])
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'address',
+ addr])
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'up'])
+ time.sleep(0.1)
+ os.kill(pid, signal.SIGCONT)
+
+ eap_connect(dev[0], hapd, "PSK", "psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+
+ addr2 = dev[0].get_status_field("address")
+ if addr != addr2:
+ raise Exception("Failed to restore MAC address")
+
+def test_ap_wpa2_eap_server_get_id(dev, apdev):
+ """Internal EAP server and dot1xAuthSessionUserName"""
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TLS", "tls user", ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ sta = hapd.get_sta(dev[0].own_addr())
+ if 'dot1xAuthSessionUserName' not in sta:
+ raise Exception("No dot1xAuthSessionUserName included")
+ user = sta['dot1xAuthSessionUserName']
+ if user != "tls user":
+ raise Exception("Unexpected dot1xAuthSessionUserName value: " + user)
+
+def test_ap_wpa2_radius_server_get_id(dev, apdev):
+ """External RADIUS server and dot1xAuthSessionUserName"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TTLS", "test-user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+ sta = hapd.get_sta(dev[0].own_addr())
+ if 'dot1xAuthSessionUserName' not in sta:
+ raise Exception("No dot1xAuthSessionUserName included")
+ user = sta['dot1xAuthSessionUserName']
+ if user != "real-user":
+ raise Exception("Unexpected dot1xAuthSessionUserName value: " + user)
+
+def test_openssl_systemwide_policy(dev, apdev, test_params):
+ """OpenSSL systemwide policy and overrides"""
+ prefix = "openssl_systemwide_policy"
+ pidfile = os.path.join(test_params['logdir'], prefix + '.pid-wpas')
+ try:
+ with HWSimRadio() as (radio, iface):
+ run_openssl_systemwide_policy(iface, apdev, test_params)
+ finally:
+ if os.path.exists(pidfile):
+ with open(pidfile, 'r') as f:
+ pid = int(f.read().strip())
+ os.kill(pid, signal.SIGTERM)
+
+def write_openssl_cnf(cnf, MinProtocol=None, CipherString=None):
+ with open(cnf, "w") as f:
+ f.write("""openssl_conf = default_conf
+[default_conf]
+ssl_conf = ssl_sect
+[ssl_sect]
+system_default = system_default_sect
+[system_default_sect]
+""")
+ if MinProtocol:
+ f.write("MinProtocol = %s\n" % MinProtocol)
+ if CipherString:
+ f.write("CipherString = %s\n" % CipherString)
+
+def run_openssl_systemwide_policy(iface, apdev, test_params):
+ prefix = "openssl_systemwide_policy"
+ logfile = os.path.join(test_params['logdir'], prefix + '.log-wpas')
+ pidfile = os.path.join(test_params['logdir'], prefix + '.pid-wpas')
+ conffile = os.path.join(test_params['logdir'], prefix + '.conf')
+ openssl_cnf = os.path.join(test_params['logdir'], prefix + '.openssl.cnf')
+
+ write_openssl_cnf(openssl_cnf, "TLSv1.2", "DEFAULT@SECLEVEL=2")
+
+ with open(conffile, 'w') as f:
+ f.write("ctrl_interface=DIR=/var/run/wpa_supplicant\n")
+
+ params = int_eap_server_params()
+ params['tls_flags'] = "[DISABLE-TLSv1.1][DISABLE-TLSv1.2][DISABLE-TLSv1.3]"
+
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ prg = os.path.join(test_params['logdir'],
+ 'alt-wpa_supplicant/wpa_supplicant/wpa_supplicant')
+ if not os.path.exists(prg):
+ prg = '../../wpa_supplicant/wpa_supplicant'
+ arg = [prg, '-BddtK', '-P', pidfile, '-f', logfile,
+ '-Dnl80211', '-c', conffile, '-i', iface]
+ logger.info("Start wpa_supplicant: " + str(arg))
+ subprocess.call(arg, env={'OPENSSL_CONF': openssl_cnf})
+ wpas = WpaSupplicant(ifname=iface)
+ try:
+ finish_openssl_systemwide_policy(wpas)
+ finally:
+ wpas.close_monitor()
+ wpas.request("TERMINATE")
+
+def finish_openssl_systemwide_policy(wpas):
+ if "PONG" not in wpas.request("PING"):
+ raise Exception("Could not PING wpa_supplicant")
+ tls = wpas.request("GET tls_library")
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("Not using OpenSSL")
+
+ # Use default configuration without any TLS version overrides. This should
+ # end up using OpenSSL systemwide policy and result in failure to find a
+ # compatible protocol version.
+ ca_file = os.path.join(os.getcwd(), "auth_serv/ca.pem")
+ id = wpas.connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password", phase2="auth=PAP",
+ ca_cert=ca_file,
+ scan_freq="2412", wait_connect=False)
+ ev = wpas.wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP not started")
+ ev = wpas.wait_event(["CTRL-EVENT-EAP-STATUS status='local TLS alert'"],
+ timeout=1)
+ if ev is None:
+ raise HwsimSkip("OpenSSL systemwide policy not supported")
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+ # Explicitly allow TLSv1.0 to be used to override OpenSSL systemwide policy
+ wpas.set_network_quoted(id, "openssl_ciphers", "DEFAULT@SECLEVEL=1")
+ wpas.set_network_quoted(id, "phase1", "tls_disable_tlsv1_0=0")
+ wpas.select_network(id, freq="2412")
+ wpas.wait_connected()
+
+def test_ap_wpa2_eap_tls_tod(dev, apdev):
+ """EAP-TLS server certificate validation and TOD-STRICT"""
+ check_tls_tod(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-certpol.pem"
+ params["private_key"] = "auth_serv/server-certpol.key"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TLS", identity="tls user",
+ wait_connect=False, scan_freq="2412",
+ ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ tod0 = None
+ tod1 = None
+ while tod0 is None or tod1 is None:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PEER-CERT"], timeout=10)
+ if ev is None:
+ raise Exception("Peer certificate not reported")
+ if "depth=1 " in ev and "hash=" in ev:
+ tod1 = " tod=1" in ev
+ if "depth=0 " in ev and "hash=" in ev:
+ tod0 = " tod=1" in ev
+ dev[0].wait_connected()
+ if not tod0:
+ raise Exception("TOD-STRICT policy not reported for server certificate")
+ if tod1:
+ raise Exception("TOD-STRICT policy unexpectedly reported for CA certificate")
+
+def test_ap_wpa2_eap_tls_tod_tofu(dev, apdev):
+ """EAP-TLS server certificate validation and TOD-TOFU"""
+ check_tls_tod(dev[0])
+ params = int_eap_server_params()
+ params["server_cert"] = "auth_serv/server-certpol2.pem"
+ params["private_key"] = "auth_serv/server-certpol2.key"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TLS", identity="tls user",
+ wait_connect=False, scan_freq="2412",
+ ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ tod0 = None
+ tod1 = None
+ while tod0 is None or tod1 is None:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PEER-CERT"], timeout=10)
+ if ev is None:
+ raise Exception("Peer certificate not reported")
+ if "depth=1 " in ev and "hash=" in ev:
+ tod1 = " tod=2" in ev
+ if "depth=0 " in ev and "hash=" in ev:
+ tod0 = " tod=2" in ev
+ dev[0].wait_connected()
+ if not tod0:
+ raise Exception("TOD-TOFU policy not reported for server certificate")
+ if tod1:
+ raise Exception("TOD-TOFU policy unexpectedly reported for CA certificate")
+
+def test_ap_wpa2_eap_sake_no_control_port(dev, apdev):
+ """WPA2-Enterprise connection using EAP-SAKE without nl80211 control port"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['driver_params'] = "control_port=0"
+ hapd = hostapd.add_ap(apdev[0], params)
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="control_port=0")
+ eap_connect(wpas, hapd, "SAKE", "sake user",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+ eap_reauth(wpas, "SAKE")
+
+ logger.info("Negative test with incorrect password")
+ wpas.request("REMOVE_NETWORK all")
+ eap_connect(wpas, hapd, "SAKE", "sake user",
+ password_hex="ff23456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ expect_failure=True)
+
+def test_ap_wpa3_eap_transition_disable(dev, apdev):
+ """WPA3-Enterprise transition disable indication"""
+ skip_without_tkip(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa3-eap")
+ params["ieee80211w"] = "1"
+ params['transition_disable'] = '0x04'
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = dev[0].connect("test-wpa3-eap", key_mgmt="WPA-EAP", ieee80211w="1",
+ proto="WPA WPA2", pairwise="CCMP", group="TKIP CCMP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ ev = dev[0].wait_event(["TRANSITION-DISABLE"], timeout=1)
+ if ev is None:
+ raise Exception("Transition disable not indicated")
+ if ev.split(' ')[1] != "04":
+ raise Exception("Unexpected transition disable bitmap: " + ev)
+
+ val = dev[0].get_network(id, "ieee80211w")
+ if val != "2":
+ raise Exception("Unexpected ieee80211w value: " + val)
+ val = dev[0].get_network(id, "key_mgmt")
+ if val != "WPA-EAP":
+ raise Exception("Unexpected key_mgmt value: " + val)
+ val = dev[0].get_network(id, "group")
+ if val != "CCMP":
+ raise Exception("Unexpected group value: " + val)
+ val = dev[0].get_network(id, "proto")
+ if val != "RSN":
+ raise Exception("Unexpected proto value: " + val)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
diff --git a/contrib/wpa/tests/hwsim/test_ap_ft.py b/contrib/wpa/tests/hwsim/test_ap_ft.py
new file mode 100644
index 000000000000..00b1635db072
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_ft.py
@@ -0,0 +1,3461 @@
+# Fast BSS Transition tests
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import os
+import time
+import logging
+logger = logging.getLogger()
+import signal
+import struct
+import subprocess
+
+import hwsim_utils
+from hwsim import HWSimRadio
+import hostapd
+from tshark import run_tshark
+from utils import *
+from wlantest import Wlantest
+from test_ap_psk import check_mib, find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+from test_rrm import check_beacon_req
+from test_suite_b import check_suite_b_192_capa
+
+def ft_base_rsn():
+ params = {"wpa": "2",
+ "wpa_key_mgmt": "FT-PSK",
+ "rsn_pairwise": "CCMP"}
+ return params
+
+def ft_base_mixed():
+ params = {"wpa": "3",
+ "wpa_key_mgmt": "WPA-PSK FT-PSK",
+ "wpa_pairwise": "TKIP",
+ "rsn_pairwise": "CCMP"}
+ return params
+
+def ft_params(rsn=True, ssid=None, passphrase=None):
+ if rsn:
+ params = ft_base_rsn()
+ else:
+ params = ft_base_mixed()
+ if ssid:
+ params["ssid"] = ssid
+ if passphrase:
+ params["wpa_passphrase"] = passphrase
+
+ params["mobility_domain"] = "a1b2"
+ params["r0_key_lifetime"] = "10000"
+ params["pmk_r1_push"] = "1"
+ params["reassociation_deadline"] = "1000"
+ return params
+
+def ft_params1a(rsn=True, ssid=None, passphrase=None):
+ params = ft_params(rsn, ssid, passphrase)
+ params['nas_identifier'] = "nas1.w1.fi"
+ params['r1_key_holder'] = "000102030405"
+ return params
+
+def ft_params1(rsn=True, ssid=None, passphrase=None, discovery=False):
+ params = ft_params1a(rsn, ssid, passphrase)
+ if discovery:
+ params['r0kh'] = "ff:ff:ff:ff:ff:ff * 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f"
+ params['r1kh'] = "00:00:00:00:00:00 00:00:00:00:00:00 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f"
+ else:
+ params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
+ "02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f"]
+ params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f"
+ return params
+
+def ft_params1_old_key(rsn=True, ssid=None, passphrase=None):
+ params = ft_params1a(rsn, ssid, passphrase)
+ params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f",
+ "02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f"]
+ params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
+ return params
+
+def ft_params2a(rsn=True, ssid=None, passphrase=None):
+ params = ft_params(rsn, ssid, passphrase)
+ params['nas_identifier'] = "nas2.w1.fi"
+ params['r1_key_holder'] = "000102030406"
+ return params
+
+def ft_params2(rsn=True, ssid=None, passphrase=None, discovery=False):
+ params = ft_params2a(rsn, ssid, passphrase)
+ if discovery:
+ params['r0kh'] = "ff:ff:ff:ff:ff:ff * 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f"
+ params['r1kh'] = "00:00:00:00:00:00 00:00:00:00:00:00 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f"
+ else:
+ params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f",
+ "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"]
+ params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f"
+ return params
+
+def ft_params2_old_key(rsn=True, ssid=None, passphrase=None):
+ params = ft_params2a(rsn, ssid, passphrase)
+ params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f",
+ "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f"]
+ params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
+ return params
+
+def ft_params1_r0kh_mismatch(rsn=True, ssid=None, passphrase=None):
+ params = ft_params(rsn, ssid, passphrase)
+ params['nas_identifier'] = "nas1.w1.fi"
+ params['r1_key_holder'] = "000102030405"
+ params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
+ "12:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f"]
+ params['r1kh'] = "12:00:00:00:04:00 10:01:02:03:04:06 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f"
+ return params
+
+def ft_params2_incorrect_rrb_key(rsn=True, ssid=None, passphrase=None):
+ params = ft_params(rsn, ssid, passphrase)
+ params['nas_identifier'] = "nas2.w1.fi"
+ params['r1_key_holder'] = "000102030406"
+ params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0ef1200102030405060708090a0b0c0d0ef1",
+ "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0ef2000102030405060708090a0b0c0d0ef2"]
+ params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0ef3300102030405060708090a0b0c0d0ef3"
+ return params
+
+def ft_params2_r0kh_mismatch(rsn=True, ssid=None, passphrase=None):
+ params = ft_params(rsn, ssid, passphrase)
+ params['nas_identifier'] = "nas2.w1.fi"
+ params['r1_key_holder'] = "000102030406"
+ params['r0kh'] = ["12:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f",
+ "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"]
+ params['r1kh'] = "12:00:00:00:03:00 10:01:02:03:04:05 300102030405060708090a0b0c0d0e0f300102030405060708090a0b0c0d0e0f"
+ return params
+
+def run_roams(dev, apdev, hapd0, hapd1, ssid, passphrase, over_ds=False,
+ sae=False, eap=False, fail_test=False, roams=1,
+ pairwise_cipher="CCMP", group_cipher="CCMP", ptk_rekey="0",
+ test_connectivity=True, eap_identity="gpsk user", conndev=False,
+ force_initial_conn_to_first_ap=False, sha384=False,
+ group_mgmt=None, ocv=None, sae_password=None,
+ sae_password_id=None, sae_and_psk=False, pmksa_caching=False,
+ roam_with_reassoc=False, also_non_ft=False, only_one_way=False,
+ wait_before_roam=0, return_after_initial=False, ieee80211w="1",
+ sae_transition=False, beacon_prot=False):
+ logger.info("Connect to first AP")
+
+ copts = {}
+ copts["proto"] = "WPA2"
+ copts["ieee80211w"] = ieee80211w
+ copts["scan_freq"] = "2412"
+ copts["pairwise"] = pairwise_cipher
+ copts["group"] = group_cipher
+ copts["wpa_ptk_rekey"] = ptk_rekey
+ if group_mgmt:
+ copts["group_mgmt"] = group_mgmt
+ if ocv:
+ copts["ocv"] = ocv
+ if beacon_prot:
+ copts["beacon_prot"] = "1"
+ if eap:
+ if pmksa_caching:
+ copts["ft_eap_pmksa_caching"] = "1"
+ if also_non_ft:
+ copts["key_mgmt"] = "WPA-EAP-SUITE-B-192 FT-EAP-SHA384" if sha384 else "WPA-EAP FT-EAP"
+ else:
+ copts["key_mgmt"] = "FT-EAP-SHA384" if sha384 else "FT-EAP"
+ copts["eap"] = "GPSK"
+ copts["identity"] = eap_identity
+ copts["password"] = "abcdefghijklmnop0123456789abcdef"
+ else:
+ if sae_transition:
+ copts["key_mgmt"] = "FT-SAE FT-PSK"
+ elif sae:
+ copts["key_mgmt"] = "SAE FT-SAE" if sae_and_psk else "FT-SAE"
+ else:
+ copts["key_mgmt"] = "FT-PSK"
+ if passphrase:
+ copts["psk"] = passphrase
+ if sae_password:
+ copts["sae_password"] = sae_password
+ if sae_password_id:
+ copts["sae_password_id"] = sae_password_id
+ if force_initial_conn_to_first_ap:
+ copts["bssid"] = apdev[0]['bssid']
+ netw = dev.connect(ssid, **copts)
+ if pmksa_caching:
+ if dev.get_status_field('bssid') == apdev[0]['bssid']:
+ hapd0.wait_sta()
+ else:
+ hapd1.wait_sta()
+ dev.request("DISCONNECT")
+ dev.wait_disconnected()
+ dev.request("RECONNECT")
+ ev = dev.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-EAP-STARTED"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Reconnect timed out")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ raise Exception("Unexpected disconnection after RECONNECT")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP start after RECONNECT")
+
+ if dev.get_status_field('bssid') == apdev[0]['bssid']:
+ ap1 = apdev[0]
+ ap2 = apdev[1]
+ hapd1ap = hapd0
+ hapd2ap = hapd1
+ else:
+ ap1 = apdev[1]
+ ap2 = apdev[0]
+ hapd1ap = hapd1
+ hapd2ap = hapd0
+ if test_connectivity:
+ hapd1ap.wait_sta()
+ if conndev:
+ hwsim_utils.test_connectivity_iface(dev, hapd1ap, conndev)
+ else:
+ hwsim_utils.test_connectivity(dev, hapd1ap)
+
+ if return_after_initial:
+ return ap2['bssid']
+
+ if wait_before_roam:
+ time.sleep(wait_before_roam)
+ dev.scan_for_bss(ap2['bssid'], freq="2412")
+
+ for i in range(0, roams):
+ dev.dump_monitor()
+ hapd1ap.dump_monitor()
+ hapd2ap.dump_monitor()
+
+ # Roaming artificially fast can make data test fail because the key is
+ # set later.
+ time.sleep(0.01)
+ logger.info("Roam to the second AP")
+ if roam_with_reassoc:
+ dev.set_network(netw, "bssid", ap2['bssid'])
+ dev.request("REASSOCIATE")
+ dev.wait_connected()
+ elif over_ds:
+ dev.roam_over_ds(ap2['bssid'], fail_test=fail_test)
+ else:
+ dev.roam(ap2['bssid'], fail_test=fail_test)
+ if fail_test:
+ return
+ if dev.get_status_field('bssid') != ap2['bssid']:
+ raise Exception("Did not connect to correct AP")
+ if (i == 0 or i == roams - 1) and test_connectivity:
+ hapd2ap.wait_sta()
+ dev.dump_monitor()
+ hapd1ap.dump_monitor()
+ hapd2ap.dump_monitor()
+ if conndev:
+ hwsim_utils.test_connectivity_iface(dev, hapd2ap, conndev)
+ else:
+ hwsim_utils.test_connectivity(dev, hapd2ap)
+
+ dev.dump_monitor()
+ hapd1ap.dump_monitor()
+ hapd2ap.dump_monitor()
+
+ if only_one_way:
+ return
+ # Roaming artificially fast can make data test fail because the key is
+ # set later.
+ time.sleep(0.01)
+ logger.info("Roam back to the first AP")
+ if roam_with_reassoc:
+ dev.set_network(netw, "bssid", ap1['bssid'])
+ dev.request("REASSOCIATE")
+ dev.wait_connected()
+ elif over_ds:
+ dev.roam_over_ds(ap1['bssid'])
+ else:
+ dev.roam(ap1['bssid'])
+ if dev.get_status_field('bssid') != ap1['bssid']:
+ raise Exception("Did not connect to correct AP")
+ if (i == 0 or i == roams - 1) and test_connectivity:
+ hapd1ap.wait_sta()
+ dev.dump_monitor()
+ hapd1ap.dump_monitor()
+ hapd2ap.dump_monitor()
+ if conndev:
+ hwsim_utils.test_connectivity_iface(dev, hapd1ap, conndev)
+ else:
+ hwsim_utils.test_connectivity(dev, hapd1ap)
+
+def test_ap_ft(dev, apdev):
+ """WPA2-PSK-FT AP"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
+ if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing RSN element info")
+
+def test_ap_ft_old_key(dev, apdev):
+ """WPA2-PSK-FT AP (old key)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1_old_key(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2_old_key(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
+
+def test_ap_ft_multi_akm(dev, apdev):
+ """WPA2-PSK-FT AP with non-FT AKMs enabled"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["wpa_key_mgmt"] = "FT-PSK WPA-PSK WPA-PSK-SHA256"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["wpa_key_mgmt"] = "FT-PSK WPA-PSK WPA-PSK-SHA256"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ Wlantest.setup(hapd0)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase(passphrase)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
+ if "[WPA2-PSK+FT/PSK+PSK-SHA256-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing RSN element info")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[2].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
+ scan_freq="2412")
+
+def test_ap_ft_local_key_gen(dev, apdev):
+ """WPA2-PSK-FT AP with local key generation (without pull/push)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1a(ssid=ssid, passphrase=passphrase)
+ params['ft_psk_generate_local'] = "1"
+ del params['pmk_r1_push']
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2a(ssid=ssid, passphrase=passphrase)
+ params['ft_psk_generate_local'] = "1"
+ del params['pmk_r1_push']
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
+ if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing RSN element info")
+
+def test_ap_ft_vlan(dev, apdev):
+ """WPA2-PSK-FT AP with VLAN"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ hostapd.send_file(apdev[1], filename, filename)
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, conndev="brvlan1")
+ if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing RSN element info")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_ft_vlan_disconnected(dev, apdev):
+ """WPA2-PSK-FT AP with VLAN and local key generation"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ hostapd.send_file(apdev[1], filename, filename)
+
+ params = ft_params1a(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ params['ft_psk_generate_local'] = "1"
+ hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ params = ft_params2a(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ params['ft_psk_generate_local'] = "1"
+ hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, conndev="brvlan1")
+ if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing RSN element info")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_ft_vlan_2(dev, apdev):
+ """WPA2-PSK-FT AP with VLAN and dest-AP does not have VLAN info locally"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, conndev="brvlan1",
+ force_initial_conn_to_first_ap=True)
+ if "[WPA2-FT/PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing RSN element info")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_ft_many(dev, apdev):
+ """WPA2-PSK-FT AP multiple times"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, roams=50)
+
+def test_ap_ft_many_vlan(dev, apdev):
+ """WPA2-PSK-FT AP with VLAN multiple times"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ hostapd.send_file(apdev[1], filename, filename)
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, roams=50,
+ conndev="brvlan1")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_ft_mixed(dev, apdev):
+ """WPA2-PSK-FT mixed-mode AP"""
+ skip_without_tkip(dev[0])
+ ssid = "test-ft-mixed"
+ passphrase = "12345678"
+
+ params = ft_params1(rsn=False, ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ vals = key_mgmt.split(' ')
+ if vals[0] != "WPA-PSK" or vals[1] != "FT-PSK":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+ params = ft_params2(rsn=False, ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase,
+ group_cipher="TKIP CCMP")
+
+def test_ap_ft_pmf(dev, apdev):
+ """WPA2-PSK-FT AP with PMF"""
+ run_ap_ft_pmf(dev, apdev, "1")
+
+def test_ap_ft_pmf_over_ds(dev, apdev):
+ """WPA2-PSK-FT AP with PMF (over DS)"""
+ run_ap_ft_pmf(dev, apdev, "1", over_ds=True)
+
+def test_ap_ft_pmf_required(dev, apdev):
+ """WPA2-PSK-FT AP with PMF required on STA"""
+ run_ap_ft_pmf(dev, apdev, "2")
+
+def test_ap_ft_pmf_required_over_ds(dev, apdev):
+ """WPA2-PSK-FT AP with PMF required on STA (over DS)"""
+ run_ap_ft_pmf(dev, apdev, "2", over_ds=True)
+
+def test_ap_ft_pmf_beacon_prot(dev, apdev):
+ """WPA2-PSK-FT AP with PMF and beacon protection"""
+ run_ap_ft_pmf(dev, apdev, "1", beacon_prot=True)
+
+def run_ap_ft_pmf(dev, apdev, ieee80211w, over_ds=False, beacon_prot=False):
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ if beacon_prot:
+ params["beacon_prot"] = "1"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ if beacon_prot:
+ params["beacon_prot"] = "1"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ Wlantest.setup(hapd0)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase(passphrase)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
+ ieee80211w=ieee80211w, over_ds=over_ds, beacon_prot=beacon_prot)
+
+def test_ap_ft_pmf_required_mismatch(dev, apdev):
+ """WPA2-PSK-FT AP with PMF required on STA but AP2 not enabling PMF"""
+ run_ap_ft_pmf_required_mismatch(dev, apdev)
+
+def test_ap_ft_pmf_required_mismatch_over_ds(dev, apdev):
+ """WPA2-PSK-FT AP with PMF required on STA but AP2 not enabling PMF (over DS)"""
+ run_ap_ft_pmf_required_mismatch(dev, apdev, over_ds=True)
+
+def run_ap_ft_pmf_required_mismatch(dev, apdev, over_ds=False):
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "0"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, ieee80211w="2",
+ force_initial_conn_to_first_ap=True, fail_test=True,
+ over_ds=over_ds)
+
+def test_ap_ft_pmf_bip_cmac_128(dev, apdev):
+ """WPA2-PSK-FT AP with PMF/BIP-CMAC-128"""
+ run_ap_ft_pmf_bip(dev, apdev, "AES-128-CMAC")
+
+def test_ap_ft_pmf_bip_gmac_128(dev, apdev):
+ """WPA2-PSK-FT AP with PMF/BIP-GMAC-128"""
+ run_ap_ft_pmf_bip(dev, apdev, "BIP-GMAC-128")
+
+def test_ap_ft_pmf_bip_gmac_256(dev, apdev):
+ """WPA2-PSK-FT AP with PMF/BIP-GMAC-256"""
+ run_ap_ft_pmf_bip(dev, apdev, "BIP-GMAC-256")
+
+def test_ap_ft_pmf_bip_cmac_256(dev, apdev):
+ """WPA2-PSK-FT AP with PMF/BIP-CMAC-256"""
+ run_ap_ft_pmf_bip(dev, apdev, "BIP-CMAC-256")
+
+def run_ap_ft_pmf_bip(dev, apdev, cipher):
+ if cipher not in dev[0].get_capability("group_mgmt"):
+ raise HwsimSkip("Cipher %s not supported" % cipher)
+
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params["group_mgmt_cipher"] = cipher
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params["group_mgmt_cipher"] = cipher
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
+ group_mgmt=cipher)
+
+def test_ap_ft_ocv(dev, apdev):
+ """WPA2-PSK-FT AP with OCV"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params["ocv"] = "1"
+ try:
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params["ocv"] = "1"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, ocv="1")
+
+def test_ap_ft_over_ds(dev, apdev):
+ """WPA2-PSK-FT AP over DS"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
+ check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-4"),
+ ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-4")])
+
+def cleanup_ap_ft_separate_hostapd():
+ subprocess.call(["brctl", "delif", "br0ft", "veth0"],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(["brctl", "delif", "br1ft", "veth1"],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(["ip", "link", "del", "veth0"],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(["ip", "link", "del", "veth1"],
+ stderr=open('/dev/null', 'w'))
+ for ifname in ['br0ft', 'br1ft', 'br-ft']:
+ subprocess.call(['ip', 'link', 'set', 'dev', ifname, 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', ifname],
+ stderr=open('/dev/null', 'w'))
+
+def test_ap_ft_separate_hostapd(dev, apdev, params):
+ """WPA2-PSK-FT AP and separate hostapd process"""
+ try:
+ run_ap_ft_separate_hostapd(dev, apdev, params, False)
+ finally:
+ cleanup_ap_ft_separate_hostapd()
+
+def test_ap_ft_over_ds_separate_hostapd(dev, apdev, params):
+ """WPA2-PSK-FT AP over DS and separate hostapd process"""
+ try:
+ run_ap_ft_separate_hostapd(dev, apdev, params, True)
+ finally:
+ cleanup_ap_ft_separate_hostapd()
+
+def run_ap_ft_separate_hostapd(dev, apdev, params, over_ds):
+ ssid = "test-ft"
+ passphrase = "12345678"
+ logdir = params['logdir']
+ pidfile = os.path.join(logdir, 'ap_ft_over_ds_separate_hostapd.pid')
+ logfile = os.path.join(logdir, 'ap_ft_over_ds_separate_hostapd.hapd')
+ global_ctrl = '/var/run/hostapd-ft'
+ br_ifname = 'br-ft'
+
+ try:
+ subprocess.check_call(['brctl', 'addbr', br_ifname])
+ subprocess.check_call(['brctl', 'setfd', br_ifname, '0'])
+ subprocess.check_call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+
+ subprocess.check_call(["ip", "link", "add", "veth0", "type", "veth",
+ "peer", "name", "veth0br"])
+ subprocess.check_call(["ip", "link", "add", "veth1", "type", "veth",
+ "peer", "name", "veth1br"])
+ subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth0br', 'up'])
+ subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth1br', 'up'])
+ subprocess.check_call(['brctl', 'addif', br_ifname, 'veth0br'])
+ subprocess.check_call(['brctl', 'addif', br_ifname, 'veth1br'])
+
+ subprocess.check_call(['brctl', 'addbr', 'br0ft'])
+ subprocess.check_call(['brctl', 'setfd', 'br0ft', '0'])
+ subprocess.check_call(['ip', 'link', 'set', 'dev', 'br0ft', 'up'])
+ subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth0', 'up'])
+ subprocess.check_call(['brctl', 'addif', 'br0ft', 'veth0'])
+ subprocess.check_call(['brctl', 'addbr', 'br1ft'])
+ subprocess.check_call(['brctl', 'setfd', 'br1ft', '0'])
+ subprocess.check_call(['ip', 'link', 'set', 'dev', 'br1ft', 'up'])
+ subprocess.check_call(['ip', 'link', 'set', 'dev', 'veth1', 'up'])
+ subprocess.check_call(['brctl', 'addif', 'br1ft', 'veth1'])
+ except subprocess.CalledProcessError:
+ raise HwsimSkip("Bridge or veth not supported (kernel CONFIG_VETH)")
+
+ with HWSimRadio() as (radio, iface):
+ prg = os.path.join(logdir, 'alt-hostapd/hostapd/hostapd')
+ if not os.path.exists(prg):
+ prg = '../../hostapd/hostapd'
+ cmd = [prg, '-B', '-ddKt',
+ '-P', pidfile, '-f', logfile, '-g', global_ctrl]
+ subprocess.check_call(cmd)
+
+ hglobal = hostapd.HostapdGlobal(global_ctrl_override=global_ctrl)
+ apdev_ft = {'ifname': iface}
+ apdev2 = [apdev_ft, apdev[1]]
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params['bridge'] = 'br0ft'
+ hapd0 = hostapd.add_ap(apdev2[0], params,
+ global_ctrl_override=global_ctrl)
+ apdev2[0]['bssid'] = hapd0.own_addr()
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params['bridge'] = 'br1ft'
+ hapd1 = hostapd.add_ap(apdev2[1], params)
+
+ run_roams(dev[0], apdev2, hapd0, hapd1, ssid, passphrase,
+ over_ds=over_ds, test_connectivity=False, roams=2)
+
+ hglobal.terminate()
+
+ if os.path.exists(pidfile):
+ with open(pidfile, 'r') as f:
+ pid = int(f.read())
+ f.close()
+ os.kill(pid, signal.SIGTERM)
+
+def test_ap_ft_over_ds_ocv(dev, apdev):
+ """WPA2-PSK-FT AP over DS"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params["ocv"] = "1"
+ try:
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params["ocv"] = "1"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ ocv="1")
+
+def test_ap_ft_over_ds_disabled(dev, apdev):
+ """WPA2-PSK-FT AP over DS disabled"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['ft_over_ds'] = '0'
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['ft_over_ds'] = '0'
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ fail_test=True)
+
+def test_ap_ft_vlan_over_ds(dev, apdev):
+ """WPA2-PSK-FT AP over DS with VLAN"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ hostapd.send_file(apdev[1], filename, filename)
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ conndev="brvlan1")
+ check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-4"),
+ ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-4")])
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_ft_over_ds_many(dev, apdev):
+ """WPA2-PSK-FT AP over DS multiple times"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ roams=50)
+
+def test_ap_ft_vlan_over_ds_many(dev, apdev):
+ """WPA2-PSK-FT AP over DS with VLAN multiple times"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ hostapd.send_file(apdev[1], filename, filename)
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ roams=50, conndev="brvlan1")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+@remote_compatible
+def test_ap_ft_over_ds_unknown_target(dev, apdev):
+ """WPA2-PSK-FT AP"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ dev[0].roam_over_ds("02:11:22:33:44:55", fail_test=True)
+
+@remote_compatible
+def test_ap_ft_over_ds_unexpected(dev, apdev):
+ """WPA2-PSK-FT AP over DS and unexpected response"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
+ ap1 = apdev[0]
+ ap2 = apdev[1]
+ hapd1ap = hapd0
+ hapd2ap = hapd1
+ else:
+ ap1 = apdev[1]
+ ap2 = apdev[0]
+ hapd1ap = hapd1
+ hapd2ap = hapd0
+
+ addr = dev[0].own_addr()
+ hapd1ap.set("ext_mgmt_frame_handling", "1")
+ logger.info("Foreign STA address")
+ msg = {}
+ msg['fc'] = 13 << 4
+ msg['da'] = addr
+ msg['sa'] = ap1['bssid']
+ msg['bssid'] = ap1['bssid']
+ msg['payload'] = binascii.unhexlify("06021122334455660102030405060000")
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("No over-the-DS in progress")
+ msg['payload'] = binascii.unhexlify("0602" + addr.replace(':', '') + "0102030405060000")
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("Non-zero status code")
+ msg['payload'] = binascii.unhexlify("0602" + addr.replace(':', '') + "0102030405060100")
+ hapd1ap.mgmt_tx(msg)
+
+ hapd1ap.dump_monitor()
+
+ dev[0].scan_for_bss(ap2['bssid'], freq="2412")
+ if "OK" not in dev[0].request("FT_DS " + ap2['bssid']):
+ raise Exception("FT_DS failed")
+
+ req = hapd1ap.mgmt_rx()
+
+ logger.info("Foreign Target AP")
+ msg['payload'] = binascii.unhexlify("0602" + addr.replace(':', '') + "0102030405060000")
+ hapd1ap.mgmt_tx(msg)
+
+ addrs = addr.replace(':', '') + ap2['bssid'].replace(':', '')
+
+ logger.info("No IEs")
+ msg['payload'] = binascii.unhexlify("0602" + addrs + "0000")
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("Invalid IEs (trigger parsing failure)")
+ msg['payload'] = binascii.unhexlify("0602" + addrs + "00003700")
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("Too short MDIE")
+ msg['payload'] = binascii.unhexlify("0602" + addrs + "000036021122")
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("Mobility domain mismatch")
+ msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603112201")
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("No FTIE")
+ msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201")
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("FTIE SNonce mismatch")
+ msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201375e0000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + "1000000000000000000000000000000000000000000000000000000000000001" + "030a6e6173322e77312e6669")
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("No R0KH-ID subelem in FTIE")
+ snonce = binascii.hexlify(req['payload'][111:111+32]).decode()
+ msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b20137520000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce)
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("No R0KH-ID subelem mismatch in FTIE")
+ snonce = binascii.hexlify(req['payload'][111:111+32]).decode()
+ msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201375e0000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce + "030a11223344556677889900")
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("No R1KH-ID subelem in FTIE")
+ r0khid = binascii.hexlify(req['payload'][145:145+10]).decode()
+ msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b201375e0000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce + "030a" + r0khid)
+ hapd1ap.mgmt_tx(msg)
+
+ logger.info("No RSNE")
+ r0khid = binascii.hexlify(req['payload'][145:145+10]).decode()
+ msg['payload'] = binascii.unhexlify("0602" + addrs + "00003603a1b20137660000" + "00000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000" + snonce + "030a" + r0khid + "0106000102030405")
+ hapd1ap.mgmt_tx(msg)
+
+def test_ap_ft_pmf_over_ds(dev, apdev):
+ """WPA2-PSK-FT AP over DS with PMF"""
+ run_ap_ft_pmf_bip_over_ds(dev, apdev, None)
+
+def test_ap_ft_pmf_bip_cmac_128_over_ds(dev, apdev):
+ """WPA2-PSK-FT AP over DS with PMF/BIP-CMAC-128"""
+ run_ap_ft_pmf_bip_over_ds(dev, apdev, "AES-128-CMAC")
+
+def test_ap_ft_pmf_bip_gmac_128_over_ds(dev, apdev):
+ """WPA2-PSK-FT AP over DS with PMF/BIP-GMAC-128"""
+ run_ap_ft_pmf_bip_over_ds(dev, apdev, "BIP-GMAC-128")
+
+def test_ap_ft_pmf_bip_gmac_256_over_ds(dev, apdev):
+ """WPA2-PSK-FT AP over DS with PMF/BIP-GMAC-256"""
+ run_ap_ft_pmf_bip_over_ds(dev, apdev, "BIP-GMAC-256")
+
+def test_ap_ft_pmf_bip_cmac_256_over_ds(dev, apdev):
+ """WPA2-PSK-FT AP over DS with PMF/BIP-CMAC-256"""
+ run_ap_ft_pmf_bip_over_ds(dev, apdev, "BIP-CMAC-256")
+
+def run_ap_ft_pmf_bip_over_ds(dev, apdev, cipher):
+ if cipher and cipher not in dev[0].get_capability("group_mgmt"):
+ raise HwsimSkip("Cipher %s not supported" % cipher)
+
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ if cipher:
+ params["group_mgmt_cipher"] = cipher
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ if cipher:
+ params["group_mgmt_cipher"] = cipher
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ Wlantest.setup(hapd0)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase(passphrase)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ group_mgmt=cipher)
+
+def test_ap_ft_over_ds_pull(dev, apdev):
+ """WPA2-PSK-FT AP over DS (pull PMK)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
+
+def test_ap_ft_over_ds_pull_old_key(dev, apdev):
+ """WPA2-PSK-FT AP over DS (pull PMK; old key)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1_old_key(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2_old_key(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
+
+def test_ap_ft_over_ds_pull_vlan(dev, apdev):
+ """WPA2-PSK-FT AP over DS (pull PMK) with VLAN"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ hostapd.send_file(apdev[1], filename, filename)
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ conndev="brvlan1")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def start_ft_sae(dev, apdev, wpa_ptk_rekey=None, sae_pwe=None,
+ rsne_override=None, rsnxe_override=None,
+ no_beacon_rsnxe2=False, ext_key_id=False,
+ skip_prune_assoc=False, ft_rsnxe_used=False,
+ sae_transition=False):
+ if "SAE" not in dev.get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-SAE"
+ if wpa_ptk_rekey:
+ params['wpa_ptk_rekey'] = str(wpa_ptk_rekey)
+ if sae_pwe is not None:
+ params['sae_pwe'] = sae_pwe
+ if rsne_override:
+ params['rsne_override_ft'] = rsne_override
+ if rsnxe_override:
+ params['rsnxe_override_ft'] = rsnxe_override
+ if ext_key_id:
+ params['extended_key_id'] = '1'
+ if skip_prune_assoc:
+ params['skip_prune_assoc'] = '1'
+ if ft_rsnxe_used:
+ params['ft_rsnxe_used'] = '1'
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ if not sae_transition:
+ params['wpa_key_mgmt'] = "FT-SAE"
+ if wpa_ptk_rekey:
+ params['wpa_ptk_rekey'] = str(wpa_ptk_rekey)
+ if sae_pwe is not None:
+ params['sae_pwe'] = sae_pwe
+ if rsne_override:
+ params['rsne_override_ft'] = rsne_override
+ if rsnxe_override:
+ params['rsnxe_override_ft'] = rsnxe_override
+ if no_beacon_rsnxe2:
+ params['no_beacon_rsnxe'] = "1"
+ if ext_key_id:
+ params['extended_key_id'] = '1'
+ if skip_prune_assoc:
+ params['skip_prune_assoc'] = '1'
+ if ft_rsnxe_used:
+ params['ft_rsnxe_used'] = '1'
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ key_mgmt = hapd1.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "FT-SAE" and not sae_transition:
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+
+ dev.request("SET sae_groups ")
+ return hapd0, hapd1
+
+def test_ap_ft_sae(dev, apdev):
+ """WPA2-PSK-FT-SAE AP"""
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
+
+def test_ap_ft_sae_transition(dev, apdev):
+ """WPA2-PSK-FT-SAE/PSK AP"""
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_transition=True)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
+ sae_transition=True)
+
+def test_ap_ft_sae_h2e(dev, apdev):
+ """WPA2-PSK-FT-SAE AP (H2E)"""
+ try:
+ dev[0].set("sae_pwe", "2")
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2")
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_h2e_and_loop(dev, apdev):
+ """WPA2-PSK-FT-SAE AP (AP H2E, STA loop)"""
+ dev[0].set("sae_pwe", "0")
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2")
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
+
+def test_ap_ft_sae_h2e_and_loop2(dev, apdev):
+ """WPA2-PSK-FT-SAE AP (AP loop, STA H2E)"""
+ try:
+ dev[0].set("sae_pwe", "2")
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="0")
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_h2e_downgrade_attack(dev, apdev):
+ """WPA2-PSK-FT-SAE AP (H2E downgrade attack)"""
+ try:
+ dev[0].set("sae_pwe", "2")
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
+ no_beacon_rsnxe2=True)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ force_initial_conn_to_first_ap=True,
+ return_after_initial=True)
+ dev[0].scan_for_bss(hapd1.own_addr(), freq="2412")
+ if "OK" not in dev[0].request("ROAM " + hapd1.own_addr()):
+ raise Exception("ROAM command failed")
+ # The target AP is expected to discard Reassociation Response frame due
+ # to RSNXE Used mismatch. This will result in roaming timeout and
+ # returning back to the old AP.
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev and "CTRL-EVENT-ASSOC-REJECT" in ev:
+ pass
+ elif ev and hapd1.own_addr() in ev:
+ raise Exception("Roaming succeeded unexpectedly")
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_ptk_rekey0(dev, apdev):
+ """WPA2-PSK-FT-SAE AP and PTK rekey triggered by station"""
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ ptk_rekey="1", roams=0)
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+
+def test_ap_ft_sae_ptk_rekey1(dev, apdev):
+ """WPA2-PSK-FT-SAE AP and PTK rekey triggered by station"""
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ ptk_rekey="1", only_one_way=True)
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+
+def test_ap_ft_sae_ptk_rekey_ap(dev, apdev):
+ """WPA2-PSK-FT-SAE AP and PTK rekey triggered by AP"""
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, wpa_ptk_rekey=2)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ only_one_way=True)
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+
+def test_ap_ft_sae_ptk_rekey_ap_ext_key_id(dev, apdev):
+ """WPA2-PSK-FT-SAE AP and PTK rekey triggered by AP (Ext Key ID)"""
+ check_ext_key_id_capa(dev[0])
+ try:
+ dev[0].set("extended_key_id", "1")
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, wpa_ptk_rekey=2,
+ ext_key_id=True)
+ check_ext_key_id_capa(hapd0)
+ check_ext_key_id_capa(hapd1)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ only_one_way=True)
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+ idx = int(dev[0].request("GET last_tk_key_idx"))
+ if idx != 1:
+ raise Exception("Unexpected Key ID after TK rekey: %d" % idx)
+ finally:
+ dev[0].set("extended_key_id", "0")
+
+def test_ap_ft_sae_over_ds(dev, apdev):
+ """WPA2-PSK-FT-SAE AP over DS"""
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ over_ds=True)
+
+def test_ap_ft_sae_over_ds_ptk_rekey0(dev, apdev):
+ """WPA2-PSK-FT-SAE AP over DS and PTK rekey triggered by station"""
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ over_ds=True, ptk_rekey="1", roams=0)
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+
+def test_ap_ft_sae_over_ds_ptk_rekey1(dev, apdev):
+ """WPA2-PSK-FT-SAE AP over DS and PTK rekey triggered by station"""
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ over_ds=True, ptk_rekey="1", only_one_way=True)
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+
+def test_ap_ft_sae_over_ds_ptk_rekey_ap(dev, apdev):
+ """WPA2-PSK-FT-SAE AP over DS and PTK rekey triggered by AP"""
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, wpa_ptk_rekey=2)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ over_ds=True, only_one_way=True)
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+
+def test_ap_ft_sae_h2e_rsne_override(dev, apdev):
+ """WPA2-PSK-FT-SAE AP (H2E) and RSNE override (same value)"""
+ try:
+ dev[0].set("sae_pwe", "2")
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
+ rsne_override="30260100000fac040100000fac040100000fac090c000100" + 16*"ff")
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_h2e_rsnxe_override(dev, apdev):
+ """WPA2-PSK-FT-SAE AP (H2E) and RSNXE override (same value)"""
+ try:
+ dev[0].set("sae_pwe", "2")
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
+ rsnxe_override="F40120")
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_h2e_rsne_mismatch(dev, apdev):
+ """WPA2-PSK-FT-SAE AP (H2E) and RSNE mismatch"""
+ try:
+ dev[0].set("sae_pwe", "2")
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
+ rsne_override="30260100000fac040100000fac040100000fac090c010100" + 16*"ff")
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ fail_test=True)
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_h2e_rsne_mismatch_pmkr1name(dev, apdev):
+ """WPA2-PSK-FT-SAE AP (H2E) and RSNE mismatch in PMKR1Name"""
+ try:
+ dev[0].set("sae_pwe", "2")
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
+ rsne_override="30260100000fac040100000fac040100000fac090c000100" + 16*"00")
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ fail_test=True)
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_h2e_rsnxe_mismatch(dev, apdev):
+ """WPA2-PSK-FT-SAE AP (H2E) and RSNXE mismatch"""
+ try:
+ dev[0].set("sae_pwe", "2")
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2",
+ rsnxe_override="F40160")
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True,
+ fail_test=True)
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_rsnxe_used_mismatch(dev, apdev):
+ """FT-SAE AP and unexpected RSNXE Used in ReassocReq"""
+ try:
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="2")
+ dev[0].set("sae_pwe", "0")
+ dev[0].set("ft_rsnxe_used", "1")
+ next = run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
+ sae=True, return_after_initial=True)
+ if "OK" not in dev[0].request("ROAM " + next):
+ raise Exception("ROAM command failed")
+ # The target AP is expected to discard Reassociation Request frame due
+ # to RSNXE Used mismatch. This will result in roaming timeout and
+ # returning back to the old AP.
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=5)
+ if ev and next in ev:
+ raise Exception("Roaming succeeded unexpectedly")
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_rsnxe_used_mismatch2(dev, apdev):
+ """FT-SAE AP and unexpected RSNXE Used in ReassocResp"""
+ try:
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, sae_pwe="0",
+ ft_rsnxe_used=True)
+ dev[0].set("sae_pwe", "2")
+ next = run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
+ sae=True, return_after_initial=True)
+ if "OK" not in dev[0].request("ROAM " + next):
+ raise Exception("ROAM command failed")
+ # The STA is expected to discard Reassociation Response frame due to
+ # RSNXE Used mismatch. This will result in returning back to the old AP.
+ ev = dev[0].wait_disconnected()
+ if next not in ev:
+ raise Exception("Unexpected disconnection BSSID: " + ev)
+ if "reason=13 locally_generated=1" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+ ev = dev[0].wait_connected()
+ if next in ev:
+ raise Exception("Roaming succeeded unexpectedly")
+
+ hapd0.set("ft_rsnxe_used", "0")
+ hapd1.set("ft_rsnxe_used", "0")
+ dev[0].roam(next);
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_pw_id(dev, apdev):
+ """FT-SAE with Password Identifier"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ ssid = "test-ft"
+
+ params = ft_params1(ssid=ssid)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-SAE"
+ params['sae_password'] = 'secret|id=pwid'
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-SAE"
+ params['sae_password'] = 'secret|id=pwid'
+ hapd = hostapd.add_ap(apdev[1], params)
+
+ dev[0].request("SET sae_groups ")
+ run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase=None, sae=True,
+ sae_password="secret", sae_password_id="pwid")
+
+def test_ap_ft_sae_with_both_akms(dev, apdev):
+ """SAE + FT-SAE configuration"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-SAE SAE"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-SAE SAE"
+ hapd = hostapd.add_ap(apdev[1], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "FT-SAE":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+
+ dev[0].request("SET sae_groups ")
+ run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase, sae=True,
+ sae_and_psk=True)
+
+def test_ap_ft_sae_pmksa_caching(dev, apdev):
+ """WPA2-FT-SAE AP and PMKSA caching for initial mobility domain association"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-SAE"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-SAE"
+ hapd = hostapd.add_ap(apdev[1], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "FT-SAE":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+
+ dev[0].request("SET sae_groups ")
+ run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase, sae=True,
+ pmksa_caching=True)
+
+def test_ap_ft_sae_pmksa_caching_pwe(dev, apdev):
+ """WPA2-FT-SAE AP and PMKSA caching for initial mobility domain association (STA PWE both)"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-SAE"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-SAE"
+ hapd = hostapd.add_ap(apdev[1], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "FT-SAE":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+
+ try:
+ dev[0].request("SET sae_groups ")
+ dev[0].set("sae_pwe", "2")
+ run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase, sae=True,
+ pmksa_caching=True)
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def test_ap_ft_sae_pmksa_caching_h2e(dev, apdev):
+ """WPA2-FT-SAE AP and PMKSA caching for initial mobility domain association (H2E)"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-SAE"
+ params['sae_pwe'] = "1"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-SAE"
+ params['sae_pwe'] = "1"
+ hapd = hostapd.add_ap(apdev[1], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "FT-SAE":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+
+ try:
+ dev[0].request("SET sae_groups ")
+ dev[0].set("sae_pwe", "1")
+ run_roams(dev[0], apdev, hapd0, hapd, ssid, passphrase, sae=True,
+ pmksa_caching=True)
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def generic_ap_ft_eap(dev, apdev, vlan=False, cui=False, over_ds=False,
+ discovery=False, roams=1, wpa_ptk_rekey=0,
+ only_one_way=False):
+ ssid = "test-ft"
+ passphrase = "12345678"
+ if vlan:
+ identity = "gpsk-vlan1"
+ conndev = "brvlan1"
+ elif cui:
+ identity = "gpsk-cui"
+ conndev = False
+ else:
+ identity = "gpsk user"
+ conndev = False
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase, discovery=discovery)
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ if vlan:
+ params["dynamic_vlan"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd = hostapd.add_ap(apdev[0], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "FT-EAP":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+ params = ft_params2(ssid=ssid, passphrase=passphrase, discovery=discovery)
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ if vlan:
+ params["dynamic_vlan"] = "1"
+ if wpa_ptk_rekey:
+ params["wpa_ptk_rekey"] = str(wpa_ptk_rekey)
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase, eap=True,
+ over_ds=over_ds, roams=roams, eap_identity=identity,
+ conndev=conndev, only_one_way=only_one_way)
+ if "[WPA2-FT/EAP-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing RSN element info")
+ check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-3"),
+ ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-3")])
+ if only_one_way:
+ return
+
+ # Verify EAPOL reauthentication after FT protocol
+ if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
+ ap = hapd
+ else:
+ ap = hapd1
+ ap.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not succeed")
+ time.sleep(0.1)
+ if conndev:
+ hwsim_utils.test_connectivity_iface(dev[0], ap, conndev)
+ else:
+ hwsim_utils.test_connectivity(dev[0], ap)
+
+def test_ap_ft_eap(dev, apdev):
+ """WPA2-EAP-FT AP"""
+ generic_ap_ft_eap(dev, apdev)
+
+def test_ap_ft_eap_cui(dev, apdev):
+ """WPA2-EAP-FT AP with CUI"""
+ generic_ap_ft_eap(dev, apdev, vlan=False, cui=True)
+
+def test_ap_ft_eap_vlan(dev, apdev):
+ """WPA2-EAP-FT AP with VLAN"""
+ generic_ap_ft_eap(dev, apdev, vlan=True)
+
+def test_ap_ft_eap_vlan_multi(dev, apdev):
+ """WPA2-EAP-FT AP with VLAN"""
+ generic_ap_ft_eap(dev, apdev, vlan=True, roams=50)
+
+def test_ap_ft_eap_over_ds(dev, apdev):
+ """WPA2-EAP-FT AP using over-the-DS"""
+ generic_ap_ft_eap(dev, apdev, over_ds=True)
+
+def test_ap_ft_eap_dis(dev, apdev):
+ """WPA2-EAP-FT AP with AP discovery"""
+ generic_ap_ft_eap(dev, apdev, discovery=True)
+
+def test_ap_ft_eap_dis_over_ds(dev, apdev):
+ """WPA2-EAP-FT AP with AP discovery and over-the-DS"""
+ generic_ap_ft_eap(dev, apdev, over_ds=True, discovery=True)
+
+def test_ap_ft_eap_vlan(dev, apdev):
+ """WPA2-EAP-FT AP with VLAN"""
+ generic_ap_ft_eap(dev, apdev, vlan=True)
+
+def test_ap_ft_eap_vlan_multi(dev, apdev):
+ """WPA2-EAP-FT AP with VLAN"""
+ generic_ap_ft_eap(dev, apdev, vlan=True, roams=50)
+
+def test_ap_ft_eap_vlan_over_ds(dev, apdev):
+ """WPA2-EAP-FT AP with VLAN + over_ds"""
+ generic_ap_ft_eap(dev, apdev, vlan=True, over_ds=True)
+
+def test_ap_ft_eap_vlan_over_ds_multi(dev, apdev):
+ """WPA2-EAP-FT AP with VLAN + over_ds"""
+ generic_ap_ft_eap(dev, apdev, vlan=True, over_ds=True, roams=50)
+
+def generic_ap_ft_eap_pull(dev, apdev, vlan=False):
+ """WPA2-EAP-FT AP (pull PMK)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+ if vlan:
+ identity = "gpsk-vlan1"
+ conndev = "brvlan1"
+ else:
+ identity = "gpsk user"
+ conndev = False
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params["pmk_r1_push"] = "0"
+ if vlan:
+ params["dynamic_vlan"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd = hostapd.add_ap(apdev[0], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "FT-EAP":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params["pmk_r1_push"] = "0"
+ if vlan:
+ params["dynamic_vlan"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase, eap=True,
+ eap_identity=identity, conndev=conndev)
+
+def test_ap_ft_eap_pull(dev, apdev):
+ """WPA2-EAP-FT AP (pull PMK)"""
+ generic_ap_ft_eap_pull(dev, apdev)
+
+def test_ap_ft_eap_pull_vlan(dev, apdev):
+ """WPA2-EAP-FT AP (pull PMK) - with VLAN"""
+ generic_ap_ft_eap_pull(dev, apdev, vlan=True)
+
+def test_ap_ft_eap_pull_wildcard(dev, apdev):
+ """WPA2-EAP-FT AP (pull PMK) - wildcard R0KH/R1KH"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase, discovery=True)
+ params['wpa_key_mgmt'] = "WPA-EAP FT-EAP"
+ params["ieee8021x"] = "1"
+ params["pmk_r1_push"] = "0"
+ params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["ft_psk_generate_local"] = "1"
+ params["eap_server"] = "0"
+ params["rkh_pos_timeout"] = "100"
+ params["rkh_neg_timeout"] = "50"
+ params["rkh_pull_timeout"] = "1234"
+ params["rkh_pull_retries"] = "10"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase, discovery=True)
+ params['wpa_key_mgmt'] = "WPA-EAP FT-EAP"
+ params["ieee8021x"] = "1"
+ params["pmk_r1_push"] = "0"
+ params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["ft_psk_generate_local"] = "1"
+ params["eap_server"] = "0"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase, eap=True)
+
+def test_ap_ft_eap_pull_wildcard_multi_bss(dev, apdev, params):
+ """WPA2-EAP-FT AP (pull PMK) - wildcard R0KH/R1KH with multiple BSSs"""
+ bssconf = os.path.join(params['logdir'],
+ 'ap_ft_eap_pull_wildcard_multi_bss.bss.conf')
+ ssid = "test-ft"
+ passphrase = "12345678"
+ radius = hostapd.radius_params()
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase, discovery=True)
+ params['wpa_key_mgmt'] = "WPA-EAP FT-EAP"
+ params["ieee8021x"] = "1"
+ params["pmk_r1_push"] = "0"
+ params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["eap_server"] = "0"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd = hostapd.add_ap(apdev[0], params)
+ ifname2 = apdev[0]['ifname'] + "-2"
+ bssid2 = "02:00:00:00:03:01"
+ params['nas_identifier'] = "nas1b.w1.fi"
+ params['r1_key_holder'] = "000102030415"
+ with open(bssconf, 'w') as f:
+ f.write("driver=nl80211\n")
+ f.write("hw_mode=g\n")
+ f.write("channel=1\n")
+ f.write("ieee80211n=1\n")
+ f.write("interface=%s\n" % ifname2)
+ f.write("bssid=%s\n" % bssid2)
+ f.write("ctrl_interface=/var/run/hostapd\n")
+
+ fields = ["ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
+ "wpa", "rsn_pairwise", "auth_server_addr"]
+ for name in fields:
+ f.write("%s=%s\n" % (name, params[name]))
+ for name, val in params.items():
+ if name in fields:
+ continue
+ f.write("%s=%s\n" % (name, val))
+ hapd2 = hostapd.add_bss(apdev[0], ifname2, bssconf)
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase, discovery=True)
+ params['wpa_key_mgmt'] = "WPA-EAP FT-EAP"
+ params["ieee8021x"] = "1"
+ params["pmk_r1_push"] = "0"
+ params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["eap_server"] = "0"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ # The first iteration of the roaming test will use wildcard R0KH discovery
+ # and RRB sequence number synchronization while the second iteration shows
+ # the clean RRB exchange where those extra steps are not needed.
+ for i in range(2):
+ hapd.note("Test iteration %d" % i)
+ dev[0].note("Test iteration %d" % i)
+
+ id = dev[0].connect(ssid, key_mgmt="FT-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ bssid=bssid2,
+ scan_freq="2412")
+ res = dev[0].get_status_field("bssid")
+ if res != bssid2:
+ raise Exception("Unexpected BSSID after initial connection: " + res)
+
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ dev[0].set_network(id, "bssid", "00:00:00:00:00:00")
+ dev[0].roam(apdev[1]['bssid'])
+ res = dev[0].get_status_field("bssid")
+ if res != apdev[1]['bssid']:
+ raise Exception("Unexpected BSSID after first roam: " + res)
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].roam(apdev[0]['bssid'])
+ res = dev[0].get_status_field("bssid")
+ if res != apdev[0]['bssid']:
+ raise Exception("Unexpected BSSID after second roam: " + res)
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ hapd2.dump_monitor()
+
+@remote_compatible
+def test_ap_ft_mismatching_rrb_key_push(dev, apdev):
+ """WPA2-PSK-FT AP over DS with mismatching RRB key (push)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ fail_test=True)
+
+@remote_compatible
+def test_ap_ft_mismatching_rrb_key_pull(dev, apdev):
+ """WPA2-PSK-FT AP over DS with mismatching RRB key (pull)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ fail_test=True)
+
+@remote_compatible
+def test_ap_ft_mismatching_r0kh_id_pull(dev, apdev):
+ """WPA2-PSK-FT AP over DS with mismatching R0KH-ID (pull)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ params["nas_identifier"] = "nas0.w1.fi"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ dev[0].roam_over_ds(apdev[1]['bssid'], fail_test=True)
+
+@remote_compatible
+def test_ap_ft_mismatching_rrb_r0kh_push(dev, apdev):
+ """WPA2-PSK-FT AP over DS with mismatching R0KH key (push)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ fail_test=True)
+
+@remote_compatible
+def test_ap_ft_mismatching_rrb_r0kh_pull(dev, apdev):
+ """WPA2-PSK-FT AP over DS with mismatching R0KH key (pull)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ fail_test=True)
+
+def test_ap_ft_mismatching_rrb_key_push_eap(dev, apdev):
+ """WPA2-EAP-FT AP over DS with mismatching RRB key (push)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ fail_test=True, eap=True)
+
+def test_ap_ft_mismatching_rrb_key_pull_eap(dev, apdev):
+ """WPA2-EAP-FT AP over DS with mismatching RRB key (pull)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ fail_test=True, eap=True)
+
+def test_ap_ft_mismatching_r0kh_id_pull_eap(dev, apdev):
+ """WPA2-EAP-FT AP over DS with mismatching R0KH-ID (pull)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ params["nas_identifier"] = "nas0.w1.fi"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="FT-EAP", proto="WPA2", ieee80211w="1",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ dev[0].roam_over_ds(apdev[1]['bssid'], fail_test=True)
+
+def test_ap_ft_mismatching_rrb_r0kh_push_eap(dev, apdev):
+ """WPA2-EAP-FT AP over DS with mismatching R0KH key (push)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ fail_test=True, eap=True)
+
+def test_ap_ft_mismatching_rrb_r0kh_pull_eap(dev, apdev):
+ """WPA2-EAP-FT AP over DS with mismatching R0KH key (pull)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ radius = hostapd.radius_params()
+ params = ft_params1_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["pmk_r1_push"] = "0"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ fail_test=True, eap=True)
+
+def test_ap_ft_gtk_rekey(dev, apdev):
+ """WPA2-PSK-FT AP and GTK rekey"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['wpa_group_rekey'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ ieee80211w="1", scan_freq="2412")
+
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out after initial association")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['wpa_group_rekey'] = '1'
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ dev[0].roam(apdev[1]['bssid'])
+ if dev[0].get_status_field('bssid') != apdev[1]['bssid']:
+ raise Exception("Did not connect to correct AP")
+ hwsim_utils.test_connectivity(dev[0], hapd1)
+
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out after FT protocol")
+ hwsim_utils.test_connectivity(dev[0], hapd1)
+
+def test_ft_psk_key_lifetime_in_memory(dev, apdev, params):
+ """WPA2-PSK-FT and key lifetime in memory"""
+ ssid = "test-ft"
+ passphrase = "04c2726b4b8d5f1b4db9c07aa4d9e9d8f765cb5d25ec817e6cc4fcdd5255db0"
+ psk = '93c90846ff67af9037ed83fb72b63dbeddaa81d47f926c20909b5886f1d9358d'
+ pmk = binascii.unhexlify(psk)
+ p = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], p)
+ p = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], p)
+
+ pid = find_wpas_process(dev[0])
+
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ # The decrypted copy of GTK is freed only after the CTRL-EVENT-CONNECTED
+ # event has been delivered, so verify that wpa_supplicant has returned to
+ # eloop before reading process memory.
+ time.sleep(1)
+ dev[0].ping()
+
+ buf = read_process_memory(pid, pmk)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].relog()
+ pmkr0 = None
+ pmkr1 = None
+ ptk = None
+ gtk = None
+ with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+ for l in f.readlines():
+ if "FT: PMK-R0 - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ pmkr0 = binascii.unhexlify(val)
+ if "FT: PMK-R1 - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ pmkr1 = binascii.unhexlify(val)
+ if "FT: KCK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ kck = binascii.unhexlify(val)
+ if "FT: KEK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ kek = binascii.unhexlify(val)
+ if "FT: TK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ tk = binascii.unhexlify(val)
+ if "WPA: Group Key - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ gtk = binascii.unhexlify(val)
+ if not pmkr0 or not pmkr1 or not kck or not kek or not tk or not gtk:
+ raise Exception("Could not find keys from debug log")
+ if len(gtk) != 16:
+ raise Exception("Unexpected GTK length")
+
+ logger.info("Checking keys in memory while associated")
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, pmkr0, "PMK-R0")
+ get_key_locations(buf, pmkr1, "PMK-R1")
+ if pmk not in buf:
+ raise HwsimSkip("PMK not found while associated")
+ if pmkr0 not in buf:
+ raise HwsimSkip("PMK-R0 not found while associated")
+ if pmkr1 not in buf:
+ raise HwsimSkip("PMK-R1 not found while associated")
+ if kck not in buf:
+ raise Exception("KCK not found while associated")
+ if kek not in buf:
+ raise Exception("KEK not found while associated")
+ #if tk in buf:
+ # raise Exception("TK found from memory")
+
+ logger.info("Checking keys in memory after disassociation")
+ buf = read_process_memory(pid, pmk)
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, pmkr0, "PMK-R0")
+ get_key_locations(buf, pmkr1, "PMK-R1")
+
+ # Note: PMK/PSK is still present in network configuration
+
+ fname = os.path.join(params['logdir'],
+ 'ft_psk_key_lifetime_in_memory.memctx-')
+ verify_not_present(buf, pmkr0, fname, "PMK-R0")
+ verify_not_present(buf, pmkr1, fname, "PMK-R1")
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ if gtk in buf:
+ get_key_locations(buf, gtk, "GTK")
+ verify_not_present(buf, gtk, fname, "GTK")
+
+ dev[0].request("REMOVE_NETWORK all")
+
+ logger.info("Checking keys in memory after network profile removal")
+ buf = read_process_memory(pid, pmk)
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, pmkr0, "PMK-R0")
+ get_key_locations(buf, pmkr1, "PMK-R1")
+
+ verify_not_present(buf, pmk, fname, "PMK")
+ verify_not_present(buf, pmkr0, fname, "PMK-R0")
+ verify_not_present(buf, pmkr1, fname, "PMK-R1")
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ verify_not_present(buf, gtk, fname, "GTK")
+
+@remote_compatible
+def test_ap_ft_invalid_resp(dev, apdev):
+ """WPA2-PSK-FT AP and invalid response IEs"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ tests = [
+ # Various IEs for test coverage. The last one is FTIE with invalid
+ # R1KH-ID subelement.
+ "020002000000" + "3800" + "38051122334455" + "3754000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010100",
+ # FTIE with invalid R0KH-ID subelement (len=0).
+ "020002000000" + "3754000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010300",
+ # FTIE with invalid R0KH-ID subelement (len=49).
+ "020002000000" + "378500010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001033101020304050607080910111213141516171819202122232425262728293031323334353637383940414243444546474849",
+ # Invalid RSNE.
+ "020002000000" + "3000",
+ # Required IEs missing from protected IE count.
+ "020002000000" + "3603a1b201" + "375200010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001" + "3900",
+ # RIC missing from protected IE count.
+ "020002000000" + "3603a1b201" + "375200020203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001" + "3900",
+ # Protected IE missing.
+ "020002000000" + "3603a1b201" + "375200ff0203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001020304050607080900010203040506070809000102030405060708090001" + "3900" + "0000"]
+ for t in tests:
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ hapd1.set("ext_mgmt_frame_handling", "1")
+ hapd1.dump_monitor()
+ if "OK" not in dev[0].request("ROAM " + apdev[1]['bssid']):
+ raise Exception("ROAM failed")
+ auth = None
+ for i in range(20):
+ msg = hapd1.mgmt_rx()
+ if msg['subtype'] == 11:
+ auth = msg
+ break
+ if not auth:
+ raise Exception("Authentication frame not seen")
+
+ resp = {}
+ resp['fc'] = auth['fc']
+ resp['da'] = auth['sa']
+ resp['sa'] = auth['da']
+ resp['bssid'] = auth['bssid']
+ resp['payload'] = binascii.unhexlify(t)
+ hapd1.mgmt_tx(resp)
+ hapd1.set("ext_mgmt_frame_handling", "0")
+ dev[0].wait_disconnected()
+
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+def test_ap_ft_gcmp_256(dev, apdev):
+ """WPA2-PSK-FT AP with GCMP-256 cipher"""
+ if "GCMP-256" not in dev[0].get_capability("pairwise"):
+ raise HwsimSkip("Cipher GCMP-256 not supported")
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['rsn_pairwise'] = "GCMP-256"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['rsn_pairwise'] = "GCMP-256"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
+ pairwise_cipher="GCMP-256", group_cipher="GCMP-256")
+
+def setup_ap_ft_oom(dev, apdev):
+ skip_with_fips(dev[0])
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
+ dst = apdev[1]['bssid']
+ else:
+ dst = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(dst, freq="2412")
+
+ return dst
+
+def test_ap_ft_oom(dev, apdev):
+ """WPA2-PSK-FT and OOM"""
+ dst = setup_ap_ft_oom(dev, apdev)
+ with alloc_fail(dev[0], 1, "wpa_ft_gen_req_ies"):
+ dev[0].roam(dst, check_bssid=False, fail_test=True)
+
+def test_ap_ft_oom2(dev, apdev):
+ """WPA2-PSK-FT and OOM (2)"""
+ dst = setup_ap_ft_oom(dev, apdev)
+ with fail_test(dev[0], 1, "wpa_ft_mic"):
+ dev[0].roam(dst, fail_test=True, assoc_reject_ok=True)
+
+def test_ap_ft_oom3(dev, apdev):
+ """WPA2-PSK-FT and OOM (3)"""
+ dst = setup_ap_ft_oom(dev, apdev)
+ with fail_test(dev[0], 1, "os_get_random;wpa_ft_prepare_auth_request"):
+ dev[0].roam(dst)
+
+def test_ap_ft_oom4(dev, apdev):
+ """WPA2-PSK-FT and OOM (4)"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+ dst = setup_ap_ft_oom(dev, apdev)
+ dev[0].request("REMOVE_NETWORK all")
+ with alloc_fail(dev[0], 1, "=sme_update_ft_ies"):
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+def test_ap_ft_ap_oom(dev, apdev):
+ """WPA2-PSK-FT and AP OOM"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ with alloc_fail(hapd0, 1, "wpa_ft_store_pmk_r0"):
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ # This roam will fail due to missing PMK-R0 (OOM prevented storing it)
+ dev[0].roam(bssid1, check_bssid=False)
+
+def test_ap_ft_ap_oom2(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 2"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ with alloc_fail(hapd0, 1, "wpa_ft_store_pmk_r1"):
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ dev[0].roam(bssid1)
+ if dev[0].get_status_field('bssid') != bssid1:
+ raise Exception("Did not roam to AP1")
+ # This roam will fail due to missing PMK-R1 (OOM prevented storing it)
+ dev[0].roam(bssid0)
+
+def test_ap_ft_ap_oom3(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 3"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ with alloc_fail(hapd1, 1, "wpa_ft_pull_pmk_r1"):
+ # This will fail due to not being able to send out PMK-R1 pull request
+ dev[0].roam(bssid1, check_bssid=False)
+
+ with fail_test(hapd1, 2, "os_get_random;wpa_ft_pull_pmk_r1"):
+ # This will fail due to not being able to send out PMK-R1 pull request
+ dev[0].roam(bssid1, check_bssid=False)
+
+ with fail_test(hapd1, 2, "aes_siv_encrypt;wpa_ft_pull_pmk_r1"):
+ # This will fail due to not being able to send out PMK-R1 pull request
+ dev[0].roam(bssid1, check_bssid=False)
+
+def test_ap_ft_ap_oom3b(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 3b"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ with fail_test(hapd1, 1, "os_get_random;wpa_ft_pull_pmk_r1"):
+ # This will fail due to not being able to send out PMK-R1 pull request
+ dev[0].roam(bssid1)
+
+def test_ap_ft_ap_oom4(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 4"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ with alloc_fail(hapd1, 1, "wpa_ft_gtk_subelem"):
+ dev[0].roam(bssid1)
+ if dev[0].get_status_field('bssid') != bssid1:
+ raise Exception("Did not roam to AP1")
+
+ with fail_test(hapd0, 1, "i802_get_seqnum;wpa_ft_gtk_subelem"):
+ dev[0].roam(bssid0)
+ if dev[0].get_status_field('bssid') != bssid0:
+ raise Exception("Did not roam to AP0")
+
+ with fail_test(hapd0, 1, "aes_wrap;wpa_ft_gtk_subelem"):
+ dev[0].roam(bssid1)
+ if dev[0].get_status_field('bssid') != bssid1:
+ raise Exception("Did not roam to AP1")
+
+def test_ap_ft_ap_oom5(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 5"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ with alloc_fail(hapd1, 1, "=wpa_ft_process_auth_req"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+ with fail_test(hapd1, 1, "os_get_random;wpa_ft_process_auth_req"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+ with fail_test(hapd1, 1, "sha256_prf_bits;wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+ with fail_test(hapd1, 3, "wpa_pmk_r1_to_ptk;wpa_ft_process_auth_req"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+ with fail_test(hapd1, 1, "wpa_derive_pmk_r1_name;wpa_ft_process_auth_req"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+def test_ap_ft_ap_oom6(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 6"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ with fail_test(hapd0, 1, "wpa_derive_pmk_r0;wpa_auth_derive_ptk_ft"):
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ with fail_test(hapd0, 1, "wpa_derive_pmk_r1;wpa_auth_derive_ptk_ft"):
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ with fail_test(hapd0, 1, "wpa_pmk_r1_to_ptk;wpa_auth_derive_ptk_ft"):
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+def test_ap_ft_ap_oom7a(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 7a"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ ieee80211w="2", scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ with alloc_fail(hapd1, 1, "wpa_ft_igtk_subelem"):
+ # This will fail to roam
+ dev[0].roam(bssid1)
+
+def test_ap_ft_ap_oom7b(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 7b"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ ieee80211w="2", scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ with fail_test(hapd1, 1, "aes_wrap;wpa_ft_igtk_subelem"):
+ # This will fail to roam
+ dev[0].roam(bssid1)
+
+def test_ap_ft_ap_oom7c(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 7c"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ ieee80211w="2", scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ with alloc_fail(hapd1, 1, "=wpa_sm_write_assoc_resp_ies"):
+ # This will fail to roam
+ dev[0].roam(bssid1)
+
+def test_ap_ft_ap_oom7d(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 7d"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ ieee80211w="2", scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ with fail_test(hapd1, 1, "wpa_ft_mic;wpa_sm_write_assoc_resp_ies"):
+ # This will fail to roam
+ dev[0].roam(bssid1)
+
+def test_ap_ft_ap_oom8(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 8"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['ft_psk_generate_local'] = "1"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['ft_psk_generate_local'] = "1"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ with fail_test(hapd1, 1, "wpa_derive_pmk_r0;wpa_ft_psk_pmk_r1"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+ with fail_test(hapd1, 1, "wpa_derive_pmk_r1;wpa_ft_psk_pmk_r1"):
+ # This will fail to roam
+ dev[0].roam(bssid1, check_bssid=False)
+
+def test_ap_ft_ap_oom9(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 9"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+
+ with alloc_fail(hapd0, 1, "wpa_ft_action_rx"):
+ # This will fail to roam
+ if "OK" not in dev[0].request("FT_DS " + bssid1):
+ raise Exception("FT_DS failed")
+ wait_fail_trigger(hapd0, "GET_ALLOC_FAIL")
+
+ with alloc_fail(hapd1, 1, "wpa_ft_rrb_rx_request"):
+ # This will fail to roam
+ if "OK" not in dev[0].request("FT_DS " + bssid1):
+ raise Exception("FT_DS failed")
+ wait_fail_trigger(hapd1, "GET_ALLOC_FAIL")
+
+ with alloc_fail(hapd1, 1, "wpa_ft_send_rrb_auth_resp"):
+ # This will fail to roam
+ if "OK" not in dev[0].request("FT_DS " + bssid1):
+ raise Exception("FT_DS failed")
+ wait_fail_trigger(hapd1, "GET_ALLOC_FAIL")
+
+def test_ap_ft_ap_oom10(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 10"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid1, freq="2412")
+
+ with fail_test(hapd0, 1, "aes_siv_decrypt;wpa_ft_rrb_rx_pull"):
+ # This will fail to roam
+ if "OK" not in dev[0].request("FT_DS " + bssid1):
+ raise Exception("FT_DS failed")
+ wait_fail_trigger(hapd0, "GET_FAIL")
+
+ with fail_test(hapd0, 1, "wpa_derive_pmk_r1;wpa_ft_rrb_rx_pull"):
+ # This will fail to roam
+ if "OK" not in dev[0].request("FT_DS " + bssid1):
+ raise Exception("FT_DS failed")
+ wait_fail_trigger(hapd0, "GET_FAIL")
+
+ with fail_test(hapd0, 1, "aes_siv_encrypt;wpa_ft_rrb_rx_pull"):
+ # This will fail to roam
+ if "OK" not in dev[0].request("FT_DS " + bssid1):
+ raise Exception("FT_DS failed")
+ wait_fail_trigger(hapd0, "GET_FAIL")
+
+ with fail_test(hapd1, 1, "aes_siv_decrypt;wpa_ft_rrb_rx_resp"):
+ # This will fail to roam
+ if "OK" not in dev[0].request("FT_DS " + bssid1):
+ raise Exception("FT_DS failed")
+ wait_fail_trigger(hapd1, "GET_FAIL")
+
+def test_ap_ft_ap_oom11(dev, apdev):
+ """WPA2-PSK-FT and AP OOM 11"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ dev[0].scan_for_bss(bssid0, freq="2412")
+ with fail_test(hapd0, 1, "wpa_derive_pmk_r1;wpa_ft_generate_pmk_r1"):
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ wait_fail_trigger(hapd0, "GET_FAIL")
+
+ dev[1].scan_for_bss(bssid0, freq="2412")
+ with fail_test(hapd0, 1, "aes_siv_encrypt;wpa_ft_generate_pmk_r1"):
+ dev[1].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ wait_fail_trigger(hapd0, "GET_FAIL")
+
+def test_ap_ft_over_ds_proto_ap(dev, apdev):
+ """WPA2-PSK-FT AP over DS protocol testing for AP processing"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+ _bssid0 = bssid0.replace(':', '')
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ addr = dev[0].own_addr()
+ _addr = addr.replace(':', '')
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ _bssid1 = bssid1.replace(':', '')
+
+ hapd0.set("ext_mgmt_frame_handling", "1")
+ hdr = "d0003a01" + _bssid0 + _addr + _bssid0 + "1000"
+ valid = "0601" + _addr + _bssid1
+ tests = ["0601",
+ "0601" + _addr,
+ "0601" + _addr + _bssid0,
+ "0601" + _addr + "ffffffffffff",
+ "0601" + _bssid0 + _bssid0,
+ valid,
+ valid + "01",
+ valid + "3700",
+ valid + "3600",
+ valid + "3603ffffff",
+ valid + "3603a1b2ff",
+ valid + "3603a1b2ff" + "3700",
+ valid + "3603a1b2ff" + "37520000" + 16*"00" + 32*"00" + 32*"00",
+ valid + "3603a1b2ff" + "37520001" + 16*"00" + 32*"00" + 32*"00",
+ valid + "3603a1b2ff" + "37550000" + 16*"00" + 32*"00" + 32*"00" + "0301aa",
+ valid + "3603a1b2ff" + "37550000" + 16*"00" + 32*"00" + 32*"00" + "0301aa" + "3000",
+ valid + "3603a1b2ff" + "37550000" + 16*"00" + 32*"00" + 32*"00" + "0301aa" + "30260100000fac040100000fac040100000facff00000100a225368fe0983b5828a37a0acb37f253",
+ valid + "3603a1b2ff" + "37550000" + 16*"00" + 32*"00" + 32*"00" + "0301aa" + "30260100000fac040100000fac030100000fac0400000100a225368fe0983b5828a37a0acb37f253",
+ valid + "3603a1b2ff" + "37550000" + 16*"00" + 32*"00" + 32*"00" + "0301aa" + "30260100000fac040100000fac040100000fac0400000100a225368fe0983b5828a37a0acb37f253",
+ valid + "0001"]
+ for t in tests:
+ hapd0.dump_monitor()
+ if "OK" not in hapd0.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ hapd0.set("ext_mgmt_frame_handling", "0")
+
+def test_ap_ft_over_ds_proto(dev, apdev):
+ """WPA2-PSK-FT AP over DS protocol testing"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ # FT Action Response while no FT-over-DS in progress
+ msg = {}
+ msg['fc'] = 13 << 4
+ msg['da'] = dev[0].own_addr()
+ msg['sa'] = apdev[0]['bssid']
+ msg['bssid'] = apdev[0]['bssid']
+ msg['payload'] = binascii.unhexlify("06020200000000000200000004000000")
+ hapd0.mgmt_tx(msg)
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ hapd0.set("ext_mgmt_frame_handling", "1")
+ hapd0.dump_monitor()
+ dev[0].request("FT_DS " + apdev[1]['bssid'])
+ for i in range(0, 10):
+ req = hapd0.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out")
+ if req['subtype'] == 13:
+ break
+ req = None
+ if not req:
+ raise Exception("FT Action frame not received")
+
+ # FT Action Response for unexpected Target AP
+ msg['payload'] = binascii.unhexlify("0602020000000000" + "f20000000400" + "0000")
+ hapd0.mgmt_tx(msg)
+
+ # FT Action Response without MDIE
+ msg['payload'] = binascii.unhexlify("0602020000000000" + "020000000400" + "0000")
+ hapd0.mgmt_tx(msg)
+
+ # FT Action Response without FTIE
+ msg['payload'] = binascii.unhexlify("0602020000000000" + "020000000400" + "0000" + "3603a1b201")
+ hapd0.mgmt_tx(msg)
+
+ # FT Action Response with FTIE SNonce mismatch
+ msg['payload'] = binascii.unhexlify("0602020000000000" + "020000000400" + "0000" + "3603a1b201" + "3766000000000000000000000000000000000000c4e67ac1999bebd00ff4ae4d5dcaf87896bb060b469f7c78d49623fb395c3455ffffff6b693fe6f8d8c5dfac0a22344750775bd09437f98b238c9f87b97f790c0106000102030406030a6e6173312e77312e6669")
+ hapd0.mgmt_tx(msg)
+
+@remote_compatible
+def test_ap_ft_rrb(dev, apdev):
+ """WPA2-PSK-FT RRB protocol testing"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+
+ _dst_ll = binascii.unhexlify(apdev[0]['bssid'].replace(':', ''))
+ _src_ll = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ proto = b'\x89\x0d'
+ ehdr = _dst_ll + _src_ll + proto
+
+ # Too short RRB frame
+ pkt = ehdr + b'\x01'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # RRB discarded frame wikth unrecognized type
+ pkt = ehdr + b'\x02' + b'\x02' + b'\x01\x00' + _src_ll
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # RRB frame too short for action frame
+ pkt = ehdr + b'\x01' + b'\x02' + b'\x01\x00' + _src_ll
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Too short RRB frame (not enough room for Action Frame body)
+ pkt = ehdr + b'\x01' + b'\x02' + b'\x00\x00' + _src_ll
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Unexpected Action frame category
+ pkt = ehdr + b'\x01' + b'\x02' + b'\x0e\x00' + _src_ll + b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Unexpected Action in RRB Request
+ pkt = ehdr + b'\x01' + b'\x00' + b'\x0e\x00' + _src_ll + b'\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Target AP address in RRB Request does not match with own address
+ pkt = ehdr + b'\x01' + b'\x00' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Not enough room for status code in RRB Response
+ pkt = ehdr + b'\x01' + b'\x01' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # RRB discarded frame with unknown packet_type
+ pkt = ehdr + b'\x01' + b'\x02' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # RRB Response with non-zero status code; no STA match
+ pkt = ehdr + b'\x01' + b'\x01' + b'\x10\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + b'\xff\xff'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # RRB Response with zero status code and extra data; STA match
+ pkt = ehdr + b'\x01' + b'\x01' + b'\x11\x00' + _src_ll + b'\x06\x01' + _src_ll + b'\x00\x00\x00\x00\x00\x00' + b'\x00\x00' + b'\x00'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Too short PMK-R1 pull
+ pkt = ehdr + b'\x01' + b'\xc8' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Too short PMK-R1 resp
+ pkt = ehdr + b'\x01' + b'\xc9' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Too short PMK-R1 push
+ pkt = ehdr + b'\x01' + b'\xca' + b'\x0e\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # No matching R0KH address found for PMK-R0 pull response
+ pkt = ehdr + b'\x01' + b'\xc9' + b'\x5a\x00' + _src_ll + b'\x06\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + 76 * b'\00'
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+@remote_compatible
+def test_rsn_ie_proto_ft_psk_sta(dev, apdev):
+ """RSN element protocol testing for FT-PSK + PMF cases on STA side"""
+ bssid = apdev[0]['bssid']
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "1"
+ # This is the RSN element used normally by hostapd
+ params['own_ie_override'] = '30140100000fac040100000fac040100000fac048c00' + '3603a1b201'
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ ieee80211w="1", scan_freq="2412",
+ pairwise="CCMP", group="CCMP")
+
+ tests = [('PMKIDCount field included',
+ '30160100000fac040100000fac040100000fac048c000000' + '3603a1b201'),
+ ('Extra IE before RSNE',
+ 'dd0400000000' + '30140100000fac040100000fac040100000fac048c00' + '3603a1b201'),
+ ('PMKIDCount and Group Management Cipher suite fields included',
+ '301a0100000fac040100000fac040100000fac048c000000000fac06' + '3603a1b201'),
+ ('Extra octet after defined fields (future extensibility)',
+ '301b0100000fac040100000fac040100000fac048c000000000fac0600' + '3603a1b201'),
+ ('No RSN Capabilities field (PMF disabled in practice)',
+ '30120100000fac040100000fac040100000fac04' + '3603a1b201')]
+ for txt, ie in tests:
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ logger.info(txt)
+ hapd.disable()
+ hapd.set('own_ie_override', ie)
+ hapd.enable()
+ dev[0].request("BSS_FLUSH 0")
+ dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ logger.info('Invalid RSNE causing internal hostapd error')
+ hapd.disable()
+ hapd.set('own_ie_override', '30130100000fac040100000fac040100000fac048c' + '3603a1b201')
+ hapd.enable()
+ dev[0].request("BSS_FLUSH 0")
+ dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+ dev[0].select_network(id, freq=2412)
+ # hostapd fails to generate EAPOL-Key msg 3/4, so this connection cannot
+ # complete.
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+ dev[0].request("DISCONNECT")
+
+def start_ft(apdev, wpa_ptk_rekey=None):
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ if wpa_ptk_rekey:
+ params['wpa_ptk_rekey'] = str(wpa_ptk_rekey)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ if wpa_ptk_rekey:
+ params['wpa_ptk_rekey'] = str(wpa_ptk_rekey)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ return hapd0, hapd1
+
+def check_ptk_rekey(dev, hapd0=None, hapd1=None):
+ ev = dev.wait_event(["CTRL-EVENT-DISCONNECTED",
+ "WPA: Key negotiation completed"], timeout=5)
+ if ev is None:
+ raise Exception("No event received after roam")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ raise Exception("Unexpected disconnection after roam")
+
+ if not hapd0 or not hapd1:
+ return
+ if dev.get_status_field('bssid') == hapd0.own_addr():
+ hapd = hapd0
+ else:
+ hapd = hapd1
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev, hapd)
+
+def test_ap_ft_ptk_rekey(dev, apdev):
+ """WPA2-PSK-FT PTK rekeying triggered by station after roam"""
+ hapd0, hapd1 = start_ft(apdev)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", ptk_rekey="1")
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+
+def test_ap_ft_ptk_rekey2(dev, apdev):
+ """WPA2-PSK-FT PTK rekeying triggered by station after one roam"""
+ hapd0, hapd1 = start_ft(apdev)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", ptk_rekey="1",
+ only_one_way=True)
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+
+def test_ap_ft_ptk_rekey_ap(dev, apdev):
+ """WPA2-PSK-FT PTK rekeying triggered by AP after roam"""
+ hapd0, hapd1 = start_ft(apdev, wpa_ptk_rekey=2)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678")
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+
+def test_ap_ft_ptk_rekey_ap2(dev, apdev):
+ """WPA2-PSK-FT PTK rekeying triggered by AP after one roam"""
+ hapd0, hapd1 = start_ft(apdev, wpa_ptk_rekey=2)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678",
+ only_one_way=True)
+ check_ptk_rekey(dev[0], hapd0, hapd1)
+
+def test_ap_ft_eap_ptk_rekey_ap(dev, apdev):
+ """WPA2-EAP-FT PTK rekeying triggered by AP"""
+ generic_ap_ft_eap(dev, apdev, only_one_way=True, wpa_ptk_rekey=2)
+ check_ptk_rekey(dev[0])
+
+def test_ap_ft_internal_rrb_check(dev, apdev):
+ """RRB internal delivery only to WPA enabled BSS"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd = hostapd.add_ap(apdev[0], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "FT-EAP":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+
+ hapd1 = hostapd.add_ap(apdev[1], {"ssid": ssid})
+
+ # Connect to WPA enabled AP
+ dev[0].connect(ssid, key_mgmt="FT-EAP", proto="WPA2", ieee80211w="1",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ # Try over_ds roaming to non-WPA-enabled AP.
+ # If hostapd does not check hapd->wpa_auth internally, it will crash now.
+ dev[0].roam_over_ds(apdev[1]['bssid'], fail_test=True)
+
+def test_ap_ft_extra_ie(dev, apdev):
+ """WPA2-PSK-FT AP with WPA2-PSK enabled and unexpected MDE"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["wpa_key_mgmt"] = "WPA-PSK FT-PSK"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ dev[1].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ dev[2].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK", proto="WPA2",
+ scan_freq="2412")
+ try:
+ # Add Mobility Domain element to test AP validation code.
+ dev[0].request("VENDOR_ELEM_ADD 13 3603a1b201")
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK", proto="WPA2",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("No connection result")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Non-FT association accepted with MDE")
+ if "status_code=43" not in ev:
+ raise Exception("Unexpected status code: " + ev)
+ dev[0].request("DISCONNECT")
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def test_ap_ft_ric(dev, apdev):
+ """WPA2-PSK-FT AP and RIC"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].set("ric_ies", "")
+ dev[0].set("ric_ies", '""')
+ if "FAIL" not in dev[0].request("SET ric_ies q"):
+ raise Exception("Invalid ric_ies value accepted")
+
+ tests = ["3900",
+ "3900ff04eeeeeeee",
+ "390400000000",
+ "390400000000" + "390400000000",
+ "390400000000" + "dd050050f20202",
+ "390400000000" + "dd3d0050f2020201" + 55*"00",
+ "390400000000" + "dd3d0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff0000",
+ "390401010000" + "dd3d0050f2020201aa3000dc050000000000000000000000000000000000000000000000000000dc050000000000000000000000000000808d5b0028230000"]
+ for t in tests:
+ dev[0].set("ric_ies", t)
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
+ test_connectivity=False)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def ie_hex(ies, id):
+ return binascii.hexlify(struct.pack('BB', id, len(ies[id])) + ies[id]).decode()
+
+def test_ap_ft_reassoc_proto(dev, apdev):
+ """WPA2-PSK-FT AP Reassociation Request frame parsing"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ ieee80211w="1", scan_freq="2412")
+ if dev[0].get_status_field('bssid') == hapd0.own_addr():
+ hapd1ap = hapd0
+ hapd2ap = hapd1
+ else:
+ hapd1ap = hapd1
+ hapd2ap = hapd0
+
+ dev[0].scan_for_bss(hapd2ap.own_addr(), freq="2412")
+ hapd2ap.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("ROAM " + hapd2ap.own_addr())
+
+ while True:
+ req = hapd2ap.mgmt_rx()
+ hapd2ap.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ if req['subtype'] == 11:
+ break
+
+ while True:
+ req = hapd2ap.mgmt_rx()
+ if req['subtype'] == 2:
+ break
+ hapd2ap.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+
+ # IEEE 802.11 header + fixed fields before IEs
+ hdr = binascii.hexlify(req['frame'][0:34]).decode()
+ ies = parse_ie(binascii.hexlify(req['frame'][34:]))
+ # First elements: SSID, Supported Rates, Extended Supported Rates
+ ies1 = ie_hex(ies, 0) + ie_hex(ies, 1) + ie_hex(ies, 50)
+
+ rsne = ie_hex(ies, 48)
+ mde = ie_hex(ies, 54)
+ fte = ie_hex(ies, 55)
+ tests = []
+ # RSN: Trying to use FT, but MDIE not included
+ tests += [rsne]
+ # RSN: Attempted to use unknown MDIE
+ tests += [rsne + "3603000000"]
+ # Invalid RSN pairwise cipher
+ tests += ["30260100000fac040100000fac030100000fac040000010029208a42cd25c85aa571567dce10dae3"]
+ # FT: No PMKID in RSNIE
+ tests += ["30160100000fac040100000fac040100000fac0400000000" + ie_hex(ies, 54)]
+ # FT: Invalid FTIE
+ tests += [rsne + mde]
+ # FT: RIC IE(s) in the frame, but not included in protected IE count
+ # FT: Failed to parse FT IEs
+ tests += [rsne + mde + fte + "3900"]
+ # FT: SNonce mismatch in FTIE
+ tests += [rsne + mde + "37520000" + 16*"00" + 32*"00" + 32*"00"]
+ # FT: ANonce mismatch in FTIE
+ tests += [rsne + mde + fte[0:40] + 32*"00" + fte[104:]]
+ # FT: No R0KH-ID subelem in FTIE
+ tests += [rsne + mde + "3752" + fte[4:168]]
+ # FT: R0KH-ID in FTIE did not match with the current R0KH-ID
+ tests += [rsne + mde + "3755" + fte[4:168] + "0301ff"]
+ # FT: No R1KH-ID subelem in FTIE
+ tests += [rsne + mde + "375e" + fte[4:168] + "030a" + binascii.hexlify(b"nas1.w1.fi").decode()]
+ # FT: Unknown R1KH-ID used in ReassocReq
+ tests += [rsne + mde + "3766" + fte[4:168] + "030a" + binascii.hexlify(b"nas1.w1.fi").decode() + "0106000000000000"]
+ # FT: PMKID in Reassoc Req did not match with the PMKR1Name derived from auth request
+ tests += [rsne[:-32] + 16*"00" + mde + fte]
+ # Invalid MIC in FTIE
+ tests += [rsne + mde + fte[0:8] + 16*"00" + fte[40:]]
+ for t in tests:
+ hapd2ap.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + ies1 + t)
+
+def test_ap_ft_reassoc_local_fail(dev, apdev):
+ """WPA2-PSK-FT AP Reassociation Request frame and local failure"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ ieee80211w="1", scan_freq="2412")
+ if dev[0].get_status_field('bssid') == hapd0.own_addr():
+ hapd1ap = hapd0
+ hapd2ap = hapd1
+ else:
+ hapd1ap = hapd1
+ hapd2ap = hapd0
+
+ dev[0].scan_for_bss(hapd2ap.own_addr(), freq="2412")
+ # FT: Failed to calculate MIC
+ with fail_test(hapd2ap, 1, "wpa_ft_validate_reassoc"):
+ dev[0].request("ROAM " + hapd2ap.own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Association reject not seen")
+
+def test_ap_ft_reassoc_replay(dev, apdev, params):
+ """WPA2-PSK-FT AP and replayed Reassociation Request frame"""
+ capfile = os.path.join(params['logdir'], "hwsim0.pcapng")
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ if dev[0].get_status_field('bssid') == hapd0.own_addr():
+ hapd1ap = hapd0
+ hapd2ap = hapd1
+ else:
+ hapd1ap = hapd1
+ hapd2ap = hapd0
+
+ dev[0].scan_for_bss(hapd2ap.own_addr(), freq="2412")
+ hapd2ap.set("ext_mgmt_frame_handling", "1")
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("ROAM " + hapd2ap.own_addr()):
+ raise Exception("ROAM failed")
+
+ reassocreq = None
+ count = 0
+ while count < 100:
+ req = hapd2ap.mgmt_rx()
+ count += 1
+ hapd2ap.dump_monitor()
+ hapd2ap.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ if req['subtype'] == 2:
+ reassocreq = req
+ ev = hapd2ap.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("No TX status seen")
+ cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
+ if "OK" not in hapd2ap.request(cmd):
+ raise Exception("MGMT_TX_STATUS_PROCESS failed")
+ break
+ hapd2ap.set("ext_mgmt_frame_handling", "0")
+ if reassocreq is None:
+ raise Exception("No Reassociation Request frame seen")
+ dev[0].wait_connected()
+ dev[0].dump_monitor()
+ hapd2ap.dump_monitor()
+
+ hwsim_utils.test_connectivity(dev[0], hapd2ap)
+
+ logger.info("Replay the last Reassociation Request frame")
+ hapd2ap.dump_monitor()
+ hapd2ap.set("ext_mgmt_frame_handling", "1")
+ hapd2ap.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ ev = hapd2ap.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("No TX status seen")
+ cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
+ if "OK" not in hapd2ap.request(cmd):
+ raise Exception("MGMT_TX_STATUS_PROCESS failed")
+ hapd2ap.set("ext_mgmt_frame_handling", "0")
+
+ try:
+ hwsim_utils.test_connectivity(dev[0], hapd2ap)
+ ok = True
+ except:
+ ok = False
+
+ ap = hapd2ap.own_addr()
+ sta = dev[0].own_addr()
+ filt = "wlan.fc.type == 2 && " + \
+ "wlan.da == " + sta + " && " + \
+ "wlan.sa == " + ap + " && " + \
+ "wlan.fc.protected == 1"
+ fields = ["wlan.ccmp.extiv"]
+ res = run_tshark(capfile, filt, fields)
+ vals = res.splitlines()
+ logger.info("CCMP PN: " + str(vals))
+ if len(vals) < 2:
+ raise Exception("Could not find all CCMP protected frames from capture")
+ if len(set(vals)) < len(vals):
+ raise Exception("Duplicate CCMP PN used")
+
+ if not ok:
+ raise Exception("The second hwsim connectivity test failed")
+
+def test_ap_ft_psk_file(dev, apdev):
+ """WPA2-PSK-FT AP with PSK from a file"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1a(ssid=ssid, passphrase=passphrase)
+ params['wpa_psk_file'] = 'hostapd.wpa_psk'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[1].connect(ssid, psk="very secret",
+ key_mgmt="FT-PSK", proto="WPA2", ieee80211w="1",
+ scan_freq="2412", wait_connect=False)
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ ieee80211w="1", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].connect(ssid, psk="very secret", key_mgmt="FT-PSK", proto="WPA2",
+ ieee80211w="1", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].connect(ssid, psk="secret passphrase",
+ key_mgmt="FT-PSK", proto="WPA2", ieee80211w="1",
+ scan_freq="2412")
+ dev[2].connect(ssid, psk="another passphrase for all STAs",
+ key_mgmt="FT-PSK", proto="WPA2", ieee80211w="1",
+ scan_freq="2412")
+ ev = dev[1].wait_event(["WPA: 4-Way Handshake failed"], timeout=10)
+ if ev is None:
+ raise Exception("Timed out while waiting for failure report")
+ dev[1].request("REMOVE_NETWORK all")
+
+def test_ap_ft_eap_ap_config_change(dev, apdev):
+ """WPA2-EAP-FT AP changing from 802.1X-only to FT-only"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+ bssid = apdev[0]['bssid']
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase, discovery=True)
+ params['wpa_key_mgmt'] = "WPA-EAP"
+ params["ieee8021x"] = "1"
+ params["pmk_r1_push"] = "0"
+ params["r0kh"] = "ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["r1kh"] = "00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"
+ params["eap_server"] = "0"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(ssid, key_mgmt="FT-EAP WPA-EAP", proto="WPA2",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ hapd.disable()
+ hapd.set('wpa_key_mgmt', "FT-EAP")
+ hapd.enable()
+
+ dev[0].request("BSS_FLUSH 0")
+ dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+def test_ap_ft_eap_sha384(dev, apdev):
+ """WPA2-EAP-FT with SHA384"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-EAP-SHA384"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ conf = hapd0.request("GET_CONFIG")
+ if "key_mgmt=FT-EAP-SHA384" not in conf.splitlines():
+ logger.info("GET_CONFIG:\n" + conf)
+ raise Exception("GET_CONFIG did not report correct key_mgmt")
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-EAP-SHA384"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True,
+ sha384=True)
+
+def test_ap_ft_eap_sha384_reassoc(dev, apdev):
+ """WPA2-EAP-FT with SHA384 using REASSOCIATE"""
+ check_suite_b_192_capa(dev)
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "WPA-EAP-SUITE-B-192 FT-EAP-SHA384"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "WPA-EAP-SUITE-B-192 FT-EAP-SHA384"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True,
+ sha384=True, also_non_ft=True, roam_with_reassoc=True)
+
+def test_ap_ft_eap_sha384_over_ds(dev, apdev):
+ """WPA2-EAP-FT with SHA384 over DS"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-EAP-SHA384"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-EAP-SHA384"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+ eap=True, sha384=True)
+
+def test_ap_ft_roam_rrm(dev, apdev):
+ """WPA2-PSK-FT AP and radio measurement request"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["rrm_beacon_report"] = "1"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ addr = dev[0].own_addr()
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+ scan_freq="2412")
+ check_beacon_req(hapd0, addr, 1)
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["rrm_beacon_report"] = "1"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+
+ dev[0].scan_for_bss(bssid1, freq=2412)
+ dev[0].roam(bssid1)
+ check_beacon_req(hapd1, addr, 2)
+
+ dev[0].scan_for_bss(bssid0, freq=2412)
+ dev[0].roam(bssid0)
+ check_beacon_req(hapd0, addr, 3)
+
+def test_ap_ft_pmksa_caching(dev, apdev):
+ """FT-EAP and PMKSA caching for initial mobility domain association"""
+ ssid = "test-ft"
+ identity = "gpsk user"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid)
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params["mobility_domain"] = "c3d4"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = ft_params2(ssid=ssid)
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params["ieee8021x"] = "1"
+ params["mobility_domain"] = "c3d4"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd, hapd1, ssid, None, eap=True,
+ eap_identity=identity, pmksa_caching=True)
+
+def test_ap_ft_pmksa_caching_sha384(dev, apdev):
+ """FT-EAP-SHA384 and PMKSA caching for initial mobility domain association"""
+ ssid = "test-ft"
+ identity = "gpsk user"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid)
+ params['wpa_key_mgmt'] = "FT-EAP-SHA384"
+ params["ieee8021x"] = "1"
+ params["mobility_domain"] = "c3d4"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = ft_params2(ssid=ssid)
+ params['wpa_key_mgmt'] = "FT-EAP-SHA384"
+ params["ieee8021x"] = "1"
+ params["mobility_domain"] = "c3d4"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd, hapd1, ssid, None, eap=True,
+ eap_identity=identity, pmksa_caching=True, sha384=True)
+
+def test_ap_ft_r1_key_expiration(dev, apdev):
+ """WPA2-PSK-FT and PMK-R1 expiration"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['r1_max_key_lifetime'] = "2"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['r1_max_key_lifetime'] = "2"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ # This succeeds, but results in having to run another PMK-R1 pull before the
+ # second AP can complete FT protocol.
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, wait_before_roam=4)
+
+def test_ap_ft_r0_key_expiration(dev, apdev):
+ """WPA2-PSK-FT and PMK-R0 expiration"""
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params.pop('r0_key_lifetime', None)
+ params['ft_r0_key_lifetime'] = "2"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params.pop('r0_key_lifetime', None)
+ params['ft_r0_key_lifetime'] = "2"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ bssid2 = run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
+ return_after_initial=True)
+ time.sleep(4)
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ if "OK" not in dev[0].request("ROAM " + bssid2):
+ raise Exception("ROAM failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-AUTH-REJECT",
+ "CTRL-EVENT-ASSOC-REJECT"], timeout=5)
+ dev[0].request("DISCONNECT")
+ if ev is None or "CTRL-EVENT-AUTH-REJECT" not in ev:
+ raise Exception("FT protocol failure not reported")
+ if "status_code=53" not in ev:
+ raise Exception("Unexpected status in FT protocol failure: " + ev)
+
+ # Generate a new PMK-R0
+ dev[0].dump_monitor()
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+def test_ap_ft_no_full_ap_client_state(dev, apdev):
+ """WPA2-PSK-FT AP with full_ap_client_state=0"""
+ run_ap_ft_skip_prune_assoc(dev, apdev, False, False)
+
+def test_ap_ft_skip_prune_assoc(dev, apdev):
+ """WPA2-PSK-FT AP with skip_prune_assoc"""
+ run_ap_ft_skip_prune_assoc(dev, apdev, True, True)
+
+def test_ap_ft_skip_prune_assoc2(dev, apdev):
+ """WPA2-PSK-FT AP with skip_prune_assoc (disable full_ap_client_state)"""
+ run_ap_ft_skip_prune_assoc(dev, apdev, True, False, test_connectivity=False)
+
+def test_ap_ft_skip_prune_assoc_pmf(dev, apdev):
+ """WPA2-PSK-FT/PMF AP with skip_prune_assoc"""
+ run_ap_ft_skip_prune_assoc(dev, apdev, True, True, pmf=True)
+
+def test_ap_ft_skip_prune_assoc_pmf_over_ds(dev, apdev):
+ """WPA2-PSK-FT/PMF AP with skip_prune_assoc (over DS)"""
+ run_ap_ft_skip_prune_assoc(dev, apdev, True, True, pmf=True, over_ds=True)
+
+def run_ap_ft_skip_prune_assoc(dev, apdev, skip_prune_assoc,
+ full_ap_client_state, test_connectivity=True,
+ pmf=False, over_ds=False):
+ ssid = "test-ft"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ if skip_prune_assoc:
+ params['skip_prune_assoc'] = '1'
+ if not full_ap_client_state:
+ params['driver_params'] = "full_ap_client_state=0"
+ if pmf:
+ params["ieee80211w"] = "2"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ if skip_prune_assoc:
+ params['skip_prune_assoc'] = '1'
+ if not full_ap_client_state:
+ params['driver_params'] = "full_ap_client_state=0"
+ if pmf:
+ params["ieee80211w"] = "2"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase,
+ ieee80211w="2" if pmf else "0",
+ over_ds=over_ds, test_connectivity=test_connectivity)
+
+def test_ap_ft_sae_skip_prune_assoc(dev, apdev):
+ """WPA2-PSK-FT-SAE AP with skip_prune_assoc"""
+ hapd0, hapd1 = start_ft_sae(dev[0], apdev, skip_prune_assoc=True)
+ run_roams(dev[0], apdev, hapd0, hapd1, "test-ft", "12345678", sae=True)
diff --git a/contrib/wpa/tests/hwsim/test_ap_hs20.py b/contrib/wpa/tests/hwsim/test_ap_hs20.py
new file mode 100644
index 000000000000..9c4ba9597513
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_hs20.py
@@ -0,0 +1,6496 @@
+# Hotspot 2.0 tests
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import base64
+import binascii
+import struct
+import time
+import logging
+logger = logging.getLogger()
+import os
+import os.path
+import socket
+import subprocess
+
+import hostapd
+from utils import *
+import hwsim_utils
+from tshark import run_tshark
+from wlantest import Wlantest
+from wpasupplicant import WpaSupplicant
+from wlantest import WlantestCapture
+from test_ap_eap import check_eap_capa, check_domain_match_full
+from test_gas import gas_rx, parse_gas, action_response, anqp_initial_resp, send_gas_resp, ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE
+
+def hs20_ap_params(ssid="test-hs20"):
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_key_mgmt'] = "WPA-EAP"
+ params['ieee80211w'] = "1"
+ params['ieee8021x'] = "1"
+ params['auth_server_addr'] = "127.0.0.1"
+ params['auth_server_port'] = "1812"
+ params['auth_server_shared_secret'] = "radius"
+ params['interworking'] = "1"
+ params['access_network_type'] = "14"
+ params['internet'] = "1"
+ params['asra'] = "0"
+ params['esr'] = "0"
+ params['uesa'] = "0"
+ params['venue_group'] = "7"
+ params['venue_type'] = "1"
+ params['venue_name'] = ["eng:Example venue", "fin:Esimerkkipaikka"]
+ params['roaming_consortium'] = ["112233", "1020304050", "010203040506",
+ "fedcba"]
+ params['domain_name'] = "example.com,another.example.com"
+ params['nai_realm'] = ["0,example.com,13[5:6],21[2:4][5:7]",
+ "0,another.example.com"]
+ params['hs20'] = "1"
+ params['hs20_wan_metrics'] = "01:8000:1000:80:240:3000"
+ params['hs20_conn_capab'] = ["1:0:2", "6:22:1", "17:5060:0"]
+ params['hs20_operating_class'] = "5173"
+ params['anqp_3gpp_cell_net'] = "244,91"
+ return params
+
+def check_auto_select(dev, bssid):
+ dev.scan_for_bss(bssid, freq="2412")
+ dev.request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev.wait_connected(timeout=15)
+ if bssid not in ev:
+ raise Exception("Connected to incorrect network")
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def interworking_select(dev, bssid, type=None, no_match=False, freq=None):
+ dev.dump_monitor()
+ if bssid and freq and not no_match:
+ dev.scan_for_bss(bssid, freq=freq)
+ freq_extra = " freq=" + str(freq) if freq else ""
+ dev.request("INTERWORKING_SELECT" + freq_extra)
+ ev = dev.wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Network selection timed out")
+ if no_match:
+ if "INTERWORKING-NO-MATCH" not in ev:
+ raise Exception("Unexpected network match")
+ return
+ if "INTERWORKING-NO-MATCH" in ev:
+ logger.info("Matching network not found - try again")
+ dev.dump_monitor()
+ dev.request("INTERWORKING_SELECT" + freq_extra)
+ ev = dev.wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Network selection timed out")
+ if "INTERWORKING-NO-MATCH" in ev:
+ raise Exception("Matching network not found")
+ if bssid and bssid not in ev:
+ raise Exception("Unexpected BSSID in match")
+ if type and "type=" + type not in ev:
+ raise Exception("Network type not recognized correctly")
+
+def check_sp_type(dev, sp_type):
+ type = dev.get_status_field("sp_type")
+ if type is None:
+ raise Exception("sp_type not available")
+ if type != sp_type:
+ raise Exception("sp_type did not indicate %s network" % sp_type)
+
+def hlr_auc_gw_available():
+ if not os.path.exists("/tmp/hlr_auc_gw.sock"):
+ raise HwsimSkip("No hlr_auc_gw socket available")
+ if not os.path.exists("../../hostapd/hlr_auc_gw"):
+ raise HwsimSkip("No hlr_auc_gw available")
+
+def interworking_ext_sim_connect(dev, bssid, method):
+ dev.request("INTERWORKING_CONNECT " + bssid)
+ interworking_ext_sim_auth(dev, method)
+
+def interworking_ext_sim_auth(dev, method):
+ ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Network connected timed out")
+ if "(" + method + ")" not in ev:
+ raise Exception("Unexpected EAP method selection")
+
+ ev = dev.wait_event(["CTRL-REQ-SIM"], timeout=15)
+ if ev is None:
+ raise Exception("Wait for external SIM processing request timed out")
+ p = ev.split(':', 2)
+ if p[1] != "GSM-AUTH":
+ raise Exception("Unexpected CTRL-REQ-SIM type")
+ id = p[0].split('-')[3]
+ rand = p[2].split(' ')[0]
+
+ res = subprocess.check_output(["../../hostapd/hlr_auc_gw",
+ "-m",
+ "auth_serv/hlr_auc_gw.milenage_db",
+ "GSM-AUTH-REQ 232010000000000 " + rand]).decode()
+ if "GSM-AUTH-RESP" not in res:
+ raise Exception("Unexpected hlr_auc_gw response")
+ resp = res.split(' ')[2].rstrip()
+
+ dev.request("CTRL-RSP-SIM-" + id + ":GSM-AUTH:" + resp)
+ dev.wait_connected(timeout=15)
+
+def interworking_connect(dev, bssid, method):
+ dev.request("INTERWORKING_CONNECT " + bssid)
+ interworking_auth(dev, method)
+
+def interworking_auth(dev, method):
+ ev = dev.wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Network connected timed out")
+ if "(" + method + ")" not in ev:
+ raise Exception("Unexpected EAP method selection")
+
+ dev.wait_connected(timeout=15)
+
+def check_probe_resp(wt, bssid_unexpected, bssid_expected):
+ if bssid_unexpected:
+ count = wt.get_bss_counter("probe_response", bssid_unexpected)
+ if count > 0:
+ raise Exception("Unexpected Probe Response frame from AP")
+
+ if bssid_expected:
+ count = wt.get_bss_counter("probe_response", bssid_expected)
+ if count == 0:
+ raise Exception("No Probe Response frame from AP")
+
+def test_ap_anqp_sharing(dev, apdev):
+ """ANQP sharing within ESS and explicit unshare"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].flush_scan_cache()
+
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['nai_realm'] = ["0,example.com,13[5:6],21[2:4][5:7]"]
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com", 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com"})
+ logger.info("Normal network selection with shared ANQP results")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ interworking_select(dev[0], None, "home", freq="2412")
+ dev[0].dump_monitor()
+ state = dev[0].get_status_field('wpa_state')
+ if state != "DISCONNECTED":
+ raise Exception("Unexpected wpa_state after INTERWORKING_SELECT: " + state)
+
+ logger.debug("BSS entries:\n" + dev[0].request("BSS RANGE=ALL"))
+ res1 = dev[0].get_bss(bssid)
+ res2 = dev[0].get_bss(bssid2)
+ if 'anqp_nai_realm' not in res1:
+ raise Exception("anqp_nai_realm not found for AP1")
+ if 'anqp_nai_realm' not in res2:
+ raise Exception("anqp_nai_realm not found for AP2")
+ if res1['anqp_nai_realm'] != res2['anqp_nai_realm']:
+ raise Exception("ANQP results were not shared between BSSes")
+
+ logger.info("Explicit ANQP request to unshare ANQP results")
+ dev[0].request("ANQP_GET " + bssid + " 263")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP operation timed out")
+
+ dev[0].request("ANQP_GET " + bssid2 + " 263")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP operation timed out")
+
+ res1 = dev[0].get_bss(bssid)
+ res2 = dev[0].get_bss(bssid2)
+ if res1['anqp_nai_realm'] == res2['anqp_nai_realm']:
+ raise Exception("ANQP results were not unshared")
+
+def test_ap_anqp_domain_id(dev, apdev):
+ """ANQP Domain ID"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].flush_scan_cache()
+
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_domain_id'] = '1234'
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_domain_id'] = '1234'
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com", 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com"})
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ interworking_select(dev[0], None, "home", freq="2412")
+
+def test_ap_anqp_no_sharing_diff_ess(dev, apdev):
+ """ANQP no sharing between ESSs"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].flush_scan_cache()
+
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20-another")
+ params['hessid'] = bssid
+ params['nai_realm'] = ["0,example.com,13[5:6],21[2:4][5:7]"]
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com", 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com"})
+ logger.info("Normal network selection with shared ANQP results")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ interworking_select(dev[0], None, "home", freq="2412")
+
+def test_ap_anqp_no_sharing_missing_info(dev, apdev):
+ """ANQP no sharing due to missing information"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].flush_scan_cache()
+
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ del params['roaming_consortium']
+ del params['domain_name']
+ del params['anqp_3gpp_cell_net']
+ del params['nai_realm']
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['nai_realm'] = ["0,example.com,13[5:6],21[2:4][5:7]"]
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com", 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com"})
+ logger.info("Normal network selection with shared ANQP results")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ interworking_select(dev[0], None, "home", freq="2412")
+
+def test_ap_anqp_sharing_oom(dev, apdev):
+ """ANQP sharing within ESS and explicit unshare OOM"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].flush_scan_cache()
+
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['nai_realm'] = ["0,example.com,13[5:6],21[2:4][5:7]"]
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com", 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com"})
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ interworking_select(dev[0], None, "home", freq="2412")
+ dev[0].dump_monitor()
+
+ with alloc_fail(dev[0], 1, "wpa_bss_anqp_clone"):
+ dev[0].request("ANQP_GET " + bssid + " 263")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP operation timed out")
+
+def test_ap_nai_home_realm_query(dev, apdev):
+ """NAI Home Realm Query"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,13[5:6],21[2:4][5:7]",
+ "0,another.example.org"]
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan(freq="2412")
+ dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid + " realm=example.com")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP operation timed out")
+ nai1 = dev[0].get_bss(bssid)['anqp_nai_realm']
+ dev[0].dump_monitor()
+
+ dev[0].request("ANQP_GET " + bssid + " 263")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP operation timed out")
+ nai2 = dev[0].get_bss(bssid)['anqp_nai_realm']
+
+ if len(nai1) >= len(nai2):
+ raise Exception("Unexpected NAI Realm list response lengths")
+ if binascii.hexlify(b"example.com").decode() not in nai1:
+ raise Exception("Home realm not reported")
+ if binascii.hexlify(b"example.org").decode() in nai1:
+ raise Exception("Non-home realm reported")
+ if binascii.hexlify(b"example.com").decode() not in nai2:
+ raise Exception("Home realm not reported in wildcard query")
+ if binascii.hexlify(b"example.org").decode() not in nai2:
+ raise Exception("Non-home realm not reported in wildcard query ")
+
+ cmds = ["foo",
+ "00:11:22:33:44:55 123",
+ "00:11:22:33:44:55 qq"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + cmd):
+ raise Exception("Invalid HS20_GET_NAI_HOME_REALM_LIST accepted: " + cmd)
+
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid):
+ raise Exception("HS20_GET_NAI_HOME_REALM_LIST failed")
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP operation timed out")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected ANQP response: " + ev)
+
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid + " 01000b6578616d706c652e636f6d"):
+ raise Exception("HS20_GET_NAI_HOME_REALM_LIST failed")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=10)
+ if ev is None:
+ raise Exception("No ANQP response")
+ if "NAI Realm list" not in ev:
+ raise Exception("Missing NAI Realm list: " + ev)
+
+ dev[0].add_cred_values({'realm': "example.com", 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com"})
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("HS20_GET_NAI_HOME_REALM_LIST " + bssid):
+ raise Exception("HS20_GET_NAI_HOME_REALM_LIST failed")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=10)
+ if ev is None:
+ raise Exception("No ANQP response")
+ if "NAI Realm list" not in ev:
+ raise Exception("Missing NAI Realm list: " + ev)
+
+@remote_compatible
+def test_ap_interworking_scan_filtering(dev, apdev):
+ """Interworking scan filtering with HESSID and access network type"""
+ try:
+ _test_ap_interworking_scan_filtering(dev, apdev)
+ finally:
+ dev[0].request("SET hessid 00:00:00:00:00:00")
+ dev[0].request("SET access_network_type 15")
+
+def _test_ap_interworking_scan_filtering(dev, apdev):
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ ssid = "test-hs20-ap1"
+ params['ssid'] = ssid
+ params['hessid'] = bssid
+ hapd0 = hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ ssid2 = "test-hs20-ap2"
+ params['ssid'] = ssid2
+ params['hessid'] = bssid2
+ params['access_network_type'] = "1"
+ del params['venue_group']
+ del params['venue_type']
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+
+ Wlantest.setup(hapd0)
+ wt = Wlantest()
+ wt.flush()
+
+ # Make sure wlantest has seen both BSSs to avoid issues in trying to clear
+ # counters for non-existing BSS.
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ wt.clear_bss_counters(bssid)
+ wt.clear_bss_counters(bssid2)
+
+ logger.info("Check probe request filtering based on HESSID")
+
+ dev[0].request("SET hessid " + bssid2)
+ dev[0].scan(freq="2412")
+ time.sleep(0.03)
+ check_probe_resp(wt, bssid, bssid2)
+
+ logger.info("Check probe request filtering based on access network type")
+
+ wt.clear_bss_counters(bssid)
+ wt.clear_bss_counters(bssid2)
+ dev[0].request("SET hessid 00:00:00:00:00:00")
+ dev[0].request("SET access_network_type 14")
+ dev[0].scan(freq="2412")
+ time.sleep(0.03)
+ check_probe_resp(wt, bssid2, bssid)
+
+ wt.clear_bss_counters(bssid)
+ wt.clear_bss_counters(bssid2)
+ dev[0].request("SET hessid 00:00:00:00:00:00")
+ dev[0].request("SET access_network_type 1")
+ dev[0].scan(freq="2412")
+ time.sleep(0.03)
+ check_probe_resp(wt, bssid, bssid2)
+
+ logger.info("Check probe request filtering based on HESSID and ANT")
+
+ wt.clear_bss_counters(bssid)
+ wt.clear_bss_counters(bssid2)
+ dev[0].request("SET hessid " + bssid)
+ dev[0].request("SET access_network_type 14")
+ dev[0].scan(freq="2412")
+ time.sleep(0.03)
+ check_probe_resp(wt, bssid2, bssid)
+
+ wt.clear_bss_counters(bssid)
+ wt.clear_bss_counters(bssid2)
+ dev[0].request("SET hessid " + bssid2)
+ dev[0].request("SET access_network_type 14")
+ dev[0].scan(freq="2412")
+ time.sleep(0.03)
+ check_probe_resp(wt, bssid, None)
+ check_probe_resp(wt, bssid2, None)
+
+ wt.clear_bss_counters(bssid)
+ wt.clear_bss_counters(bssid2)
+ dev[0].request("SET hessid " + bssid)
+ dev[0].request("SET access_network_type 1")
+ dev[0].scan(freq="2412")
+ time.sleep(0.03)
+ check_probe_resp(wt, bssid, None)
+ check_probe_resp(wt, bssid2, None)
+
+def test_ap_hs20_select(dev, apdev):
+ """Hotspot 2.0 network selection"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com", 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com"})
+ interworking_select(dev[0], bssid, "home")
+
+ dev[0].remove_cred(id)
+ id = dev[0].add_cred_values({'realm': "example.com", 'username': "test",
+ 'password': "secret",
+ 'domain': "no.match.example.com"})
+ interworking_select(dev[0], bssid, "roaming", freq="2412")
+
+ dev[0].set_cred_quoted(id, "realm", "no.match.example.com")
+ interworking_select(dev[0], bssid, no_match=True, freq="2412")
+
+ res = dev[0].request("SCAN_RESULTS")
+ if "[HS20]" not in res:
+ raise Exception("HS20 flag missing from scan results: " + res)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.org,21"]
+ params['hessid'] = bssid2
+ params['domain_name'] = "example.org"
+ hostapd.add_ap(apdev[1], params)
+ dev[0].remove_cred(id)
+ id = dev[0].add_cred_values({'realm': "example.org", 'username': "test",
+ 'password': "secret",
+ 'domain': "example.org"})
+ interworking_select(dev[0], bssid2, "home", freq="2412")
+
+def hs20_simulated_sim(dev, ap, method):
+ bssid = ap['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_3gpp_cell_net'] = "555,444"
+ params['domain_name'] = "wlan.mnc444.mcc555.3gppnetwork.org"
+ hostapd.add_ap(ap, params)
+
+ dev.hs20_enable()
+ dev.add_cred_values({'imsi': "555444-333222111", 'eap': method,
+ 'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123"})
+ interworking_select(dev, bssid, "home", freq="2412")
+ interworking_connect(dev, bssid, method)
+ check_sp_type(dev, "home")
+
+def test_ap_hs20_sim(dev, apdev):
+ """Hotspot 2.0 with simulated SIM and EAP-SIM"""
+ hlr_auc_gw_available()
+ hs20_simulated_sim(dev[0], apdev[0], "SIM")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-ALREADY-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on already-connected event")
+
+def test_ap_hs20_sim_invalid(dev, apdev):
+ """Hotspot 2.0 with simulated SIM and EAP-SIM - invalid IMSI"""
+ hlr_auc_gw_available()
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_3gpp_cell_net'] = "555,444"
+ params['domain_name'] = "wlan.mnc444.mcc555.3gppnetwork.org"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values({'imsi': "555444-3332221110", 'eap': "SIM",
+ 'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123"})
+ # This hits "No valid IMSI available" in build_root_nai()
+ interworking_select(dev[0], bssid, freq="2412")
+
+def test_ap_hs20_sim_oom(dev, apdev):
+ """Hotspot 2.0 with simulated SIM and EAP-SIM - OOM"""
+ hlr_auc_gw_available()
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_3gpp_cell_net'] = "555,444"
+ params['domain_name'] = "wlan.mnc444.mcc555.3gppnetwork.org"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values({'imsi': "555444-333222111", 'eap': "SIM",
+ 'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123"})
+ dev[0].scan_for_bss(bssid, freq=2412)
+ interworking_select(dev[0], bssid, freq="2412")
+
+ with alloc_fail(dev[0], 1, "wpa_config_add_network;interworking_connect_3gpp"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with alloc_fail(dev[0], 1, "=interworking_connect_3gpp"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+def test_ap_hs20_aka(dev, apdev):
+ """Hotspot 2.0 with simulated USIM and EAP-AKA"""
+ hlr_auc_gw_available()
+ hs20_simulated_sim(dev[0], apdev[0], "AKA")
+
+def test_ap_hs20_aka_prime(dev, apdev):
+ """Hotspot 2.0 with simulated USIM and EAP-AKA'"""
+ hlr_auc_gw_available()
+ hs20_simulated_sim(dev[0], apdev[0], "AKA'")
+
+def test_ap_hs20_ext_sim(dev, apdev):
+ """Hotspot 2.0 with external SIM processing"""
+ hlr_auc_gw_available()
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_3gpp_cell_net'] = "232,01"
+ params['domain_name'] = "wlan.mnc001.mcc232.3gppnetwork.org"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ try:
+ dev[0].request("SET external_sim 1")
+ dev[0].add_cred_values({'imsi': "23201-0000000000", 'eap': "SIM"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_ext_sim_connect(dev[0], bssid, "SIM")
+ check_sp_type(dev[0], "home")
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def test_ap_hs20_ext_sim_roaming(dev, apdev):
+ """Hotspot 2.0 with external SIM processing in roaming network"""
+ hlr_auc_gw_available()
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_3gpp_cell_net'] = "244,91;310,026;232,01;234,56"
+ params['domain_name'] = "wlan.mnc091.mcc244.3gppnetwork.org"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ try:
+ dev[0].request("SET external_sim 1")
+ dev[0].add_cred_values({'imsi': "23201-0000000000", 'eap': "SIM"})
+ interworking_select(dev[0], bssid, "roaming", freq="2412")
+ interworking_ext_sim_connect(dev[0], bssid, "SIM")
+ check_sp_type(dev[0], "roaming")
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def test_ap_hs20_username(dev, apdev):
+ """Hotspot 2.0 connection in username/password credential"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['disable_dgaf'] = '1'
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ check_sp_type(dev[0], "home")
+ status = dev[0].get_status()
+ if status['pairwise_cipher'] != "CCMP":
+ raise Exception("Unexpected pairwise cipher")
+ if status['hs20'] != "3":
+ raise Exception("Unexpected HS 2.0 support indication")
+
+ dev[1].connect("test-hs20", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="hs20-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ scan_freq="2412")
+
+def test_ap_hs20_connect_api(dev, apdev):
+ """Hotspot 2.0 connection with connect API"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['disable_dgaf'] = '1'
+ hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.hs20_enable()
+ wpas.flush_scan_cache()
+ id = wpas.add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ interworking_select(wpas, bssid, "home", freq="2412")
+ interworking_connect(wpas, bssid, "TTLS")
+ check_sp_type(wpas, "home")
+ status = wpas.get_status()
+ if status['pairwise_cipher'] != "CCMP":
+ raise Exception("Unexpected pairwise cipher")
+ if status['hs20'] != "3":
+ raise Exception("Unexpected HS 2.0 support indication")
+
+def test_ap_hs20_auto_interworking(dev, apdev):
+ """Hotspot 2.0 connection with auto_interworking=1"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['disable_dgaf'] = '1'
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable(auto_interworking=True)
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected(timeout=15)
+ check_sp_type(dev[0], "home")
+ status = dev[0].get_status()
+ if status['pairwise_cipher'] != "CCMP":
+ raise Exception("Unexpected pairwise cipher")
+ if status['hs20'] != "3":
+ raise Exception("Unexpected HS 2.0 support indication")
+
+def test_ap_hs20_auto_interworking_global_pmf(dev, apdev):
+ """Hotspot 2.0 connection with auto_interworking=1 and pmf=2"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable(auto_interworking=True)
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ try:
+ dev[0].set("pmf", "2")
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected(timeout=15)
+ pmf = dev[0].get_status_field("pmf")
+ if pmf != "1":
+ raise Exception("Unexpected PMF state: " + str(pmf))
+ finally:
+ dev[0].set("pmf", "0")
+
+def test_ap_hs20_auto_interworking_global_pmf_fail(dev, apdev):
+ """Hotspot 2.0 connection with auto_interworking=1 and pmf=2 failure"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['ieee80211w'] = "0"
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable(auto_interworking=True)
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ try:
+ dev[0].set("pmf", "2")
+ dev[0].request("REASSOCIATE")
+ for i in range(2):
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "INTERWORKING-SELECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection result not reported")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ dev[0].request("DISCONNECT")
+ finally:
+ dev[0].set("pmf", "0")
+
+@remote_compatible
+def test_ap_hs20_auto_interworking_no_match(dev, apdev):
+ """Hotspot 2.0 connection with auto_interworking=1 and no matching network"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "mismatch"})
+
+ dev[0].hs20_enable(auto_interworking=True)
+ id = dev[0].connect("mismatch", psk="12345678", scan_freq="2412",
+ only_add_network=True)
+ dev[0].request("ENABLE_NETWORK " + str(id) + " no-connect")
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ for i in range(5):
+ logger.info("start ping")
+ if "PONG" not in dev[0].ctrl.request("PING", timeout=2):
+ raise Exception("PING failed")
+ logger.info("ping done")
+ fetch = 0
+ scan = 0
+ for j in range(15):
+ ev = dev[0].wait_event(["ANQP fetch completed",
+ "CTRL-EVENT-SCAN-RESULTS"], timeout=0.05)
+ if ev is None:
+ break
+ if "ANQP fetch completed" in ev:
+ fetch += 1
+ else:
+ scan += 1
+ if fetch > 2 * scan + 3:
+ raise Exception("Too many ANQP fetch iterations")
+ dev[0].dump_monitor()
+ dev[0].request("DISCONNECT")
+
+@remote_compatible
+def test_ap_hs20_auto_interworking_no_cred_match(dev, apdev):
+ """Hotspot 2.0 connection with auto_interworking=1 but no cred match"""
+ bssid = apdev[0]['bssid']
+ params = {"ssid": "test"}
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable(auto_interworking=True)
+ dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com"})
+
+ id = dev[0].connect("test", psk="12345678", only_add_network=True)
+ dev[0].request("ENABLE_NETWORK %s" % id)
+ logger.info("Verify that scanning continues when there is partial network block match")
+ for i in range(0, 2):
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("Scan timed out")
+ logger.info("Scan completed")
+
+def eap_test(dev, ap, eap_params, method, user, release=0):
+ bssid = ap['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com," + eap_params]
+ if release > 0:
+ params['hs20_release'] = str(release)
+ hapd = hostapd.add_ap(ap, params)
+
+ dev.flush_scan_cache()
+ dev.hs20_enable()
+ dev.add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': user,
+ 'password': "password"})
+ interworking_select(dev, bssid, freq="2412")
+ interworking_connect(dev, bssid, method)
+ return hapd
+
+@remote_compatible
+def test_ap_hs20_eap_unknown(dev, apdev):
+ """Hotspot 2.0 connection with unknown EAP method"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,99"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values(default_cred())
+ interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_eap_peap_mschapv2(dev, apdev):
+ """Hotspot 2.0 connection with PEAP/MSCHAPV2"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ eap_test(dev[0], apdev[0], "25[3:26]", "PEAP", "user")
+
+def test_ap_hs20_eap_peap_default(dev, apdev):
+ """Hotspot 2.0 connection with PEAP/MSCHAPV2 (as default)"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ eap_test(dev[0], apdev[0], "25", "PEAP", "user")
+
+def test_ap_hs20_eap_peap_gtc(dev, apdev):
+ """Hotspot 2.0 connection with PEAP/GTC"""
+ eap_test(dev[0], apdev[0], "25[3:6]", "PEAP", "user")
+
+@remote_compatible
+def test_ap_hs20_eap_peap_unknown(dev, apdev):
+ """Hotspot 2.0 connection with PEAP/unknown"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,25[3:99]"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values(default_cred())
+ interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_eap_ttls_chap(dev, apdev):
+ """Hotspot 2.0 connection with TTLS/CHAP"""
+ skip_with_fips(dev[0])
+ eap_test(dev[0], apdev[0], "21[2:2]", "TTLS", "chap user")
+
+def test_ap_hs20_eap_ttls_mschap(dev, apdev):
+ """Hotspot 2.0 connection with TTLS/MSCHAP"""
+ skip_with_fips(dev[0])
+ eap_test(dev[0], apdev[0], "21[2:3]", "TTLS", "mschap user")
+
+def test_ap_hs20_eap_ttls_default(dev, apdev):
+ """Hotspot 2.0 connection with TTLS/default"""
+ skip_with_fips(dev[0])
+ eap_test(dev[0], apdev[0], "21", "TTLS", "hs20-test")
+
+def test_ap_hs20_eap_ttls_eap_mschapv2(dev, apdev):
+ """Hotspot 2.0 connection with TTLS/EAP-MSCHAPv2"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ eap_test(dev[0], apdev[0], "21[3:26][6:7][99:99]", "TTLS", "user")
+
+@remote_compatible
+def test_ap_hs20_eap_ttls_eap_unknown(dev, apdev):
+ """Hotspot 2.0 connection with TTLS/EAP-unknown"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,21[3:99]"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values(default_cred())
+ interworking_select(dev[0], None, no_match=True, freq="2412")
+
+@remote_compatible
+def test_ap_hs20_eap_ttls_eap_unsupported(dev, apdev):
+ """Hotspot 2.0 connection with TTLS/EAP-OTP(unsupported)"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,21[3:5]"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values(default_cred())
+ interworking_select(dev[0], None, no_match=True, freq="2412")
+
+@remote_compatible
+def test_ap_hs20_eap_ttls_unknown(dev, apdev):
+ """Hotspot 2.0 connection with TTLS/unknown"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,21[2:5]"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values(default_cred())
+ interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_eap_fast_mschapv2(dev, apdev):
+ """Hotspot 2.0 connection with FAST/EAP-MSCHAPV2"""
+ check_eap_capa(dev[0], "FAST")
+ eap_test(dev[0], apdev[0], "43[3:26]", "FAST", "user")
+
+def test_ap_hs20_eap_fast_gtc(dev, apdev):
+ """Hotspot 2.0 connection with FAST/EAP-GTC"""
+ check_eap_capa(dev[0], "FAST")
+ eap_test(dev[0], apdev[0], "43[3:6]", "FAST", "user")
+
+def test_ap_hs20_eap_tls(dev, apdev):
+ """Hotspot 2.0 connection with EAP-TLS"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,13[5:6]"]
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].flush_scan_cache()
+ dev[0].hs20_enable()
+ dev[0].add_cred_values({'realm': "example.com",
+ 'username': "certificate-user",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'client_cert': "auth_serv/user.pem",
+ 'private_key': "auth_serv/user.key"})
+ interworking_select(dev[0], bssid, freq="2412")
+ interworking_connect(dev[0], bssid, "TLS")
+
+@remote_compatible
+def test_ap_hs20_eap_cert_unknown(dev, apdev):
+ """Hotspot 2.0 connection with certificate, but unknown EAP method"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,99[5:6]"]
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values({'realm': "example.com",
+ 'username': "certificate-user",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'client_cert': "auth_serv/user.pem",
+ 'private_key': "auth_serv/user.key"})
+ interworking_select(dev[0], None, no_match=True, freq="2412")
+
+@remote_compatible
+def test_ap_hs20_eap_cert_unsupported(dev, apdev):
+ """Hotspot 2.0 connection with certificate, but unsupported TTLS"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,21[5:6]"]
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values({'realm': "example.com",
+ 'username': "certificate-user",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'client_cert': "auth_serv/user.pem",
+ 'private_key': "auth_serv/user.key"})
+ interworking_select(dev[0], None, no_match=True, freq="2412")
+
+@remote_compatible
+def test_ap_hs20_eap_invalid_cred(dev, apdev):
+ """Hotspot 2.0 connection with invalid cred configuration"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values({'realm': "example.com",
+ 'username': "certificate-user",
+ 'client_cert': "auth_serv/user.pem"})
+ interworking_select(dev[0], None, no_match=True, freq="2412")
+
+def test_ap_hs20_nai_realms(dev, apdev):
+ """Hotspot 2.0 connection and multiple NAI realms and TTLS/PAP"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['nai_realm'] = ["0,no.match.here;example.com;no.match.here.either,21[2:1][5:7]"]
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].flush_scan_cache()
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "pap user",
+ 'password': "password",
+ 'domain': "example.com"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ check_sp_type(dev[0], "home")
+
+def test_ap_hs20_roaming_consortium(dev, apdev):
+ """Hotspot 2.0 connection based on roaming consortium match"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].flush_scan_cache()
+ dev[0].hs20_enable()
+ for consortium in ["112233", "1020304050", "010203040506", "fedcba"]:
+ id = dev[0].add_cred_values({'username': "user",
+ 'password': "password",
+ 'domain': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'roaming_consortium': consortium,
+ 'eap': "PEAP"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "PEAP")
+ check_sp_type(dev[0], "home")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-ALREADY-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on already-connected event")
+ dev[0].remove_cred(id)
+
+def test_ap_hs20_roaming_consortiums_match(dev, apdev):
+ """Hotspot 2.0 connection based on roaming_consortiums match"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].flush_scan_cache()
+ dev[0].hs20_enable()
+ tests = [("112233", "112233"),
+ ("ffffff,1020304050,eeeeee", "1020304050")]
+ for consortium, selected in tests:
+ id = dev[0].add_cred_values({'username': "user",
+ 'password': "password",
+ 'domain': "my.home.example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'roaming_consortiums': consortium,
+ 'eap': "PEAP"})
+ interworking_select(dev[0], bssid, "roaming", freq="2412")
+ interworking_connect(dev[0], bssid, "PEAP")
+ check_sp_type(dev[0], "roaming")
+ network_id = dev[0].get_status_field("id")
+ sel = dev[0].get_network(network_id, "roaming_consortium_selection")
+ if sel != selected:
+ raise Exception("Unexpected roaming_consortium_selection value: " +
+ sel)
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-ALREADY-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on already-connected event")
+ dev[0].remove_cred(id)
+
+def test_ap_hs20_max_roaming_consortiums(dev, apdev):
+ """Maximum number of cred roaming_consortiums"""
+ id = dev[0].add_cred()
+ consortium = (36*",ffffff")[1:]
+ if "OK" not in dev[0].request('SET_CRED %d roaming_consortiums "%s"' % (id, consortium)):
+ raise Exception("Maximum number of consortium OIs rejected")
+ consortium = (37*",ffffff")[1:]
+ if "FAIL" not in dev[0].request('SET_CRED %d roaming_consortiums "%s"' % (id, consortium)):
+ raise Exception("Over maximum number of consortium OIs accepted")
+ dev[0].remove_cred(id)
+
+def test_ap_hs20_roaming_consortium_invalid(dev, apdev):
+ """Hotspot 2.0 connection and invalid roaming consortium ANQP-element"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ # Override Roaming Consortium ANQP-element with an incorrectly encoded
+ # value.
+ params['anqp_elem'] = "261:04fedcba"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'username': "user",
+ 'password': "password",
+ 'domain': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'roaming_consortium': "fedcba",
+ 'eap': "PEAP"})
+ interworking_select(dev[0], bssid, "home", freq="2412", no_match=True)
+
+def test_ap_hs20_roaming_consortium_element(dev, apdev):
+ """Hotspot 2.0 connection and invalid roaming consortium element"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ del params['roaming_consortium']
+ params['vendor_elements'] = '6f00'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ id = dev[0].add_cred_values({'username': "user",
+ 'password': "password",
+ 'domain': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'roaming_consortium': "112233",
+ 'eap': "PEAP"})
+ interworking_select(dev[0], bssid, freq="2412", no_match=True)
+
+ hapd.set('vendor_elements', '6f020001')
+ if "OK" not in hapd.request("UPDATE_BEACON"):
+ raise Exception("UPDATE_BEACON failed")
+ dev[0].request("BSS_FLUSH 0")
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ interworking_select(dev[0], bssid, freq="2412", no_match=True)
+
+def test_ap_hs20_roaming_consortium_constraints(dev, apdev):
+ """Hotspot 2.0 connection and roaming consortium constraints"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['bss_load_test'] = "12:200:20000"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+
+ vals = {'username': "user",
+ 'password': "password",
+ 'domain': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'roaming_consortium': "fedcba",
+ 'eap': "TTLS"}
+ vals2 = vals.copy()
+ vals2['required_roaming_consortium'] = "223344"
+ id = dev[0].add_cred_values(vals2)
+ interworking_select(dev[0], bssid, "home", freq="2412", no_match=True)
+ dev[0].remove_cred(id)
+
+ vals2 = vals.copy()
+ vals2['min_dl_bandwidth_home'] = "65500"
+ id = dev[0].add_cred_values(vals2)
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-AP"], timeout=15)
+ if ev is None:
+ raise Exception("No AP found")
+ if "below_min_backhaul=1" not in ev:
+ raise Exception("below_min_backhaul not reported")
+ dev[0].remove_cred(id)
+
+ vals2 = vals.copy()
+ vals2['max_bss_load'] = "100"
+ id = dev[0].add_cred_values(vals2)
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-AP"], timeout=15)
+ if ev is None:
+ raise Exception("No AP found")
+ if "over_max_bss_load=1" not in ev:
+ raise Exception("over_max_bss_load not reported")
+ dev[0].remove_cred(id)
+
+ vals2 = vals.copy()
+ vals2['req_conn_capab'] = "6:1234"
+ vals2['domain'] = 'example.org'
+ id = dev[0].add_cred_values(vals2)
+
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-AP"], timeout=15)
+ if ev is None:
+ raise Exception("No AP found")
+ if "conn_capab_missing=1" not in ev:
+ raise Exception("conn_capab_missing not reported")
+ dev[0].remove_cred(id)
+
+ values = default_cred()
+ values['roaming_consortium'] = "fedcba"
+ id3 = dev[0].add_cred_values(values)
+
+ vals2 = vals.copy()
+ vals2['roaming_consortium'] = "fedcba"
+ vals2['priority'] = "2"
+ id = dev[0].add_cred_values(vals2)
+
+ values = default_cred()
+ values['roaming_consortium'] = "fedcba"
+ id2 = dev[0].add_cred_values(values)
+
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-AP"], timeout=15)
+ if ev is None:
+ raise Exception("No AP found")
+ dev[0].remove_cred(id)
+ dev[0].remove_cred(id2)
+ dev[0].remove_cred(id3)
+
+def test_ap_hs20_3gpp_constraints(dev, apdev):
+ """Hotspot 2.0 connection and 3GPP credential constraints"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_3gpp_cell_net'] = "555,444"
+ params['domain_name'] = "wlan.mnc444.mcc555.3gppnetwork.org"
+ params['bss_load_test'] = "12:200:20000"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+
+ vals = {'imsi': "555444-333222111",
+ 'eap': "SIM",
+ 'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123"}
+ vals2 = vals.copy()
+ vals2['required_roaming_consortium'] = "223344"
+ id = dev[0].add_cred_values(vals2)
+ interworking_select(dev[0], bssid, "home", freq="2412", no_match=True)
+ dev[0].remove_cred(id)
+
+ vals2 = vals.copy()
+ vals2['min_dl_bandwidth_home'] = "65500"
+ id = dev[0].add_cred_values(vals2)
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-AP"], timeout=15)
+ if ev is None:
+ raise Exception("No AP found")
+ if "below_min_backhaul=1" not in ev:
+ raise Exception("below_min_backhaul not reported")
+ dev[0].remove_cred(id)
+
+ vals2 = vals.copy()
+ vals2['max_bss_load'] = "100"
+ id = dev[0].add_cred_values(vals2)
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-AP"], timeout=15)
+ if ev is None:
+ raise Exception("No AP found")
+ if "over_max_bss_load=1" not in ev:
+ raise Exception("over_max_bss_load not reported")
+ dev[0].remove_cred(id)
+
+ values = default_cred()
+ values['roaming_consortium'] = "fedcba"
+ id3 = dev[0].add_cred_values(values)
+
+ vals2 = vals.copy()
+ vals2['roaming_consortium'] = "fedcba"
+ vals2['priority'] = "2"
+ id = dev[0].add_cred_values(vals2)
+
+ values = default_cred()
+ values['roaming_consortium'] = "fedcba"
+ id2 = dev[0].add_cred_values(values)
+
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-AP"], timeout=15)
+ if ev is None:
+ raise Exception("No AP found")
+ dev[0].remove_cred(id)
+ dev[0].remove_cred(id2)
+ dev[0].remove_cred(id3)
+
+ hapd.disable()
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_3gpp_cell_net'] = "555,444"
+ params['bss_load_test'] = "12:200:20000"
+ hapd = hostapd.add_ap(apdev[0], params)
+ vals2 = vals.copy()
+ vals2['req_conn_capab'] = "6:1234"
+ id = dev[0].add_cred_values(vals2)
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-AP"], timeout=15)
+ if ev is None:
+ raise Exception("No AP found")
+ if "conn_capab_missing=1" not in ev:
+ raise Exception("conn_capab_missing not reported")
+ dev[0].remove_cred(id)
+
+def test_ap_hs20_connect_no_full_match(dev, apdev):
+ """Hotspot 2.0 connection and no full match"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_3gpp_cell_net'] = "555,444"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].flush_scan_cache()
+ dev[0].hs20_enable()
+
+ vals = {'username': "user",
+ 'password': "password",
+ 'domain': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'roaming_consortium': "fedcba",
+ 'eap': "TTLS",
+ 'min_dl_bandwidth_home': "65500"}
+ id = dev[0].add_cred_values(vals)
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-AP"], timeout=15)
+ if ev is None:
+ raise Exception("No AP found")
+ if "below_min_backhaul=1" not in ev:
+ raise Exception("below_min_backhaul not reported")
+ interworking_connect(dev[0], bssid, "TTLS")
+ dev[0].remove_cred(id)
+ dev[0].wait_disconnected()
+
+ vals = {'imsi': "555444-333222111", 'eap': "SIM",
+ 'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+ 'min_dl_bandwidth_roaming': "65500"}
+ id = dev[0].add_cred_values(vals)
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-AP"], timeout=15)
+ if ev is None:
+ raise Exception("No AP found")
+ if "below_min_backhaul=1" not in ev:
+ raise Exception("below_min_backhaul not reported")
+ interworking_connect(dev[0], bssid, "SIM")
+ dev[0].remove_cred(id)
+ dev[0].wait_disconnected()
+
+def test_ap_hs20_username_roaming(dev, apdev):
+ """Hotspot 2.0 connection in username/password credential (roaming)"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,13[5:6],21[2:4][5:7]",
+ "0,roaming.example.com,21[2:4][5:7]",
+ "0,another.example.com"]
+ params['domain_name'] = "another.example.com"
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "roaming.example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com"})
+ interworking_select(dev[0], bssid, "roaming", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ check_sp_type(dev[0], "roaming")
+
+def test_ap_hs20_username_unknown(dev, apdev):
+ """Hotspot 2.0 connection in username/password credential (no domain in cred)"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password"})
+ interworking_select(dev[0], bssid, "unknown", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ check_sp_type(dev[0], "unknown")
+
+def test_ap_hs20_username_unknown2(dev, apdev):
+ """Hotspot 2.0 connection in username/password credential (no domain advertized)"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ del params['domain_name']
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"})
+ interworking_select(dev[0], bssid, "unknown", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ check_sp_type(dev[0], "unknown")
+
+def test_ap_hs20_gas_while_associated(dev, apdev):
+ """Hotspot 2.0 connection with GAS query while associated"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+
+ logger.info("Verifying GAS query while associated")
+ dev[0].request("FETCH_ANQP")
+ for i in range(0, 6):
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("Operation timed out")
+
+def test_ap_hs20_gas_with_another_ap_while_associated(dev, apdev):
+ """GAS query with another AP while associated"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid2
+ params['nai_realm'] = ["0,no-match.example.org,13[5:6],21[2:4][5:7]"]
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ dev[0].dump_monitor()
+
+ logger.info("Verifying GAS query with same AP while associated")
+ dev[0].request("ANQP_GET " + bssid + " 263")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP operation timed out")
+ dev[0].dump_monitor()
+
+ logger.info("Verifying GAS query with another AP while associated")
+ dev[0].scan_for_bss(bssid2, 2412)
+ dev[0].request("ANQP_GET " + bssid2 + " 263")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP operation timed out")
+
+def test_ap_hs20_gas_while_associated_with_pmf(dev, apdev):
+ """Hotspot 2.0 connection with GAS query while associated and using PMF"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_gas_while_associated_with_pmf(dev, apdev)
+ finally:
+ dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_gas_while_associated_with_pmf(dev, apdev):
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid2
+ params['nai_realm'] = ["0,no-match.example.org,13[5:6],21[2:4][5:7]"]
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].flush_scan_cache()
+ dev[0].hs20_enable()
+ dev[0].request("SET pmf 2")
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+
+ logger.info("Verifying GAS query while associated")
+ dev[0].request("FETCH_ANQP")
+ for i in range(0, 2 * 6):
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("Operation timed out")
+
+def test_ap_hs20_gas_with_another_ap_while_using_pmf(dev, apdev):
+ """GAS query with another AP while associated and using PMF"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_gas_with_another_ap_while_using_pmf(dev, apdev)
+ finally:
+ dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_gas_with_another_ap_while_using_pmf(dev, apdev):
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid2
+ params['nai_realm'] = ["0,no-match.example.org,13[5:6],21[2:4][5:7]"]
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ dev[0].request("SET pmf 2")
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ dev[0].dump_monitor()
+ hapd.wait_sta()
+
+ logger.info("Verifying GAS query with same AP while associated")
+ dev[0].request("ANQP_GET " + bssid + " 263")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP operation timed out")
+ dev[0].dump_monitor()
+
+ logger.info("Verifying GAS query with another AP while associated")
+ dev[0].scan_for_bss(bssid2, 2412)
+ dev[0].request("ANQP_GET " + bssid2 + " 263")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP operation timed out")
+
+def test_ap_hs20_gas_frag_while_associated(dev, apdev):
+ """Hotspot 2.0 connection with fragmented GAS query while associated"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.set("gas_frag_limit", "50")
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ hapd.wait_sta()
+
+ logger.info("Verifying GAS query while associated")
+ dev[0].request("FETCH_ANQP")
+ for i in range(0, 6):
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("Operation timed out")
+
+def test_ap_hs20_multiple_connects(dev, apdev):
+ """Hotspot 2.0 connection through multiple network selections"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ values = {'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"}
+ id = dev[0].add_cred_values(values)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ for i in range(0, 3):
+ logger.info("Starting Interworking network selection")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ while True:
+ ev = dev[0].wait_event(["INTERWORKING-NO-MATCH",
+ "INTERWORKING-ALREADY-CONNECTED",
+ "CTRL-EVENT-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection timed out")
+ if "INTERWORKING-NO-MATCH" in ev:
+ raise Exception("Matching AP not found")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ break
+ if i == 2 and "INTERWORKING-ALREADY-CONNECTED" in ev:
+ break
+ if i == 0:
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+
+ networks = dev[0].list_networks()
+ if len(networks) > 1:
+ raise Exception("Duplicated network block detected")
+
+def test_ap_hs20_disallow_aps(dev, apdev):
+ """Hotspot 2.0 connection and disallow_aps"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ values = {'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"}
+ id = dev[0].add_cred_values(values)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ logger.info("Verify disallow_aps bssid")
+ dev[0].request("SET disallow_aps bssid " + bssid.replace(':', ''))
+ dev[0].request("INTERWORKING_SELECT auto")
+ ev = dev[0].wait_event(["INTERWORKING-NO-MATCH"], timeout=15)
+ if ev is None:
+ raise Exception("Network selection timed out")
+ dev[0].dump_monitor()
+
+ logger.info("Verify disallow_aps ssid")
+ dev[0].request("SET disallow_aps ssid 746573742d68733230")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-NO-MATCH"], timeout=15)
+ if ev is None:
+ raise Exception("Network selection timed out")
+ dev[0].dump_monitor()
+
+ logger.info("Verify disallow_aps clear")
+ dev[0].request("SET disallow_aps ")
+ interworking_select(dev[0], bssid, "home", freq="2412")
+
+ dev[0].request("SET disallow_aps bssid " + bssid.replace(':', ''))
+ ret = dev[0].request("INTERWORKING_CONNECT " + bssid)
+ if "FAIL" not in ret:
+ raise Exception("INTERWORKING_CONNECT to disallowed BSS not rejected")
+
+ if "FAIL" not in dev[0].request("INTERWORKING_CONNECT foo"):
+ raise Exception("Invalid INTERWORKING_CONNECT not rejected")
+ if "FAIL" not in dev[0].request("INTERWORKING_CONNECT 00:11:22:33:44:55"):
+ raise Exception("Invalid INTERWORKING_CONNECT not rejected")
+
+def policy_test(dev, ap, values, only_one=True):
+ dev.dump_monitor()
+ if ap:
+ logger.info("Verify network selection to AP " + ap['ifname'])
+ bssid = ap['bssid']
+ dev.scan_for_bss(bssid, freq="2412")
+ else:
+ logger.info("Verify network selection")
+ bssid = None
+ dev.hs20_enable()
+ id = dev.add_cred_values(values)
+ dev.request("INTERWORKING_SELECT auto freq=2412")
+ events = []
+ while True:
+ ev = dev.wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH",
+ "INTERWORKING-BLACKLISTED",
+ "INTERWORKING-SELECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Network selection timed out")
+ events.append(ev)
+ if "INTERWORKING-NO-MATCH" in ev:
+ raise Exception("Matching AP not found")
+ if bssid and only_one and "INTERWORKING-AP" in ev and bssid not in ev:
+ raise Exception("Unexpected AP claimed acceptable")
+ if "INTERWORKING-SELECTED" in ev:
+ if bssid and bssid not in ev:
+ raise Exception("Selected incorrect BSS")
+ break
+
+ ev = dev.wait_connected(timeout=15)
+ if bssid and bssid not in ev:
+ raise Exception("Connected to incorrect BSS")
+
+ conn_bssid = dev.get_status_field("bssid")
+ if bssid and conn_bssid != bssid:
+ raise Exception("bssid information points to incorrect BSS")
+
+ dev.remove_cred(id)
+ dev.dump_monitor()
+ return events
+
+def default_cred(domain=None, user="hs20-test"):
+ cred = {'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': user,
+ 'password': "password"}
+ if domain:
+ cred['domain'] = domain
+ return cred
+
+def test_ap_hs20_prefer_home(dev, apdev):
+ """Hotspot 2.0 required roaming consortium"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hs20_ap_params()
+ params['domain_name'] = "example.org"
+ hostapd.add_ap(apdev[0], params)
+
+ params = hs20_ap_params()
+ params['ssid'] = "test-hs20-other"
+ params['domain_name'] = "example.com"
+ hostapd.add_ap(apdev[1], params)
+
+ values = default_cred()
+ values['domain'] = "example.com"
+ policy_test(dev[0], apdev[1], values, only_one=False)
+ values['domain'] = "example.org"
+ policy_test(dev[0], apdev[0], values, only_one=False)
+
+def test_ap_hs20_req_roaming_consortium(dev, apdev):
+ """Hotspot 2.0 required roaming consortium"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ params = hs20_ap_params()
+ params['ssid'] = "test-hs20-other"
+ params['roaming_consortium'] = ["223344"]
+ hostapd.add_ap(apdev[1], params)
+
+ values = default_cred()
+ values['required_roaming_consortium'] = "223344"
+ policy_test(dev[0], apdev[1], values)
+ values['required_roaming_consortium'] = "112233"
+ policy_test(dev[0], apdev[0], values)
+
+ id = dev[0].add_cred()
+ dev[0].set_cred(id, "required_roaming_consortium", "112233")
+ dev[0].set_cred(id, "required_roaming_consortium", "112233445566778899aabbccddeeff")
+
+ for val in ["", "1", "11", "1122", "1122334",
+ "112233445566778899aabbccddeeff00"]:
+ if "FAIL" not in dev[0].request('SET_CRED {} required_roaming_consortium {}'.format(id, val)):
+ raise Exception("Invalid roaming consortium value accepted: " + val)
+
+def test_ap_hs20_req_roaming_consortium_no_match(dev, apdev):
+ """Hotspot 2.0 required roaming consortium and no match"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hs20_ap_params()
+ del params['roaming_consortium']
+ hostapd.add_ap(apdev[0], params)
+
+ params = hs20_ap_params()
+ params['ssid'] = "test-hs20-other"
+ params['roaming_consortium'] = ["223345"]
+ hostapd.add_ap(apdev[1], params)
+
+ values = default_cred()
+ values['required_roaming_consortium'] = "223344"
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values(values)
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-NO-MATCH"], timeout=10)
+ if ev is None:
+ raise Exception("INTERWORKING-NO-MATCH not reported")
+
+def test_ap_hs20_excluded_ssid(dev, apdev):
+ """Hotspot 2.0 exclusion based on SSID"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hs20_ap_params()
+ params['roaming_consortium'] = ["223344"]
+ params['anqp_3gpp_cell_net'] = "555,444"
+ hostapd.add_ap(apdev[0], params)
+
+ params = hs20_ap_params()
+ params['ssid'] = "test-hs20-other"
+ params['roaming_consortium'] = ["223344"]
+ params['anqp_3gpp_cell_net'] = "555,444"
+ hostapd.add_ap(apdev[1], params)
+
+ values = default_cred()
+ values['excluded_ssid'] = "test-hs20"
+ events = policy_test(dev[0], apdev[1], values)
+ ev = [e for e in events if "INTERWORKING-BLACKLISTED " + apdev[0]['bssid'] in e]
+ if len(ev) != 1:
+ raise Exception("Excluded network not reported")
+ values['excluded_ssid'] = "test-hs20-other"
+ events = policy_test(dev[0], apdev[0], values)
+ ev = [e for e in events if "INTERWORKING-BLACKLISTED " + apdev[1]['bssid'] in e]
+ if len(ev) != 1:
+ raise Exception("Excluded network not reported")
+
+ values = default_cred()
+ values['roaming_consortium'] = "223344"
+ values['eap'] = "TTLS"
+ values['phase2'] = "auth=MSCHAPV2"
+ values['excluded_ssid'] = "test-hs20"
+ events = policy_test(dev[0], apdev[1], values)
+ ev = [e for e in events if "INTERWORKING-BLACKLISTED " + apdev[0]['bssid'] in e]
+ if len(ev) != 1:
+ raise Exception("Excluded network not reported")
+
+ values = {'imsi': "555444-333222111", 'eap': "SIM",
+ 'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+ 'excluded_ssid': "test-hs20"}
+ events = policy_test(dev[0], apdev[1], values)
+ ev = [e for e in events if "INTERWORKING-BLACKLISTED " + apdev[0]['bssid'] in e]
+ if len(ev) != 1:
+ raise Exception("Excluded network not reported")
+
+def test_ap_hs20_roam_to_higher_prio(dev, apdev):
+ """Hotspot 2.0 and roaming from current to higher priority network"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params(ssid="test-hs20-visited")
+ params['domain_name'] = "visited.example.org"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"})
+ logger.info("Connect to the only network option")
+ interworking_select(dev[0], bssid, "roaming", freq="2412")
+ dev[0].dump_monitor()
+ interworking_connect(dev[0], bssid, "TTLS")
+
+ logger.info("Start another AP (home operator) and reconnect")
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20-home")
+ params['domain_name'] = "example.com"
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].scan_for_bss(bssid2, freq="2412", force_scan=True)
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-NO-MATCH",
+ "INTERWORKING-ALREADY-CONNECTED",
+ "CTRL-EVENT-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection timed out")
+ if "INTERWORKING-NO-MATCH" in ev:
+ raise Exception("Matching AP not found")
+ if "INTERWORKING-ALREADY-CONNECTED" in ev:
+ raise Exception("Unexpected AP selected")
+ if bssid2 not in ev:
+ raise Exception("Unexpected BSSID after reconnection")
+
+def test_ap_hs20_domain_suffix_match_full(dev, apdev):
+ """Hotspot 2.0 and domain_suffix_match"""
+ check_domain_match_full(dev[0])
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'domain_suffix_match': "server.w1.fi"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ dev[0].dump_monitor()
+ interworking_connect(dev[0], bssid, "TTLS")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ dev[0].set_cred_quoted(id, "domain_suffix_match", "no-match.example.com")
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"])
+ if ev is None:
+ raise Exception("TLS certificate error not reported")
+ if "Domain suffix mismatch" not in ev:
+ raise Exception("Domain suffix mismatch not reported")
+
+def test_ap_hs20_domain_suffix_match(dev, apdev):
+ """Hotspot 2.0 and domain_suffix_match"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ check_domain_match_full(dev[0])
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'domain_suffix_match': "w1.fi"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ dev[0].dump_monitor()
+ interworking_connect(dev[0], bssid, "TTLS")
+
+def test_ap_hs20_roaming_partner_preference(dev, apdev):
+ """Hotspot 2.0 and roaming partner preference"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hs20_ap_params()
+ params['domain_name'] = "roaming.example.org"
+ hostapd.add_ap(apdev[0], params)
+
+ params = hs20_ap_params()
+ params['ssid'] = "test-hs20-other"
+ params['domain_name'] = "roaming.example.net"
+ hostapd.add_ap(apdev[1], params)
+
+ logger.info("Verify default vs. specified preference")
+ values = default_cred()
+ values['roaming_partner'] = "roaming.example.net,1,127,*"
+ policy_test(dev[0], apdev[1], values, only_one=False)
+ values['roaming_partner'] = "roaming.example.net,1,129,*"
+ policy_test(dev[0], apdev[0], values, only_one=False)
+
+ logger.info("Verify partial FQDN match")
+ values['roaming_partner'] = "example.net,0,0,*"
+ policy_test(dev[0], apdev[1], values, only_one=False)
+ values['roaming_partner'] = "example.net,0,255,*"
+ policy_test(dev[0], apdev[0], values, only_one=False)
+
+def test_ap_hs20_max_bss_load(dev, apdev):
+ """Hotspot 2.0 and maximum BSS load"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hs20_ap_params()
+ params['bss_load_test'] = "12:200:20000"
+ hostapd.add_ap(apdev[0], params)
+
+ params = hs20_ap_params()
+ params['ssid'] = "test-hs20-other"
+ params['bss_load_test'] = "5:20:10000"
+ hostapd.add_ap(apdev[1], params)
+
+ logger.info("Verify maximum BSS load constraint")
+ values = default_cred()
+ values['domain'] = "example.com"
+ values['max_bss_load'] = "100"
+ events = policy_test(dev[0], apdev[1], values, only_one=False)
+
+ ev = [e for e in events if "INTERWORKING-AP " + apdev[0]['bssid'] in e]
+ if len(ev) != 1 or "over_max_bss_load=1" not in ev[0]:
+ raise Exception("Maximum BSS Load case not noticed")
+ ev = [e for e in events if "INTERWORKING-AP " + apdev[1]['bssid'] in e]
+ if len(ev) != 1 or "over_max_bss_load=1" in ev[0]:
+ raise Exception("Maximum BSS Load case reported incorrectly")
+
+ logger.info("Verify maximum BSS load does not prevent connection")
+ values['max_bss_load'] = "1"
+ events = policy_test(dev[0], None, values)
+
+ ev = [e for e in events if "INTERWORKING-AP " + apdev[0]['bssid'] in e]
+ if len(ev) != 1 or "over_max_bss_load=1" not in ev[0]:
+ raise Exception("Maximum BSS Load case not noticed")
+ ev = [e for e in events if "INTERWORKING-AP " + apdev[1]['bssid'] in e]
+ if len(ev) != 1 or "over_max_bss_load=1" not in ev[0]:
+ raise Exception("Maximum BSS Load case not noticed")
+
+def test_ap_hs20_max_bss_load2(dev, apdev):
+ """Hotspot 2.0 and maximum BSS load with one AP not advertising"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hs20_ap_params()
+ params['bss_load_test'] = "12:200:20000"
+ hostapd.add_ap(apdev[0], params)
+
+ params = hs20_ap_params()
+ params['ssid'] = "test-hs20-other"
+ hostapd.add_ap(apdev[1], params)
+
+ logger.info("Verify maximum BSS load constraint with AP advertisement")
+ values = default_cred()
+ values['domain'] = "example.com"
+ values['max_bss_load'] = "100"
+ events = policy_test(dev[0], apdev[1], values, only_one=False)
+
+ ev = [e for e in events if "INTERWORKING-AP " + apdev[0]['bssid'] in e]
+ if len(ev) != 1 or "over_max_bss_load=1" not in ev[0]:
+ raise Exception("Maximum BSS Load case not noticed")
+ ev = [e for e in events if "INTERWORKING-AP " + apdev[1]['bssid'] in e]
+ if len(ev) != 1 or "over_max_bss_load=1" in ev[0]:
+ raise Exception("Maximum BSS Load case reported incorrectly")
+
+def test_ap_hs20_max_bss_load_roaming(dev, apdev):
+ """Hotspot 2.0 and maximum BSS load (roaming)"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hs20_ap_params()
+ params['bss_load_test'] = "12:200:20000"
+ hostapd.add_ap(apdev[0], params)
+
+ values = default_cred()
+ values['domain'] = "roaming.example.com"
+ values['max_bss_load'] = "100"
+ events = policy_test(dev[0], apdev[0], values, only_one=True)
+ ev = [e for e in events if "INTERWORKING-AP " + apdev[0]['bssid'] in e]
+ if len(ev) != 1:
+ raise Exception("No INTERWORKING-AP event")
+ if "over_max_bss_load=1" in ev[0]:
+ raise Exception("Maximum BSS Load reported for roaming")
+
+def test_ap_hs20_multi_cred_sp_prio(dev, apdev):
+ """Hotspot 2.0 multi-cred sp_priority"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_multi_cred_sp_prio(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_hs20_multi_cred_sp_prio(dev, apdev):
+ hlr_auc_gw_available()
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ del params['domain_name']
+ params['anqp_3gpp_cell_net'] = "232,01"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("SET external_sim 1")
+ id1 = dev[0].add_cred_values({'imsi': "23201-0000000000", 'eap': "SIM",
+ 'provisioning_sp': "example.com",
+ 'sp_priority' :"1"})
+ id2 = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com",
+ 'provisioning_sp': "example.com",
+ 'sp_priority': "2"})
+ dev[0].dump_monitor()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ interworking_ext_sim_auth(dev[0], "SIM")
+ check_sp_type(dev[0], "unknown")
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].set_cred(id1, "sp_priority", "2")
+ dev[0].set_cred(id2, "sp_priority", "1")
+ dev[0].dump_monitor()
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ interworking_auth(dev[0], "TTLS")
+ check_sp_type(dev[0], "unknown")
+
+def test_ap_hs20_multi_cred_sp_prio2(dev, apdev):
+ """Hotspot 2.0 multi-cred sp_priority with two BSSes"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_multi_cred_sp_prio2(dev, apdev)
+ finally:
+ dev[0].request("SET external_sim 0")
+
+def _test_ap_hs20_multi_cred_sp_prio2(dev, apdev):
+ hlr_auc_gw_available()
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ del params['nai_realm']
+ del params['domain_name']
+ params['anqp_3gpp_cell_net'] = "232,01"
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ params['ssid'] = "test-hs20-other"
+ params['hessid'] = bssid2
+ del params['domain_name']
+ del params['anqp_3gpp_cell_net']
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ dev[0].request("SET external_sim 1")
+ id1 = dev[0].add_cred_values({'imsi': "23201-0000000000", 'eap': "SIM",
+ 'provisioning_sp': "example.com",
+ 'sp_priority': "1"})
+ id2 = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com",
+ 'provisioning_sp': "example.com",
+ 'sp_priority': "2"})
+ dev[0].dump_monitor()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ interworking_ext_sim_auth(dev[0], "SIM")
+ check_sp_type(dev[0], "unknown")
+ conn_bssid = dev[0].get_status_field("bssid")
+ if conn_bssid != bssid:
+ raise Exception("Connected to incorrect BSS")
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].set_cred(id1, "sp_priority", "2")
+ dev[0].set_cred(id2, "sp_priority", "1")
+ dev[0].dump_monitor()
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ interworking_auth(dev[0], "TTLS")
+ check_sp_type(dev[0], "unknown")
+ conn_bssid = dev[0].get_status_field("bssid")
+ if conn_bssid != bssid2:
+ raise Exception("Connected to incorrect BSS")
+
+def test_ap_hs20_multi_cred_sp_prio_same(dev, apdev):
+ """Hotspot 2.0 multi-cred and same sp_priority"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ hlr_auc_gw_available()
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ del params['domain_name']
+ params['anqp_3gpp_cell_net'] = "232,01"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ id1 = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "domain1.example.com",
+ 'provisioning_sp': "example.com",
+ 'sp_priority': "1"})
+ id2 = dev[0].add_cred_values({'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "domain2.example.com",
+ 'provisioning_sp': "example.com",
+ 'sp_priority': "1"})
+ dev[0].dump_monitor()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ check_auto_select(dev[0], bssid)
+
+def check_conn_capab_selection(dev, type, missing):
+ dev.request("INTERWORKING_SELECT freq=2412")
+ ev = dev.wait_event(["INTERWORKING-AP"])
+ if ev is None:
+ raise Exception("Network selection timed out")
+ if "type=" + type not in ev:
+ raise Exception("Unexpected network type")
+ if missing and "conn_capab_missing=1" not in ev:
+ raise Exception("conn_capab_missing not reported")
+ if not missing and "conn_capab_missing=1" in ev:
+ raise Exception("conn_capab_missing reported unexpectedly")
+
+def conn_capab_cred(domain=None, req_conn_capab=None):
+ cred = default_cred(domain=domain)
+ if req_conn_capab:
+ cred['req_conn_capab'] = req_conn_capab
+ return cred
+
+def test_ap_hs20_req_conn_capab(dev, apdev):
+ """Hotspot 2.0 network selection with req_conn_capab"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ logger.info("Not used in home network")
+ values = conn_capab_cred(domain="example.com", req_conn_capab="6:1234")
+ id = dev[0].add_cred_values(values)
+ check_conn_capab_selection(dev[0], "home", False)
+
+ logger.info("Used in roaming network")
+ dev[0].remove_cred(id)
+ values = conn_capab_cred(domain="example.org", req_conn_capab="6:1234")
+ id = dev[0].add_cred_values(values)
+ check_conn_capab_selection(dev[0], "roaming", True)
+
+ logger.info("Verify that req_conn_capab does not prevent connection if no other network is available")
+ check_auto_select(dev[0], bssid)
+
+ logger.info("Additional req_conn_capab checks")
+
+ dev[0].remove_cred(id)
+ values = conn_capab_cred(domain="example.org", req_conn_capab="1:0")
+ id = dev[0].add_cred_values(values)
+ check_conn_capab_selection(dev[0], "roaming", True)
+
+ dev[0].remove_cred(id)
+ values = conn_capab_cred(domain="example.org", req_conn_capab="17:5060")
+ id = dev[0].add_cred_values(values)
+ check_conn_capab_selection(dev[0], "roaming", True)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20b")
+ params['hs20_conn_capab'] = ["1:0:2", "6:22:1", "17:5060:0", "50:0:1"]
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].remove_cred(id)
+ values = conn_capab_cred(domain="example.org", req_conn_capab="50")
+ id = dev[0].add_cred_values(values)
+ dev[0].set_cred(id, "req_conn_capab", "6:22")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ for i in range(0, 2):
+ ev = dev[0].wait_event(["INTERWORKING-AP"])
+ if ev is None:
+ raise Exception("Network selection timed out")
+ if bssid in ev and "conn_capab_missing=1" not in ev:
+ raise Exception("Missing protocol connection capability not reported")
+ if bssid2 in ev and "conn_capab_missing=1" in ev:
+ raise Exception("Protocol connection capability not reported correctly")
+
+def test_ap_hs20_req_conn_capab2(dev, apdev):
+ """Hotspot 2.0 network selection with req_conn_capab (not present)"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ del params['hs20_conn_capab']
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ values = conn_capab_cred(domain="example.org", req_conn_capab="6:1234")
+ id = dev[0].add_cred_values(values)
+ check_conn_capab_selection(dev[0], "roaming", False)
+
+def test_ap_hs20_req_conn_capab_and_roaming_partner_preference(dev, apdev):
+ """Hotspot 2.0 and req_conn_capab with roaming partner preference"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['domain_name'] = "roaming.example.org"
+ params['hs20_conn_capab'] = ["1:0:2", "6:22:1", "17:5060:0", "50:0:1"]
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20-b")
+ params['domain_name'] = "roaming.example.net"
+ hostapd.add_ap(apdev[1], params)
+
+ values = default_cred()
+ values['roaming_partner'] = "roaming.example.net,1,127,*"
+ id = dev[0].add_cred_values(values)
+ check_auto_select(dev[0], bssid2)
+
+ dev[0].set_cred(id, "req_conn_capab", "50")
+ check_auto_select(dev[0], bssid)
+
+ dev[0].remove_cred(id)
+ id = dev[0].add_cred_values(values)
+ dev[0].set_cred(id, "req_conn_capab", "51")
+ check_auto_select(dev[0], bssid2)
+
+def check_bandwidth_selection(dev, type, below):
+ dev.request("INTERWORKING_SELECT freq=2412")
+ ev = dev.wait_event(["INTERWORKING-AP"])
+ if ev is None:
+ raise Exception("Network selection timed out")
+ logger.debug("BSS entries:\n" + dev.request("BSS RANGE=ALL"))
+ if "type=" + type not in ev:
+ raise Exception("Unexpected network type")
+ if below and "below_min_backhaul=1" not in ev:
+ raise Exception("below_min_backhaul not reported")
+ if not below and "below_min_backhaul=1" in ev:
+ raise Exception("below_min_backhaul reported unexpectedly")
+
+def bw_cred(domain=None, dl_home=None, ul_home=None, dl_roaming=None, ul_roaming=None):
+ cred = default_cred(domain=domain)
+ if dl_home:
+ cred['min_dl_bandwidth_home'] = str(dl_home)
+ if ul_home:
+ cred['min_ul_bandwidth_home'] = str(ul_home)
+ if dl_roaming:
+ cred['min_dl_bandwidth_roaming'] = str(dl_roaming)
+ if ul_roaming:
+ cred['min_ul_bandwidth_roaming'] = str(ul_roaming)
+ return cred
+
+def test_ap_hs20_min_bandwidth_home(dev, apdev):
+ """Hotspot 2.0 network selection with min bandwidth (home)"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ values = bw_cred(domain="example.com", dl_home=5490, ul_home=58)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "home", False)
+ dev[0].remove_cred(id)
+
+ values = bw_cred(domain="example.com", dl_home=5491, ul_home=58)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "home", True)
+ dev[0].remove_cred(id)
+
+ values = bw_cred(domain="example.com", dl_home=5490, ul_home=59)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "home", True)
+ dev[0].remove_cred(id)
+
+ values = bw_cred(domain="example.com", dl_home=5491, ul_home=59)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "home", True)
+ check_auto_select(dev[0], bssid)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20-b")
+ params['hs20_wan_metrics'] = "01:8000:1000:1:1:3000"
+ hostapd.add_ap(apdev[1], params)
+
+ check_auto_select(dev[0], bssid2)
+
+def test_ap_hs20_min_bandwidth_home2(dev, apdev):
+ """Hotspot 2.0 network selection with min bandwidth - special cases"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ values = bw_cred(domain="example.com", dl_home=5490, ul_home=58)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "home", False)
+
+ logger.info("WAN link at capacity")
+ hapd.set('hs20_wan_metrics', "09:8000:1000:80:240:3000")
+ check_bandwidth_selection(dev[0], "home", True)
+
+ logger.info("Downlink/Uplink Load was not measured")
+ hapd.set('hs20_wan_metrics', "01:8000:1000:80:240:0")
+ check_bandwidth_selection(dev[0], "home", False)
+
+ logger.info("Uplink and Downlink max values")
+ hapd.set('hs20_wan_metrics', "01:4294967295:4294967295:80:240:3000")
+ check_bandwidth_selection(dev[0], "home", False)
+
+ dev[0].remove_cred(id)
+
+def test_ap_hs20_min_bandwidth_home_hidden_ssid_in_scan_res(dev, apdev):
+ """Hotspot 2.0 network selection with min bandwidth (home) while hidden SSID is included in scan results"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'secret',
+ "ignore_broadcast_ssid": "1"})
+ dev[0].scan_for_bss(bssid, freq=2412)
+ hapd.disable()
+ hapd_global = hostapd.HostapdGlobal(apdev[0])
+ hapd_global.flush()
+ hapd_global.remove(apdev[0]['ifname'])
+
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ values = bw_cred(domain="example.com", dl_home=5490, ul_home=58)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "home", False)
+ dev[0].remove_cred(id)
+
+ values = bw_cred(domain="example.com", dl_home=5491, ul_home=58)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "home", True)
+ dev[0].remove_cred(id)
+
+ values = bw_cred(domain="example.com", dl_home=5490, ul_home=59)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "home", True)
+ dev[0].remove_cred(id)
+
+ values = bw_cred(domain="example.com", dl_home=5491, ul_home=59)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "home", True)
+ check_auto_select(dev[0], bssid)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20-b")
+ params['hs20_wan_metrics'] = "01:8000:1000:1:1:3000"
+ hostapd.add_ap(apdev[1], params)
+
+ check_auto_select(dev[0], bssid2)
+
+ dev[0].flush_scan_cache()
+
+def test_ap_hs20_min_bandwidth_roaming(dev, apdev):
+ """Hotspot 2.0 network selection with min bandwidth (roaming)"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ values = bw_cred(domain="example.org", dl_roaming=5490, ul_roaming=58)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "roaming", False)
+ dev[0].remove_cred(id)
+
+ values = bw_cred(domain="example.org", dl_roaming=5491, ul_roaming=58)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "roaming", True)
+ dev[0].remove_cred(id)
+
+ values = bw_cred(domain="example.org", dl_roaming=5490, ul_roaming=59)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "roaming", True)
+ dev[0].remove_cred(id)
+
+ values = bw_cred(domain="example.org", dl_roaming=5491, ul_roaming=59)
+ id = dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "roaming", True)
+ check_auto_select(dev[0], bssid)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20-b")
+ params['hs20_wan_metrics'] = "01:8000:1000:1:1:3000"
+ hostapd.add_ap(apdev[1], params)
+
+ check_auto_select(dev[0], bssid2)
+
+def test_ap_hs20_min_bandwidth_and_roaming_partner_preference(dev, apdev):
+ """Hotspot 2.0 and minimum bandwidth with roaming partner preference"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['domain_name'] = "roaming.example.org"
+ params['hs20_wan_metrics'] = "01:8000:1000:1:1:3000"
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20-b")
+ params['domain_name'] = "roaming.example.net"
+ hostapd.add_ap(apdev[1], params)
+
+ values = default_cred()
+ values['roaming_partner'] = "roaming.example.net,1,127,*"
+ id = dev[0].add_cred_values(values)
+ check_auto_select(dev[0], bssid2)
+
+ dev[0].set_cred(id, "min_dl_bandwidth_roaming", "6000")
+ check_auto_select(dev[0], bssid)
+
+ dev[0].set_cred(id, "min_dl_bandwidth_roaming", "10000")
+ check_auto_select(dev[0], bssid2)
+
+def test_ap_hs20_min_bandwidth_no_wan_metrics(dev, apdev):
+ """Hotspot 2.0 network selection with min bandwidth but no WAN Metrics"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ del params['hs20_wan_metrics']
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ values = bw_cred(domain="example.com", dl_home=10000, ul_home=10000,
+ dl_roaming=10000, ul_roaming=10000)
+ dev[0].add_cred_values(values)
+ check_bandwidth_selection(dev[0], "home", False)
+
+def test_ap_hs20_deauth_req_ess(dev, apdev):
+ """Hotspot 2.0 connection and deauthentication request for ESS"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_deauth_req_ess(dev, apdev)
+ finally:
+ dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_deauth_req_ess(dev, apdev):
+ dev[0].request("SET pmf 2")
+ hapd = eap_test(dev[0], apdev[0], "21[3:26]", "TTLS", "user")
+ dev[0].dump_monitor()
+ addr = dev[0].p2p_interface_addr()
+ hapd.wait_sta()
+ hapd.request("HS20_DEAUTH_REQ " + addr + " 1 120 http://example.com/")
+ ev = dev[0].wait_event(["HS20-DEAUTH-IMMINENT-NOTICE"])
+ if ev is None:
+ raise Exception("Timeout on deauth imminent notice")
+ if "1 120 http://example.com/" not in ev:
+ raise Exception("Unexpected deauth imminent notice: " + ev)
+ hapd.request("DEAUTHENTICATE " + addr)
+ dev[0].wait_disconnected(timeout=10)
+ if "[TEMP-DISABLED]" not in dev[0].list_networks()[0]['flags']:
+ raise Exception("Network not marked temporarily disabled")
+ ev = dev[0].wait_event(["SME: Trying to authenticate",
+ "Trying to associate",
+ "CTRL-EVENT-CONNECTED"], timeout=5)
+ if ev is not None:
+ raise Exception("Unexpected connection attempt")
+
+def test_ap_hs20_deauth_req_bss(dev, apdev):
+ """Hotspot 2.0 connection and deauthentication request for BSS"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_deauth_req_bss(dev, apdev)
+ finally:
+ dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_deauth_req_bss(dev, apdev):
+ dev[0].request("SET pmf 2")
+ hapd = eap_test(dev[0], apdev[0], "21[3:26]", "TTLS", "user")
+ dev[0].dump_monitor()
+ addr = dev[0].p2p_interface_addr()
+ hapd.wait_sta()
+ hapd.request("HS20_DEAUTH_REQ " + addr + " 0 120 http://example.com/")
+ ev = dev[0].wait_event(["HS20-DEAUTH-IMMINENT-NOTICE"])
+ if ev is None:
+ raise Exception("Timeout on deauth imminent notice")
+ if "0 120 http://example.com/" not in ev:
+ raise Exception("Unexpected deauth imminent notice: " + ev)
+ hapd.request("DEAUTHENTICATE " + addr + " reason=4")
+ ev = dev[0].wait_disconnected(timeout=10)
+ if "reason=4" not in ev:
+ raise Exception("Unexpected disconnection reason")
+ if "[TEMP-DISABLED]" not in dev[0].list_networks()[0]['flags']:
+ raise Exception("Network not marked temporarily disabled")
+ ev = dev[0].wait_event(["SME: Trying to authenticate",
+ "Trying to associate",
+ "CTRL-EVENT-CONNECTED"], timeout=5)
+ if ev is not None:
+ raise Exception("Unexpected connection attempt")
+
+def test_ap_hs20_deauth_req_from_radius(dev, apdev):
+ """Hotspot 2.0 connection and deauthentication request from RADIUS"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_deauth_req_from_radius(dev, apdev)
+ finally:
+ dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_deauth_req_from_radius(dev, apdev):
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,21[2:4]"]
+ params['hs20_deauth_req_timeout'] = "2"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET pmf 2")
+ dev[0].hs20_enable()
+ dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-deauth-test",
+ 'password': "password"})
+ interworking_select(dev[0], bssid, freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ ev = dev[0].wait_event(["HS20-DEAUTH-IMMINENT-NOTICE"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on deauth imminent notice")
+ if " 1 100" not in ev:
+ raise Exception("Unexpected deauth imminent contents")
+ dev[0].wait_disconnected(timeout=3)
+
+def test_ap_hs20_deauth_req_without_pmf(dev, apdev):
+ """Hotspot 2.0 connection and deauthentication request without PMF"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].request("SET pmf 0")
+ hapd = eap_test(dev[0], apdev[0], "21[3:26]", "TTLS", "user", release=1)
+ dev[0].dump_monitor()
+ id = int(dev[0].get_status_field("id"))
+ dev[0].set_network(id, "ieee80211w", "0")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ addr = dev[0].own_addr()
+ hapd.wait_sta()
+ hapd.request("HS20_DEAUTH_REQ " + addr + " 1 120 http://example.com/")
+ ev = dev[0].wait_event(["HS20-DEAUTH-IMMINENT-NOTICE"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Deauth imminent notice without PMF accepted")
+ with alloc_fail(hapd, 1, "wpabuf_alloc;hostapd_ctrl_iface_hs20_deauth_req"):
+ if "FAIL" not in hapd.request("HS20_DEAUTH_REQ " + addr + " 1 120 http://example.com/"):
+ raise Exception("HS20_DEAUTH_REQ accepted during OOM")
+
+def test_ap_hs20_deauth_req_pmf_htc(dev, apdev):
+ """Hotspot 2.0 connection and deauthentication request PMF misbehavior (+HTC)"""
+ try:
+ run_ap_hs20_deauth_req_pmf_htc(dev, apdev)
+ finally:
+ stop_monitor(apdev[1]["ifname"])
+
+def run_ap_hs20_deauth_req_pmf_htc(dev, apdev):
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].request("SET pmf 0")
+ hapd = eap_test(dev[0], apdev[0], "21[3:26]", "TTLS", "user", release=1)
+ dev[0].dump_monitor()
+ addr = dev[0].own_addr()
+ hapd.wait_sta()
+
+ sock = start_monitor(apdev[1]["ifname"])
+ radiotap = radiotap_build()
+ bssid = hapd.own_addr().replace(':', '')
+ addr = dev[0].own_addr().replace(':', '')
+ payload = "0a1a0101dd1b506f9a0101780013687474703a2f2f6578616d706c652e636f6d2f"
+ # Claim there is a HT Control field, but then start the frame body from
+ # there and do not encrypt the Robust Action frame.
+ frame = binascii.unhexlify("d0803a01" + addr + 2 * bssid + "0000" + payload)
+ # Claim there is a HT Control field and start the frame body in the correct
+ # location, but do not encrypt the Robust Action frame. Make the first octet
+ # of HT Control field use a non-robust Action Category value.
+ frame2 = binascii.unhexlify("d0803a01" + addr + 2 * bssid + "0000" + "04000000" + payload)
+
+ sock.send(radiotap + frame)
+ sock.send(radiotap + frame2)
+
+ ev = dev[0].wait_event(["HS20-DEAUTH-IMMINENT-NOTICE"], timeout=1)
+ if ev is not None:
+ raise Exception("Deauth imminent notice without PMF accepted")
+
+def test_ap_hs20_remediation_required(dev, apdev):
+ """Hotspot 2.0 connection and remediation required from RADIUS"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_remediation_required(dev, apdev)
+ finally:
+ dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_remediation_required(dev, apdev):
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,21[2:4]"]
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET pmf 1")
+ dev[0].hs20_enable()
+ dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-subrem-test",
+ 'password': "password"})
+ interworking_select(dev[0], bssid, freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ ev = dev[0].wait_event(["HS20-SUBSCRIPTION-REMEDIATION"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on subscription remediation notice")
+ if " 1 https://example.com/" not in ev:
+ raise Exception("Unexpected subscription remediation event contents")
+
+def test_ap_hs20_remediation_required_ctrl(dev, apdev):
+ """Hotspot 2.0 connection and subrem from ctrl_iface"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_remediation_required_ctrl(dev, apdev)
+ finally:
+ dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_remediation_required_ctrl(dev, apdev):
+ bssid = apdev[0]['bssid']
+ addr = dev[0].own_addr()
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,21[2:4]"]
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET pmf 1")
+ dev[0].hs20_enable()
+ dev[0].add_cred_values(default_cred())
+ interworking_select(dev[0], bssid, freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+
+ hapd.request("HS20_WNM_NOTIF " + addr + " https://example.com/")
+ ev = dev[0].wait_event(["HS20-SUBSCRIPTION-REMEDIATION"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on subscription remediation notice")
+ if " 1 https://example.com/" not in ev:
+ raise Exception("Unexpected subscription remediation event contents")
+
+ hapd.request("HS20_WNM_NOTIF " + addr)
+ ev = dev[0].wait_event(["HS20-SUBSCRIPTION-REMEDIATION"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on subscription remediation notice")
+ if not ev.endswith("HS20-SUBSCRIPTION-REMEDIATION "):
+ raise Exception("Unexpected subscription remediation event contents: " + ev)
+
+ if "FAIL" not in hapd.request("HS20_WNM_NOTIF "):
+ raise Exception("Unexpected HS20_WNM_NOTIF success")
+ if "FAIL" not in hapd.request("HS20_WNM_NOTIF foo"):
+ raise Exception("Unexpected HS20_WNM_NOTIF success")
+ if "FAIL" not in hapd.request("HS20_WNM_NOTIF " + addr + " https://12345678923456789842345678456783456712345678923456789842345678456783456712345678923456789842345678456783456712345678923456789842345678456783456712345678923456789842345678456783456712345678923456789842345678456783456712345678923456789842345678456783456712345678927.very.long.example.com/"):
+ raise Exception("Unexpected HS20_WNM_NOTIF success")
+ if "OK" not in hapd.request("HS20_WNM_NOTIF " + addr + " "):
+ raise Exception("HS20_WNM_NOTIF failed with empty URL")
+
+def test_ap_hs20_session_info(dev, apdev):
+ """Hotspot 2.0 connection and session information from RADIUS"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_session_info(dev, apdev)
+ finally:
+ dev[0].request("SET pmf 0")
+
+def _test_ap_hs20_session_info(dev, apdev):
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,21[2:4]"]
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET pmf 1")
+ dev[0].hs20_enable()
+ dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-session-info-test",
+ 'password': "password"})
+ interworking_select(dev[0], bssid, freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on ESS disassociation imminent notice")
+ if " 1 59904 https://example.com/" not in ev:
+ raise Exception("Unexpected ESS disassociation imminent event contents")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ if ev is None:
+ raise Exception("Scan not started")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=30)
+ if ev is None:
+ raise Exception("Scan not completed")
+
+def test_ap_hs20_osen(dev, apdev):
+ """Hotspot 2.0 OSEN connection"""
+ params = {'ssid': "osen",
+ 'osen': "1",
+ 'auth_server_addr': "127.0.0.1",
+ 'auth_server_port': "1812",
+ 'auth_server_shared_secret': "radius"}
+ hostapd.add_ap(apdev[0], params)
+
+ dev[1].connect("osen", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ if "WEP40" in dev[2].get_capability("group"):
+ dev[2].connect("osen", key_mgmt="NONE", wep_key0='"hello"',
+ scan_freq="2412", wait_connect=False)
+ dev[0].flush_scan_cache()
+ dev[0].connect("osen", proto="OSEN", key_mgmt="OSEN", pairwise="CCMP",
+ group="GTK_NOT_USED CCMP",
+ eap="WFA-UNAUTH-TLS", identity="osen@example.com",
+ ca_cert="auth_serv/ca.pem",
+ scan_freq="2412")
+ res = dev[0].get_bss(apdev[0]['bssid'])['flags']
+ if "[OSEN-OSEN-CCMP]" not in res:
+ raise Exception("OSEN not reported in BSS")
+ if "[WEP]" in res:
+ raise Exception("WEP reported in BSS")
+ res = dev[0].request("SCAN_RESULTS")
+ if "[OSEN-OSEN-CCMP]" not in res:
+ raise Exception("OSEN not reported in SCAN_RESULTS")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("osen", proto="OSEN", key_mgmt="OSEN", pairwise="CCMP",
+ group="GTK_NOT_USED CCMP",
+ eap="WFA-UNAUTH-TLS", identity="osen@example.com",
+ ca_cert="auth_serv/ca.pem",
+ scan_freq="2412")
+ wpas.request("DISCONNECT")
+
+def test_ap_hs20_osen_single_ssid(dev, apdev):
+ """Hotspot 2.0 OSEN-single-SSID connection"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['wpa_key_mgmt'] = "WPA-EAP OSEN"
+ params['hessid'] = bssid
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # RSN-OSEN (for OSU)
+ dev[0].connect("test-hs20", proto="OSEN", key_mgmt="OSEN", pairwise="CCMP",
+ group="CCMP GTK_NOT_USED",
+ eap="WFA-UNAUTH-TLS", identity="osen@example.com",
+ ca_cert="auth_serv/ca.pem", ieee80211w='2',
+ scan_freq="2412")
+ # RSN-EAP (for data connection)
+ dev[1].connect("test-hs20", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="hs20-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pairwise="CCMP", group="CCMP",
+ ieee80211w='2', scan_freq="2412")
+
+ res = dev[0].get_bss(apdev[0]['bssid'])['flags']
+ if "[WPA2-EAP+OSEN-CCMP]" not in res:
+ raise Exception("OSEN not reported in BSS")
+ if "[WEP]" in res:
+ raise Exception("WEP reported in BSS")
+ res = dev[0].request("SCAN_RESULTS")
+ if "[WPA2-EAP+OSEN-CCMP]" not in res:
+ raise Exception("OSEN not reported in SCAN_RESULTS")
+
+ hwsim_utils.test_connectivity(dev[1], hapd)
+ hwsim_utils.test_connectivity(dev[0], hapd, broadcast=False)
+ hwsim_utils.test_connectivity(dev[0], hapd, timeout=1,
+ success_expected=False)
+
+def test_ap_hs20_network_preference(dev, apdev):
+ """Hotspot 2.0 network selection with preferred home network"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].flush_scan_cache()
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ values = {'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"}
+ dev[0].add_cred_values(values)
+
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", "home")
+ dev[0].set_network_quoted(id, "psk", "12345678")
+ dev[0].set_network(id, "priority", "1")
+ dev[0].request("ENABLE_NETWORK %s no-connect" % id)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_connected(timeout=15)
+ if bssid not in ev:
+ raise Exception("Unexpected network selected")
+
+ bssid2 = apdev[1]['bssid']
+ params = hostapd.wpa2_params(ssid="home", passphrase="12345678")
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "INTERWORKING-ALREADY-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection timed out")
+ if "INTERWORKING-ALREADY-CONNECTED" in ev:
+ raise Exception("No roam to higher priority network")
+ if bssid2 not in ev:
+ raise Exception("Unexpected network selected")
+
+def test_ap_hs20_network_preference2(dev, apdev):
+ """Hotspot 2.0 network selection with preferred credential"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].flush_scan_cache()
+ bssid2 = apdev[1]['bssid']
+ params = hostapd.wpa2_params(ssid="home", passphrase="12345678")
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ values = {'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com",
+ 'priority': "1"}
+ dev[0].add_cred_values(values)
+
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", "home")
+ dev[0].set_network_quoted(id, "psk", "12345678")
+ dev[0].request("ENABLE_NETWORK %s no-connect" % id)
+
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_connected(timeout=15)
+ if bssid2 not in ev:
+ raise Exception("Unexpected network selected")
+
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "INTERWORKING-ALREADY-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection timed out")
+ if "INTERWORKING-ALREADY-CONNECTED" in ev:
+ raise Exception("No roam to higher priority network")
+ if bssid not in ev:
+ raise Exception("Unexpected network selected")
+
+def test_ap_hs20_network_preference3(dev, apdev):
+ """Hotspot 2.0 network selection with two credential (one preferred)"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].flush_scan_cache()
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20b")
+ params['nai_realm'] = "0,example.org,13[5:6],21[2:4][5:7]"
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ values = {'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'priority': "1"}
+ dev[0].add_cred_values(values)
+ values = {'realm': "example.org",
+ 'username': "hs20-test",
+ 'password': "password"}
+ id = dev[0].add_cred_values(values)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_connected(timeout=15)
+ if bssid not in ev:
+ raise Exception("Unexpected network selected")
+
+ dev[0].set_cred(id, "priority", "2")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "INTERWORKING-ALREADY-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection timed out")
+ if "INTERWORKING-ALREADY-CONNECTED" in ev:
+ raise Exception("No roam to higher priority network")
+ if bssid2 not in ev:
+ raise Exception("Unexpected network selected")
+
+def test_ap_hs20_network_preference4(dev, apdev):
+ """Hotspot 2.0 network selection with username vs. SIM credential"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].flush_scan_cache()
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20b")
+ params['hessid'] = bssid2
+ params['anqp_3gpp_cell_net'] = "555,444"
+ params['domain_name'] = "wlan.mnc444.mcc555.3gppnetwork.org"
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ values = {'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'priority': "1"}
+ dev[0].add_cred_values(values)
+ values = {'imsi': "555444-333222111",
+ 'eap': "SIM",
+ 'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123"}
+ id = dev[0].add_cred_values(values)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_connected(timeout=15)
+ if bssid not in ev:
+ raise Exception("Unexpected network selected")
+
+ dev[0].set_cred(id, "priority", "2")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "INTERWORKING-ALREADY-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection timed out")
+ if "INTERWORKING-ALREADY-CONNECTED" in ev:
+ raise Exception("No roam to higher priority network")
+ if bssid2 not in ev:
+ raise Exception("Unexpected network selected")
+
+def test_ap_hs20_interworking_select_blocking_scan(dev, apdev):
+ """Ongoing INTERWORKING_SELECT blocking SCAN"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ values = {'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"}
+ dev[0].add_cred_values(values)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ if "FAIL-BUSY" not in dev[0].request("SCAN"):
+ raise Exception("Unexpected SCAN command result")
+ dev[0].wait_connected(timeout=15)
+
+def test_ap_hs20_fetch_osu(dev, apdev):
+ """Hotspot 2.0 OSU provider and icon fetch"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo.png"
+ params['osu_ssid'] = '"HS 2.0 OSU open"'
+ params['osu_method_list'] = "1"
+ params['osu_friendly_name'] = ["eng:Test OSU", "fin:Testi-OSU"]
+ params['osu_icon'] = "w1fi_logo"
+ params['osu_service_desc'] = ["eng:Example services", "fin:Esimerkkipalveluja"]
+ params['osu_server_uri'] = "https://example.com/osu/"
+ hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20b")
+ params['hessid'] = bssid2
+ params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo.png"
+ params['osu_ssid'] = '"HS 2.0 OSU OSEN"'
+ params['osu_method_list'] = "0"
+ params['osu_nai'] = "osen@example.com"
+ params['osu_friendly_name'] = ["eng:Test2 OSU", "fin:Testi2-OSU"]
+ params['osu_icon'] = "w1fi_logo"
+ params['osu_service_desc'] = ["eng:Example services2", "fin:Esimerkkipalveluja2"]
+ params['osu_server_uri'] = "https://example.org/osu/"
+ hostapd.add_ap(apdev[1], params)
+
+ with open("w1fi_logo.png", "rb") as f:
+ orig_logo = f.read()
+ dev[0].hs20_enable()
+ dir = "/tmp/osu-fetch"
+ if os.path.isdir(dir):
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ else:
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ try:
+ dev[1].scan_for_bss(bssid, freq="2412")
+ dev[2].scan_for_bss(bssid, freq="2412")
+ dev[0].request("SET osu_dir " + dir)
+ dev[0].request("FETCH_OSU")
+ if "FAIL" not in dev[1].request("HS20_ICON_REQUEST foo w1fi_logo"):
+ raise Exception("Invalid HS20_ICON_REQUEST accepted")
+ if "OK" not in dev[1].request("HS20_ICON_REQUEST " + bssid + " w1fi_logo"):
+ raise Exception("HS20_ICON_REQUEST failed")
+ if "OK" not in dev[2].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON failed")
+ icons = 0
+ while True:
+ ev = dev[0].wait_event(["OSU provider fetch completed",
+ "RX-HS20-ANQP-ICON"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on OSU fetch")
+ if "OSU provider fetch completed" in ev:
+ break
+ if "RX-HS20-ANQP-ICON" in ev:
+ with open(ev.split(' ')[1], "rb") as f:
+ logo = f.read()
+ if logo == orig_logo:
+ icons += 1
+
+ with open(dir + "/osu-providers.txt", "r") as f:
+ prov = f.read()
+ logger.debug("osu-providers.txt: " + prov)
+ if "OSU-PROVIDER " + bssid not in prov:
+ raise Exception("Missing OSU_PROVIDER(1)")
+ if "OSU-PROVIDER " + bssid2 not in prov:
+ raise Exception("Missing OSU_PROVIDER(2)")
+ finally:
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ os.rmdir(dir)
+
+ if icons != 2:
+ raise Exception("Unexpected number of icons fetched")
+
+ ev = dev[1].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on GAS-QUERY-DONE")
+ ev = dev[1].wait_event(["GAS-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on GAS-QUERY-DONE")
+ if "freq=2412 status_code=0 result=SUCCESS" not in ev:
+ raise Exception("Unexpected GAS-QUERY-DONE: " + ev)
+ ev = dev[1].wait_event(["RX-HS20-ANQP"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on icon fetch")
+ if "Icon Binary File" not in ev:
+ raise Exception("Unexpected ANQP element")
+
+ ev = dev[2].wait_event(["RX-HS20-ICON"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on RX-HS20-ICON")
+ event_icon_len = ev.split(' ')[3]
+ if " w1fi_logo " not in ev:
+ raise Exception("RX-HS20-ICON did not have the expected file name")
+ if bssid not in ev:
+ raise Exception("RX-HS20-ICON did not have the expected BSSID")
+ if "FAIL" in dev[2].request("GET_HS20_ICON " + bssid + " w1fi_logo 0 10"):
+ raise Exception("GET_HS20_ICON 0..10 failed")
+ if "FAIL" in dev[2].request("GET_HS20_ICON " + bssid + " w1fi_logo 5 10"):
+ raise Exception("GET_HS20_ICON 5..15 failed")
+ if "FAIL" not in dev[2].request("GET_HS20_ICON " + bssid + " w1fi_logo 100000 10"):
+ raise Exception("Unexpected success of GET_HS20_ICON with too large offset")
+ if "FAIL" not in dev[2].request("GET_HS20_ICON " + bssid + " no_such_logo 0 10"):
+ raise Exception("GET_HS20_ICON for not existing icon succeeded")
+ if "FAIL" not in dev[2].request("GET_HS20_ICON " + bssid + " w1fi_logo 0 3070"):
+ raise Exception("GET_HS20_ICON with too many output bytes to fit the buffer succeeded")
+ if "FAIL" not in dev[2].request("GET_HS20_ICON " + bssid + " w1fi_logo 0 0"):
+ raise Exception("GET_HS20_ICON 0..0 succeeded")
+ icon = b''
+ pos = 0
+ while True:
+ if pos > 100000:
+ raise Exception("Unexpectedly long icon")
+ res = dev[2].request("GET_HS20_ICON " + bssid + " w1fi_logo %d 1000" % pos)
+ if res.startswith("FAIL"):
+ break
+ icon += base64.b64decode(res)
+ pos += 1000
+ hex = binascii.hexlify(icon).decode()
+ if not hex.startswith("0009696d6167652f706e677d1d"):
+ raise Exception("Unexpected beacon binary header: " + hex)
+ with open('w1fi_logo.png', 'rb') as f:
+ data = f.read()
+ if icon[13:] != data:
+ raise Exception("Unexpected icon data")
+ if len(icon) != int(event_icon_len):
+ raise Exception("Unexpected RX-HS20-ICON event length: " + event_icon_len)
+
+ for i in range(3):
+ if "OK" not in dev[i].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON failed [2]")
+ for i in range(3):
+ ev = dev[i].wait_event(["RX-HS20-ICON"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on RX-HS20-ICON [2]")
+
+ if "FAIL" not in dev[2].request("DEL_HS20_ICON foo w1fi_logo"):
+ raise Exception("Invalid DEL_HS20_ICON accepted")
+ if "OK" not in dev[2].request("DEL_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("DEL_HS20_ICON failed")
+ if "OK" not in dev[1].request("DEL_HS20_ICON " + bssid):
+ raise Exception("DEL_HS20_ICON failed")
+ if "OK" not in dev[0].request("DEL_HS20_ICON "):
+ raise Exception("DEL_HS20_ICON failed")
+ for i in range(3):
+ if "FAIL" not in dev[i].request("DEL_HS20_ICON "):
+ raise Exception("DEL_HS20_ICON accepted when no icons left")
+
+def test_ap_hs20_fetch_osu_no_info(dev, apdev):
+ """Hotspot 2.0 OSU provider and no AP with info"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dir = "/tmp/osu-fetch"
+ if os.path.isdir(dir):
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ else:
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ dev[0].scan_for_bss(bssid, freq="2412")
+ try:
+ dev[0].request("SET osu_dir " + dir)
+ dev[0].request("FETCH_OSU")
+ ev = dev[0].wait_event(["OSU provider fetch completed"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on OSU fetch")
+ finally:
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ os.rmdir(dir)
+
+def test_ap_hs20_fetch_osu_no_icon(dev, apdev):
+ """Hotspot 2.0 OSU provider and no icon found"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo-no-file.png"
+ params['osu_ssid'] = '"HS 2.0 OSU open"'
+ params['osu_method_list'] = "1"
+ params['osu_friendly_name'] = ["eng:Test OSU", "fin:Testi-OSU"]
+ params['osu_icon'] = "w1fi_logo"
+ params['osu_service_desc'] = ["eng:Example services",
+ "fin:Esimerkkipalveluja"]
+ params['osu_server_uri'] = "https://example.com/osu/"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dir = "/tmp/osu-fetch"
+ if os.path.isdir(dir):
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ else:
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ dev[0].scan_for_bss(bssid, freq="2412")
+ try:
+ dev[0].request("SET osu_dir " + dir)
+ dev[0].request("FETCH_OSU")
+ ev = dev[0].wait_event(["OSU provider fetch completed"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on OSU fetch")
+ finally:
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ os.rmdir(dir)
+
+def test_ap_hs20_fetch_osu_single_ssid(dev, apdev):
+ """Hotspot 2.0 OSU provider and single SSID"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo-no-file.png"
+ params['osu_ssid'] = '"HS 2.0 OSU open"'
+ params['osu_method_list'] = "1"
+ params['osu_friendly_name'] = ["eng:Test OSU", "fin:Testi-OSU"]
+ params['osu_nai2'] = "osen@example.com"
+ params['osu_icon'] = "w1fi_logo"
+ params['osu_service_desc'] = ["eng:Example services",
+ "fin:Esimerkkipalveluja"]
+ params['osu_server_uri'] = "https://example.com/osu/"
+ params['wpa_key_mgmt'] = "WPA-EAP OSEN"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dir = "/tmp/osu-fetch"
+ if os.path.isdir(dir):
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ else:
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ dev[0].scan_for_bss(bssid, freq="2412")
+ try:
+ dev[0].request("SET osu_dir " + dir)
+ dev[0].request("FETCH_OSU")
+ ev = dev[0].wait_event(["OSU provider fetch completed"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on OSU fetch")
+ osu_ssid = False
+ osu_ssid2 = False
+ osu_nai = False
+ osu_nai2 = False
+ with open(os.path.join(dir, "osu-providers.txt"), "r") as f:
+ for l in f.readlines():
+ logger.info(l.strip())
+ if l.strip() == "osu_ssid=HS 2.0 OSU open":
+ osu_ssid = True
+ if l.strip() == "osu_ssid2=test-hs20":
+ osu_ssid2 = True
+ if l.strip().startswith("osu_nai="):
+ osu_nai = True
+ if l.strip() == "osu_nai2=osen@example.com":
+ osu_nai2 = True
+ if not osu_ssid:
+ raise Exception("osu_ssid not reported")
+ if not osu_ssid2:
+ raise Exception("osu_ssid2 not reported")
+ if osu_nai:
+ raise Exception("osu_nai reported unexpectedly")
+ if not osu_nai2:
+ raise Exception("osu_nai2 not reported")
+ finally:
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ os.rmdir(dir)
+
+def test_ap_hs20_fetch_osu_single_ssid2(dev, apdev):
+ """Hotspot 2.0 OSU provider and single SSID (two OSU providers)"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo-no-file.png"
+ params['osu_ssid'] = '"HS 2.0 OSU open"'
+ params['osu_method_list'] = "1"
+ params['osu_friendly_name'] = ["eng:Test OSU", "fin:Testi-OSU"]
+ params['osu_nai2'] = "osen@example.com"
+ params['osu_icon'] = "w1fi_logo"
+ params['osu_service_desc'] = ["eng:Example services",
+ "fin:Esimerkkipalveluja"]
+ params['osu_server_uri'] = "https://example.com/osu/"
+ params['wpa_key_mgmt'] = "WPA-EAP OSEN"
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+
+ hapd.set('osu_server_uri', 'https://another.example.com/osu/')
+ hapd.set('osu_method_list', "1")
+ hapd.set('osu_nai2', "osen@another.example.com")
+ hapd.enable()
+
+ dev[0].hs20_enable()
+ dir = "/tmp/osu-fetch"
+ if os.path.isdir(dir):
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ else:
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ dev[0].scan_for_bss(bssid, freq="2412")
+ try:
+ dev[0].request("SET osu_dir " + dir)
+ dev[0].request("FETCH_OSU")
+ ev = dev[0].wait_event(["OSU provider fetch completed"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on OSU fetch")
+ osu_ssid = False
+ osu_ssid2 = False
+ osu_nai = False
+ osu_nai2 = False
+ osu_nai2b = False
+ with open(os.path.join(dir, "osu-providers.txt"), "r") as f:
+ for l in f.readlines():
+ logger.info(l.strip())
+ if l.strip() == "osu_ssid=HS 2.0 OSU open":
+ osu_ssid = True
+ if l.strip() == "osu_ssid2=test-hs20":
+ osu_ssid2 = True
+ if l.strip().startswith("osu_nai="):
+ osu_nai = True
+ if l.strip() == "osu_nai2=osen@example.com":
+ osu_nai2 = True
+ if l.strip() == "osu_nai2=osen@another.example.com":
+ osu_nai2b = True
+ if not osu_ssid:
+ raise Exception("osu_ssid not reported")
+ if not osu_ssid2:
+ raise Exception("osu_ssid2 not reported")
+ if osu_nai:
+ raise Exception("osu_nai reported unexpectedly")
+ if not osu_nai2:
+ raise Exception("osu_nai2 not reported")
+ if not osu_nai2b:
+ raise Exception("osu_nai2b not reported")
+ finally:
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ os.rmdir(dir)
+
+def get_icon(dev, bssid, iconname):
+ icon = b''
+ pos = 0
+ while True:
+ if pos > 100000:
+ raise Exception("Unexpectedly long icon")
+ res = dev.request("GET_HS20_ICON " + bssid + " " + iconname + " %d 3000" % pos)
+ if res.startswith("FAIL"):
+ break
+ icon += base64.b64decode(res)
+ pos += 3000
+ if len(icon) < 13:
+ raise Exception("Too short GET_HS20_ICON response")
+ return icon[0:13], icon[13:]
+
+def test_ap_hs20_req_hs20_icon(dev, apdev):
+ """Hotspot 2.0 OSU provider and multi-icon fetch with REQ_HS20_ICON"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = ["128:80:zxx:image/png:w1fi_logo:w1fi_logo.png",
+ "128:80:zxx:image/png:test_logo:auth_serv/sha512-server.pem"]
+ params['osu_ssid'] = '"HS 2.0 OSU open"'
+ params['osu_method_list'] = "1"
+ params['osu_friendly_name'] = ["eng:Test OSU", "fin:Testi-OSU"]
+ params['osu_icon'] = ["w1fi_logo", "w1fi_logo2"]
+ params['osu_service_desc'] = ["eng:Example services",
+ "fin:Esimerkkipalveluja"]
+ params['osu_server_uri'] = "https://example.com/osu/"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ run_req_hs20_icon(dev, bssid)
+
+def run_req_hs20_icon(dev, bssid):
+ # First, fetch two icons from the AP to wpa_supplicant
+
+ if "OK" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON failed")
+ ev = dev[0].wait_event(["RX-HS20-ICON"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on RX-HS20-ICON (1)")
+
+ if "OK" not in dev[0].request("REQ_HS20_ICON " + bssid + " test_logo"):
+ raise Exception("REQ_HS20_ICON failed")
+ ev = dev[0].wait_event(["RX-HS20-ICON"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on RX-HS20-ICON (2)")
+
+ # Then, fetch the icons from wpa_supplicant for validation
+
+ hdr, data1 = get_icon(dev[0], bssid, "w1fi_logo")
+ hdr, data2 = get_icon(dev[0], bssid, "test_logo")
+
+ with open('w1fi_logo.png', 'rb') as f:
+ data = f.read()
+ if data1 != data:
+ raise Exception("Unexpected icon data (1)")
+
+ with open('auth_serv/sha512-server.pem', 'rb') as f:
+ data = f.read()
+ if data2 != data:
+ raise Exception("Unexpected icon data (2)")
+
+ # Finally, delete the icons from wpa_supplicant
+
+ if "OK" not in dev[0].request("DEL_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("DEL_HS20_ICON failed")
+ if "OK" not in dev[0].request("DEL_HS20_ICON " + bssid + " test_logo"):
+ raise Exception("DEL_HS20_ICON failed")
+
+def test_ap_hs20_req_operator_icon(dev, apdev):
+ """Hotspot 2.0 operator icons"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = ["128:80:zxx:image/png:w1fi_logo:w1fi_logo.png",
+ "500:300:fi:image/png:test_logo:auth_serv/sha512-server.pem"]
+ params['operator_icon'] = ["w1fi_logo", "unknown_logo", "test_logo"]
+ hostapd.add_ap(apdev[0], params)
+
+ value = struct.pack('<HH', 128, 80) + b"zxx"
+ value += struct.pack('B', 9) + b"image/png"
+ value += struct.pack('B', 9) + b"w1fi_logo"
+
+ value += struct.pack('<HH', 500, 300) + b"fi\0"
+ value += struct.pack('B', 9) + b"image/png"
+ value += struct.pack('B', 9) + b"test_logo"
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " hs20:12"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+ if ev is None or "Operator Icon Metadata" not in ev:
+ raise Exception("Did not receive Operator Icon Metadata")
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE event not seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ bss = dev[0].get_bss(bssid)
+ if "hs20_operator_icon_metadata" not in bss:
+ raise Exception("hs20_operator_icon_metadata missing from BSS entry")
+ if bss["hs20_operator_icon_metadata"] != binascii.hexlify(value).decode():
+ raise Exception("Unexpected hs20_operator_icon_metadata value: " +
+ bss["hs20_operator_icon_metadata"])
+
+ run_req_hs20_icon(dev, bssid)
+
+def test_ap_hs20_req_hs20_icon_oom(dev, apdev):
+ """Hotspot 2.0 icon fetch OOM with REQ_HS20_ICON"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = ["128:80:zxx:image/png:w1fi_logo:w1fi_logo.png",
+ "128:80:zxx:image/png:test_logo:auth_serv/sha512-server.pem"]
+ params['osu_ssid'] = '"HS 2.0 OSU open"'
+ params['osu_method_list'] = "1"
+ params['osu_friendly_name'] = ["eng:Test OSU", "fin:Testi-OSU"]
+ params['osu_icon'] = ["w1fi_logo", "w1fi_logo2"]
+ params['osu_service_desc'] = ["eng:Example services",
+ "fin:Esimerkkipalveluja"]
+ params['osu_server_uri'] = "https://example.com/osu/"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ if "FAIL" not in dev[0].request("REQ_HS20_ICON 11:22:33:44:55:66 w1fi_logo"):
+ raise Exception("REQ_HS20_ICON succeeded with unknown BSSID")
+
+ with alloc_fail(dev[0], 1, "hs20_build_anqp_req;hs20_anqp_send_req"):
+ if "FAIL" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON succeeded during OOM")
+
+ with alloc_fail(dev[0], 1, "gas_query_req;hs20_anqp_send_req"):
+ if "FAIL" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON succeeded during OOM")
+
+ with alloc_fail(dev[0], 1, "=hs20_anqp_send_req"):
+ if "FAIL" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON succeeded during OOM")
+ with alloc_fail(dev[0], 2, "=hs20_anqp_send_req"):
+ if "FAIL" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON succeeded during OOM")
+
+ if "OK" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON failed")
+ ev = dev[0].wait_event(["RX-HS20-ICON"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on RX-HS20-ICON (1)")
+
+ with alloc_fail(dev[0], 1, "hs20_get_icon"):
+ if "FAIL" not in dev[0].request("GET_HS20_ICON " + bssid + "w1fi_logo 0 100"):
+ raise Exception("GET_HS20_ICON succeeded during OOM")
+
+ if "OK" not in dev[0].request("DEL_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("DEL_HS20_ICON failed")
+
+ with alloc_fail(dev[0], 1, "=hs20_process_icon_binary_file"):
+ if "OK" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON failed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+def test_ap_hs20_req_hs20_icon_parallel(dev, apdev):
+ """Hotspot 2.0 OSU provider and multi-icon parallel fetch with REQ_HS20_ICON"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = ["128:80:zxx:image/png:w1fi_logo:w1fi_logo.png",
+ "128:80:zxx:image/png:test_logo:auth_serv/sha512-server.pem"]
+ params['osu_ssid'] = '"HS 2.0 OSU open"'
+ params['osu_method_list'] = "1"
+ params['osu_friendly_name'] = ["eng:Test OSU", "fin:Testi-OSU"]
+ params['osu_icon'] = ["w1fi_logo", "w1fi_logo2"]
+ params['osu_service_desc'] = ["eng:Example services",
+ "fin:Esimerkkipalveluja"]
+ params['osu_server_uri'] = "https://example.com/osu/"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ # First, fetch two icons from the AP to wpa_supplicant
+
+ if "OK" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON failed")
+
+ if "OK" not in dev[0].request("REQ_HS20_ICON " + bssid + " test_logo"):
+ raise Exception("REQ_HS20_ICON failed")
+ ev = dev[0].wait_event(["RX-HS20-ICON"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on RX-HS20-ICON (1)")
+ ev = dev[0].wait_event(["RX-HS20-ICON"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on RX-HS20-ICON (2)")
+
+ # Then, fetch the icons from wpa_supplicant for validation
+
+ hdr, data1 = get_icon(dev[0], bssid, "w1fi_logo")
+ hdr, data2 = get_icon(dev[0], bssid, "test_logo")
+
+ with open('w1fi_logo.png', 'rb') as f:
+ data = f.read()
+ if data1 != data:
+ raise Exception("Unexpected icon data (1)")
+
+ with open('auth_serv/sha512-server.pem', 'rb') as f:
+ data = f.read()
+ if data2 != data:
+ raise Exception("Unexpected icon data (2)")
+
+ # Finally, delete the icons from wpa_supplicant
+
+ if "OK" not in dev[0].request("DEL_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("DEL_HS20_ICON failed")
+ if "OK" not in dev[0].request("DEL_HS20_ICON " + bssid + " test_logo"):
+ raise Exception("DEL_HS20_ICON failed")
+
+def test_ap_hs20_fetch_osu_stop(dev, apdev):
+ """Hotspot 2.0 OSU provider fetch stopped"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo.png"
+ params['osu_ssid'] = '"HS 2.0 OSU open"'
+ params['osu_method_list'] = "1"
+ params['osu_friendly_name'] = ["eng:Test OSU", "fin:Testi-OSU"]
+ params['osu_icon'] = "w1fi_logo"
+ params['osu_service_desc'] = ["eng:Example services",
+ "fin:Esimerkkipalveluja"]
+ params['osu_server_uri'] = "https://example.com/osu/"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dir = "/tmp/osu-fetch"
+ if os.path.isdir(dir):
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ else:
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ try:
+ dev[0].request("SET osu_dir " + dir)
+ dev[0].request("SCAN freq=2412-2462")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not start")
+ if "FAIL" not in dev[0].request("FETCH_OSU"):
+ raise Exception("FETCH_OSU accepted while scanning")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("Scan timed out")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("FETCH_ANQP")
+ if "FAIL" not in dev[0].request("FETCH_OSU"):
+ raise Exception("FETCH_OSU accepted while in FETCH_ANQP")
+ dev[0].request("STOP_FETCH_ANQP")
+ dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ for i in range(5):
+ msg = hapd.mgmt_rx()
+ if msg['subtype'] == 13:
+ break
+ if "FAIL" not in dev[0].request("FETCH_OSU"):
+ raise Exception("FETCH_OSU accepted while in INTERWORKING_SELECT")
+ ev = dev[0].wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Network selection timed out")
+
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("FETCH_OSU"):
+ raise Exception("FETCH_OSU failed")
+ dev[0].request("CANCEL_FETCH_OSU")
+
+ for i in range(15):
+ time.sleep(0.5)
+ if dev[0].get_driver_status_field("scan_state") == "SCAN_COMPLETED":
+ break
+
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("FETCH_OSU"):
+ raise Exception("FETCH_OSU failed")
+ if "FAIL" not in dev[0].request("FETCH_OSU"):
+ raise Exception("FETCH_OSU accepted while in FETCH_OSU")
+ ev = dev[0].wait_event(["GAS-QUERY-START"], 10)
+ if ev is None:
+ raise Exception("GAS timed out")
+ if "FAIL" not in dev[0].request("FETCH_OSU"):
+ raise Exception("FETCH_OSU accepted while in FETCH_OSU")
+ dev[0].request("CANCEL_FETCH_OSU")
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], 10)
+ if ev is None:
+ raise Exception("GAS event timed out after CANCEL_FETCH_OSU")
+ finally:
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ os.rmdir(dir)
+
+def test_ap_hs20_fetch_osu_proto(dev, apdev):
+ """Hotspot 2.0 OSU provider and protocol testing"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dir = "/tmp/osu-fetch"
+ if os.path.isdir(dir):
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ else:
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+
+ tests = [("Empty provider list (no OSU SSID field)", b''),
+ ("HS 2.0: Not enough room for OSU SSID",
+ binascii.unhexlify('01')),
+ ("HS 2.0: Invalid OSU SSID Length 33",
+ binascii.unhexlify('21') + 33*b'A'),
+ ("HS 2.0: Not enough room for Number of OSU Providers",
+ binascii.unhexlify('0130')),
+ ("Truncated OSU Provider",
+ binascii.unhexlify('013001020000')),
+ ("HS 2.0: Ignored 5 bytes of extra data after OSU Providers",
+ binascii.unhexlify('0130001122334455')),
+ ("HS 2.0: Not enough room for OSU Friendly Name Length",
+ binascii.unhexlify('013001000000')),
+ ("HS 2.0: Not enough room for OSU Friendly Name Duples",
+ build_prov('0100')),
+ ("Invalid OSU Friendly Name", build_prov('040000000000')),
+ ("Invalid OSU Friendly Name(2)", build_prov('040004000000')),
+ ("HS 2.0: Not enough room for OSU Server URI length",
+ build_prov('0000')),
+ ("HS 2.0: Not enough room for OSU Server URI",
+ build_prov('000001')),
+ ("HS 2.0: Not enough room for OSU Method list length",
+ build_prov('000000')),
+ ("HS 2.0: Not enough room for OSU Method list",
+ build_prov('00000001')),
+ ("HS 2.0: Not enough room for Icons Available Length",
+ build_prov('00000000')),
+ ("HS 2.0: Not enough room for Icons Available Length(2)",
+ build_prov('00000001ff00')),
+ ("HS 2.0: Not enough room for Icons Available",
+ build_prov('000000000100')),
+ ("HS 2.0: Invalid Icon Metadata",
+ build_prov('00000000010000')),
+ ("HS 2.0: Not room for Icon Type",
+ build_prov('000000000900111122223333330200')),
+ ("HS 2.0: Not room for Icon Filename length",
+ build_prov('000000000900111122223333330100')),
+ ("HS 2.0: Not room for Icon Filename",
+ build_prov('000000000900111122223333330001')),
+ ("HS 2.0: Not enough room for OSU_NAI",
+ build_prov('000000000000')),
+ ("HS 2.0: Not enough room for OSU_NAI(2)",
+ build_prov('00000000000001')),
+ ("HS 2.0: Not enough room for OSU Service Description Length",
+ build_prov('00000000000000')),
+ ("HS 2.0: Not enough room for OSU Service Description Length(2)",
+ build_prov('0000000000000000')),
+ ("HS 2.0: Not enough room for OSU Service Description Duples",
+ build_prov('000000000000000100')),
+ ("Invalid OSU Service Description",
+ build_prov('00000000000000040000000000')),
+ ("Invalid OSU Service Description(2)",
+ build_prov('00000000000000040004000000'))]
+
+ try:
+ dev[0].request("SET osu_dir " + dir)
+ run_fetch_osu_icon_failure(hapd, dev, bssid)
+ for note, prov in tests:
+ run_fetch_osu(hapd, dev, bssid, note, prov)
+ finally:
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ os.rmdir(dir)
+
+def test_ap_hs20_fetch_osu_invalid_dir(dev, apdev):
+ """Hotspot 2.0 OSU provider and invalid directory"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo.png"
+ params['osu_ssid'] = '"HS 2.0 OSU open"'
+ params['osu_method_list'] = "1"
+ params['osu_friendly_name'] = ["eng:Test OSU", "fin:Testi-OSU"]
+ params['osu_icon'] = "w1fi_logo"
+ params['osu_service_desc'] = ["eng:Example services",
+ "fin:Esimerkkipalveluja"]
+ params['osu_server_uri'] = "https://example.com/osu/"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dir = "/tmp/osu-fetch-no-such-dir"
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("SET osu_dir " + dir)
+ dev[0].request("FETCH_OSU no-scan")
+ ev = dev[0].wait_event(["Could not write OSU provider information"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on OSU fetch")
+
+def test_ap_hs20_fetch_osu_oom(dev, apdev):
+ """Hotspot 2.0 OSU provider and OOM"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hs20_icon'] = "128:80:zxx:image/png:w1fi_logo:w1fi_logo.png"
+ params['osu_ssid'] = '"HS 2.0 OSU open"'
+ params['osu_method_list'] = "1"
+ params['osu_friendly_name'] = ["eng:Test OSU", "fin:Testi-OSU"]
+ params['osu_icon'] = "w1fi_logo"
+ params['osu_service_desc'] = ["eng:Example services",
+ "fin:Esimerkkipalveluja"]
+ params['osu_server_uri'] = "https://example.com/osu/"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dir = "/tmp/osu-fetch"
+ if os.path.isdir(dir):
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ else:
+ try:
+ os.makedirs(dir)
+ except:
+ pass
+ dev[0].scan_for_bss(bssid, freq="2412")
+ try:
+ dev[0].request("SET osu_dir " + dir)
+ with alloc_fail(dev[0], 1, "=hs20_osu_add_prov"):
+ dev[0].request("FETCH_OSU no-scan")
+ ev = dev[0].wait_event(["OSU provider fetch completed"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on OSU fetch")
+ with alloc_fail(dev[0], 1, "hs20_anqp_send_req;hs20_next_osu_icon"):
+ dev[0].request("FETCH_OSU no-scan")
+ ev = dev[0].wait_event(["OSU provider fetch completed"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on OSU fetch")
+ finally:
+ files = [f for f in os.listdir(dir) if f.startswith("osu-")]
+ for f in files:
+ os.remove(dir + "/" + f)
+ os.rmdir(dir)
+
+def build_prov(prov):
+ data = binascii.unhexlify(prov)
+ return binascii.unhexlify('013001') + struct.pack('<H', len(data)) + data
+
+def handle_osu_prov_fetch(hapd, dev, prov):
+ # GAS/ANQP query for OSU Providers List
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+ dialog_token = gas['dialog_token']
+
+ resp = action_response(query)
+ osu_prov = struct.pack('<HH', 0xdddd, len(prov) + 6) + binascii.unhexlify('506f9a110800') + prov
+ data = struct.pack('<H', len(osu_prov)) + osu_prov
+ resp['payload'] = anqp_initial_resp(dialog_token, 0) + data
+ send_gas_resp(hapd, resp)
+
+ ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP query response for OSU Providers not received")
+ if "OSU Providers list" not in ev:
+ raise Exception("ANQP query response for OSU Providers not received(2)")
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP query for OSU Providers list not completed")
+
+def start_osu_fetch(hapd, dev, bssid, note):
+ hapd.set("ext_mgmt_frame_handling", "0")
+ dev[0].request("BSS_FLUSH 0")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].dump_monitor()
+ dev[0].request("NOTE " + note)
+ dev[0].request("FETCH_OSU no-scan")
+
+def wait_osu_fetch_completed(dev):
+ ev = dev[0].wait_event(["OSU provider fetch completed"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on OSU fetch")
+
+def run_fetch_osu_icon_failure(hapd, dev, bssid):
+ start_osu_fetch(hapd, dev, bssid, "Icon fetch failure")
+
+ prov = binascii.unhexlify('01ff' + '01' + '800019000b656e6754657374204f53550c66696e54657374692d4f53551868747470733a2f2f6578616d706c652e636f6d2f6f73752f01011b00800050007a787809696d6167652f706e6709773166695f6c6f676f002a0013656e674578616d706c652073657276696365731566696e4573696d65726b6b6970616c76656c756a61')
+ handle_osu_prov_fetch(hapd, dev, prov)
+
+ # GAS/ANQP query for icon
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+ dialog_token = gas['dialog_token']
+
+ resp = action_response(query)
+ # Unexpected Advertisement Protocol in response
+ adv_proto = struct.pack('8B', 108, 6, 127, 0xdd, 0x00, 0x11, 0x22, 0x33)
+ data = struct.pack('<H', 0)
+ resp['payload'] = struct.pack('<BBBHH', ACTION_CATEG_PUBLIC,
+ GAS_INITIAL_RESPONSE,
+ gas['dialog_token'], 0, 0) + adv_proto + data
+ send_gas_resp(hapd, resp)
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP query for icon not completed")
+
+ wait_osu_fetch_completed(dev)
+
+def run_fetch_osu(hapd, dev, bssid, note, prov):
+ start_osu_fetch(hapd, dev, bssid, note)
+ handle_osu_prov_fetch(hapd, dev, prov)
+ wait_osu_fetch_completed(dev)
+
+def test_ap_hs20_ft(dev, apdev):
+ """Hotspot 2.0 connection with FT"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params['nas_identifier'] = "nas1.w1.fi"
+ params['r1_key_holder'] = "000102030405"
+ params["mobility_domain"] = "a1b2"
+ params["reassociation_deadline"] = "1000"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ dev[0].dump_monitor()
+ key_mgmt = dev[0].get_status_field("key_mgmt")
+ if key_mgmt != "FT-EAP":
+ raise Exception("Unexpected key_mgmt: " + key_mgmt)
+ # speed up testing by avoiding unnecessary scanning of other channels
+ nid = dev[0].get_status_field("id")
+ dev[0].set_network(nid, "scan_freq", "2412")
+
+ params = hs20_ap_params()
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ hapd.disable()
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Disconnection not reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("Connection to AP2 not reported")
+ key_mgmt = dev[0].get_status_field("key_mgmt")
+ if key_mgmt != "WPA2/IEEE 802.1X/EAP":
+ raise Exception("Unexpected key_mgmt: " + key_mgmt)
+
+def test_ap_hs20_remediation_sql(dev, apdev, params):
+ """Hotspot 2.0 connection and remediation required using SQLite for user DB"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ import sqlite3
+ except ImportError:
+ raise HwsimSkip("No sqlite3 module available")
+ dbfile = params['prefix'] + ".eap-user.db"
+ try:
+ os.remove(dbfile)
+ except:
+ pass
+ con = sqlite3.connect(dbfile)
+ with con:
+ cur = con.cursor()
+ cur.execute("CREATE TABLE users(identity TEXT PRIMARY KEY, methods TEXT, password TEXT, remediation TEXT, phase2 INTEGER)")
+ cur.execute("CREATE TABLE wildcards(identity TEXT PRIMARY KEY, methods TEXT)")
+ cur.execute("INSERT INTO users(identity,methods,password,phase2,remediation) VALUES ('user-mschapv2','TTLS-MSCHAPV2','password',1,'user')")
+ cur.execute("INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS')")
+ cur.execute("CREATE TABLE authlog(timestamp TEXT, session TEXT, nas_ip TEXT, username TEXT, note TEXT)")
+
+ try:
+ params = {"ssid": "as", "beacon_int": "2000",
+ "radius_server_clients": "auth_serv/radius_clients.conf",
+ "radius_server_auth_port": '18128',
+ "eap_server": "1",
+ "eap_user_file": "sqlite:" + dbfile,
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key",
+ "subscr_remediation_url": "https://example.org/",
+ "subscr_remediation_method": "1"}
+ hostapd.add_ap(apdev[1], params)
+
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['auth_server_port'] = "18128"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET pmf 1")
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "user-mschapv2",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem"})
+ interworking_select(dev[0], bssid, freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ ev = dev[0].wait_event(["HS20-SUBSCRIPTION-REMEDIATION"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on subscription remediation notice")
+ if " 1 https://example.org/" not in ev:
+ raise Exception("Unexpected subscription remediation event contents")
+
+ with con:
+ cur = con.cursor()
+ cur.execute("SELECT * from authlog")
+ rows = cur.fetchall()
+ if len(rows) < 1:
+ raise Exception("No authlog entries")
+
+ finally:
+ os.remove(dbfile)
+ dev[0].request("SET pmf 0")
+
+def test_ap_hs20_sim_provisioning(dev, apdev, params):
+ """Hotspot 2.0 AAA server behavior for SIM provisioning"""
+ check_eap_capa(dev[0], "SIM")
+ try:
+ import sqlite3
+ except ImportError:
+ raise HwsimSkip("No sqlite3 module available")
+ dbfile = params['prefix'] + ".eap-user.db"
+ try:
+ os.remove(dbfile)
+ except:
+ pass
+ con = sqlite3.connect(dbfile)
+ with con:
+ cur = con.cursor()
+ cur.execute("CREATE TABLE users(identity TEXT PRIMARY KEY, methods TEXT, password TEXT, remediation TEXT, phase2 INTEGER, last_msk TEXT)")
+ cur.execute("CREATE TABLE wildcards(identity TEXT PRIMARY KEY, methods TEXT)")
+ cur.execute("INSERT INTO wildcards(identity,methods) VALUES ('1','SIM')")
+ cur.execute("CREATE TABLE authlog(timestamp TEXT, session TEXT, nas_ip TEXT, username TEXT, note TEXT)")
+ cur.execute("CREATE TABLE current_sessions(mac_addr TEXT PRIMARY KEY, identity TEXT, start_time TEXT, nas TEXT, hs20_t_c_filtering BOOLEAN, waiting_coa_ack BOOLEAN, coa_ack_received BOOLEAN)")
+
+ try:
+ params = {"ssid": "as", "beacon_int": "2000",
+ "radius_server_clients": "auth_serv/radius_clients.conf",
+ "radius_server_auth_port": '18128',
+ "eap_server": "1",
+ "eap_user_file": "sqlite:" + dbfile,
+ "eap_sim_db": "unix:/tmp/hlr_auc_gw.sock",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key",
+ "hs20_sim_provisioning_url":
+ "https://example.org/?hotspot2dot0-mobile-identifier-hash=",
+ "subscr_remediation_method": "1"}
+ hostapd.add_ap(apdev[1], params)
+
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['auth_server_port'] = "18128"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET pmf 1")
+ dev[0].hs20_enable()
+ dev[0].connect("test-hs20", proto="RSN", key_mgmt="WPA-EAP", eap="SIM",
+ ieee80211w="1",
+ identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ scan_freq="2412", update_identifier="54321")
+ ev = dev[0].wait_event(["HS20-SUBSCRIPTION-REMEDIATION"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected subscription remediation notice")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ dev[0].connect("test-hs20", proto="RSN", key_mgmt="WPA-EAP", eap="SIM",
+ ieee80211w="1",
+ identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ scan_freq="2412", update_identifier="0")
+ ev = dev[0].wait_event(["HS20-SUBSCRIPTION-REMEDIATION"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on subscription remediation notice")
+ if " 1 https://example.org/?hotspot2dot0-mobile-identifier-hash=" not in ev:
+ raise Exception("Unexpected subscription remediation event contents: " + ev)
+ id_hash = ev.split(' ')[2].split('=')[1]
+
+ with con:
+ cur = con.cursor()
+ cur.execute("SELECT * from authlog")
+ rows = cur.fetchall()
+ if len(rows) < 1:
+ raise Exception("No authlog entries")
+
+ with con:
+ cur = con.cursor()
+ cur.execute("SELECT * from sim_provisioning")
+ rows = cur.fetchall()
+ if len(rows) != 1:
+ raise Exeception("Unexpected number of rows in sim_provisioning (%d; expected %d)" % (len(rows), 1))
+ logger.info("sim_provisioning: " + str(rows))
+ if len(rows[0][0]) != 32:
+ raise Exception("Unexpected mobile_identifier_hash length in DB")
+ if rows[0][1] != "232010000000000":
+ raise Exception("Unexpected IMSI in DB")
+ if rows[0][2] != dev[0].own_addr():
+ raise Exception("Unexpected MAC address in DB")
+ if rows[0][0] != id_hash:
+ raise Exception("hotspot2dot0-mobile-identifier-hash mismatch")
+ finally:
+ dev[0].request("SET pmf 0")
+
+def test_ap_hs20_external_selection(dev, apdev):
+ """Hotspot 2.0 connection using external network selection and creation"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['disable_dgaf'] = '1'
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].connect("test-hs20", proto="RSN", key_mgmt="WPA-EAP", eap="TTLS",
+ ieee80211w="1",
+ identity="hs20-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ scan_freq="2412", update_identifier="54321",
+ roaming_consortium_selection="1020304050")
+ if dev[0].get_status_field("hs20") != "3":
+ raise Exception("Unexpected hs20 indication")
+ network_id = dev[0].get_status_field("id")
+ sel = dev[0].get_network(network_id, "roaming_consortium_selection")
+ if sel != "1020304050":
+ raise Exception("Unexpected roaming_consortium_selection value: " + sel)
+
+def test_ap_hs20_random_mac_addr(dev, apdev):
+ """Hotspot 2.0 connection with random MAC address"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['disable_dgaf'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ addr = wpas.p2p_interface_addr()
+ wpas.request("SET mac_addr 1")
+ wpas.request("SET preassoc_mac_addr 1")
+ wpas.request("SET rand_addr_lifetime 60")
+ wpas.hs20_enable()
+ wpas.flush_scan_cache()
+ id = wpas.add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ interworking_select(wpas, bssid, "home", freq="2412")
+ interworking_connect(wpas, bssid, "TTLS")
+ addr1 = wpas.get_driver_status_field("addr")
+ if addr == addr1:
+ raise Exception("Did not use random MAC address")
+
+ sta = hapd.get_sta(addr)
+ if sta['addr'] != "FAIL":
+ raise Exception("Unexpected STA association with permanent address")
+ sta = hapd.get_sta(addr1)
+ if sta['addr'] != addr1:
+ raise Exception("STA association with random address not found")
+
+def test_ap_hs20_multi_network_and_cred_removal(dev, apdev):
+ """Multiple networks and cred removal"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,25[3:26]"]
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].add_network()
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "user",
+ 'password': "password"})
+ interworking_select(dev[0], bssid, freq="2412")
+ interworking_connect(dev[0], bssid, "PEAP")
+ dev[0].add_network()
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=10)
+
+ hapd.disable()
+ hapd.set("ssid", "another ssid")
+ hapd.enable()
+
+ interworking_select(dev[0], bssid, freq="2412")
+ interworking_connect(dev[0], bssid, "PEAP")
+ dev[0].add_network()
+ if len(dev[0].list_networks()) != 5:
+ raise Exception("Unexpected number of networks prior to remove_crec")
+
+ dev[0].dump_monitor()
+ dev[0].remove_cred(id)
+ if len(dev[0].list_networks()) != 3:
+ raise Exception("Unexpected number of networks after to remove_crec")
+ dev[0].wait_disconnected(timeout=10)
+
+def test_ap_hs20_interworking_add_network(dev, apdev):
+ """Hotspot 2.0 connection using INTERWORKING_ADD_NETWORK"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['nai_realm'] = ["0,example.com,21[3:26][6:7][99:99]"]
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].add_cred_values(default_cred(user="user"))
+ interworking_select(dev[0], bssid, freq=2412)
+ id = dev[0].interworking_add_network(bssid)
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+
+def _test_ap_hs20_proxyarp(dev, apdev):
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['disable_dgaf'] = '0'
+ params['proxy_arp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "OK" in hapd.request("ENABLE"):
+ raise Exception("Incomplete hostapd configuration was accepted")
+ hapd.set("ap_isolate", "1")
+ if "OK" in hapd.request("ENABLE"):
+ raise Exception("Incomplete hostapd configuration was accepted")
+ hapd.set('bridge', 'ap-br0')
+ hapd.dump_monitor()
+ try:
+ hapd.enable()
+ except:
+ # For now, do not report failures due to missing kernel support
+ raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ if "AP-ENABLED" not in ev:
+ raise Exception("AP startup failed")
+
+ dev[0].hs20_enable()
+ subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+
+ dev[1].connect("test-hs20", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="hs20-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ scan_freq="2412")
+ time.sleep(0.1)
+
+ addr0 = dev[0].p2p_interface_addr()
+ addr1 = dev[1].p2p_interface_addr()
+
+ src_ll_opt0 = b"\x01\x01" + binascii.unhexlify(addr0.replace(':', ''))
+ src_ll_opt1 = b"\x01\x01" + binascii.unhexlify(addr1.replace(':', ''))
+
+ pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
+ ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2",
+ opt=src_ll_opt0)
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ pkt = build_ns(src_ll=addr1, ip_src="aaaa:bbbb:dddd::2",
+ ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:dddd::2",
+ opt=src_ll_opt1)
+ if "OK" not in dev[1].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ pkt = build_ns(src_ll=addr1, ip_src="aaaa:bbbb:eeee::2",
+ ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:eeee::2",
+ opt=src_ll_opt1)
+ if "OK" not in dev[1].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ matches = get_permanent_neighbors("ap-br0")
+ logger.info("After connect: " + str(matches))
+ if len(matches) != 3:
+ raise Exception("Unexpected number of neighbor entries after connect")
+ if 'aaaa:bbbb:cccc::2 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+ raise Exception("dev0 addr missing")
+ if 'aaaa:bbbb:dddd::2 dev ap-br0 lladdr 02:00:00:00:01:00 PERMANENT' not in matches:
+ raise Exception("dev1 addr(1) missing")
+ if 'aaaa:bbbb:eeee::2 dev ap-br0 lladdr 02:00:00:00:01:00 PERMANENT' not in matches:
+ raise Exception("dev1 addr(2) missing")
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ time.sleep(0.5)
+ matches = get_permanent_neighbors("ap-br0")
+ logger.info("After disconnect: " + str(matches))
+ if len(matches) > 0:
+ raise Exception("Unexpected neighbor entries after disconnect")
+
+def test_ap_hs20_hidden_ssid_in_scan_res(dev, apdev):
+ """Hotspot 2.0 connection with hidden SSId in scan results"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'secret',
+ "ignore_broadcast_ssid": "1"})
+ dev[0].scan_for_bss(bssid, freq=2412)
+ hapd.disable()
+ hapd_global = hostapd.HostapdGlobal(apdev[0])
+ hapd_global.flush()
+ hapd_global.remove(apdev[0]['ifname'])
+
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+
+ # clear BSS table to avoid issues in following test cases
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.disable()
+ dev[0].flush_scan_cache()
+ dev[0].flush_scan_cache()
+
+def test_ap_hs20_proxyarp(dev, apdev):
+ """Hotspot 2.0 and ProxyARP"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_proxyarp(dev, apdev)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'ap-br0'],
+ stderr=open('/dev/null', 'w'))
+
+def _test_ap_hs20_proxyarp_dgaf(dev, apdev, disabled):
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['disable_dgaf'] = '1' if disabled else '0'
+ params['proxy_arp'] = '1'
+ params['na_mcast_to_ucast'] = '1'
+ params['ap_isolate'] = '1'
+ params['bridge'] = 'ap-br0'
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ try:
+ hapd.enable()
+ except:
+ # For now, do not report failures due to missing kernel support
+ raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if ev is None:
+ raise Exception("AP startup timed out")
+
+ dev[0].hs20_enable()
+ subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+
+ dev[1].connect("test-hs20", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="hs20-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ scan_freq="2412")
+ time.sleep(0.1)
+
+ addr0 = dev[0].p2p_interface_addr()
+
+ src_ll_opt0 = b"\x01\x01" + binascii.unhexlify(addr0.replace(':', ''))
+
+ pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
+ ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2",
+ opt=src_ll_opt0)
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ pkt = build_ra(src_ll=apdev[0]['bssid'], ip_src="aaaa:bbbb:cccc::33",
+ ip_dst="ff01::1")
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ pkt = build_na(src_ll=apdev[0]['bssid'], ip_src="aaaa:bbbb:cccc::44",
+ ip_dst="ff01::1", target="aaaa:bbbb:cccc::55")
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ pkt = build_dhcp_ack(dst_ll="ff:ff:ff:ff:ff:ff", src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.123", chaddr=addr0)
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+ # another copy for additional code coverage
+ pkt = build_dhcp_ack(dst_ll=addr0, src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.123", chaddr=addr0)
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ matches = get_permanent_neighbors("ap-br0")
+ logger.info("After connect: " + str(matches))
+ if len(matches) != 2:
+ raise Exception("Unexpected number of neighbor entries after connect")
+ if 'aaaa:bbbb:cccc::2 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+ raise Exception("dev0 addr missing")
+ if '192.168.1.123 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+ raise Exception("dev0 IPv4 addr missing")
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ time.sleep(0.5)
+ matches = get_permanent_neighbors("ap-br0")
+ logger.info("After disconnect: " + str(matches))
+ if len(matches) > 0:
+ raise Exception("Unexpected neighbor entries after disconnect")
+
+def test_ap_hs20_proxyarp_disable_dgaf(dev, apdev):
+ """Hotspot 2.0 and ProxyARP with DGAF disabled"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_proxyarp_dgaf(dev, apdev, True)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'ap-br0'],
+ stderr=open('/dev/null', 'w'))
+
+def test_ap_hs20_proxyarp_enable_dgaf(dev, apdev):
+ """Hotspot 2.0 and ProxyARP with DGAF enabled"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ _test_ap_hs20_proxyarp_dgaf(dev, apdev, False)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'ap-br0'],
+ stderr=open('/dev/null', 'w'))
+
+def ip_checksum(buf):
+ sum = 0
+ if len(buf) & 0x01:
+ buf += b'\x00'
+ for i in range(0, len(buf), 2):
+ val, = struct.unpack('H', buf[i:i+2])
+ sum += val
+ while (sum >> 16):
+ sum = (sum & 0xffff) + (sum >> 16)
+ return struct.pack('H', ~sum & 0xffff)
+
+def ipv6_solicited_node_mcaddr(target):
+ prefix = socket.inet_pton(socket.AF_INET6, "ff02::1:ff00:0")
+ mask = socket.inet_pton(socket.AF_INET6, "::ff:ffff")
+ _target = socket.inet_pton(socket.AF_INET6, target)
+ p = struct.unpack('4I', prefix)
+ m = struct.unpack('4I', mask)
+ t = struct.unpack('4I', _target)
+ res = (p[0] | (t[0] & m[0]),
+ p[1] | (t[1] & m[1]),
+ p[2] | (t[2] & m[2]),
+ p[3] | (t[3] & m[3]))
+ return socket.inet_ntop(socket.AF_INET6, struct.pack('4I', *res))
+
+def build_icmpv6(ipv6_addrs, type, code, payload):
+ start = struct.pack("BB", type, code)
+ end = payload
+ icmp = start + b'\x00\x00' + end
+ pseudo = ipv6_addrs + struct.pack(">LBBBB", len(icmp), 0, 0, 0, 58)
+ csum = ip_checksum(pseudo + icmp)
+ return start + csum + end
+
+def build_ra(src_ll, ip_src, ip_dst, cur_hop_limit=0, router_lifetime=0,
+ reachable_time=0, retrans_timer=0, opt=None):
+ link_mc = binascii.unhexlify("3333ff000002")
+ _src_ll = binascii.unhexlify(src_ll.replace(':', ''))
+ proto = b'\x86\xdd'
+ ehdr = link_mc + _src_ll + proto
+ _ip_src = socket.inet_pton(socket.AF_INET6, ip_src)
+ _ip_dst = socket.inet_pton(socket.AF_INET6, ip_dst)
+
+ adv = struct.pack('>BBHLL', cur_hop_limit, 0, router_lifetime,
+ reachable_time, retrans_timer)
+ if opt:
+ payload = adv + opt
+ else:
+ payload = adv
+ icmp = build_icmpv6(_ip_src + _ip_dst, 134, 0, payload)
+
+ ipv6 = struct.pack('>BBBBHBB', 0x60, 0, 0, 0, len(icmp), 58, 255)
+ ipv6 += _ip_src + _ip_dst
+
+ return ehdr + ipv6 + icmp
+
+def build_ns(src_ll, ip_src, ip_dst, target, opt=None):
+ link_mc = binascii.unhexlify("3333ff000002")
+ _src_ll = binascii.unhexlify(src_ll.replace(':', ''))
+ proto = b'\x86\xdd'
+ ehdr = link_mc + _src_ll + proto
+ _ip_src = socket.inet_pton(socket.AF_INET6, ip_src)
+ if ip_dst is None:
+ ip_dst = ipv6_solicited_node_mcaddr(target)
+ _ip_dst = socket.inet_pton(socket.AF_INET6, ip_dst)
+
+ reserved = b'\x00\x00\x00\x00'
+ _target = socket.inet_pton(socket.AF_INET6, target)
+ if opt:
+ payload = reserved + _target + opt
+ else:
+ payload = reserved + _target
+ icmp = build_icmpv6(_ip_src + _ip_dst, 135, 0, payload)
+
+ ipv6 = struct.pack('>BBBBHBB', 0x60, 0, 0, 0, len(icmp), 58, 255)
+ ipv6 += _ip_src + _ip_dst
+
+ return ehdr + ipv6 + icmp
+
+def send_ns(dev, src_ll=None, target=None, ip_src=None, ip_dst=None, opt=None,
+ hapd_bssid=None):
+ if hapd_bssid:
+ if src_ll is None:
+ src_ll = hapd_bssid
+ cmd = "DATA_TEST_FRAME ifname=ap-br0 "
+ else:
+ if src_ll is None:
+ src_ll = dev.p2p_interface_addr()
+ cmd = "DATA_TEST_FRAME "
+
+ if opt is None:
+ opt = b"\x01\x01" + binascii.unhexlify(src_ll.replace(':', ''))
+
+ pkt = build_ns(src_ll=src_ll, ip_src=ip_src, ip_dst=ip_dst, target=target,
+ opt=opt)
+ if "OK" not in dev.request(cmd + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+def build_na(src_ll, ip_src, ip_dst, target, opt=None, flags=0):
+ link_mc = binascii.unhexlify("3333ff000002")
+ _src_ll = binascii.unhexlify(src_ll.replace(':', ''))
+ proto = b'\x86\xdd'
+ ehdr = link_mc + _src_ll + proto
+ _ip_src = socket.inet_pton(socket.AF_INET6, ip_src)
+ _ip_dst = socket.inet_pton(socket.AF_INET6, ip_dst)
+
+ _target = socket.inet_pton(socket.AF_INET6, target)
+ if opt:
+ payload = struct.pack('>Bxxx', flags) + _target + opt
+ else:
+ payload = struct.pack('>Bxxx', flags) + _target
+ icmp = build_icmpv6(_ip_src + _ip_dst, 136, 0, payload)
+
+ ipv6 = struct.pack('>BBBBHBB', 0x60, 0, 0, 0, len(icmp), 58, 255)
+ ipv6 += _ip_src + _ip_dst
+
+ return ehdr + ipv6 + icmp
+
+def send_na(dev, src_ll=None, target=None, ip_src=None, ip_dst=None, opt=None,
+ hapd_bssid=None):
+ if hapd_bssid:
+ if src_ll is None:
+ src_ll = hapd_bssid
+ cmd = "DATA_TEST_FRAME ifname=ap-br0 "
+ else:
+ if src_ll is None:
+ src_ll = dev.p2p_interface_addr()
+ cmd = "DATA_TEST_FRAME "
+
+ pkt = build_na(src_ll=src_ll, ip_src=ip_src, ip_dst=ip_dst, target=target,
+ opt=opt)
+ if "OK" not in dev.request(cmd + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+def build_dhcp_ack(dst_ll, src_ll, ip_src, ip_dst, yiaddr, chaddr,
+ subnet_mask="255.255.255.0", truncated_opt=False,
+ wrong_magic=False, force_tot_len=None, no_dhcp=False,
+ udp_checksum=True):
+ _dst_ll = binascii.unhexlify(dst_ll.replace(':', ''))
+ _src_ll = binascii.unhexlify(src_ll.replace(':', ''))
+ proto = b'\x08\x00'
+ ehdr = _dst_ll + _src_ll + proto
+ _ip_src = socket.inet_pton(socket.AF_INET, ip_src)
+ _ip_dst = socket.inet_pton(socket.AF_INET, ip_dst)
+ _subnet_mask = socket.inet_pton(socket.AF_INET, subnet_mask)
+
+ _ciaddr = b'\x00\x00\x00\x00'
+ _yiaddr = socket.inet_pton(socket.AF_INET, yiaddr)
+ _siaddr = b'\x00\x00\x00\x00'
+ _giaddr = b'\x00\x00\x00\x00'
+ _chaddr = binascii.unhexlify(chaddr.replace(':', '') + "00000000000000000000")
+ payload = struct.pack('>BBBBL3BB', 2, 1, 6, 0, 12345, 0, 0, 0, 0)
+ payload += _ciaddr + _yiaddr + _siaddr + _giaddr + _chaddr + 192*b'\x00'
+ # magic
+ if wrong_magic:
+ payload += b'\x63\x82\x53\x00'
+ else:
+ payload += b'\x63\x82\x53\x63'
+ if truncated_opt:
+ payload += b'\x22\xff\x00'
+ # Option: DHCP Message Type = ACK
+ payload += b'\x35\x01\x05'
+ # Pad Option
+ payload += b'\x00'
+ # Option: Subnet Mask
+ payload += b'\x01\x04' + _subnet_mask
+ # Option: Time Offset
+ payload += struct.pack('>BBL', 2, 4, 0)
+ # End Option
+ payload += b'\xff'
+ # Pad Option
+ payload += b'\x00\x00\x00\x00'
+
+ if no_dhcp:
+ payload = struct.pack('>BBBBL3BB', 2, 1, 6, 0, 12345, 0, 0, 0, 0)
+ payload += _ciaddr + _yiaddr + _siaddr + _giaddr + _chaddr + 192*b'\x00'
+
+ if udp_checksum:
+ pseudohdr = _ip_src + _ip_dst + struct.pack('>BBH', 0, 17,
+ 8 + len(payload))
+ udphdr = struct.pack('>HHHH', 67, 68, 8 + len(payload), 0)
+ checksum, = struct.unpack('>H', ip_checksum(pseudohdr + udphdr + payload))
+ else:
+ checksum = 0
+ udp = struct.pack('>HHHH', 67, 68, 8 + len(payload), checksum) + payload
+
+ if force_tot_len:
+ tot_len = force_tot_len
+ else:
+ tot_len = 20 + len(udp)
+ start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
+ ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
+ csum = ip_checksum(ipv4)
+ ipv4 = start + csum + _ip_src + _ip_dst
+
+ return ehdr + ipv4 + udp
+
+def build_arp(dst_ll, src_ll, opcode, sender_mac, sender_ip,
+ target_mac, target_ip):
+ _dst_ll = binascii.unhexlify(dst_ll.replace(':', ''))
+ _src_ll = binascii.unhexlify(src_ll.replace(':', ''))
+ proto = b'\x08\x06'
+ ehdr = _dst_ll + _src_ll + proto
+
+ _sender_mac = binascii.unhexlify(sender_mac.replace(':', ''))
+ _sender_ip = socket.inet_pton(socket.AF_INET, sender_ip)
+ _target_mac = binascii.unhexlify(target_mac.replace(':', ''))
+ _target_ip = socket.inet_pton(socket.AF_INET, target_ip)
+
+ arp = struct.pack('>HHBBH', 1, 0x0800, 6, 4, opcode)
+ arp += _sender_mac + _sender_ip
+ arp += _target_mac + _target_ip
+
+ return ehdr + arp
+
+def send_arp(dev, dst_ll="ff:ff:ff:ff:ff:ff", src_ll=None, opcode=1,
+ sender_mac=None, sender_ip="0.0.0.0",
+ target_mac="00:00:00:00:00:00", target_ip="0.0.0.0",
+ hapd_bssid=None):
+ if hapd_bssid:
+ if src_ll is None:
+ src_ll = hapd_bssid
+ if sender_mac is None:
+ sender_mac = hapd_bssid
+ cmd = "DATA_TEST_FRAME ifname=ap-br0 "
+ else:
+ if src_ll is None:
+ src_ll = dev.p2p_interface_addr()
+ if sender_mac is None:
+ sender_mac = dev.p2p_interface_addr()
+ cmd = "DATA_TEST_FRAME "
+
+ pkt = build_arp(dst_ll=dst_ll, src_ll=src_ll, opcode=opcode,
+ sender_mac=sender_mac, sender_ip=sender_ip,
+ target_mac=target_mac, target_ip=target_ip)
+ if "OK" not in dev.request(cmd + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+def get_permanent_neighbors(ifname):
+ cmd = subprocess.Popen(['ip', 'nei'], stdout=subprocess.PIPE)
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ return [line for line in res.splitlines() if "PERMANENT" in line and ifname in line]
+
+def get_bridge_macs(ifname):
+ cmd = subprocess.Popen(['brctl', 'showmacs', ifname],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read()
+ cmd.stdout.close()
+ return res.decode()
+
+def tshark_get_arp(cap, filter):
+ res = run_tshark(cap, filter,
+ ["eth.dst", "eth.src",
+ "arp.src.hw_mac", "arp.src.proto_ipv4",
+ "arp.dst.hw_mac", "arp.dst.proto_ipv4"],
+ wait=False)
+ frames = []
+ for l in res.splitlines():
+ frames.append(l.split('\t'))
+ return frames
+
+def tshark_get_ns(cap):
+ res = run_tshark(cap, "icmpv6.type == 135",
+ ["eth.dst", "eth.src",
+ "ipv6.src", "ipv6.dst",
+ "icmpv6.nd.ns.target_address",
+ "icmpv6.opt.linkaddr"],
+ wait=False)
+ frames = []
+ for l in res.splitlines():
+ frames.append(l.split('\t'))
+ return frames
+
+def tshark_get_na(cap):
+ res = run_tshark(cap, "icmpv6.type == 136",
+ ["eth.dst", "eth.src",
+ "ipv6.src", "ipv6.dst",
+ "icmpv6.nd.na.target_address",
+ "icmpv6.opt.linkaddr"],
+ wait=False)
+ frames = []
+ for l in res.splitlines():
+ frames.append(l.split('\t'))
+ return frames
+
+def _test_proxyarp_open(dev, apdev, params, ebtables=False):
+ cap_br = params['prefix'] + ".ap-br0.pcap"
+ cap_dev0 = params['prefix'] + ".%s.pcap" % dev[0].ifname
+ cap_dev1 = params['prefix'] + ".%s.pcap" % dev[1].ifname
+ cap_dev2 = params['prefix'] + ".%s.pcap" % dev[2].ifname
+
+ bssid = apdev[0]['bssid']
+ params = {'ssid': 'open'}
+ params['proxy_arp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ hapd.set("ap_isolate", "1")
+ hapd.set('bridge', 'ap-br0')
+ hapd.dump_monitor()
+ try:
+ hapd.enable()
+ except:
+ # For now, do not report failures due to missing kernel support
+ raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ if "AP-ENABLED" not in ev:
+ raise Exception("AP startup failed")
+
+ params2 = {'ssid': 'another'}
+ hapd2 = hostapd.add_ap(apdev[1], params2, no_enable=True)
+ hapd2.set('bridge', 'ap-br0')
+ hapd2.enable()
+
+ subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+ if ebtables:
+ for chain in ['FORWARD', 'OUTPUT']:
+ try:
+ err = subprocess.call(['ebtables', '-A', chain, '-p', 'ARP',
+ '-d', 'Broadcast',
+ '-o', apdev[0]['ifname'],
+ '-j', 'DROP'])
+ if err != 0:
+ raise
+ except:
+ raise HwsimSkip("No ebtables available")
+
+ time.sleep(0.5)
+ cmd = {}
+ cmd[0] = WlantestCapture('ap-br0', cap_br)
+ cmd[1] = WlantestCapture(dev[0].ifname, cap_dev0)
+ cmd[2] = WlantestCapture(dev[1].ifname, cap_dev1)
+ cmd[3] = WlantestCapture(dev[2].ifname, cap_dev2)
+
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("open", key_mgmt="NONE", scan_freq="2412")
+ dev[2].connect("another", key_mgmt="NONE", scan_freq="2412")
+ time.sleep(1.1)
+
+ brcmd = subprocess.Popen(['brctl', 'show'], stdout=subprocess.PIPE)
+ res = brcmd.stdout.read().decode()
+ brcmd.stdout.close()
+ logger.info("Bridge setup: " + res)
+
+ brcmd = subprocess.Popen(['brctl', 'showstp', 'ap-br0'],
+ stdout=subprocess.PIPE)
+ res = brcmd.stdout.read().decode()
+ brcmd.stdout.close()
+ logger.info("Bridge showstp: " + res)
+
+ addr0 = dev[0].p2p_interface_addr()
+ addr1 = dev[1].p2p_interface_addr()
+ addr2 = dev[2].p2p_interface_addr()
+
+ pkt = build_dhcp_ack(dst_ll="ff:ff:ff:ff:ff:ff", src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.124", chaddr=addr0)
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+ # Change address and verify unicast
+ pkt = build_dhcp_ack(dst_ll=addr0, src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.123", chaddr=addr0,
+ udp_checksum=False)
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Not-associated client MAC address
+ pkt = build_dhcp_ack(dst_ll="ff:ff:ff:ff:ff:ff", src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.125", chaddr="22:33:44:55:66:77")
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # No IP address
+ pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="0.0.0.0", chaddr=addr1)
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Zero subnet mask
+ pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.126", chaddr=addr1,
+ subnet_mask="0.0.0.0")
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Truncated option
+ pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.127", chaddr=addr1,
+ truncated_opt=True)
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Wrong magic
+ pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.128", chaddr=addr1,
+ wrong_magic=True)
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # Wrong IPv4 total length
+ pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.129", chaddr=addr1,
+ force_tot_len=1000)
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ # BOOTP
+ pkt = build_dhcp_ack(dst_ll=addr1, src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.129", chaddr=addr1,
+ no_dhcp=True)
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ macs = get_bridge_macs("ap-br0")
+ logger.info("After connect (showmacs): " + str(macs))
+
+ matches = get_permanent_neighbors("ap-br0")
+ logger.info("After connect: " + str(matches))
+ if len(matches) != 1:
+ raise Exception("Unexpected number of neighbor entries after connect")
+ if '192.168.1.123 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+ raise Exception("dev0 IPv4 addr missing")
+
+ targets = ["192.168.1.123", "192.168.1.124", "192.168.1.125",
+ "192.168.1.126"]
+ for target in targets:
+ send_arp(dev[1], sender_ip="192.168.1.100", target_ip=target)
+
+ for target in targets:
+ send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.101",
+ target_ip=target)
+
+ for target in targets:
+ send_arp(dev[2], sender_ip="192.168.1.103", target_ip=target)
+
+ # ARP Probe from wireless STA
+ send_arp(dev[1], target_ip="192.168.1.127")
+ # ARP Announcement from wireless STA
+ send_arp(dev[1], sender_ip="192.168.1.127", target_ip="192.168.1.127")
+ send_arp(dev[1], sender_ip="192.168.1.127", target_ip="192.168.1.127",
+ opcode=2)
+
+ macs = get_bridge_macs("ap-br0")
+ logger.info("After ARP Probe + Announcement (showmacs): " + str(macs))
+
+ matches = get_permanent_neighbors("ap-br0")
+ logger.info("After ARP Probe + Announcement: " + str(matches))
+
+ # ARP Request for the newly introduced IP address from wireless STA
+ send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.127")
+
+ # ARP Request for the newly introduced IP address from bridge
+ send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.102",
+ target_ip="192.168.1.127")
+ send_arp(dev[2], sender_ip="192.168.1.103", target_ip="192.168.1.127")
+
+ # ARP Probe from bridge
+ send_arp(hapd, hapd_bssid=bssid, target_ip="192.168.1.130")
+ send_arp(dev[2], target_ip="192.168.1.131")
+ # ARP Announcement from bridge (not to be learned by AP for proxyarp)
+ send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.130",
+ target_ip="192.168.1.130")
+ send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.130",
+ target_ip="192.168.1.130", opcode=2)
+ send_arp(dev[2], sender_ip="192.168.1.131", target_ip="192.168.1.131")
+ send_arp(dev[2], sender_ip="192.168.1.131", target_ip="192.168.1.131",
+ opcode=2)
+
+ macs = get_bridge_macs("ap-br0")
+ logger.info("After ARP Probe + Announcement (showmacs): " + str(macs))
+
+ matches = get_permanent_neighbors("ap-br0")
+ logger.info("After ARP Probe + Announcement: " + str(matches))
+
+ # ARP Request for the newly introduced IP address from wireless STA
+ send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.130")
+ # ARP Response from bridge (AP does not proxy for non-wireless devices)
+ send_arp(hapd, hapd_bssid=bssid, dst_ll=addr0, sender_ip="192.168.1.130",
+ target_ip="192.168.1.123", opcode=2)
+
+ # ARP Request for the newly introduced IP address from wireless STA
+ send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.131")
+ # ARP Response from bridge (AP does not proxy for non-wireless devices)
+ send_arp(dev[2], dst_ll=addr0, sender_ip="192.168.1.131",
+ target_ip="192.168.1.123", opcode=2)
+
+ # ARP Request for the newly introduced IP address from bridge
+ send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.102",
+ target_ip="192.168.1.130")
+ send_arp(dev[2], sender_ip="192.168.1.104", target_ip="192.168.1.131")
+
+ # ARP Probe from wireless STA (duplicate address; learned through DHCP)
+ send_arp(dev[1], target_ip="192.168.1.123")
+ # ARP Probe from wireless STA (duplicate address; learned through ARP)
+ send_arp(dev[0], target_ip="192.168.1.127")
+
+ # Gratuitous ARP Reply for another STA's IP address
+ send_arp(dev[0], opcode=2, sender_mac=addr0, sender_ip="192.168.1.127",
+ target_mac=addr1, target_ip="192.168.1.127")
+ send_arp(dev[1], opcode=2, sender_mac=addr1, sender_ip="192.168.1.123",
+ target_mac=addr0, target_ip="192.168.1.123")
+ # ARP Request to verify previous mapping
+ send_arp(dev[1], sender_ip="192.168.1.127", target_ip="192.168.1.123")
+ send_arp(dev[0], sender_ip="192.168.1.123", target_ip="192.168.1.127")
+
+ try:
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "ap-br0")
+ except Exception as e:
+ logger.info("test_connectibity_iface failed: " + str(e))
+ raise HwsimSkip("Assume kernel did not have the required patches for proxyarp")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "ap-br0")
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ time.sleep(1.5)
+ for i in range(len(cmd)):
+ cmd[i].close()
+ time.sleep(0.1)
+ macs = get_bridge_macs("ap-br0")
+ logger.info("After disconnect (showmacs): " + str(macs))
+ matches = get_permanent_neighbors("ap-br0")
+ logger.info("After disconnect: " + str(matches))
+ if len(matches) > 0:
+ raise Exception("Unexpected neighbor entries after disconnect")
+ if ebtables:
+ cmd = subprocess.Popen(['ebtables', '-L', '--Lc'],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ logger.info("ebtables results:\n" + res)
+
+ # Verify that expected ARP messages were seen and no unexpected
+ # ARP messages were seen.
+
+ arp_req = tshark_get_arp(cap_dev0, "arp.opcode == 1")
+ arp_reply = tshark_get_arp(cap_dev0, "arp.opcode == 2")
+ logger.info("dev0 seen ARP requests:\n" + str(arp_req))
+ logger.info("dev0 seen ARP replies:\n" + str(arp_reply))
+
+ if ['ff:ff:ff:ff:ff:ff', addr1,
+ addr1, '192.168.1.100',
+ '00:00:00:00:00:00', '192.168.1.123'] in arp_req:
+ raise Exception("dev0 saw ARP request from dev1")
+ if ['ff:ff:ff:ff:ff:ff', addr2,
+ addr2, '192.168.1.103',
+ '00:00:00:00:00:00', '192.168.1.123'] in arp_req:
+ raise Exception("dev0 saw ARP request from dev2")
+ # TODO: Uncomment once fixed in kernel
+ #if ['ff:ff:ff:ff:ff:ff', bssid,
+ # bssid, '192.168.1.101',
+ # '00:00:00:00:00:00', '192.168.1.123'] in arp_req:
+ # raise Exception("dev0 saw ARP request from br")
+
+ if ebtables:
+ for req in arp_req:
+ if req[1] != addr0:
+ raise Exception("Unexpected foreign ARP request on dev0")
+
+ arp_req = tshark_get_arp(cap_dev1, "arp.opcode == 1")
+ arp_reply = tshark_get_arp(cap_dev1, "arp.opcode == 2")
+ logger.info("dev1 seen ARP requests:\n" + str(arp_req))
+ logger.info("dev1 seen ARP replies:\n" + str(arp_reply))
+
+ if ['ff:ff:ff:ff:ff:ff', addr2,
+ addr2, '192.168.1.103',
+ '00:00:00:00:00:00', '192.168.1.123'] in arp_req:
+ raise Exception("dev1 saw ARP request from dev2")
+ if [addr1, addr0, addr0, '192.168.1.123', addr1, '192.168.1.100'] not in arp_reply:
+ raise Exception("dev1 did not get ARP response for 192.168.1.123")
+
+ if ebtables:
+ for req in arp_req:
+ if req[1] != addr1:
+ raise Exception("Unexpected foreign ARP request on dev1")
+
+ arp_req = tshark_get_arp(cap_dev2, "arp.opcode == 1")
+ arp_reply = tshark_get_arp(cap_dev2, "arp.opcode == 2")
+ logger.info("dev2 seen ARP requests:\n" + str(arp_req))
+ logger.info("dev2 seen ARP replies:\n" + str(arp_reply))
+
+ if [addr2, addr0,
+ addr0, '192.168.1.123',
+ addr2, '192.168.1.103'] not in arp_reply:
+ raise Exception("dev2 did not get ARP response for 192.168.1.123")
+
+ arp_req = tshark_get_arp(cap_br, "arp.opcode == 1")
+ arp_reply = tshark_get_arp(cap_br, "arp.opcode == 2")
+ logger.info("br seen ARP requests:\n" + str(arp_req))
+ logger.info("br seen ARP replies:\n" + str(arp_reply))
+
+ # TODO: Uncomment once fixed in kernel
+ #if [bssid, addr0,
+ # addr0, '192.168.1.123',
+ # bssid, '192.168.1.101'] not in arp_reply:
+ # raise Exception("br did not get ARP response for 192.168.1.123")
+
+def _test_proxyarp_open_ipv6(dev, apdev, params, ebtables=False):
+ cap_br = params['prefix'] + ".ap-br0.pcap"
+ cap_dev0 = params['prefix'] + ".%s.pcap" % dev[0].ifname
+ cap_dev1 = params['prefix'] + ".%s.pcap" % dev[1].ifname
+ cap_dev2 = params['prefix'] + ".%s.pcap" % dev[2].ifname
+
+ bssid = apdev[0]['bssid']
+ params = {'ssid': 'open'}
+ params['proxy_arp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ hapd.set("ap_isolate", "1")
+ hapd.set('bridge', 'ap-br0')
+ hapd.dump_monitor()
+ try:
+ hapd.enable()
+ except:
+ # For now, do not report failures due to missing kernel support
+ raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ if "AP-ENABLED" not in ev:
+ raise Exception("AP startup failed")
+
+ params2 = {'ssid': 'another'}
+ hapd2 = hostapd.add_ap(apdev[1], params2, no_enable=True)
+ hapd2.set('bridge', 'ap-br0')
+ hapd2.enable()
+
+ subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+ if ebtables:
+ for chain in ['FORWARD', 'OUTPUT']:
+ try:
+ err = subprocess.call(['ebtables', '-A', chain,
+ '-d', 'Multicast',
+ '-p', 'IPv6',
+ '--ip6-protocol', 'ipv6-icmp',
+ '--ip6-icmp-type',
+ 'neighbor-solicitation',
+ '-o', apdev[0]['ifname'], '-j', 'DROP'])
+ if err != 0:
+ raise
+ subprocess.call(['ebtables', '-A', chain, '-d', 'Multicast',
+ '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+ '--ip6-icmp-type', 'neighbor-advertisement',
+ '-o', apdev[0]['ifname'], '-j', 'DROP'])
+ subprocess.call(['ebtables', '-A', chain,
+ '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+ '--ip6-icmp-type', 'router-solicitation',
+ '-o', apdev[0]['ifname'], '-j', 'DROP'])
+ # Multicast Listener Report Message
+ subprocess.call(['ebtables', '-A', chain, '-d', 'Multicast',
+ '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+ '--ip6-icmp-type', '143',
+ '-o', apdev[0]['ifname'], '-j', 'DROP'])
+ except:
+ raise HwsimSkip("No ebtables available")
+
+ time.sleep(0.5)
+ cmd = {}
+ cmd[0] = WlantestCapture('ap-br0', cap_br)
+ cmd[1] = WlantestCapture(dev[0].ifname, cap_dev0)
+ cmd[2] = WlantestCapture(dev[1].ifname, cap_dev1)
+ cmd[3] = WlantestCapture(dev[2].ifname, cap_dev2)
+
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("open", key_mgmt="NONE", scan_freq="2412")
+ dev[2].connect("another", key_mgmt="NONE", scan_freq="2412")
+ time.sleep(0.1)
+
+ brcmd = subprocess.Popen(['brctl', 'show'], stdout=subprocess.PIPE)
+ res = brcmd.stdout.read().decode()
+ brcmd.stdout.close()
+ logger.info("Bridge setup: " + res)
+
+ brcmd = subprocess.Popen(['brctl', 'showstp', 'ap-br0'],
+ stdout=subprocess.PIPE)
+ res = brcmd.stdout.read().decode()
+ brcmd.stdout.close()
+ logger.info("Bridge showstp: " + res)
+
+ addr0 = dev[0].p2p_interface_addr()
+ addr1 = dev[1].p2p_interface_addr()
+ addr2 = dev[2].p2p_interface_addr()
+
+ src_ll_opt0 = b"\x01\x01" + binascii.unhexlify(addr0.replace(':', ''))
+ src_ll_opt1 = b"\x01\x01" + binascii.unhexlify(addr1.replace(':', ''))
+
+ # DAD NS
+ send_ns(dev[0], ip_src="::", target="aaaa:bbbb:cccc::2")
+
+ send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2")
+ # test frame without source link-layer address option
+ send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+ opt='')
+ # test frame with bogus option
+ send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+ opt=b"\x70\x01\x01\x02\x03\x04\x05\x05")
+ # test frame with truncated source link-layer address option
+ send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+ opt=b"\x01\x01\x01\x02\x03\x04")
+ # test frame with foreign source link-layer address option
+ send_ns(dev[0], ip_src="aaaa:bbbb:cccc::2", target="aaaa:bbbb:cccc::2",
+ opt=b"\x01\x01\x01\x02\x03\x04\x05\x06")
+
+ send_ns(dev[1], ip_src="aaaa:bbbb:dddd::2", target="aaaa:bbbb:dddd::2")
+
+ send_ns(dev[1], ip_src="aaaa:bbbb:eeee::2", target="aaaa:bbbb:eeee::2")
+ # another copy for additional code coverage
+ send_ns(dev[1], ip_src="aaaa:bbbb:eeee::2", target="aaaa:bbbb:eeee::2")
+
+ macs = get_bridge_macs("ap-br0")
+ logger.info("After connect (showmacs): " + str(macs))
+
+ matches = get_permanent_neighbors("ap-br0")
+ logger.info("After connect: " + str(matches))
+ if len(matches) != 3:
+ raise Exception("Unexpected number of neighbor entries after connect")
+ if 'aaaa:bbbb:cccc::2 dev ap-br0 lladdr 02:00:00:00:00:00 PERMANENT' not in matches:
+ raise Exception("dev0 addr missing")
+ if 'aaaa:bbbb:dddd::2 dev ap-br0 lladdr 02:00:00:00:01:00 PERMANENT' not in matches:
+ raise Exception("dev1 addr(1) missing")
+ if 'aaaa:bbbb:eeee::2 dev ap-br0 lladdr 02:00:00:00:01:00 PERMANENT' not in matches:
+ raise Exception("dev1 addr(2) missing")
+
+ send_ns(dev[0], target="aaaa:bbbb:dddd::2", ip_src="aaaa:bbbb:cccc::2")
+ time.sleep(0.1)
+ send_ns(dev[1], target="aaaa:bbbb:cccc::2", ip_src="aaaa:bbbb:dddd::2")
+ time.sleep(0.1)
+ send_ns(hapd, hapd_bssid=bssid, target="aaaa:bbbb:dddd::2",
+ ip_src="aaaa:bbbb:ffff::2")
+ time.sleep(0.1)
+ send_ns(dev[2], target="aaaa:bbbb:cccc::2", ip_src="aaaa:bbbb:ff00::2")
+ time.sleep(0.1)
+ send_ns(dev[2], target="aaaa:bbbb:dddd::2", ip_src="aaaa:bbbb:ff00::2")
+ time.sleep(0.1)
+ send_ns(dev[2], target="aaaa:bbbb:eeee::2", ip_src="aaaa:bbbb:ff00::2")
+ time.sleep(0.1)
+
+ # Try to probe for an already assigned address
+ send_ns(dev[1], target="aaaa:bbbb:cccc::2", ip_src="::")
+ time.sleep(0.1)
+ send_ns(hapd, hapd_bssid=bssid, target="aaaa:bbbb:cccc::2", ip_src="::")
+ time.sleep(0.1)
+ send_ns(dev[2], target="aaaa:bbbb:cccc::2", ip_src="::")
+ time.sleep(0.1)
+
+ # Unsolicited NA
+ send_na(dev[1], target="aaaa:bbbb:cccc:aeae::3",
+ ip_src="aaaa:bbbb:cccc:aeae::3", ip_dst="ff02::1")
+ send_na(hapd, hapd_bssid=bssid, target="aaaa:bbbb:cccc:aeae::4",
+ ip_src="aaaa:bbbb:cccc:aeae::4", ip_dst="ff02::1")
+ send_na(dev[2], target="aaaa:bbbb:cccc:aeae::5",
+ ip_src="aaaa:bbbb:cccc:aeae::5", ip_dst="ff02::1")
+
+ try:
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "ap-br0")
+ except Exception as e:
+ logger.info("test_connectibity_iface failed: " + str(e))
+ raise HwsimSkip("Assume kernel did not have the required patches for proxyarp")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "ap-br0")
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ time.sleep(0.5)
+ for i in range(len(cmd)):
+ cmd[i].close()
+ macs = get_bridge_macs("ap-br0")
+ logger.info("After disconnect (showmacs): " + str(macs))
+ matches = get_permanent_neighbors("ap-br0")
+ logger.info("After disconnect: " + str(matches))
+ if len(matches) > 0:
+ raise Exception("Unexpected neighbor entries after disconnect")
+ if ebtables:
+ cmd = subprocess.Popen(['ebtables', '-L', '--Lc'],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ logger.info("ebtables results:\n" + res)
+
+ ns = tshark_get_ns(cap_dev0)
+ logger.info("dev0 seen NS: " + str(ns))
+ na = tshark_get_na(cap_dev0)
+ logger.info("dev0 seen NA: " + str(na))
+
+ if [addr0, addr1, 'aaaa:bbbb:dddd::2', 'aaaa:bbbb:cccc::2',
+ 'aaaa:bbbb:dddd::2', addr1] not in na:
+ # For now, skip the test instead of reporting the error since the IPv6
+ # proxyarp support is not yet in the upstream kernel tree.
+ #raise Exception("dev0 did not get NA for aaaa:bbbb:dddd::2")
+ raise HwsimSkip("Assume kernel did not have the required patches for proxyarp (IPv6)")
+
+ if ebtables:
+ for req in ns:
+ if req[1] == bssid and req[0] == "33:33:ff:" + bssid[9:] and \
+ req[3] == 'ff02::1:ff00:300' and req[4] == 'fe80::ff:fe00:300':
+ # At least for now, ignore this special case until the kernel
+ # can be prevented from sending it out.
+ logger.info("dev0: Ignore NS from AP to own local addr: " + str(req))
+ elif req[1] != addr0:
+ raise Exception("Unexpected foreign NS on dev0: " + str(req))
+
+ ns = tshark_get_ns(cap_dev1)
+ logger.info("dev1 seen NS: " + str(ns))
+ na = tshark_get_na(cap_dev1)
+ logger.info("dev1 seen NA: " + str(na))
+
+ if [addr1, addr0, 'aaaa:bbbb:cccc::2', 'aaaa:bbbb:dddd::2',
+ 'aaaa:bbbb:cccc::2', addr0] not in na:
+ raise Exception("dev1 did not get NA for aaaa:bbbb:cccc::2")
+
+ if ebtables:
+ for req in ns:
+ if req[1] == bssid and req[0] == "33:33:ff:" + bssid[9:] and \
+ req[3] == 'ff02::1:ff00:300' and req[4] == 'fe80::ff:fe00:300':
+ # At least for now, ignore this special case until the kernel
+ # can be prevented from sending it out.
+ logger.info("dev1: Ignore NS from AP to own local addr: " + str(req))
+ elif req[1] != addr1:
+ raise Exception("Unexpected foreign NS on dev1: " + str(req))
+
+ ns = tshark_get_ns(cap_dev2)
+ logger.info("dev2 seen NS: " + str(ns))
+ na = tshark_get_na(cap_dev2)
+ logger.info("dev2 seen NA: " + str(na))
+
+ # FIX: enable once kernel implementation for proxyarp IPv6 is fixed
+ #if [addr2, addr0, 'aaaa:bbbb:cccc::2', 'aaaa:bbbb:ff00::2',
+ # 'aaaa:bbbb:cccc::2', addr0] not in na:
+ # raise Exception("dev2 did not get NA for aaaa:bbbb:cccc::2")
+ #if [addr2, addr1, 'aaaa:bbbb:dddd::2', 'aaaa:bbbb:ff00::2',
+ # 'aaaa:bbbb:dddd::2', addr1] not in na:
+ # raise Exception("dev2 did not get NA for aaaa:bbbb:dddd::2")
+ #if [addr2, addr1, 'aaaa:bbbb:eeee::2', 'aaaa:bbbb:ff00::2',
+ # 'aaaa:bbbb:eeee::2', addr1] not in na:
+ # raise Exception("dev2 did not get NA for aaaa:bbbb:eeee::2")
+
+def test_proxyarp_open(dev, apdev, params):
+ """ProxyARP with open network"""
+ try:
+ _test_proxyarp_open(dev, apdev, params)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'ap-br0'],
+ stderr=open('/dev/null', 'w'))
+
+def test_proxyarp_open_ipv6(dev, apdev, params):
+ """ProxyARP with open network (IPv6)"""
+ try:
+ _test_proxyarp_open_ipv6(dev, apdev, params)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'ap-br0'],
+ stderr=open('/dev/null', 'w'))
+
+def test_proxyarp_open_ebtables(dev, apdev, params):
+ """ProxyARP with open network"""
+ try:
+ _test_proxyarp_open(dev, apdev, params, ebtables=True)
+ finally:
+ try:
+ subprocess.call(['ebtables', '-F', 'FORWARD'])
+ subprocess.call(['ebtables', '-F', 'OUTPUT'])
+ except:
+ pass
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'ap-br0'],
+ stderr=open('/dev/null', 'w'))
+
+def test_proxyarp_open_ebtables_ipv6(dev, apdev, params):
+ """ProxyARP with open network (IPv6)"""
+ try:
+ _test_proxyarp_open_ipv6(dev, apdev, params, ebtables=True)
+ finally:
+ try:
+ subprocess.call(['ebtables', '-F', 'FORWARD'])
+ subprocess.call(['ebtables', '-F', 'OUTPUT'])
+ except:
+ pass
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'ap-br0'],
+ stderr=open('/dev/null', 'w'))
+
+def test_proxyarp_errors(dev, apdev, params):
+ """ProxyARP error cases"""
+ try:
+ run_proxyarp_errors(dev, apdev, params)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'ap-br0'],
+ stderr=open('/dev/null', 'w'))
+
+def run_proxyarp_errors(dev, apdev, params):
+ params = {'ssid': 'open',
+ 'proxy_arp': '1',
+ 'ap_isolate': '1',
+ 'bridge': 'ap-br0',
+ 'disable_dgaf': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ try:
+ hapd.enable()
+ except:
+ # For now, do not report failures due to missing kernel support
+ raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ if "AP-ENABLED" not in ev:
+ raise Exception("AP startup failed")
+
+ hapd.disable()
+ with alloc_fail(hapd, 1, "l2_packet_init;x_snoop_get_l2_packet;dhcp_snoop_init"):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE accepted unexpectedly")
+ with alloc_fail(hapd, 1, "l2_packet_init;x_snoop_get_l2_packet;ndisc_snoop_init"):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE accepted unexpectedly")
+ with fail_test(hapd, 1, "l2_packet_set_packet_filter;x_snoop_get_l2_packet;ndisc_snoop_init"):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE accepted unexpectedly")
+ with fail_test(hapd, 1, "l2_packet_set_packet_filter;x_snoop_get_l2_packet;dhcp_snoop_init"):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE accepted unexpectedly")
+ hapd.enable()
+
+ subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ addr0 = dev[0].own_addr()
+
+ pkt = build_ra(src_ll=apdev[0]['bssid'], ip_src="aaaa:bbbb:cccc::33",
+ ip_dst="ff01::1")
+ with fail_test(hapd, 1, "x_snoop_mcast_to_ucast_convert_send"):
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+
+ with alloc_fail(hapd, 1, "sta_ip6addr_add"):
+ src_ll_opt0 = b"\x01\x01" + binascii.unhexlify(addr0.replace(':', ''))
+ pkt = build_ns(src_ll=addr0, ip_src="aaaa:bbbb:cccc::2",
+ ip_dst="ff02::1:ff00:2", target="aaaa:bbbb:cccc::2",
+ opt=src_ll_opt0)
+ if "OK" not in dev[0].request("DATA_TEST_FRAME " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+def test_ap_hs20_connect_deinit(dev, apdev):
+ """Hotspot 2.0 connection interrupted with deinit"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="")
+ wpas.hs20_enable()
+ wpas.flush_scan_cache()
+ wpas.add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com"})
+
+ wpas.scan_for_bss(bssid, freq=2412)
+ hapd.disable()
+
+ wpas.request("INTERWORKING_SELECT freq=2412")
+
+ id = wpas.request("RADIO_WORK add block-work")
+ ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start (2)")
+
+ # Remove the interface while the gas-query radio work is still pending and
+ # GAS query has not yet been started.
+ wpas.interface_remove("wlan5")
+
+def test_ap_hs20_anqp_format_errors(dev, apdev):
+ """Interworking network selection and ANQP format errors"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ values = {'realm': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"}
+ id = dev[0].add_cred_values(values)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ tests = ["00", "ffff", "010011223344", "020008000005112233445500",
+ "01000400000000", "01000000000000",
+ "01000300000200", "0100040000ff0000", "01000300000100",
+ "01000300000001",
+ "01000600000056112233",
+ "01000900000002050001000111",
+ "01000600000001000000", "01000600000001ff0000",
+ "01000600000001020001",
+ "010008000000010400010001", "0100080000000104000100ff",
+ "010011000000010d00050200020100030005000600",
+ "0000"]
+ for t in tests:
+ hapd.set("anqp_elem", "263:" + t)
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-NO-MATCH"], timeout=5)
+ if ev is None:
+ raise Exception("Network selection timed out")
+ dev[0].dump_monitor()
+
+ dev[0].remove_cred(id)
+ id = dev[0].add_cred_values({'imsi': "555444-333222111", 'eap': "AKA",
+ 'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123"})
+
+ tests = ["00", "0100", "0001", "00ff", "000200ff", "0003000101",
+ "00020100"]
+ for t in tests:
+ hapd.set("anqp_elem", "264:" + t)
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+ ev = dev[0].wait_event(["INTERWORKING-NO-MATCH"], timeout=5)
+ if ev is None:
+ raise Exception("Network selection timed out")
+ dev[0].dump_monitor()
+
+def test_ap_hs20_cred_with_nai_realm(dev, apdev):
+ """Hotspot 2.0 network selection and cred_with_nai_realm cred->realm"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com",
+ 'eap': 'TTLS'})
+ interworking_select(dev[0], bssid, "home", freq=2412)
+ dev[0].remove_cred(id)
+
+ id = dev[0].add_cred_values({'realm': "foo.com",
+ 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com",
+ 'roaming_consortium': "112234",
+ 'eap': 'TTLS'})
+ interworking_select(dev[0], bssid, "home", freq=2412, no_match=True)
+ dev[0].remove_cred(id)
+
+def test_ap_hs20_cred_and_no_roaming_consortium(dev, apdev):
+ """Hotspot 2.0 network selection and no roaming consortium"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ del params['roaming_consortium']
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com",
+ 'roaming_consortium': "112234",
+ 'eap': 'TTLS'})
+ interworking_select(dev[0], bssid, "home", freq=2412)
+
+def test_ap_hs20_interworking_oom(dev, apdev):
+ """Hotspot 2.0 network selection and OOM"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['nai_realm'] = ["0,no.match.here;example.com;no.match.here.either,21[2:1][5:7]",
+ "0,example.com,13[5:6],21[2:4][5:7]",
+ "0,another.example.com"]
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com",
+ 'eap': 'TTLS'})
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ funcs = ["wpabuf_alloc;interworking_anqp_send_req",
+ "anqp_build_req;interworking_anqp_send_req",
+ "gas_query_req;interworking_anqp_send_req",
+ "dup_binstr;nai_realm_parse_realm",
+ "=nai_realm_parse_realm",
+ "=nai_realm_parse",
+ "=nai_realm_match"]
+ for func in funcs:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_event(["Starting ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP did not start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].dump_monitor()
+
+def test_ap_hs20_no_cred_connect(dev, apdev):
+ """Hotspot 2.0 and connect attempt without credential"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ if "FAIL" not in dev[0].request("INTERWORKING_CONNECT " + bssid):
+ raise Exception("Unexpected INTERWORKING_CONNECT success")
+
+def test_ap_hs20_no_rsn_connect(dev, apdev):
+ """Hotspot 2.0 and connect attempt without RSN"""
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa_params(ssid="test-hs20")
+ params['wpa_key_mgmt'] = "WPA-EAP"
+ params['ieee80211w'] = "1"
+ params['ieee8021x'] = "1"
+ params['auth_server_addr'] = "127.0.0.1"
+ params['auth_server_port'] = "1812"
+ params['auth_server_shared_secret'] = "radius"
+ params['interworking'] = "1"
+ params['roaming_consortium'] = ["112233", "1020304050", "010203040506",
+ "fedcba"]
+ params['nai_realm'] = ["0,example.com,13[5:6],21[2:4][5:7]",
+ "0,another.example.com"]
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com",
+ 'roaming_consortium': "112233",
+ 'eap': 'TTLS'})
+
+ interworking_select(dev[0], bssid, freq=2412, no_match=True)
+ if "FAIL" not in dev[0].request("INTERWORKING_CONNECT " + bssid):
+ raise Exception("Unexpected INTERWORKING_CONNECT success")
+
+def test_ap_hs20_no_match_connect(dev, apdev):
+ """Hotspot 2.0 and connect attempt without matching cred"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ id = dev[0].add_cred_values({'realm': "example.org",
+ 'username': "test",
+ 'password': "secret",
+ 'domain': "example.org",
+ 'roaming_consortium': "112234",
+ 'eap': 'TTLS'})
+
+ interworking_select(dev[0], bssid, freq=2412, no_match=True)
+ if "FAIL" not in dev[0].request("INTERWORKING_CONNECT " + bssid):
+ raise Exception("Unexpected INTERWORKING_CONNECT success")
+
+def test_ap_hs20_multiple_home_cred(dev, apdev):
+ """Hotspot 2.0 and select with multiple matching home credentials"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['nai_realm'] = ["0,example.com,13[5:6],21[2:4][5:7]"]
+ params['domain_name'] = "example.com"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ bssid2 = apdev[1]['bssid']
+ params = hs20_ap_params(ssid="test-hs20-other")
+ params['hessid'] = bssid2
+ params['nai_realm'] = ["0,example.org,13[5:6],21[2:4][5:7]"]
+ params['domain_name'] = "example.org"
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'priority': '2',
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.com"})
+ id2 = dev[0].add_cred_values({'realm': "example.org",
+ 'priority': '3',
+ 'username': "hs20-test",
+ 'password': "password",
+ 'domain': "example.org"})
+ dev[0].request("INTERWORKING_SELECT auto freq=2412")
+ ev = dev[0].wait_connected(timeout=15)
+ if bssid2 not in ev:
+ raise Exception("Connected to incorrect network")
+
+def test_ap_hs20_anqp_invalid_gas_response(dev, apdev):
+ """Hotspot 2.0 network selection and invalid GAS response"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ dev[0].hs20_enable()
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "test",
+ 'password': "secret",
+ 'domain': "example.com",
+ 'roaming_consortium': "112234",
+ 'eap': 'TTLS'})
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+
+ logger.info("ANQP: Unexpected Advertisement Protocol in response")
+ resp = action_response(query)
+ adv_proto = struct.pack('8B', 108, 6, 127, 0xdd, 0x00, 0x11, 0x22, 0x33)
+ data = struct.pack('<H', 0)
+ resp['payload'] = struct.pack('<BBBHH', ACTION_CATEG_PUBLIC,
+ GAS_INITIAL_RESPONSE,
+ gas['dialog_token'], 0, 0) + adv_proto + data
+ send_gas_resp(hapd, resp)
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No ANQP-QUERY-DONE seen")
+ if "result=INVALID_FRAME" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+
+ logger.info("ANQP: Invalid element length for Info ID 1234")
+ resp = action_response(query)
+ adv_proto = struct.pack('BBBB', 108, 2, 127, 0)
+ elements = struct.pack('<HH', 1234, 1)
+ data = struct.pack('<H', len(elements)) + elements
+ resp['payload'] = struct.pack('<BBBHH', ACTION_CATEG_PUBLIC,
+ GAS_INITIAL_RESPONSE,
+ gas['dialog_token'], 0, 0) + adv_proto + data
+ send_gas_resp(hapd, resp)
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No ANQP-QUERY-DONE seen")
+ if "result=INVALID_FRAME" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ with alloc_fail(dev[0], 1, "=anqp_add_extra"):
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+
+ resp = action_response(query)
+ elements = struct.pack('<HHHH', 1, 0, 1, 0)
+ data = struct.pack('<H', len(elements)) + elements
+ resp['payload'] = struct.pack('<BBBHH', ACTION_CATEG_PUBLIC,
+ GAS_INITIAL_RESPONSE,
+ gas['dialog_token'], 0, 0) + adv_proto + data
+ send_gas_resp(hapd, resp)
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No ANQP-QUERY-DONE seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc_copy;anqp_add_extra"):
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+
+ resp = action_response(query)
+ elements = struct.pack('<HHHH', 1, 0, 1, 0)
+ data = struct.pack('<H', len(elements)) + elements
+ resp['payload'] = struct.pack('<BBBHH', ACTION_CATEG_PUBLIC,
+ GAS_INITIAL_RESPONSE,
+ gas['dialog_token'], 0, 0) + adv_proto + data
+ send_gas_resp(hapd, resp)
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No ANQP-QUERY-DONE seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ tests = [struct.pack('<HH', 0xdddd, 0),
+ struct.pack('<HH3B', 0xdddd, 3, 0x50, 0x6f, 0x9a),
+ struct.pack('<HH4B', 0xdddd, 4, 0x50, 0x6f, 0x9a, 0),
+ struct.pack('<HH4B', 0xdddd, 4, 0x11, 0x22, 0x33, 0),
+ struct.pack('<HHHH', 1, 0, 1, 0)]
+ for elements in tests:
+ dev[0].request("INTERWORKING_SELECT freq=2412")
+
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+
+ resp = action_response(query)
+ data = struct.pack('<H', len(elements)) + elements
+ resp['payload'] = struct.pack('<BBBHH', ACTION_CATEG_PUBLIC,
+ GAS_INITIAL_RESPONSE,
+ gas['dialog_token'], 0, 0) + adv_proto + data
+ send_gas_resp(hapd, resp)
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No ANQP-QUERY-DONE seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+def test_ap_hs20_set_profile_failures(dev, apdev):
+ """Hotspot 2.0 and failures during profile configuration"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['anqp_3gpp_cell_net'] = "555,444"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'domain': "example.com",
+ 'username': "test",
+ 'password': "secret",
+ 'eap': 'TTLS'})
+ interworking_select(dev[0], bssid, "home", freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("NOTE ssid->eap.eap_methods = os_malloc()")
+ with alloc_fail(dev[0], 1, "interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].remove_cred(id)
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'domain': "example.com",
+ 'username': "hs20-test-with-domain@example.com",
+ 'password': "password"})
+ interworking_select(dev[0], bssid, "home", freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("NOTE anon = os_malloc()")
+ with alloc_fail(dev[0], 1, "interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE Successful connection with cred->username including realm")
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ dev[0].wait_connected()
+ dev[0].remove_cred(id)
+ dev[0].wait_disconnected()
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'domain': "example.com",
+ 'username': "hs20-test",
+ 'password': "password"})
+ interworking_select(dev[0], bssid, "home", freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("NOTE anon = os_malloc() (second)")
+ with alloc_fail(dev[0], 1, "interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ with alloc_fail(dev[0], 1, "wpa_config_add_network;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ with alloc_fail(dev[0], 1, "=interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set(eap)")
+ with alloc_fail(dev[0], 1, "wpa_config_parse_eap;wpa_config_set;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set(TTLS-NON_EAP_MSCHAPV2-phase2)")
+ with alloc_fail(dev[0], 1, "wpa_config_parse_str;wpa_config_set;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].remove_cred(id)
+
+ id = dev[0].add_cred_values({'roaming_consortium': "112233",
+ 'domain': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'eap': 'TTLS',
+ 'phase2': "auth=MSCHAPV2"})
+ interworking_select(dev[0], bssid, "home", freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("NOTE anon = os_strdup()")
+ with alloc_fail(dev[0], 2, "interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set_quoted(anonymous_identity)")
+ with alloc_fail(dev[0], 1, "=wpa_config_set_quoted;interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE Successful connection with cred->realm not included")
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ dev[0].wait_connected()
+ dev[0].remove_cred(id)
+ dev[0].wait_disconnected()
+
+ id = dev[0].add_cred_values({'roaming_consortium': "112233",
+ 'domain': "example.com",
+ 'realm': "example.com",
+ 'username': "user",
+ 'password': "password",
+ 'eap': 'PEAP'})
+ interworking_select(dev[0], bssid, "home", freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("NOTE id = os_strdup()")
+ with alloc_fail(dev[0], 2, "interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set_quoted(identity)")
+ with alloc_fail(dev[0], 1, "=wpa_config_set_quoted;interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].remove_cred(id)
+
+ id = dev[0].add_cred_values({'roaming_consortium': "112233",
+ 'domain': "example.com",
+ 'realm': "example.com",
+ 'username': "user",
+ 'password': "password",
+ 'eap': "TTLS"})
+ interworking_select(dev[0], bssid, "home", freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("NOTE wpa_config_set_quoted(identity) (second)")
+ with alloc_fail(dev[0], 2, "=wpa_config_set_quoted;interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set_quoted(password)")
+ with alloc_fail(dev[0], 3, "=wpa_config_set_quoted;interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ with alloc_fail(dev[0], 1, "wpa_config_add_network;interworking_connect_roaming_consortium"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ with alloc_fail(dev[0], 1, "=interworking_connect_roaming_consortium"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].remove_cred(id)
+
+ id = dev[0].add_cred_values({'roaming_consortium': "112233",
+ 'domain': "example.com",
+ 'realm': "example.com",
+ 'username': "user",
+ 'eap': "PEAP"})
+ dev[0].set_cred(id, "password", "ext:password")
+ interworking_select(dev[0], bssid, "home", freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("NOTE wpa_config_set(password)")
+ with alloc_fail(dev[0], 3, "wpa_config_set;interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ with alloc_fail(dev[0], 1, "interworking_set_hs20_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].remove_cred(id)
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'domain': "example.com",
+ 'username': "certificate-user",
+ 'phase1': "include_tls_length=0",
+ 'domain_suffix_match': "example.com",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'client_cert': "auth_serv/user.pem",
+ 'private_key': "auth_serv/user.key",
+ 'private_key_passwd': "secret"})
+ interworking_select(dev[0], bssid, "home", freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("NOTE wpa_config_set_quoted(client_cert)")
+ with alloc_fail(dev[0], 2, "=wpa_config_set_quoted;interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set_quoted(private_key)")
+ with alloc_fail(dev[0], 3, "=wpa_config_set_quoted;interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set_quoted(private_key_passwd)")
+ with alloc_fail(dev[0], 4, "=wpa_config_set_quoted;interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set_quoted(ca_cert)")
+ with alloc_fail(dev[0], 5, "=wpa_config_set_quoted;interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set_quoted(domain_suffix_match)")
+ with alloc_fail(dev[0], 6, "=wpa_config_set_quoted;interworking_set_eap_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ with alloc_fail(dev[0], 1, "interworking_set_hs20_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].remove_cred(id)
+
+ id = dev[0].add_cred_values({'imsi': "555444-333222111", 'eap': "SIM",
+ 'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123"})
+ interworking_select(dev[0], bssid, freq=2412)
+ dev[0].dump_monitor()
+ with alloc_fail(dev[0], 1, "interworking_set_hs20_params"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set_quoted(password;milenage)")
+ with alloc_fail(dev[0], 2, "=wpa_config_set_quoted;interworking_connect_3gpp"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set(eap)")
+ with alloc_fail(dev[0], 1, "wpa_config_parse_eap;wpa_config_set;interworking_connect_3gpp"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE set_root_nai:wpa_config_set(identity)")
+ with alloc_fail(dev[0], 1, "wpa_config_parse_str;interworking_connect_3gpp"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].remove_cred(id)
+
+ id = dev[0].add_cred_values({'roaming_consortium': "112233",
+ 'eap': 'TTLS',
+ 'username': "user@example.com",
+ 'password': "password"})
+ interworking_select(dev[0], bssid, freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("NOTE Interworking: No EAP method set for credential using roaming consortium")
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ dev[0].remove_cred(id)
+
+ hapd.disable()
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,25[3:26]"
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'domain': "example.com",
+ 'username': "hs20-test",
+ 'password': "password"})
+ interworking_select(dev[0], bssid, freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("NOTE wpa_config_set(PEAP/FAST-phase1)")
+ with alloc_fail(dev[0], 1, "wpa_config_parse_str;wpa_config_set;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set(PEAP/FAST-pac_interworking)")
+ with alloc_fail(dev[0], 2, "wpa_config_parse_str;wpa_config_set;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("NOTE wpa_config_set(PEAP/FAST-phase2)")
+ with alloc_fail(dev[0], 3, "wpa_config_parse_str;wpa_config_set;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ hapd.disable()
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,21"
+ hapd = hostapd.add_ap(apdev[0], params)
+ interworking_select(dev[0], bssid, freq=2412)
+ dev[0].request("NOTE wpa_config_set(TTLS-defaults-phase2)")
+ with alloc_fail(dev[0], 1, "wpa_config_parse_str;wpa_config_set;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ hapd.disable()
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,21[2:3]"
+ hapd = hostapd.add_ap(apdev[0], params)
+ interworking_select(dev[0], bssid, freq=2412)
+ dev[0].request("NOTE wpa_config_set(TTLS-NON_EAP_MSCHAP-phase2)")
+ with alloc_fail(dev[0], 1, "wpa_config_parse_str;wpa_config_set;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ hapd.disable()
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,21[2:2]"
+ hapd = hostapd.add_ap(apdev[0], params)
+ interworking_select(dev[0], bssid, freq=2412)
+ dev[0].request("NOTE wpa_config_set(TTLS-NON_EAP_CHAP-phase2)")
+ with alloc_fail(dev[0], 1, "wpa_config_parse_str;wpa_config_set;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ hapd.disable()
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,21[2:1]"
+ hapd = hostapd.add_ap(apdev[0], params)
+ interworking_select(dev[0], bssid, freq=2412)
+ dev[0].request("NOTE wpa_config_set(TTLS-NON_EAP_PAP-phase2)")
+ with alloc_fail(dev[0], 1, "wpa_config_parse_str;wpa_config_set;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ hapd.disable()
+ params = hs20_ap_params()
+ params['nai_realm'] = "0,example.com,21[3:26]"
+ hapd = hostapd.add_ap(apdev[0], params)
+ interworking_select(dev[0], bssid, freq=2412)
+ dev[0].request("NOTE wpa_config_set(TTLS-EAP-MSCHAPV2-phase2)")
+ with alloc_fail(dev[0], 1, "wpa_config_parse_str;wpa_config_set;interworking_connect"):
+ dev[0].request("INTERWORKING_CONNECT " + bssid)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ dev[0].remove_cred(id)
+
+def test_ap_hs20_unexpected(dev, apdev):
+ """Unexpected Hotspot 2.0 AP configuration"""
+ skip_without_tkip(dev[0])
+ skip_without_tkip(dev[1])
+ skip_without_tkip(dev[2])
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa_eap_params(ssid="test-hs20-fake")
+ params['wpa'] = "3"
+ params['wpa_pairwise'] = "TKIP CCMP"
+ params['rsn_pairwise'] = "CCMP"
+ params['ieee80211w'] = "1"
+ #params['vendor_elements'] = 'dd07506f9a10140000'
+ params['vendor_elements'] = 'dd04506f9a10'
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("test-hs20-fake", key_mgmt="WPA-EAP", eap="TTLS",
+ pairwise="TKIP",
+ identity="hs20-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ scan_freq="2412")
+
+ dev[1].hs20_enable()
+ dev[1].scan_for_bss(bssid, freq="2412")
+ dev[1].connect("test-hs20-fake", key_mgmt="WPA-EAP", eap="TTLS",
+ proto="WPA",
+ identity="hs20-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ scan_freq="2412")
+
+ dev[2].hs20_enable()
+ dev[2].scan_for_bss(bssid, freq="2412")
+ dev[2].connect("test-hs20-fake", key_mgmt="WPA-EAP", eap="TTLS",
+ ieee80211w="1",
+ proto="RSN", pairwise="CCMP",
+ identity="hs20-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ scan_freq="2412")
+
+def test_ap_interworking_element_update(dev, apdev):
+ """Dynamic Interworking element update"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ bss = dev[0].get_bss(bssid)
+ logger.info("Before update: " + str(bss))
+ if '6b091e0701020000000300' not in bss['ie']:
+ raise Exception("Expected Interworking element not seen before update")
+
+ # Update configuration parameters related to Interworking element
+ hapd.set('access_network_type', '2')
+ hapd.set('asra', '1')
+ hapd.set('esr', '1')
+ hapd.set('uesa', '1')
+ hapd.set('venue_group', '2')
+ hapd.set('venue_type', '8')
+ if "OK" not in hapd.request("UPDATE_BEACON"):
+ raise Exception("UPDATE_BEACON failed")
+ dev[0].request("BSS_FLUSH 0")
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ bss = dev[0].get_bss(bssid)
+ logger.info("After update: " + str(bss))
+ if '6b09f20208020000000300' not in bss['ie']:
+ raise Exception("Expected Interworking element not seen after update")
+
+def test_ap_hs20_terms_and_conditions(dev, apdev):
+ """Hotspot 2.0 Terms and Conditions signaling"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['hs20_t_c_filename'] = 'terms-and-conditions'
+ params['hs20_t_c_timestamp'] = '123456789'
+
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].connect("test-hs20", proto="RSN", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="hs20-t-c-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ ieee80211w='2', scan_freq="2412")
+ ev = dev[0].wait_event(["HS20-T-C-ACCEPTANCE"], timeout=5)
+ if ev is None:
+ raise Exception("Terms and Conditions Acceptance notification not received")
+ url = "https://example.com/t_and_c?addr=%s&ap=123" % dev[0].own_addr()
+ if url not in ev:
+ raise Exception("Unexpected URL: " + ev)
+
+def test_ap_hs20_terms_and_conditions_coa(dev, apdev):
+ """Hotspot 2.0 Terms and Conditions signaling - CoA"""
+ try:
+ import pyrad.client
+ import pyrad.packet
+ import pyrad.dictionary
+ import radius_das
+ except ImportError:
+ raise HwsimSkip("No pyrad modules available")
+
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['hs20_t_c_filename'] = 'terms-and-conditions'
+ params['hs20_t_c_timestamp'] = '123456789'
+ params['own_ip_addr'] = "127.0.0.1"
+ params['radius_das_port'] = "3799"
+ params['radius_das_client'] = "127.0.0.1 secret"
+ params['radius_das_require_event_timestamp'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].connect("test-hs20", proto="RSN", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="hs20-t-c-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ ieee80211w='2', scan_freq="2412")
+
+ ev = hapd.wait_event(["HS20-T-C-FILTERING-ADD"], timeout=5)
+ if ev is None:
+ raise Exception("Terms and Conditions filtering not enabled")
+ if ev.split(' ')[1] != dev[0].own_addr():
+ raise Exception("Unexpected STA address for filtering: " + ev)
+
+ ev = dev[0].wait_event(["HS20-T-C-ACCEPTANCE"], timeout=5)
+ if ev is None:
+ raise Exception("Terms and Conditions Acceptance notification not received")
+ url = "https://example.com/t_and_c?addr=%s&ap=123" % dev[0].own_addr()
+ if url not in ev:
+ raise Exception("Unexpected URL: " + ev)
+
+ dict = pyrad.dictionary.Dictionary("dictionary.radius")
+
+ srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
+ secret=b"secret", dict=dict)
+ srv.retries = 1
+ srv.timeout = 1
+
+ sta = hapd.get_sta(dev[0].own_addr())
+ multi_sess_id = sta['authMultiSessionId']
+
+ logger.info("CoA-Request with matching Acct-Session-Id")
+ vsa = binascii.unhexlify('00009f68090600000000')
+ req = radius_das.CoAPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ Acct_Multi_Session_Id=multi_sess_id,
+ Chargeable_User_Identity="hs20-cui",
+ Event_Timestamp=int(time.time()),
+ Vendor_Specific=vsa)
+ reply = srv.SendPacket(req)
+ logger.debug("RADIUS response from hostapd")
+ for i in list(reply.keys()):
+ logger.debug("%s: %s" % (i, reply[i]))
+ if reply.code != pyrad.packet.CoAACK:
+ raise Exception("CoA-Request failed")
+
+ ev = hapd.wait_event(["HS20-T-C-FILTERING-REMOVE"], timeout=5)
+ if ev is None:
+ raise Exception("Terms and Conditions filtering not disabled")
+ if ev.split(' ')[1] != dev[0].own_addr():
+ raise Exception("Unexpected STA address for filtering: " + ev)
+
+def test_ap_hs20_terms_and_conditions_sql(dev, apdev, params):
+ """Hotspot 2.0 Terms and Conditions using SQLite for user DB"""
+ addr = dev[0].own_addr()
+ run_ap_hs20_terms_and_conditions_sql(dev, apdev, params,
+ "https://example.com/t_and_c?addr=@1@&ap=123",
+ "https://example.com/t_and_c?addr=" + addr + "&ap=123")
+
+def test_ap_hs20_terms_and_conditions_sql2(dev, apdev, params):
+ """Hotspot 2.0 Terms and Conditions using SQLite for user DB"""
+ addr = dev[0].own_addr()
+ run_ap_hs20_terms_and_conditions_sql(dev, apdev, params,
+ "https://example.com/t_and_c?addr=@1@",
+ "https://example.com/t_and_c?addr=" + addr)
+
+def run_ap_hs20_terms_and_conditions_sql(dev, apdev, params, url_template,
+ url_expected):
+ check_eap_capa(dev[0], "MSCHAPV2")
+ try:
+ import sqlite3
+ except ImportError:
+ raise HwsimSkip("No sqlite3 module available")
+ dbfile = params['prefix'] + ".eap-user.db"
+ try:
+ os.remove(dbfile)
+ except:
+ pass
+ con = sqlite3.connect(dbfile)
+ with con:
+ cur = con.cursor()
+ cur.execute("CREATE TABLE users(identity TEXT PRIMARY KEY, methods TEXT, password TEXT, remediation TEXT, phase2 INTEGER, t_c_timestamp INTEGER)")
+ cur.execute("CREATE TABLE wildcards(identity TEXT PRIMARY KEY, methods TEXT)")
+ cur.execute("INSERT INTO users(identity,methods,password,phase2) VALUES ('user-mschapv2','TTLS-MSCHAPV2','password',1)")
+ cur.execute("INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS')")
+ cur.execute("CREATE TABLE authlog(timestamp TEXT, session TEXT, nas_ip TEXT, username TEXT, note TEXT)")
+ cur.execute("CREATE TABLE pending_tc(mac_addr TEXT PRIMARY KEY, identity TEXT)")
+ cur.execute("CREATE TABLE current_sessions(mac_addr TEXT PRIMARY KEY, identity TEXT, start_time TEXT, nas TEXT, hs20_t_c_filtering BOOLEAN, waiting_coa_ack BOOLEAN, coa_ack_received BOOLEAN)")
+
+
+ try:
+ params = {"ssid": "as", "beacon_int": "2000",
+ "radius_server_clients": "auth_serv/radius_clients.conf",
+ "radius_server_auth_port": '18128',
+ "eap_server": "1",
+ "eap_user_file": "sqlite:" + dbfile,
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key"}
+ params['hs20_t_c_server_url'] = url_template
+ authsrv = hostapd.add_ap(apdev[1], params)
+
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['auth_server_port'] = "18128"
+ params['hs20_t_c_filename'] = 'terms-and-conditions'
+ params['hs20_t_c_timestamp'] = '123456789'
+ params['own_ip_addr'] = "127.0.0.1"
+ params['radius_das_port'] = "3799"
+ params['radius_das_client'] = "127.0.0.1 radius"
+ params['radius_das_require_event_timestamp'] = "1"
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET pmf 1")
+ dev[0].hs20_enable()
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "user-mschapv2",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem"})
+ interworking_select(dev[0], bssid, freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+
+ ev = hapd.wait_event(["HS20-T-C-FILTERING-ADD"], timeout=5)
+ if ev is None:
+ raise Exception("Terms and Conditions filtering not enabled")
+ hapd.dump_monitor()
+
+ ev = dev[0].wait_event(["HS20-T-C-ACCEPTANCE"], timeout=5)
+ if ev is None:
+ raise Exception("Terms and Conditions Acceptance notification not received")
+ url = ev.split(' ')[1]
+ if url != url_expected:
+ raise Exception("Unexpected URL delivered to the client: %s (expected %s)" % (url, url_expected))
+ dev[0].dump_monitor()
+
+ with con:
+ cur = con.cursor()
+ cur.execute("SELECT * from current_sessions")
+ rows = cur.fetchall()
+ if len(rows) != 1:
+ raise Exeception("Unexpected number of rows in current_sessions (%d; expected %d)" % (len(rows), 1))
+ logger.info("current_sessions: " + str(rows))
+
+ tests = ["foo", "disconnect q", "coa %s" % dev[0].own_addr()]
+ for t in tests:
+ if "FAIL" not in authsrv.request("DAC_REQUEST " + t):
+ raise Exception("Invalid DAC_REQUEST accepted: " + t)
+ if "OK" not in authsrv.request("DAC_REQUEST coa %s t_c_clear" % dev[0].own_addr()):
+ raise Exception("DAC_REQUEST failed")
+
+ ev = hapd.wait_event(["HS20-T-C-FILTERING-REMOVE"], timeout=5)
+ if ev is None:
+ raise Exception("Terms and Conditions filtering not disabled")
+ if ev.split(' ')[1] != dev[0].own_addr():
+ raise Exception("Unexpected STA address for filtering: " + ev)
+
+ time.sleep(0.2)
+ with con:
+ cur = con.cursor()
+ cur.execute("SELECT * from current_sessions")
+ rows = cur.fetchall()
+ if len(rows) != 1:
+ raise Exeception("Unexpected number of rows in current_sessions (%d; expected %d)" % (len(rows), 1))
+ logger.info("current_sessions: " + str(rows))
+ if rows[0][4] != 0 or rows[0][5] != 0 or rows[0][6] != 1:
+ raise Exception("Unexpected current_sessions information after CoA-ACK")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ # Simulate T&C server operation on user reading the updated version
+ with con:
+ cur = con.cursor()
+ cur.execute("SELECT identity FROM pending_tc WHERE mac_addr='" +
+ dev[0].own_addr() + "'")
+ rows = cur.fetchall()
+ if len(rows) != 1:
+ raise Exception("No pending_tc entry found")
+ if rows[0][0] != 'user-mschapv2':
+ raise Exception("Unexpected pending_tc identity value")
+
+ cur.execute("UPDATE users SET t_c_timestamp=123456789 WHERE identity='user-mschapv2'")
+
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+ ev = hapd.wait_event(["HS20-T-C-FILTERING-ADD"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Terms and Conditions filtering enabled unexpectedly")
+ hapd.dump_monitor()
+
+ ev = dev[0].wait_event(["HS20-T-C-ACCEPTANCE"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected Terms and Conditions Acceptance notification")
+ dev[0].dump_monitor()
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ # New T&C available
+ hapd.set('hs20_t_c_timestamp', '123456790')
+
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+ ev = hapd.wait_event(["HS20-T-C-FILTERING-ADD"], timeout=5)
+ if ev is None:
+ raise Exception("Terms and Conditions filtering not enabled")
+ hapd.dump_monitor()
+
+ ev = dev[0].wait_event(["HS20-T-C-ACCEPTANCE"], timeout=5)
+ if ev is None:
+ raise Exception("Terms and Conditions Acceptance notification not received (2)")
+ dev[0].dump_monitor()
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ # Simulate T&C server operation on user reading the updated version
+ with con:
+ cur = con.cursor()
+ cur.execute("UPDATE users SET t_c_timestamp=123456790 WHERE identity='user-mschapv2'")
+
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+ ev = hapd.wait_event(["HS20-T-C-FILTERING-ADD"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Terms and Conditions filtering enabled unexpectedly")
+ hapd.dump_monitor()
+
+ ev = dev[0].wait_event(["HS20-T-C-ACCEPTANCE"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected Terms and Conditions Acceptance notification (2)")
+ dev[0].dump_monitor()
+ finally:
+ os.remove(dbfile)
+ dev[0].request("SET pmf 0")
+
+def test_ap_hs20_release_number_1(dev, apdev):
+ """Hotspot 2.0 with AP claiming support for Release 1"""
+ run_ap_hs20_release_number(dev, apdev, 1)
+
+def test_ap_hs20_release_number_2(dev, apdev):
+ """Hotspot 2.0 with AP claiming support for Release 2"""
+ run_ap_hs20_release_number(dev, apdev, 2)
+
+def test_ap_hs20_release_number_3(dev, apdev):
+ """Hotspot 2.0 with AP claiming support for Release 3"""
+ run_ap_hs20_release_number(dev, apdev, 3)
+
+def run_ap_hs20_release_number(dev, apdev, release):
+ check_eap_capa(dev[0], "MSCHAPV2")
+ eap_test(dev[0], apdev[0], "21[3:26][6:7][99:99]", "TTLS", "user",
+ release=release)
+ rel = dev[0].get_status_field('hs20')
+ if rel != str(release):
+ raise Exception("Unexpected release number indicated: " + rel)
+
+def test_ap_hs20_missing_pmf(dev, apdev):
+ """Hotspot 2.0 connection attempt without PMF"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['disable_dgaf'] = '1'
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].connect("test-hs20", proto="RSN", key_mgmt="WPA-EAP", eap="TTLS",
+ ieee80211w="0",
+ identity="hs20-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ scan_freq="2412", update_identifier="54321",
+ roaming_consortium_selection="1020304050",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Association rejection not reported")
+ if "status_code=31" not in ev:
+ raise Exception("Unexpected rejection reason: " + ev)
+
+def test_ap_hs20_open_osu_association(dev, apdev):
+ """Hotspot 2.0 open OSU association"""
+ try:
+ run_ap_hs20_open_osu_association(dev, apdev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def run_ap_hs20_open_osu_association(dev, apdev):
+ params = {"ssid": "HS 2.0 OSU open"}
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("HS 2.0 OSU open", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ # Test with unexpected Hotspot 2.0 Indication element in Assoc Req
+ dev[0].request("VENDOR_ELEM_ADD 13 dd07506f9a10220000")
+ dev[0].connect("HS 2.0 OSU open", key_mgmt="NONE", scan_freq="2412")
diff --git a/contrib/wpa/tests/hwsim/test_ap_ht.py b/contrib/wpa/tests/hwsim/test_ap_ht.py
new file mode 100644
index 000000000000..510fe0836fc5
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_ht.py
@@ -0,0 +1,1644 @@
+# Test cases for HT operations with hostapd
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import logging
+logger = logging.getLogger()
+import struct
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+import hwsim_utils
+
+def test_ap_ht40_scan(dev, apdev):
+ """HT40 co-ex scan"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "test-ht40",
+ "channel": "5",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2432":
+ raise Exception("Unexpected frequency")
+ pri = hapd.get_status_field("channel")
+ if pri != "5":
+ raise Exception("Unexpected primary channel")
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "-1":
+ raise Exception("Unexpected secondary channel")
+
+ status = hapd.get_status()
+ logger.info("hostapd STATUS: " + str(status))
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+ sta = hapd.get_sta(dev[0].own_addr())
+ logger.info("hostapd STA: " + str(sta))
+
+ res = dev[0].request("SIGNAL_POLL")
+ logger.info("STA SIGNAL_POLL:\n" + res.strip())
+ sig = res.splitlines()
+ if "WIDTH=40 MHz" not in sig:
+ raise Exception("Not a 40 MHz connection")
+
+ if 'supp_op_classes' not in sta or len(sta['supp_op_classes']) < 2:
+ raise Exception("No Supported Operating Classes information for STA")
+ opclass = int(sta['supp_op_classes'][0:2], 16)
+ if opclass != 84:
+ raise Exception("Unexpected Current Operating Class from STA: %d" % opclass)
+
+def test_ap_ht_wifi_generation(dev, apdev):
+ """HT and wifi_generation"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "test-ht",
+ "channel": "6"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-ht", key_mgmt="NONE", scan_freq="2437")
+ status = dev[0].get_status()
+ if 'wifi_generation' not in status:
+ # For now, assume this is because of missing kernel support
+ raise HwsimSkip("Association Request IE reporting not supported")
+ #raise Exception("Missing wifi_generation information")
+ if status['wifi_generation'] != "4":
+ raise Exception("Unexpected wifi_generation value: " + status['wifi_generation'])
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("test-ht", key_mgmt="NONE", scan_freq="2437")
+ status = wpas.get_status()
+ if 'wifi_generation' not in status:
+ # For now, assume this is because of missing kernel support
+ raise HwsimSkip("Association Request IE reporting not supported")
+ #raise Exception("Missing wifi_generation information (connect)")
+ if status['wifi_generation'] != "4":
+ raise Exception("Unexpected wifi_generation value (connect): " + status['wifi_generation'])
+
+@remote_compatible
+def test_ap_ht40_scan_conflict(dev, apdev):
+ """HT40 co-ex scan conflict"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "test-ht40",
+ "channel": "6",
+ "ht_capab": "[HT40+]"}
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-ht40",
+ "channel": "5",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2432":
+ raise Exception("Unexpected frequency")
+ pri = hapd.get_status_field("channel")
+ if pri != "5":
+ raise Exception("Unexpected primary channel")
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+@remote_compatible
+def test_ap_ht40_scan_conflict2(dev, apdev):
+ """HT40 co-ex scan conflict (HT40-)"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "test-ht40",
+ "channel": "11",
+ "ht_capab": "[HT40-]"}
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-ht40",
+ "channel": "1",
+ "ht_capab": "[HT40+]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2412":
+ raise Exception("Unexpected frequency")
+ pri = hapd.get_status_field("channel")
+ if pri != "1":
+ raise Exception("Unexpected primary channel")
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_scan_not_affected(dev, apdev):
+ """HT40 co-ex scan and other BSS not affected"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "test-ht20",
+ "channel": "11"}
+ hostapd.add_ap(apdev[1], params)
+
+ hostapd.cmd_execute(apdev[0], ['ifconfig', apdev[0]['ifname'], 'up'])
+ hostapd.cmd_execute(apdev[0], ['iw', apdev[0]['ifname'], 'scan', 'trigger',
+ 'freq', '2462'])
+ time.sleep(0.5)
+ hostapd.cmd_execute(apdev[0], ['iw', apdev[0]['ifname'], 'scan', 'dump'])
+ time.sleep(0.1)
+ hostapd.cmd_execute(apdev[0], ['ifconfig', apdev[0]['ifname'], 'down'])
+
+ params = {"ssid": "test-ht40",
+ "channel": "1",
+ "ht_capab": "[HT40+]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2412":
+ raise Exception("Unexpected frequency")
+ pri = hapd.get_status_field("channel")
+ if pri != "1":
+ raise Exception("Unexpected primary channel")
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "1":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+@remote_compatible
+def test_ap_ht40_scan_legacy_conflict(dev, apdev):
+ """HT40 co-ex scan conflict with legacy 20 MHz AP"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "legacy-20",
+ "channel": "7", "ieee80211n": "0"}
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-ht40",
+ "channel": "5",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2432":
+ raise Exception("Unexpected frequency: " + freq)
+ pri = hapd.get_status_field("channel")
+ if pri != "5":
+ raise Exception("Unexpected primary channel: " + pri)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+@remote_compatible
+def test_ap_ht40_scan_ht20_conflict(dev, apdev):
+ """HT40 co-ex scan conflict with HT 20 MHz AP"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "ht-20",
+ "channel": "7", "ieee80211n": "1"}
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-ht40",
+ "channel": "5",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2432":
+ raise Exception("Unexpected frequency: " + freq)
+ pri = hapd.get_status_field("channel")
+ if pri != "5":
+ raise Exception("Unexpected primary channel: " + pri)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_scan_intolerant(dev, apdev):
+ """HT40 co-ex scan finding an AP advertising 40 MHz intolerant"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "another-bss",
+ "channel": "1",
+ "ht_capab": "[40-INTOLERANT]"}
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-ht40",
+ "channel": "1",
+ "ht_capab": "[HT40+]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2412":
+ raise Exception("Unexpected frequency: " + freq)
+ pri = hapd.get_status_field("channel")
+ if pri != "1":
+ raise Exception("Unexpected primary channel: " + pri)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_scan_match(dev, apdev):
+ """HT40 co-ex scan matching configuration"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "test-ht40",
+ "channel": "5",
+ "ht_capab": "[HT40-]"}
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-ht40",
+ "channel": "5",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2432":
+ raise Exception("Unexpected frequency")
+ pri = hapd.get_status_field("channel")
+ if pri != "5":
+ raise Exception("Unexpected primary channel")
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "-1":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_5ghz_match(dev, apdev):
+ """HT40 co-ex scan on 5 GHz with matching pri/sec channel"""
+ clear_scan_cache(apdev[0])
+ try:
+ hapd = None
+ hapd2 = None
+ params = {"ssid": "test-ht40",
+ "hw_mode": "a",
+ "channel": "36",
+ "country_code": "US",
+ "ht_capab": "[HT40+]"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-ht40",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "5180":
+ raise Exception("Unexpected frequency")
+ pri = hapd.get_status_field("channel")
+ if pri != "36":
+ raise Exception("Unexpected primary channel")
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "1":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+ finally:
+ dev[0].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ if hapd2:
+ hapd2.request("DISABLE")
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].flush_scan_cache()
+
+def test_ap_ht40_5ghz_switch(dev, apdev):
+ """HT40 co-ex scan on 5 GHz switching pri/sec channel"""
+ clear_scan_cache(apdev[0])
+ try:
+ hapd = None
+ hapd2 = None
+ params = {"ssid": "test-ht40",
+ "hw_mode": "a",
+ "channel": "36",
+ "country_code": "US",
+ "ht_capab": "[HT40+]"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-ht40",
+ "hw_mode": "a",
+ "channel": "40",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "5180":
+ raise Exception("Unexpected frequency: " + freq)
+ pri = hapd.get_status_field("channel")
+ if pri != "36":
+ raise Exception("Unexpected primary channel: " + pri)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "1":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+ finally:
+ dev[0].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ if hapd2:
+ hapd2.request("DISABLE")
+ set_world_reg(apdev[0], apdev[1], dev[0])
+
+def test_ap_ht40_5ghz_switch2(dev, apdev):
+ """HT40 co-ex scan on 5 GHz switching pri/sec channel (2)"""
+ clear_scan_cache(apdev[0])
+ try:
+ hapd = None
+ hapd2 = None
+ params = {"ssid": "test-ht40",
+ "hw_mode": "a",
+ "channel": "36",
+ "country_code": "US",
+ "ht_capab": "[HT40+]"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "5200")
+ dev[0].set_network(id, "scan_freq", "5200")
+ dev[0].select_network(id)
+ time.sleep(1)
+
+ params = {"ssid": "test-ht40",
+ "hw_mode": "a",
+ "channel": "40",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "5180":
+ raise Exception("Unexpected frequency: " + freq)
+ pri = hapd.get_status_field("channel")
+ if pri != "36":
+ raise Exception("Unexpected primary channel: " + pri)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "1":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+ finally:
+ dev[0].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ if hapd2:
+ hapd2.request("DISABLE")
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].flush_scan_cache()
+
+def test_obss_scan(dev, apdev):
+ """Overlapping BSS scan request"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "obss-scan",
+ "channel": "6",
+ "ht_capab": "[HT40-]",
+ "obss_interval": "10"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "another-bss",
+ "channel": "9",
+ "ieee80211n": "0"}
+ hostapd.add_ap(apdev[1], params)
+ run_obss_scan(hapd, dev)
+
+def test_obss_scan_ht40_plus(dev, apdev):
+ """Overlapping BSS scan request (HT40+)"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "obss-scan",
+ "channel": "6",
+ "ht_capab": "[HT40+]",
+ "obss_interval": "10"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "another-bss",
+ "channel": "9",
+ "ieee80211n": "0"}
+ hostapd.add_ap(apdev[1], params)
+ run_obss_scan(hapd, dev, ht40plus=True)
+
+def run_obss_scan(hapd, dev, ht40plus=False):
+ dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
+ res = dev[0].request("SIGNAL_POLL")
+ logger.info("SIGNAL_POLL:\n" + res)
+ sig = res.splitlines()
+ if "FREQUENCY=2437" not in sig:
+ raise Exception("Unexpected frequency")
+ if "WIDTH=40 MHz" not in sig:
+ raise Exception("Not a 40 MHz connection")
+ if ht40plus and "CENTER_FRQ1=2447" not in sig:
+ raise Exception("Not HT40+")
+ if not ht40plus and "CENTER_FRQ1=2427" not in sig:
+ raise Exception("Not HT40-")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ logger.info("Waiting for OBSS scan to occur")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timed out while waiting for OBSS scan to start")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Timed out while waiting for OBSS scan results")
+ received = False
+ for i in range(0, 4):
+ frame = hapd.mgmt_rx(timeout=5)
+ if frame is None:
+ raise Exception("MGMT RX wait timed out")
+ if frame['subtype'] != 13:
+ continue
+ payload = frame['payload']
+ if len(payload) < 3:
+ continue
+ (category, action, ie) = struct.unpack('BBB', payload[0:3])
+ if category != 4:
+ continue
+ if action != 0:
+ continue
+ if ie == 72:
+ logger.info("20/40 BSS Coexistence report received")
+ received = True
+ break
+ if not received:
+ raise Exception("20/40 BSS Coexistence report not seen")
+
+def test_obss_scan_40_intolerant(dev, apdev):
+ """Overlapping BSS scan request with 40 MHz intolerant AP"""
+ params = {"ssid": "obss-scan",
+ "channel": "6",
+ "ht_capab": "[HT40-]",
+ "obss_interval": "10"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "another-bss",
+ "channel": "7",
+ "ht_capab": "[40-INTOLERANT]"}
+ hostapd.add_ap(apdev[1], params)
+
+ dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ logger.info("Waiting for OBSS scan to occur")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timed out while waiting for OBSS scan to start")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Timed out while waiting for OBSS scan results")
+ received = False
+ for i in range(0, 4):
+ frame = hapd.mgmt_rx(timeout=5)
+ if frame is None:
+ raise Exception("MGMT RX wait timed out")
+ if frame['subtype'] != 13:
+ continue
+ payload = frame['payload']
+ if len(payload) < 3:
+ continue
+ (category, action, ie) = struct.unpack('BBB', payload[0:3])
+ if category != 4:
+ continue
+ if action != 0:
+ continue
+ if ie == 72:
+ logger.info("20/40 BSS Coexistence report received")
+ received = True
+ break
+ if not received:
+ raise Exception("20/40 BSS Coexistence report not seen")
+
+def test_obss_coex_report_handling(dev, apdev):
+ """Overlapping BSS scan report handling with obss_interval=0"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "obss-scan",
+ "channel": "6",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
+
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "-1":
+ raise Exception("AP is not using 40 MHz channel")
+
+ # 20/40 MHz co-ex report tests: number of invalid reports and a valid report
+ # that forces 20 MHz channel.
+ tests = ['0400', '040048', '04004801', '0400480000', '0400490100',
+ '040048ff0000', '04004801ff49ff00', '04004801004900',
+ '0400480100490101', '0400480100490201ff',
+ '040048010449020005']
+ for msg in tests:
+ req = "MGMT_TX {} {} freq=2437 action={}".format(bssid, bssid, msg)
+ if "OK" not in dev[0].request(req):
+ raise Exception("Could not send management frame")
+ time.sleep(0.5)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("AP did not move to 20 MHz channel")
+
+def test_obss_coex_report_handling1(dev, apdev):
+ """Overlapping BSS scan report handling with obss_interval=1"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "obss-scan",
+ "channel": "6",
+ "ht_capab": "[HT40+]",
+ "obss_interval": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
+
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "1":
+ raise Exception("AP is not using 40 MHz channel")
+
+ # 20/40 MHz co-ex report forcing 20 MHz channel
+ msg = '040048010449020005'
+ req = "MGMT_TX {} {} freq=2437 action={}".format(bssid, bssid, msg)
+ if "OK" not in dev[0].request(req):
+ raise Exception("Could not send management frame")
+ time.sleep(0.5)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("AP did not move to 20 MHz channel")
+
+ # No 20/40 MHz co-ex reports forcing 20 MHz channel during next interval
+ for i in range(20):
+ sec = hapd.get_status_field("secondary_channel")
+ if sec == "1":
+ break
+ time.sleep(0.5)
+ if sec != "1":
+ raise Exception("AP did not return to 40 MHz channel")
+
+def test_obss_coex_report_handling2(dev, apdev):
+ """Overlapping BSS scan report handling with obss_interval=1 and no overlap"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "obss-scan",
+ "channel": "6",
+ "ht_capab": "[HT40+]",
+ "obss_interval": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("obss-scan", key_mgmt="NONE", scan_freq="2437")
+
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "1":
+ raise Exception("AP is not using 40 MHz channel")
+
+ # 20/40 MHz co-ex report that does not force a move to 20 MHz channel
+ # (out of affected range and matching primary channel cases)
+ msg = '0400' + '480100' + '49020001' + '49020006'
+ req = "MGMT_TX {} {} freq=2437 action={}".format(bssid, bssid, msg)
+ if "OK" not in dev[0].request(req):
+ raise Exception("Could not send management frame")
+ time.sleep(0.5)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "1":
+ raise Exception("Unexpected move to 20 MHz channel")
+
+ # 20/40 MHz co-ex report forcing 20 MHz channel
+ # (out of affected range and in affected range but not matching primary)
+ msg = '0400' + '480100' + '4903000105'
+ req = "MGMT_TX {} {} freq=2437 action={}".format(bssid, bssid, msg)
+ if "OK" not in dev[0].request(req):
+ raise Exception("Could not send management frame")
+ time.sleep(0.5)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("AP did not move to 20 MHz channel")
+
+def test_olbc(dev, apdev):
+ """OLBC detection"""
+ params = {"ssid": "test-olbc",
+ "channel": "6",
+ "ht_capab": "[HT40-]",
+ "ap_table_expiration_time": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ status = hapd.get_status()
+ if status['olbc'] != '0' or status['olbc_ht'] != '0':
+ raise Exception("Unexpected OLBC information")
+
+ params = {"ssid": "olbc-ap",
+ "hw_mode": "b",
+ "channel": "6",
+ "wmm_enabled": "0"}
+ hostapd.add_ap(apdev[1], params)
+ time.sleep(0.5)
+ status = hapd.get_status()
+ if status['olbc'] != '1' or status['olbc_ht'] != '1':
+ raise Exception("Missing OLBC information")
+
+ hostapd.remove_bss(apdev[1])
+
+ logger.info("Waiting for OLBC state to time out")
+ cleared = False
+ for i in range(0, 15):
+ time.sleep(1)
+ status = hapd.get_status()
+ if status['olbc'] == '0' and status['olbc_ht'] == '0':
+ cleared = True
+ break
+ if not cleared:
+ raise Exception("OLBC state did nto time out")
+
+def test_olbc_table_limit(dev, apdev):
+ """OLBC AP table size limit"""
+ ifname1 = apdev[0]['ifname']
+ ifname2 = apdev[0]['ifname'] + '-2'
+ ifname3 = apdev[0]['ifname'] + '-3'
+ hostapd.add_bss(apdev[0], ifname1, 'bss-1.conf')
+ hostapd.add_bss(apdev[0], ifname2, 'bss-2.conf')
+ hostapd.add_bss(apdev[0], ifname3, 'bss-3.conf')
+
+ params = {"ssid": "test-olbc",
+ "channel": "1",
+ "ap_table_max_size": "2"}
+ hapd = hostapd.add_ap(apdev[1], params)
+
+ time.sleep(0.3)
+ with alloc_fail(hapd, 1, "ap_list_process_beacon"):
+ time.sleep(0.3)
+ hapd.set("ap_table_max_size", "1")
+ time.sleep(0.3)
+ hapd.set("ap_table_max_size", "0")
+ time.sleep(0.3)
+
+def test_olbc_5ghz(dev, apdev):
+ """OLBC detection on 5 GHz"""
+ try:
+ hapd = None
+ hapd2 = None
+ params = {"ssid": "test-olbc",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ status = hapd.get_status()
+ if status['olbc'] != '0' or status['olbc_ht'] != '0':
+ raise Exception("Unexpected OLBC information")
+
+ params = {"ssid": "olbc-ap",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ieee80211n": "0",
+ "wmm_enabled": "0"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ found = False
+ for i in range(20):
+ time.sleep(0.1)
+ status = hapd.get_status()
+ logger.debug('olbc_ht: ' + status['olbc_ht'])
+ if status['olbc_ht'] == '1':
+ found = True
+ break
+ if not found:
+ raise Exception("Missing OLBC information")
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ if hapd2:
+ hapd2.request("DISABLE")
+ set_world_reg(apdev[0], apdev[1], None)
+
+def test_ap_require_ht(dev, apdev):
+ """Require HT"""
+ params = {"ssid": "require-ht",
+ "require_ht": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[1].connect("require-ht", key_mgmt="NONE", scan_freq="2412",
+ disable_ht="1", wait_connect=False)
+ dev[0].connect("require-ht", key_mgmt="NONE", scan_freq="2412")
+ ev = dev[1].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+ dev[1].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Association rejection timed out")
+ if "status_code=27" not in ev:
+ raise Exception("Unexpected rejection status code")
+ dev[2].connect("require-ht", key_mgmt="NONE", scan_freq="2412",
+ ht_mcs="0x01 00 00 00 00 00 00 00 00 00",
+ disable_max_amsdu="1", ampdu_factor="2",
+ ampdu_density="1", disable_ht40="1", disable_sgi="1",
+ disable_ldpc="1", rx_stbc="2", tx_stbc="1")
+
+ sta = hapd.get_sta(dev[0].own_addr())
+ if 'supp_op_classes' not in sta or len(sta['supp_op_classes']) < 2:
+ raise Exception("No Supported Operating Classes information for STA")
+ opclass = int(sta['supp_op_classes'][0:2], 16)
+ if opclass != 81:
+ raise Exception("Unexpected Current Operating Class from STA: %d" % opclass)
+
+def test_ap_ht_stbc(dev, apdev):
+ """HT STBC overrides"""
+ params = {"ssid": "ht"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ht", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("ht", key_mgmt="NONE", scan_freq="2412",
+ rx_stbc="0", tx_stbc="0")
+ dev[2].connect("ht", key_mgmt="NONE", scan_freq="2412",
+ rx_stbc="1", tx_stbc="1")
+
+@remote_compatible
+def test_ap_require_ht_limited_rates(dev, apdev):
+ """Require HT with limited supported rates"""
+ params = {"ssid": "require-ht",
+ "supported_rates": "60 120 240 360 480 540",
+ "require_ht": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[1].connect("require-ht", key_mgmt="NONE", scan_freq="2412",
+ disable_ht="1", wait_connect=False)
+ dev[0].connect("require-ht", key_mgmt="NONE", scan_freq="2412")
+ ev = dev[1].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+ dev[1].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Association rejection timed out")
+ if "status_code=27" not in ev:
+ raise Exception("Unexpected rejection status code")
+
+@remote_compatible
+def test_ap_ht_capab_not_supported(dev, apdev):
+ """HT configuration with driver not supporting all ht_capab entries"""
+ params = {"ssid": "test-ht40",
+ "channel": "5",
+ "ht_capab": "[HT40-][LDPC][SMPS-STATIC][SMPS-DYNAMIC][GF][SHORT-GI-20][SHORT-GI-40][TX-STBC][RX-STBC1][RX-STBC12][RX-STBC123][DELAYED-BA][MAX-AMSDU-7935][DSSS_CCK-40][LSIG-TXOP-PROT]"}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success")
+
+def test_ap_ht_40mhz_intolerant_sta(dev, apdev):
+ """Associated STA indicating 40 MHz intolerant"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "intolerant",
+ "channel": "6",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if hapd.get_status_field("num_sta_ht40_intolerant") != "0":
+ raise Exception("Unexpected num_sta_ht40_intolerant value")
+ if hapd.get_status_field("secondary_channel") != "-1":
+ raise Exception("Unexpected secondary_channel")
+
+ dev[0].connect("intolerant", key_mgmt="NONE", scan_freq="2437")
+ if hapd.get_status_field("num_sta_ht40_intolerant") != "0":
+ raise Exception("Unexpected num_sta_ht40_intolerant value")
+ if hapd.get_status_field("secondary_channel") != "-1":
+ raise Exception("Unexpected secondary_channel")
+
+ dev[2].connect("intolerant", key_mgmt="NONE", scan_freq="2437",
+ ht40_intolerant="1")
+ time.sleep(1)
+ if hapd.get_status_field("num_sta_ht40_intolerant") != "1":
+ raise Exception("Unexpected num_sta_ht40_intolerant value (expected 1)")
+ if hapd.get_status_field("secondary_channel") != "0":
+ raise Exception("Unexpected secondary_channel (did not disable 40 MHz)")
+
+ dev[2].request("DISCONNECT")
+ time.sleep(1)
+ if hapd.get_status_field("num_sta_ht40_intolerant") != "0":
+ raise Exception("Unexpected num_sta_ht40_intolerant value (expected 0)")
+ if hapd.get_status_field("secondary_channel") != "-1":
+ raise Exception("Unexpected secondary_channel (did not re-enable 40 MHz)")
+
+def test_ap_ht_40mhz_intolerant_sta_deinit(dev, apdev):
+ """Associated STA indicating 40 MHz intolerant and hostapd deinit"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "intolerant",
+ "channel": "6",
+ "ht_capab": "[HT40-]",
+ "obss_interval": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("intolerant", key_mgmt="NONE", scan_freq="2437",
+ ht40_intolerant="1")
+ time.sleep(1)
+ if hapd.get_status_field("num_sta_ht40_intolerant") != "1":
+ raise Exception("Unexpected num_sta_ht40_intolerant value (expected 1)")
+ hglobal = hostapd.HostapdGlobal()
+ hglobal.remove(apdev[0]['ifname'])
+
+ dev[0].request("DISCONNECT")
+
+def test_ap_ht_40mhz_intolerant_ap(dev, apdev):
+ """Associated STA reports 40 MHz intolerant AP after association"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "ht",
+ "channel": "6",
+ "ht_capab": "[HT40-]",
+ "obss_interval": "3"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ht", key_mgmt="NONE", scan_freq="2437")
+
+ if hapd.get_status_field("secondary_channel") != "-1":
+ raise Exception("Unexpected secondary channel information")
+
+ logger.info("Start 40 MHz intolerant AP")
+ params = {"ssid": "intolerant",
+ "channel": "5",
+ "ht_capab": "[40-INTOLERANT]"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ logger.info("Waiting for co-ex report from STA")
+ ok = False
+ for i in range(4):
+ ev = dev[0].wait_event(['CTRL-EVENT-SCAN-RESULTS'], timeout=20)
+ if ev is None:
+ raise Exception("No OBSS scan seen")
+ time.sleep(1)
+ if hapd.get_status_field("secondary_channel") == "0":
+ logger.info("AP moved to 20 MHz channel")
+ ok = True
+ break
+ if not ok:
+ raise Exception("AP did not move to 20 MHz channel")
+
+ if "OK" not in hapd2.request("DISABLE"):
+ raise Exception("Failed to disable 40 MHz intolerant AP")
+
+ # make sure the intolerant AP disappears from scan results more quickly
+ dev[0].scan(type="ONLY", freq="2432", only_new=True)
+ dev[0].scan(type="ONLY", freq="2432", only_new=True)
+ dev[0].dump_monitor()
+
+ logger.info("Waiting for AP to move back to 40 MHz channel")
+ ok = False
+ for i in range(0, 30):
+ time.sleep(1)
+ if hapd.get_status_field("secondary_channel") == "-1":
+ logger.info("AP moved to 40 MHz channel")
+ ok = True
+ break
+ if not ok:
+ raise Exception("AP did not move to 40 MHz channel")
+
+def test_ap_ht40_csa(dev, apdev):
+ """HT with 40 MHz channel width and CSA"""
+ csa_supported(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "ht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ht", key_mgmt="NONE", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5200 ht sec_channel_offset=-1 bandwidth=40")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5200" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected STA disconnection during CSA")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5180 ht sec_channel_offset=1 bandwidth=40")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected STA disconnection during CSA")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ dev[0].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ set_world_reg(apdev[0], None, dev[0])
+ dev[0].flush_scan_cache()
+
+def test_ap_ht40_csa2(dev, apdev):
+ """HT with 40 MHz channel width and CSA"""
+ csa_supported(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "ht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ht", key_mgmt="NONE", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5220 ht sec_channel_offset=1 bandwidth=40")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5220" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected STA disconnection during CSA")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5180 ht sec_channel_offset=1 bandwidth=40")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected STA disconnection during CSA")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ dev[0].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ set_world_reg(apdev[0], None, dev[0])
+ dev[0].flush_scan_cache()
+
+def test_ap_ht40_csa3(dev, apdev):
+ """HT with 40 MHz channel width and CSA"""
+ csa_supported(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "ht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ht", key_mgmt="NONE", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5240 ht sec_channel_offset=-1 bandwidth=40")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5240" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected STA disconnection during CSA")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5180 ht sec_channel_offset=1 bandwidth=40")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected STA disconnection during CSA")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ dev[0].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ set_world_reg(apdev[0], None, dev[0])
+ dev[0].flush_scan_cache()
+
+def test_ap_ht_20_to_40_csa(dev, apdev):
+ """HT with 20 MHz channel width doing CSA to 40 MHz"""
+ csa_supported(dev[0])
+
+ params = {"ssid": "ht",
+ "channel": "1",
+ "ieee80211n": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ht", key_mgmt="NONE", scan_freq="2412")
+ hapd.wait_sta()
+ res = dev[0].request("SIGNAL_POLL")
+ logger.info("SIGNAL_POLL:\n" + res)
+ sig = res.splitlines()
+ if 'WIDTH=20 MHz' not in sig:
+ raise Exception("20 MHz channel bandwidth not used on the original channel")
+
+ hapd.request("CHAN_SWITCH 5 2462 ht sec_channel_offset=-1 bandwidth=40")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=2462" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected STA disconnection during CSA")
+ res = dev[0].request("SIGNAL_POLL")
+ logger.info("SIGNAL_POLL:\n" + res)
+ sig = res.splitlines()
+ if 'WIDTH=40 MHz' not in sig:
+ raise Exception("40 MHz channel bandwidth not used on the new channel")
+
+@remote_compatible
+def test_prefer_ht20(dev, apdev):
+ """Preference on HT20 over no-HT"""
+ params = {"ssid": "test",
+ "channel": "1",
+ "ieee80211n": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ params = {"ssid": "test",
+ "channel": "1",
+ "ieee80211n": "1"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+ if dev[0].get_status_field('bssid') != bssid2:
+ raise Exception("Unexpected BSS selected")
+
+ est = dev[0].get_bss(bssid)['est_throughput']
+ if est != "54000":
+ raise Exception("Unexpected BSS0 est_throughput: " + est)
+
+ est = dev[0].get_bss(bssid2)['est_throughput']
+ if est != "65000":
+ raise Exception("Unexpected BSS1 est_throughput: " + est)
+
+def test_prefer_ht40(dev, apdev):
+ """Preference on HT40 over HT20"""
+ params = {"ssid": "test",
+ "channel": "1",
+ "ieee80211n": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ params = {"ssid": "test",
+ "channel": "1",
+ "ieee80211n": "1",
+ "ht_capab": "[HT40+]"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+ if dev[0].get_status_field('bssid') != bssid2:
+ raise Exception("Unexpected BSS selected")
+
+ est = dev[0].get_bss(bssid)['est_throughput']
+ if est != "65000":
+ raise Exception("Unexpected BSS0 est_throughput: " + est)
+
+ est = dev[0].get_bss(bssid2)['est_throughput']
+ if est != "135000":
+ raise Exception("Unexpected BSS1 est_throughput: " + est)
+
+@remote_compatible
+def test_prefer_ht20_during_roam(dev, apdev):
+ """Preference on HT20 over no-HT in roaming consideration"""
+ params = {"ssid": "test",
+ "channel": "1",
+ "ieee80211n": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+
+ params = {"ssid": "test",
+ "channel": "1",
+ "ieee80211n": "1"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].scan(freq=2412)
+ dev[0].wait_connected()
+
+ if dev[0].get_status_field('bssid') != bssid2:
+ raise Exception("Unexpected BSS selected")
+
+@remote_compatible
+def test_ap_ht40_5ghz_invalid_pair(dev, apdev):
+ """HT40 on 5 GHz with invalid channel pair"""
+ clear_scan_cache(apdev[0])
+ try:
+ params = {"ssid": "test-ht40",
+ "hw_mode": "a",
+ "channel": "40",
+ "country_code": "US",
+ "ht_capab": "[HT40+]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-DISABLED", "AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup failure timed out")
+ if "AP-ENABLED" in ev:
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("Invalid 40 MHz channel accepted")
+ finally:
+ clear_regdom(hapd, dev)
+
+@remote_compatible
+def test_ap_ht40_5ghz_disabled_sec(dev, apdev):
+ """HT40 on 5 GHz with disabled secondary channel"""
+ clear_scan_cache(apdev[0])
+ try:
+ params = {"ssid": "test-ht40",
+ "hw_mode": "a",
+ "channel": "48",
+ "country_code": "US",
+ "ht_capab": "[HT40+]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-DISABLED", "AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup failure timed out")
+ if "AP-ENABLED" in ev:
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("Invalid 40 MHz channel accepted")
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_ht40_scan_broken_ap(dev, apdev):
+ """HT40 co-ex scan and broken legacy/HT AP"""
+ clear_scan_cache(apdev[0])
+
+ # Broken AP: Include HT Capabilities element but not HT Operation element
+ params = {"ssid": "legacy-20",
+ "channel": "7", "ieee80211n": "0",
+ "wmm_enabled": "1",
+ "vendor_elements": "2d1a0e001bffff000000000000000000000100000000000000000000"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-ht40",
+ "channel": "5",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ time.sleep(0.1)
+ state = hapd.get_status_field("state")
+ if state != "HT_SCAN":
+ raise Exception("Unexpected interface state - expected HT_SCAN")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state - expected ENABLED")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2432":
+ raise Exception("Unexpected frequency: " + freq)
+ pri = hapd.get_status_field("channel")
+ if pri != "5":
+ raise Exception("Unexpected primary channel: " + pri)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "-1":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+ dev[1].connect("legacy-20", key_mgmt="NONE", scan_freq="2442")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ hwsim_utils.test_connectivity(dev[1], hapd2)
+
+def run_op_class(dev, apdev, hw_mode, channel, country, ht_capab, sec_chan,
+ freq, opclass, use_op_class=False):
+ clear_scan_cache(apdev[0])
+ try:
+ params = {"ssid": "test-ht40",
+ "hw_mode": hw_mode,
+ "channel": channel,
+ "ht_capab": ht_capab}
+ if use_op_class:
+ params['op_class'] = str(opclass)
+ if country:
+ params['country_code'] = country
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-DISABLED", "AP-ENABLED"], timeout=10)
+ if not ev:
+ raise Exception("AP setup failure timed out")
+ if "AP-DISABLED" in ev:
+ raise HwsimSkip("Channel not supported")
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != sec_chan:
+ raise Exception("Unexpected secondary_channel: " + sec)
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+ bss = dev[0].get_bss(hapd.own_addr())
+ ie = parse_ie(bss['ie'])
+ if 59 not in ie:
+ raise Exception("Missing Supported Operating Classes element")
+ rx_opclass, = struct.unpack('B', ie[59][0:1])
+ if rx_opclass != opclass:
+ raise Exception("Unexpected operating class: %d" % rx_opclass)
+ hapd.disable()
+ hapd.dump_monitor()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].request("ABORT_SCAN")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ finally:
+ set_world_reg(apdev[0], None, dev[0])
+ time.sleep(0.1)
+
+def test_ap_ht_op_class_81(dev, apdev):
+ """HT20 on operationg class 81"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "g", "1", None, "", "0", "2412", 81,
+ use_op_class=o)
+
+def test_ap_ht_op_class_83(dev, apdev):
+ """HT40 on operationg class 83"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "g", "1", None, "[HT40+]", "1", "2412", 83,
+ use_op_class=o)
+
+def test_ap_ht_op_class_84(dev, apdev):
+ """HT40 on operationg class 84"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "g", "11", None, "[HT40-]", "-1", "2462", 84,
+ use_op_class=o)
+
+def test_ap_ht_op_class_115(dev, apdev):
+ """HT20 on operationg class 115"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "36", "FI", "", "0", "5180", 115,
+ use_op_class=o)
+
+def test_ap_ht_op_class_116(dev, apdev):
+ """HT40 on operationg class 116"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "36", "FI", "[HT40+]", "1", "5180", 116,
+ use_op_class=o)
+
+def test_ap_ht_op_class_117(dev, apdev):
+ """HT40 on operationg class 117"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "40", "FI", "[HT40-]", "-1", "5200", 117,
+ use_op_class=o)
+
+def test_ap_ht_op_class_118(dev, apdev):
+ """HT20 on operationg class 118"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "60", "PA", "", "0", "5300", 118,
+ use_op_class=o)
+
+def test_ap_ht_op_class_119(dev, apdev):
+ """HT40 on operationg class 119"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "60", "PA", "[HT40+]", "1", "5300", 119,
+ use_op_class=o)
+
+def test_ap_ht_op_class_120(dev, apdev):
+ """HT40 on operationg class 120"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "64", "PA", "[HT40-]", "-1", "5320", 120,
+ use_op_class=o)
+
+def test_ap_ht_op_class_121(dev, apdev):
+ """HT20 on operationg class 121"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "100", "ZA", "", "0", "5500", 121,
+ use_op_class=o)
+
+def test_ap_ht_op_class_122(dev, apdev):
+ """HT40 on operationg class 122"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "100", "ZA", "[HT40+]", "1", "5500", 122,
+ use_op_class=o)
+
+def test_ap_ht_op_class_123(dev, apdev):
+ """HT40 on operationg class 123"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "104", "ZA", "[HT40-]", "-1", "5520", 123,
+ use_op_class=o)
+
+def test_ap_ht_op_class_124(dev, apdev):
+ """HT20 on operationg class 124"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "149", "US", "", "0", "5745", 124,
+ use_op_class=o)
+
+def test_ap_ht_op_class_125(dev, apdev):
+ """HT20 on operationg class 125"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "169", "NL", "", "0", "5845", 125,
+ use_op_class=o)
+
+def test_ap_ht_op_class_126(dev, apdev):
+ """HT40 on operationg class 126"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "149", "US", "[HT40+]", "1", "5745", 126,
+ use_op_class=o)
+
+def test_ap_ht_op_class_127(dev, apdev):
+ """HT40 on operationg class 127"""
+ for o in [False, True]:
+ run_op_class(dev, apdev, "a", "153", "US", "[HT40-]", "-1", "5765", 127,
+ use_op_class=o)
+
+def test_ap_ht40_plus_minus1(dev, apdev):
+ """HT40 with both plus and minus allowed (1)"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "test-ht40",
+ "channel": "11",
+ "ht_capab": "[HT40+][HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2462":
+ raise Exception("Unexpected frequency: " + freq)
+ pri = hapd.get_status_field("channel")
+ if pri != "11":
+ raise Exception("Unexpected primary channel: " + pri)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "-1":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_plus_minus2(dev, apdev):
+ """HT40 with both plus and minus allowed (2)"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "test-ht40",
+ "channel": "1",
+ "ht_capab": "[HT40+][HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ freq = hapd.get_status_field("freq")
+ if freq != "2412":
+ raise Exception("Unexpected frequency: " + freq)
+ pri = hapd.get_status_field("channel")
+ if pri != "1":
+ raise Exception("Unexpected primary channel: " + pri)
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "1":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq=freq)
+
+def test_ap_ht40_disable(dev, apdev):
+ """HT40 disabling"""
+ clear_scan_cache(apdev[0])
+ params = {"ssid": "test-ht40",
+ "channel": "6",
+ "ht_capab": "[HT40-]"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "-1":
+ raise Exception("Unexpected secondary channel: " + sec)
+
+ id = dev[0].connect("test-ht40", key_mgmt="NONE", scan_freq="2437")
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ logger.info("SIGNAL_POLL: " + str(sig))
+ if "WIDTH=40 MHz" not in sig:
+ raise Exception("Station did not report 40 MHz bandwidth")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ hapd.disable()
+ hapd.set("ht_capab", "")
+ hapd.enable()
+ sec = hapd.get_status_field("secondary_channel")
+ if sec != "0":
+ raise Exception("Unexpected secondary channel(2): " + sec)
+
+ dev[0].flush_scan_cache()
+ dev[0].select_network(id, freq=2437)
+ dev[0].wait_connected()
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ logger.info("SIGNAL_POLL: " + str(sig))
+ if "WIDTH=20 MHz" not in sig:
+ raise Exception("Station did not report 20 MHz bandwidth")
+
+def test_ap_ht_wmm_etsi(dev, apdev):
+ """HT and WMM contents in ETSI"""
+ run_ap_ht_wmm(dev, apdev, "FI")
+
+def test_ap_ht_wmm_fcc(dev, apdev):
+ """HT and WMM contents in FCC"""
+ run_ap_ht_wmm(dev, apdev, "US")
+
+def run_ap_ht_wmm(dev, apdev, country):
+ clear_scan_cache(apdev[0])
+ try:
+ hapd = None
+ params = {"ssid": "test",
+ "hw_mode": "a",
+ "channel": "36",
+ "country_code": country}
+ hapd = hostapd.add_ap(apdev[0], params)
+ freq = hapd.get_status_field("freq")
+ bssid = hapd.own_addr()
+ dev[0].connect("test", key_mgmt="NONE", scan_freq=freq)
+ bss = dev[0].get_bss(bssid)
+ ie = parse_ie(bss['ie'])
+ if 221 not in ie:
+ raise Exception("Could not find WMM IE")
+ wmm = ie[221]
+ if len(wmm) != 24:
+ raise Exception("Unexpected WMM IE length")
+ id, subtype, version, info, reserved = struct.unpack('>LBBBB', wmm[0:8])
+ if id != 0x0050f202 or subtype != 1 or version != 1:
+ raise Exception("Not a WMM IE")
+ ac = []
+ for i in range(4):
+ ac.append(struct.unpack('<BBH', wmm[8 + i * 4: 12 + i * 4]))
+ logger.info("WMM AC info: " + str(ac))
+
+ aifsn = (ac[0][0] & 0x0f, ac[1][0] & 0x0f,
+ ac[2][0] & 0x0f, ac[3][0] & 0x0f)
+ logger.info("AIFSN: " + str(aifsn))
+ if aifsn != (3, 7, 2, 2):
+ raise Exception("Unexpected AIFSN value: " + str(aifsn))
+
+ ecw_min = (ac[0][1] & 0x0f, ac[1][1] & 0x0f,
+ ac[2][1] & 0x0f, ac[3][1] & 0x0f)
+ logger.info("ECW min: " + str(ecw_min))
+ if ecw_min != (4, 4, 3, 2):
+ raise Exception("Unexpected ECW min value: " + str(ecw_min))
+
+ ecw_max = ((ac[0][1] & 0xf0) >> 4, (ac[1][1] & 0xf0) >> 4,
+ (ac[2][1] & 0xf0) >> 4, (ac[3][1] & 0xf0) >> 4)
+ logger.info("ECW max: " + str(ecw_max))
+ if ecw_max != (10, 10, 4, 3):
+ raise Exception("Unexpected ECW max value: " + str(ecw_max))
+
+ txop = (ac[0][2], ac[1][2], ac[2][2], ac[3][2])
+ logger.info("TXOP: " + str(txop))
+ if txop != (0, 0, 94, 47):
+ raise Exception("Unexpected TXOP value: " + str(txop))
+ finally:
+ dev[0].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ set_world_reg(apdev[0], None, dev[0])
+ dev[0].flush_scan_cache()
diff --git a/contrib/wpa/tests/hwsim/test_ap_mixed.py b/contrib/wpa/tests/hwsim/test_ap_mixed.py
new file mode 100644
index 000000000000..e758ae923cdd
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_mixed.py
@@ -0,0 +1,101 @@
+# Mixed AP module parameters enabled
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+
+import hostapd
+import hwsim_utils
+from utils import *
+
+def test_ap_mixed_security(dev, apdev):
+ """WPA/WPA2 with PSK, EAP, SAE, FT in a single BSS"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ dev[0].flush_scan_cache()
+ sae = "SAE" in dev[2].get_capability("auth_alg")
+ ssid = "test-mixed"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa_mixed_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "WPA-PSK WPA-PSK-SHA256 WPA-EAP WPA-EAP-SHA256 SAE FT-PSK FT-EAP FT-SAE"
+ params["ieee8021x"] = "1"
+ params["eap_server"] = "1"
+ params["eap_user_file"] = "auth_serv/eap_user.conf"
+ params['nas_identifier'] = "nas1.w1.fi"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(ssid, key_mgmt="WPA-PSK", proto="WPA", pairwise="TKIP",
+ psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, key_mgmt="WPA-EAP-SHA256", proto="WPA2", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ if sae:
+ dev[2].request("SET sae_groups ")
+ dev[2].connect(ssid, psk=passphrase, key_mgmt="SAE", scan_freq="2412")
+
+ logger.debug(dev[0].request("SCAN_RESULTS"))
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ logger.debug(bss)
+ if "[WPA-EAP+PSK-TKIP]" not in bss['flags']:
+ raise Exception("Unexpected flags (WPA): " + bss['flags'])
+ if sae and "[WPA2-EAP+PSK+SAE+FT/EAP+FT/PSK+FT/SAE+EAP-SHA256+PSK-SHA256-CCMP]" not in bss['flags']:
+ raise Exception("Unexpected flags (WPA2): " + bss['flags'])
+
+ if dev[0].get_status_field("key_mgmt") != "WPA-PSK":
+ raise Exception("Unexpected key_mgmt(1)")
+ if dev[0].get_status_field("pairwise_cipher") != "TKIP":
+ raise Exception("Unexpected pairwise(1)")
+ if dev[1].get_status_field("key_mgmt") != "WPA2-EAP-SHA256":
+ raise Exception("Unexpected key_mgmt(2)")
+ if sae and dev[2].get_status_field("key_mgmt") != "SAE":
+ raise Exception("Unexpected key_mgmt(3)")
+
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ if sae:
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+ hwsim_utils.test_connectivity(dev[0], dev[2])
+ for i in range(3):
+ if i < 2 or sae:
+ hwsim_utils.test_connectivity(dev[i], hapd)
+ dev[i].request("DISCONNECT")
+
+ dev[0].connect(ssid, key_mgmt="WPA-PSK WPA-PSK-SHA256", psk=passphrase,
+ scan_freq="2412")
+ dev[1].connect(ssid, key_mgmt="WPA-EAP", proto="WPA", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ if sae:
+ dev[2].connect(ssid, key_mgmt="WPA-PSK WPA-PSK-SHA256 SAE",
+ psk=passphrase, scan_freq="2412")
+
+ if dev[0].get_status_field("key_mgmt") != "WPA2-PSK-SHA256":
+ raise Exception("Unexpected key_mgmt(1b)")
+ if dev[0].get_status_field("pairwise_cipher") != "CCMP":
+ raise Exception("Unexpected pairwise(1b)")
+ if dev[1].get_status_field("key_mgmt") != "WPA/IEEE 802.1X/EAP":
+ raise Exception("Unexpected key_mgmt(2b)")
+ if sae and dev[2].get_status_field("key_mgmt") != "SAE":
+ raise Exception("Unexpected key_mgmt(3b)")
+
+ for i in range(3):
+ dev[i].request("DISCONNECT")
+
+ dev[0].connect(ssid, key_mgmt="FT-PSK", psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, key_mgmt="FT-EAP", eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ if sae:
+ dev[2].connect(ssid, psk=passphrase, key_mgmt="FT-SAE",
+ scan_freq="2412")
+
+ if dev[0].get_status_field("key_mgmt") != "FT-PSK":
+ raise Exception("Unexpected key_mgmt(1c)")
+ if dev[1].get_status_field("key_mgmt") != "FT-EAP":
+ raise Exception("Unexpected key_mgmt(2c)")
+ if sae and dev[2].get_status_field("key_mgmt") != "FT-SAE":
+ raise Exception("Unexpected key_mgmt(3c)")
diff --git a/contrib/wpa/tests/hwsim/test_ap_open.py b/contrib/wpa/tests/hwsim/test_ap_open.py
new file mode 100644
index 000000000000..a3bea763a1c4
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_open.py
@@ -0,0 +1,1017 @@
+# Open mode AP tests
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import struct
+import subprocess
+import time
+import os
+
+import hostapd
+import hwsim_utils
+from tshark import run_tshark
+from utils import *
+from wpasupplicant import WpaSupplicant
+from wlantest import WlantestCapture
+
+@remote_compatible
+def test_ap_open(dev, apdev):
+ """AP with open mode (no security) configuration"""
+ _test_ap_open(dev, apdev)
+
+def _test_ap_open(dev, apdev):
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bg_scan_period="0")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[0].request("DISCONNECT")
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No disconnection event received from hostapd")
+
+def test_ap_open_packet_loss(dev, apdev):
+ """AP with open mode configuration and large packet loss"""
+ params = {"ssid": "open",
+ "ignore_probe_probability": "0.5",
+ "ignore_auth_probability": "0.5",
+ "ignore_assoc_probability": "0.5",
+ "ignore_reassoc_probability": "0.5"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ for i in range(0, 3):
+ dev[i].connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ for i in range(0, 3):
+ dev[i].wait_connected(timeout=20)
+
+@remote_compatible
+def test_ap_open_unknown_action(dev, apdev):
+ """AP with open mode configuration and unknown Action frame"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ bssid = apdev[0]['bssid']
+ cmd = "MGMT_TX {} {} freq=2412 action=765432".format(bssid, bssid)
+ if "FAIL" in dev[0].request(cmd):
+ raise Exception("Could not send test Action frame")
+ ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on MGMT-TX-STATUS")
+ if "result=SUCCESS" not in ev:
+ raise Exception("AP did not ack Action frame")
+
+def test_ap_open_invalid_wmm_action(dev, apdev):
+ """AP with open mode configuration and invalid WMM Action frame"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ bssid = apdev[0]['bssid']
+ cmd = "MGMT_TX {} {} freq=2412 action=1100".format(bssid, bssid)
+ if "FAIL" in dev[0].request(cmd):
+ raise Exception("Could not send test Action frame")
+ ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10)
+ if ev is None or "result=SUCCESS" not in ev:
+ raise Exception("AP did not ack Action frame")
+
+@remote_compatible
+def test_ap_open_reconnect_on_inactivity_disconnect(dev, apdev):
+ """Reconnect to open mode AP after inactivity related disconnection"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ hapd.request("DEAUTHENTICATE " + dev[0].p2p_interface_addr() + " reason=4")
+ dev[0].wait_disconnected(timeout=5)
+ dev[0].wait_connected(timeout=2, error="Timeout on reconnection")
+
+@remote_compatible
+def test_ap_open_assoc_timeout(dev, apdev):
+ """AP timing out association"""
+ ssid = "test"
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].scan(freq="2412")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame not received")
+
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = struct.pack('<HHH', 0, 2, 0)
+ hapd.mgmt_tx(resp)
+
+ assoc = 0
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out")
+ if req['subtype'] == 0:
+ assoc += 1
+ if assoc == 3:
+ break
+ if assoc != 3:
+ raise Exception("Association Request frames not received: assoc=%d" % assoc)
+ hapd.set("ext_mgmt_frame_handling", "0")
+ dev[0].wait_connected(timeout=15)
+
+def test_ap_open_auth_drop_sta(dev, apdev):
+ """AP dropping station after successful authentication"""
+ hapd = hostapd.add_ap(apdev[0]['ifname'], {"ssid": "open"})
+ dev[0].scan(freq="2412")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame not received")
+
+ # turn off before sending successful response
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = struct.pack('<HHH', 0, 2, 0)
+ hapd.mgmt_tx(resp)
+
+ dev[0].wait_connected(timeout=15)
+
+@remote_compatible
+def test_ap_open_id_str(dev, apdev):
+ """AP with open mode and id_str"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412", id_str="foo",
+ wait_connect=False)
+ ev = dev[0].wait_connected(timeout=10)
+ if "id_str=foo" not in ev:
+ raise Exception("CTRL-EVENT-CONNECT did not have matching id_str: " + ev)
+ if dev[0].get_status_field("id_str") != "foo":
+ raise Exception("id_str mismatch")
+
+@remote_compatible
+def test_ap_open_select_any(dev, apdev):
+ """AP with open mode and select any network"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ id = dev[0].connect("unknown", key_mgmt="NONE", scan_freq="2412",
+ only_add_network=True)
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ only_add_network=True)
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("No result reported")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+
+ dev[0].select_network("any")
+ dev[0].wait_connected(timeout=10)
+
+@remote_compatible
+def test_ap_open_unexpected_assoc_event(dev, apdev):
+ """AP with open mode and unexpected association event"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].dump_monitor()
+ # This association will be ignored by wpa_supplicant since the current
+ # state is not to try to connect after that DISCONNECT command.
+ dev[0].cmd_execute(['iw', 'dev', dev[0].ifname, 'connect', 'open', "2412",
+ apdev[0]['bssid']])
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.3)
+ dev[0].cmd_execute(['iw', 'dev', dev[0].ifname, 'disconnect'])
+ dev[0].dump_monitor()
+ if ev is not None:
+ raise Exception("Unexpected connection")
+
+def test_ap_open_external_assoc(dev, apdev):
+ """AP with open mode and external association"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open-ext-assoc"})
+ try:
+ dev[0].request("STA_AUTOCONNECT 0")
+ id = dev[0].connect("open-ext-assoc", key_mgmt="NONE", scan_freq="2412",
+ only_add_network=True)
+ dev[0].request("ENABLE_NETWORK %s no-connect" % id)
+ dev[0].dump_monitor()
+ # This will be accepted due to matching network
+ dev[0].cmd_execute(['iw', 'dev', dev[0].ifname, 'connect',
+ 'open-ext-assoc', "2412", apdev[0]['bssid']])
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection timed out")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ raise Exception("Unexpected disconnection event")
+ dev[0].dump_monitor()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=5)
+ finally:
+ dev[0].request("STA_AUTOCONNECT 1")
+
+@remote_compatible
+def test_ap_bss_load(dev, apdev):
+ """AP with open mode (no security) configuration"""
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "open",
+ "bss_load_update_period": "10",
+ "chan_util_avg_period": "20"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ # this does not really get much useful output with mac80211_hwsim currently,
+ # but run through the channel survey update couple of times
+ for i in range(0, 10):
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ time.sleep(0.15)
+ avg = hapd.get_status_field("chan_util_avg")
+ if avg is None:
+ raise Exception("No STATUS chan_util_avg seen")
+
+def test_ap_bss_load_fail(dev, apdev):
+ """BSS Load update failing to get survey data"""
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "open",
+ "bss_load_update_period": "1"})
+ with fail_test(hapd, 1, "wpa_driver_nl80211_get_survey"):
+ wait_fail_trigger(hapd, "GET_FAIL")
+
+def hapd_out_of_mem(hapd, apdev, count, func):
+ with alloc_fail(hapd, count, func):
+ started = False
+ try:
+ hostapd.add_ap(apdev, {"ssid": "open"})
+ started = True
+ except:
+ pass
+ if started:
+ raise Exception("hostapd interface started even with memory allocation failure: %d:%s" % (count, func))
+
+def test_ap_open_out_of_memory(dev, apdev):
+ """hostapd failing to setup interface due to allocation failure"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ flags2 = hapd.request("DRIVER_FLAGS2").splitlines()[1:]
+ hapd_out_of_mem(hapd, apdev[1], 1, "hostapd_alloc_bss_data")
+
+ for i in range(1, 3):
+ hapd_out_of_mem(hapd, apdev[1], i, "hostapd_iface_alloc")
+
+ for i in range(1, 5):
+ hapd_out_of_mem(hapd, apdev[1], i, "hostapd_config_defaults;hostapd_config_alloc")
+
+ hapd_out_of_mem(hapd, apdev[1], 1, "hostapd_config_alloc")
+
+ hapd_out_of_mem(hapd, apdev[1], 1, "hostapd_driver_init")
+
+ for i in range(1, 3):
+ hapd_out_of_mem(hapd, apdev[1], i, "=wpa_driver_nl80211_drv_init")
+
+ if 'CONTROL_PORT_RX' not in flags2:
+ # eloop_register_read_sock() call from i802_init()
+ hapd_out_of_mem(hapd, apdev[1], 1, "eloop_sock_table_add_sock;?eloop_register_sock;?eloop_register_read_sock;=i802_init")
+
+ # verify that a new interface can still be added when memory allocation does
+ # not fail
+ hostapd.add_ap(apdev[1], {"ssid": "open"})
+
+def test_bssid_ignore_accept(dev, apdev):
+ """BSSID ignore/accept list"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "open"})
+
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bssid_accept=apdev[1]['bssid'])
+ dev[1].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bssid_ignore=apdev[1]['bssid'])
+ dev[2].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bssid_accept="00:00:00:00:00:00/00:00:00:00:00:00",
+ bssid_ignore=apdev[1]['bssid'])
+ if dev[0].get_status_field('bssid') != apdev[1]['bssid']:
+ raise Exception("dev[0] connected to unexpected AP")
+ if dev[1].get_status_field('bssid') != apdev[0]['bssid']:
+ raise Exception("dev[1] connected to unexpected AP")
+ if dev[2].get_status_field('bssid') != apdev[0]['bssid']:
+ raise Exception("dev[2] connected to unexpected AP")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[2].request("REMOVE_NETWORK all")
+
+ dev[2].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bssid_accept="00:00:00:00:00:00", wait_connect=False)
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bssid_accept="11:22:33:44:55:66/ff:00:00:00:00:00 " + apdev[1]['bssid'] + " aa:bb:cc:dd:ee:ff")
+ dev[1].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bssid_ignore="11:22:33:44:55:66/ff:00:00:00:00:00 " + apdev[1]['bssid'] + " aa:bb:cc:dd:ee:ff")
+ if dev[0].get_status_field('bssid') != apdev[1]['bssid']:
+ raise Exception("dev[0] connected to unexpected AP")
+ if dev[1].get_status_field('bssid') != apdev[0]['bssid']:
+ raise Exception("dev[1] connected to unexpected AP")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected dev[2] connectin")
+ dev[2].request("REMOVE_NETWORK all")
+
+def test_ap_open_wpas_in_bridge(dev, apdev):
+ """Open mode AP and wpas interface in a bridge"""
+ br_ifname = 'sta-br0'
+ ifname = 'wlan5'
+ try:
+ _test_ap_open_wpas_in_bridge(dev, apdev)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
+ subprocess.call(['brctl', 'delif', br_ifname, ifname])
+ subprocess.call(['brctl', 'delbr', br_ifname])
+ subprocess.call(['iw', ifname, 'set', '4addr', 'off'])
+
+def _test_ap_open_wpas_in_bridge(dev, apdev):
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+
+ br_ifname = 'sta-br0'
+ ifname = 'wlan5'
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ # First, try a failure case of adding an interface
+ try:
+ wpas.interface_add(ifname, br_ifname=br_ifname)
+ raise Exception("Interface addition succeeded unexpectedly")
+ except Exception as e:
+ if "Failed to add" in str(e):
+ logger.info("Ignore expected interface_add failure due to missing bridge interface: " + str(e))
+ else:
+ raise
+
+ # Next, add the bridge interface and add the interface again
+ subprocess.call(['brctl', 'addbr', br_ifname])
+ subprocess.call(['brctl', 'setfd', br_ifname, '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+ subprocess.call(['iw', ifname, 'set', '4addr', 'on'])
+ subprocess.check_call(['brctl', 'addif', br_ifname, ifname])
+ wpas.interface_add(ifname, br_ifname=br_ifname)
+
+ wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+
+@remote_compatible
+def test_ap_open_start_disabled(dev, apdev):
+ """AP with open mode and beaconing disabled"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open",
+ "start_disabled": "1"})
+ bssid = apdev[0]['bssid']
+
+ dev[0].flush_scan_cache()
+ dev[0].scan(freq=2412, only_new=True)
+ if dev[0].get_bss(bssid) is not None:
+ raise Exception("AP was seen beaconing")
+ if "OK" not in hapd.request("RELOAD"):
+ raise Exception("RELOAD failed")
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+@remote_compatible
+def test_ap_open_start_disabled2(dev, apdev):
+ """AP with open mode and beaconing disabled (2)"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open",
+ "start_disabled": "1"})
+ bssid = apdev[0]['bssid']
+
+ dev[0].flush_scan_cache()
+ dev[0].scan(freq=2412, only_new=True)
+ if dev[0].get_bss(bssid) is not None:
+ raise Exception("AP was seen beaconing")
+ if "OK" not in hapd.request("UPDATE_BEACON"):
+ raise Exception("UPDATE_BEACON failed")
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ if "OK" not in hapd.request("UPDATE_BEACON"):
+ raise Exception("UPDATE_BEACON failed")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+@remote_compatible
+def test_ap_open_ifdown(dev, apdev):
+ """AP with open mode and external ifconfig down"""
+ params = {"ssid": "open",
+ "ap_max_inactivity": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("open", key_mgmt="NONE", scan_freq="2412")
+ hapd.cmd_execute(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'down'])
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on AP-STA-DISCONNECTED (1)")
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on AP-STA-DISCONNECTED (2)")
+ ev = hapd.wait_event(["INTERFACE-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("No INTERFACE-DISABLED event")
+ # The following wait tests beacon loss detection in mac80211 on dev0.
+ # dev1 is used to test stopping of AP side functionality on client polling.
+ dev[1].request("REMOVE_NETWORK all")
+ hapd.cmd_execute(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'up'])
+ dev[0].wait_disconnected()
+ dev[1].wait_disconnected()
+ ev = hapd.wait_event(["INTERFACE-ENABLED"], timeout=10)
+ if ev is None:
+ raise Exception("No INTERFACE-ENABLED event")
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_open_disconnect_in_ps(dev, apdev, params):
+ """Disconnect with the client in PS to regression-test a kernel bug"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bg_scan_period="0")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+
+ time.sleep(0.2)
+ # enable power save mode
+ hwsim_utils.set_powersave(dev[0], hwsim_utils.PS_ENABLED)
+ time.sleep(0.1)
+ try:
+ # inject some traffic
+ sa = hapd.own_addr()
+ da = dev[0].own_addr()
+ hapd.request('DATA_TEST_CONFIG 1')
+ hapd.request('DATA_TEST_TX {} {} 0'.format(da, sa))
+ hapd.request('DATA_TEST_CONFIG 0')
+
+ # let the AP send couple of Beacon frames
+ time.sleep(0.3)
+
+ # disconnect - with traffic pending - shouldn't cause kernel warnings
+ dev[0].request("DISCONNECT")
+ finally:
+ hwsim_utils.set_powersave(dev[0], hwsim_utils.PS_DISABLED)
+
+ time.sleep(0.2)
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan_mgt.tim.partial_virtual_bitmap",
+ ["wlan_mgt.tim.partial_virtual_bitmap"])
+ if out is not None:
+ state = 0
+ for l in out.splitlines():
+ pvb = int(l, 16)
+ if pvb > 0 and state == 0:
+ state = 1
+ elif pvb == 0 and state == 1:
+ state = 2
+ if state != 2:
+ raise Exception("Didn't observe TIM bit getting set and unset (state=%d)" % state)
+
+def test_ap_open_sta_ps(dev, apdev):
+ """Station power save operation"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bg_scan_period="0")
+ hapd.wait_sta()
+
+ time.sleep(0.2)
+ try:
+ dev[0].cmd_execute(['iw', 'dev', dev[0].ifname,
+ 'set', 'power_save', 'on'])
+ run_ap_open_sta_ps(dev, hapd)
+ finally:
+ dev[0].cmd_execute(['iw', 'dev', dev[0].ifname,
+ 'set', 'power_save', 'off'])
+
+def run_ap_open_sta_ps(dev, hapd):
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ # Give time to enter PS
+ time.sleep(0.2)
+
+ phyname = dev[0].get_driver_status_field("phyname")
+ hw_conf = '/sys/kernel/debug/ieee80211/' + phyname + '/hw_conf'
+
+ try:
+ ok = False
+ for i in range(10):
+ with open(hw_conf, 'r') as f:
+ val = int(f.read())
+ if val & 2:
+ ok = True
+ break
+ time.sleep(0.2)
+
+ if not ok:
+ raise Exception("STA did not enter power save")
+
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ hapd.request("DEAUTHENTICATE " + dev[0].own_addr())
+ dev[0].wait_disconnected()
+ except FileNotFoundError:
+ raise HwsimSkip("Kernel does not support inspecting HW PS state")
+
+def test_ap_open_ps_mc_buf(dev, apdev, params):
+ """Multicast buffering with a station in power save"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bg_scan_period="0")
+ hapd.wait_sta()
+
+ buffered_mcast = 0
+ try:
+ dev[0].cmd_execute(['iw', 'dev', dev[0].ifname,
+ 'set', 'power_save', 'on'])
+ # Give time to enter PS
+ time.sleep(0.3)
+
+ for i in range(10):
+ # Verify that multicast frames are released
+ hwsim_utils.run_multicast_connectivity_test(hapd, dev[0])
+
+ # Check frames were buffered until DTIM
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan.fc.type_subtype == 0x0008",
+ ["wlan.tim.bmapctl.multicast"])
+ for line in out.splitlines():
+ buffered_mcast = int(line)
+ if buffered_mcast == 1:
+ break
+ if buffered_mcast == 1:
+ break
+ finally:
+ dev[0].cmd_execute(['iw', 'dev', dev[0].ifname,
+ 'set', 'power_save', 'off'])
+
+ if buffered_mcast != 1:
+ raise Exception("AP did not buffer multicast frames")
+
+@remote_compatible
+def test_ap_open_select_network(dev, apdev):
+ """Open mode connection and SELECT_NETWORK to change network"""
+ hapd1 = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ bssid1 = apdev[0]['bssid']
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "open2"})
+ bssid2 = apdev[1]['bssid']
+
+ id1 = dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ only_add_network=True)
+ id2 = dev[0].connect("open2", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd2)
+
+ dev[0].select_network(id1)
+ dev[0].wait_connected()
+ res = dev[0].request("BSSID_IGNORE")
+ if bssid1 in res or bssid2 in res:
+ raise Exception("Unexpected BSSID ignore list entry")
+ hwsim_utils.test_connectivity(dev[0], hapd1)
+
+ dev[0].select_network(id2)
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity(dev[0], hapd2)
+ res = dev[0].request("BSSID_IGNORE")
+ if bssid1 in res or bssid2 in res:
+ raise Exception("Unexpected BSSID ignore list entry(2)")
+
+@remote_compatible
+def test_ap_open_disable_enable(dev, apdev):
+ """AP with open mode getting disabled and re-enabled"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bg_scan_period="0")
+
+ for i in range(2):
+ hapd.request("DISABLE")
+ dev[0].wait_disconnected()
+ hapd.request("ENABLE")
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def sta_enable_disable(dev, bssid):
+ dev.scan_for_bss(bssid, freq=2412)
+ work_id = dev.request("RADIO_WORK add block-work")
+ ev = dev.wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ id = dev.connect("open", key_mgmt="NONE", scan_freq="2412",
+ only_add_network=True)
+ dev.request("ENABLE_NETWORK %d" % id)
+ if "connect@" not in dev.request("RADIO_WORK show"):
+ raise Exception("connect radio work missing")
+ dev.request("DISABLE_NETWORK %d" % id)
+ dev.request("RADIO_WORK done " + work_id)
+
+ ok = False
+ for i in range(30):
+ if "connect@" not in dev.request("RADIO_WORK show"):
+ ok = True
+ break
+ time.sleep(0.1)
+ if not ok:
+ raise Exception("connect radio work not completed")
+ ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+ dev.request("DISCONNECT")
+
+def test_ap_open_sta_enable_disable(dev, apdev):
+ """AP with open mode and wpa_supplicant ENABLE/DISABLE_NETWORK"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ bssid = apdev[0]['bssid']
+
+ sta_enable_disable(dev[0], bssid)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ sta_enable_disable(wpas, bssid)
+
+@remote_compatible
+def test_ap_open_select_twice(dev, apdev):
+ """AP with open mode and select network twice"""
+ id = dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ only_add_network=True)
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("No result reported")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ # Verify that the second SELECT_NETWORK starts a new scan immediately by
+ # waiting less than the default scan period.
+ dev[0].select_network(id)
+ dev[0].wait_connected(timeout=3)
+
+@remote_compatible
+def test_ap_open_reassoc_not_found(dev, apdev):
+ """AP with open mode and REASSOCIATE not finding a match"""
+ id = dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ only_add_network=True)
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("No result reported")
+ dev[0].request("DISCONNECT")
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+
+ dev[0].request("REASSOCIATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("No result reported")
+ dev[0].request("DISCONNECT")
+
+@remote_compatible
+def test_ap_open_sta_statistics(dev, apdev):
+ """AP with open mode and STA statistics"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ stats1 = hapd.get_sta(addr)
+ logger.info("stats1: " + str(stats1))
+ time.sleep(0.4)
+ stats2 = hapd.get_sta(addr)
+ logger.info("stats2: " + str(stats2))
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ stats3 = hapd.get_sta(addr)
+ logger.info("stats3: " + str(stats3))
+
+ # Cannot require specific inactive_msec changes without getting rid of all
+ # unrelated traffic, so for now, just print out the results in the log for
+ # manual checks.
+
+@remote_compatible
+def test_ap_open_poll_sta(dev, apdev):
+ """AP with open mode and STA poll"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ if "OK" not in hapd.request("POLL_STA " + addr):
+ raise Exception("POLL_STA failed")
+ ev = hapd.wait_event(["AP-STA-POLL-OK"], timeout=5)
+ if ev is None:
+ raise Exception("Poll response not seen")
+ if addr not in ev:
+ raise Exception("Unexpected poll response: " + ev)
+
+def test_ap_open_poll_sta_no_ack(dev, apdev):
+ """AP with open mode and STA poll without ACK"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.set("ext_mgmt_frame_handling", "0")
+ if "OK" not in hapd.request("POLL_STA " + addr):
+ raise Exception("POLL_STA failed")
+ ev = hapd.wait_event(["AP-STA-POLL-OK"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected poll response reported")
+
+def test_ap_open_pmf_default(dev, apdev):
+ """AP with open mode (no security) configuration and pmf=2"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[1].connect("open", key_mgmt="NONE", scan_freq="2412",
+ ieee80211w="2", wait_connect=False)
+ dev[2].connect("open", key_mgmt="NONE", scan_freq="2412",
+ ieee80211w="1")
+ try:
+ dev[0].request("SET pmf 2")
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ finally:
+ dev[0].request("SET pmf 0")
+ dev[2].request("DISCONNECT")
+ dev[2].wait_disconnected()
+
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected dev[1] connection")
+ dev[1].request("DISCONNECT")
+
+def test_ap_open_drv_fail(dev, apdev):
+ """AP with open mode and driver operations failing"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+
+ with fail_test(dev[0], 1, "wpa_driver_nl80211_authenticate"):
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+
+ with fail_test(dev[0], 1, "wpa_driver_nl80211_associate"):
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+
+def run_multicast_to_unicast(dev, apdev, convert):
+ params = {"ssid": "open"}
+ params["multicast_to_unicast"] = "1" if convert else "0"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ hwsim_utils.test_connectivity(dev[0], hapd, multicast_to_unicast=convert)
+ dev[0].request("DISCONNECT")
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No disconnection event received from hostapd")
+
+def test_ap_open_multicast_to_unicast(dev, apdev):
+ """Multicast-to-unicast conversion enabled"""
+ run_multicast_to_unicast(dev, apdev, True)
+
+def test_ap_open_multicast_to_unicast_disabled(dev, apdev):
+ """Multicast-to-unicast conversion disabled"""
+ run_multicast_to_unicast(dev, apdev, False)
+
+def test_ap_open_drop_duplicate(dev, apdev, params):
+ """AP dropping duplicate management frames"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open",
+ "interworking": "1"})
+ hapd.set("ext_mgmt_frame_handling", "1")
+ bssid = hapd.own_addr().replace(':', '')
+ addr = "020304050607"
+ auth = "b0003a01" + bssid + addr + bssid + '1000000001000000'
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % auth):
+ raise Exception("MGMT_RX_PROCESS failed")
+ auth = "b0083a01" + bssid + addr + bssid + '1000000001000000'
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % auth):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ ies = "00046f70656e010802040b160c12182432043048606c2d1a3c101bffff0000000000000000000001000000000000000000007f0a04000a020140004000013b155151525354737475767778797a7b7c7d7e7f808182dd070050f202000100"
+ assoc_req = "00003a01" + bssid + addr + bssid + "2000" + "21040500" + ies
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % assoc_req):
+ raise Exception("MGMT_RX_PROCESS failed")
+ assoc_req = "00083a01" + bssid + addr + bssid + "2000" + "21040500" + ies
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % assoc_req):
+ raise Exception("MGMT_RX_PROCESS failed")
+ reassoc_req = "20083a01" + bssid + addr + bssid + "2000" + "21040500" + ies
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % reassoc_req):
+ raise Exception("MGMT_RX_PROCESS failed")
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % reassoc_req):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ action = "d0003a01" + bssid + addr + bssid + "1000" + "040a006c0200000600000102000101"
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % action):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ action = "d0083a01" + bssid + addr + bssid + "1000" + "040a006c0200000600000102000101"
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % action):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan.fc.type == 0", ["wlan.fc.subtype"])
+ num_auth = 0
+ num_assoc = 0
+ num_reassoc = 0
+ num_action = 0
+ for subtype in out.splitlines():
+ val = int(subtype)
+ if val == 11:
+ num_auth += 1
+ elif val == 1:
+ num_assoc += 1
+ elif val == 3:
+ num_reassoc += 1
+ elif val == 13:
+ num_action += 1
+ if num_auth != 1:
+ raise Exception("Unexpected number of Authentication frames: %d" % num_auth)
+ if num_assoc != 1:
+ raise Exception("Unexpected number of association frames: %d" % num_assoc)
+ if num_reassoc != 1:
+ raise Exception("Unexpected number of reassociation frames: %d" % num_reassoc)
+ if num_action != 1:
+ raise Exception("Unexpected number of Action frames: %d" % num_action)
+
+def test_ap_open_select_network_freq(dev, apdev):
+ """AP with open mode and use for SELECT_NETWORK freq parameter"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ id = dev[0].connect("open", key_mgmt="NONE", only_add_network=True)
+ dev[0].select_network(id, freq=2412)
+ start = os.times()[4]
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Scan not started")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=15)
+ if ev is None:
+ raise Exception("Scan not completed")
+ end = os.times()[4]
+ logger.info("Scan duration: {} seconds".format(end - start))
+ if end - start > 3:
+ raise Exception("Scan took unexpectedly long time")
+ dev[0].wait_connected()
+
+def test_ap_open_noncountry(dev, apdev):
+ """AP with open mode and noncountry entity as Country String"""
+ _test_ap_open_country(dev, apdev, "XX", "0x58")
+
+def test_ap_open_country_table_e4(dev, apdev):
+ """AP with open mode and Table E-4 Country String"""
+ _test_ap_open_country(dev, apdev, "DE", "0x04")
+
+def test_ap_open_country_indoor(dev, apdev):
+ """AP with open mode and indoor country code"""
+ _test_ap_open_country(dev, apdev, "DE", "0x49")
+
+def test_ap_open_country_outdoor(dev, apdev):
+ """AP with open mode and outdoor country code"""
+ _test_ap_open_country(dev, apdev, "DE", "0x4f")
+
+def _test_ap_open_country(dev, apdev, country_code, country3):
+ try:
+ hapd = None
+ hapd = run_ap_open_country(dev, apdev, country_code, country3)
+ finally:
+ clear_regdom(hapd, dev)
+
+def run_ap_open_country(dev, apdev, country_code, country3):
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open",
+ "country_code": country_code,
+ "country3": country3,
+ "ieee80211d": "1"})
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ dev[0].wait_regdom(country_ie=True)
+ return hapd
+
+def test_ap_open_disable_select(dev, apdev):
+ """DISABLE_NETWORK for connected AP followed by SELECT_NETWORK"""
+ hapd1 = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "open"})
+ id = dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+ dev[0].request("DISABLE_NETWORK %d" % id)
+ dev[0].wait_disconnected()
+ res = dev[0].request("BSSID_IGNORE")
+ if hapd1.own_addr() in res or hapd2.own_addr() in res:
+ raise Exception("Unexpected BSSID ignore list entry added")
+ dev[0].request("SELECT_NETWORK %d" % id)
+ dev[0].wait_connected()
+
+def test_ap_open_reassoc_same(dev, apdev):
+ """AP with open mode and STA reassociating back to same AP without auth exchange"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ try:
+ dev[0].request("SET reassoc_same_bss_optim 1")
+ dev[0].request("REATTACH")
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ dev[0].request("SET reassoc_same_bss_optim 0")
+
+def test_ap_open_no_reflection(dev, apdev):
+ """AP with open mode, STA sending packets to itself"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ # test normal connectivity is OK
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ # test that we can't talk to ourselves
+ addr = dev[0].own_addr()
+ res = dev[0].request('DATA_TEST_CONFIG 1')
+ try:
+ assert 'OK' in res
+
+ cmd = "DATA_TEST_TX {} {} {}".format(addr, addr, 0)
+ dev[0].request(cmd)
+
+ ev = dev[0].wait_event(["DATA-TEST-RX"], timeout=1)
+
+ if ev is not None and "DATA-TEST-RX {} {}".format(addr, addr) in ev:
+ raise Exception("STA can unexpectedly talk to itself")
+ finally:
+ dev[0].request('DATA_TEST_CONFIG 0')
+
+def test_ap_no_auth_ack(dev, apdev):
+ """AP not receiving Authentication frame ACK"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open",
+ "ap_max_inactivity": "1"})
+ hapd.set("ext_mgmt_frame_handling", "1")
+ bssid = hapd.own_addr()
+ addr = "02:01:02:03:04:05"
+ frame = "b0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000" + "000001000000"
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("TX status for Authentication frame not reported")
+ if "ok=0 buf=b0" not in ev:
+ raise Exception("Unexpected TX status contents: " + ev)
+
+ # wait for STA to be removed due to timeout
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("TX status for Deauthentication frame not reported")
+ if "ok=0 buf=c0" not in ev:
+ raise Exception("Unexpected TX status contents (disconnect): " + ev)
+
+def test_ap_open_layer_2_update(dev, apdev, params):
+ """AP with open mode (no security) and Layer 2 Update frame"""
+ prefix = "ap_open_layer_2_update"
+ ifname = apdev[0]["ifname"]
+ cap = os.path.join(params['logdir'], prefix + "." + ifname + ".pcap")
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ wt = WlantestCapture(ifname, cap)
+ time.sleep(1)
+
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ time.sleep(1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ time.sleep(0.5)
+ wt.close()
+
+ # Check for Layer 2 Update frame and unexpected frames from the station
+ # that did not fully complete authentication.
+ res = run_tshark(cap, "basicxid.llc.xid.format == 0x81",
+ ["eth.src"], wait=False)
+ real_sta_seen = False
+ unexpected_sta_seen = False
+ real_addr = dev[0].own_addr()
+ for l in res.splitlines():
+ if l == real_addr:
+ real_sta_seen = True
+ else:
+ unexpected_sta_seen = True
+ if unexpected_sta_seen:
+ raise Exception("Layer 2 Update frame from unexpected STA seen")
+ if not real_sta_seen:
+ raise Exception("Layer 2 Update frame from real STA not seen")
diff --git a/contrib/wpa/tests/hwsim/test_ap_params.py b/contrib/wpa/tests/hwsim/test_ap_params.py
new file mode 100644
index 000000000000..72ac8e443ff9
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_params.py
@@ -0,0 +1,972 @@
+# Test various AP mode parameters
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import os
+import struct
+import subprocess
+import time
+
+import hwsim_utils
+import hostapd
+from tshark import run_tshark
+from utils import *
+
+@remote_compatible
+def test_ap_fragmentation_rts_set_high(dev, apdev):
+ """WPA2-PSK AP with fragmentation and RTS thresholds larger than frame length"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['rts_threshold'] = "1000"
+ params['fragm_threshold'] = "2000"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[0].request("DISCONNECT")
+ hapd.disable()
+ hapd.set('fragm_threshold', '-1')
+ hapd.set('rts_threshold', '-1')
+ hapd.enable()
+
+@remote_compatible
+def test_ap_fragmentation_open(dev, apdev):
+ """Open AP with fragmentation threshold"""
+ ssid = "fragmentation"
+ params = {}
+ params['ssid'] = ssid
+ params['fragm_threshold'] = "1000"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[0].request("DISCONNECT")
+ hapd.disable()
+ hapd.set('fragm_threshold', '-1')
+ hapd.enable()
+
+@remote_compatible
+def test_ap_fragmentation_wpa2(dev, apdev):
+ """WPA2-PSK AP with fragmentation threshold"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['fragm_threshold'] = "1000"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[0].request("DISCONNECT")
+ hapd.disable()
+ hapd.set('fragm_threshold', '-1')
+ hapd.enable()
+
+def test_ap_vendor_elements(dev, apdev):
+ """WPA2-PSK AP with vendor elements added"""
+ bssid = apdev[0]['bssid']
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['vendor_elements'] = "dd0411223301"
+ params['assocresp_elements'] = "dd0411223302"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ bss = dev[0].get_bss(bssid)
+ if "dd0411223301" not in bss['ie']:
+ raise Exception("Vendor element not shown in scan results")
+
+ hapd.set('vendor_elements', 'dd051122330203dd0400137400dd04001374ff')
+ if "OK" not in hapd.request("UPDATE_BEACON"):
+ raise Exception("UPDATE_BEACON failed")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ bss = dev[1].get_bss(bssid)
+ if "dd0411223301" in bss['ie']:
+ raise Exception("Old vendor element still in scan results")
+ if "dd051122330203" not in bss['ie']:
+ raise Exception("New vendor element not shown in scan results")
+
+def test_ap_element_parse(dev, apdev):
+ """Information element parsing - extra coverage"""
+ bssid = apdev[0]['bssid']
+ ssid = "test-wpa2-psk"
+ params = {'ssid': ssid,
+ 'vendor_elements': "380501020304059e009e009e009e009e009e00"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ bss = dev[0].get_bss(bssid)
+ if "38050102030405" not in bss['ie']:
+ raise Exception("Timeout element not shown in scan results")
+
+@remote_compatible
+def test_ap_element_parse_oom(dev, apdev):
+ """Information element parsing OOM"""
+ bssid = apdev[0]['bssid']
+ ssid = "test-wpa2-psk"
+ params = {'ssid': ssid,
+ 'vendor_elements': "dd0d506f9a0a00000600411c440028"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;ieee802_11_vendor_ie_concat"):
+ bss = dev[0].get_bss(bssid)
+ logger.info(str(bss))
+
+def test_ap_country(dev, apdev):
+ """WPA2-PSK AP setting country code and using 5 GHz band"""
+ try:
+ hapd = None
+ bssid = apdev[0]['bssid']
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['country_code'] = 'FI'
+ params['ieee80211d'] = '1'
+ params['hw_mode'] = 'a'
+ params['channel'] = '36'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ hostapd.cmd_execute(apdev[0], ['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def test_ap_acl_accept(dev, apdev):
+ """MAC ACL accept list"""
+ ssid = "acl"
+ params = {}
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.macaddr')
+ hostapd.send_file(apdev[0], filename, filename)
+ params['ssid'] = ssid
+ params['accept_mac_file'] = filename
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ hapd.request("SET macaddr_acl 1")
+ dev[1].dump_monitor()
+ dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected association")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_acl_deny(dev, apdev):
+ """MAC ACL deny list"""
+ ssid = "acl"
+ params = {}
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.macaddr')
+ hostapd.send_file(apdev[0], filename, filename)
+ params['ssid'] = ssid
+ params['deny_mac_file'] = filename
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", passive=True)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected association")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_acl_mgmt(dev, apdev):
+ """MAC ACL accept/deny management"""
+ ssid = "acl"
+ params = {}
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.macaddr')
+ hostapd.send_file(apdev[0], filename, filename)
+ params['ssid'] = ssid
+ params['deny_mac_file'] = filename
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ accept = hapd.request("ACCEPT_ACL SHOW").splitlines()
+ logger.info("accept: " + str(accept))
+ deny = hapd.request("DENY_ACL SHOW").splitlines()
+ logger.info("deny: " + str(deny))
+ if len(accept) != 0:
+ raise Exception("Unexpected number of accept entries")
+ if len(deny) != 3:
+ raise Exception("Unexpected number of deny entries")
+ if "01:01:01:01:01:01 VLAN_ID=0" not in deny:
+ raise Exception("Missing deny entry")
+
+ if "OK" not in hapd.request("ACCEPT_ACL DEL_MAC 22:33:44:55:66:77"):
+ raise Exception("DEL_MAC with empty list failed")
+ if "FAIL" not in hapd.request("ACCEPT_ACL ADD_MAC 22:33:44:55:66"):
+ raise Exception("ADD_MAC with invalid MAC address accepted")
+ hapd.request("ACCEPT_ACL ADD_MAC 22:33:44:55:66:77")
+ if "FAIL" not in hapd.request("ACCEPT_ACL DEL_MAC 22:33:44:55:66"):
+ raise Exception("DEL_MAC with invalid MAC address accepted")
+ hapd.request("DENY_ACL ADD_MAC 22:33:44:55:66:88 VLAN_ID=2")
+
+ accept = hapd.request("ACCEPT_ACL SHOW").splitlines()
+ logger.info("accept: " + str(accept))
+ deny = hapd.request("DENY_ACL SHOW").splitlines()
+ logger.info("deny: " + str(deny))
+ if len(accept) != 1:
+ raise Exception("Unexpected number of accept entries (2)")
+ if len(deny) != 4:
+ raise Exception("Unexpected number of deny entries (2)")
+ if "01:01:01:01:01:01 VLAN_ID=0" not in deny:
+ raise Exception("Missing deny entry (2)")
+ if "22:33:44:55:66:88 VLAN_ID=2" not in deny:
+ raise Exception("Missing deny entry (2)")
+ if "22:33:44:55:66:77 VLAN_ID=0" not in accept:
+ raise Exception("Missing accept entry (2)")
+
+ hapd.request("ACCEPT_ACL DEL_MAC 22:33:44:55:66:77")
+ hapd.request("DENY_ACL DEL_MAC 22:33:44:55:66:88")
+
+ accept = hapd.request("ACCEPT_ACL SHOW").splitlines()
+ logger.info("accept: " + str(accept))
+ deny = hapd.request("DENY_ACL SHOW").splitlines()
+ logger.info("deny: " + str(deny))
+ if len(accept) != 0:
+ raise Exception("Unexpected number of accept entries (3)")
+ if len(deny) != 3:
+ raise Exception("Unexpected number of deny entries (3)")
+ if "01:01:01:01:01:01 VLAN_ID=0" not in deny:
+ raise Exception("Missing deny entry (3)")
+
+ hapd.request("ACCEPT_ACL CLEAR")
+ hapd.request("DENY_ACL CLEAR")
+
+ accept = hapd.request("ACCEPT_ACL SHOW").splitlines()
+ logger.info("accept: " + str(accept))
+ deny = hapd.request("DENY_ACL SHOW").splitlines()
+ logger.info("deny: " + str(deny))
+ if len(accept) != 0:
+ raise Exception("Unexpected number of accept entries (4)")
+ if len(deny) != 0:
+ raise Exception("Unexpected number of deny entries (4)")
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ dev[0].dump_monitor()
+ hapd.request("DENY_ACL ADD_MAC " + dev[0].own_addr())
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_acl_accept_changes(dev, apdev):
+ """MAC ACL accept list changes"""
+ ssid = "acl"
+ params = {}
+ params['ssid'] = ssid
+ params['macaddr_acl'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("ACCEPT_ACL ADD_MAC " + dev[0].own_addr())
+ hapd.request("ACCEPT_ACL ADD_MAC " + dev[1].own_addr())
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ hapd.request("ACCEPT_ACL DEL_MAC " + dev[0].own_addr())
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+ ev = dev[1].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ hapd.request("ACCEPT_ACL CLEAR")
+ dev[1].wait_disconnected()
+ dev[1].request("DISCONNECT")
+
+@remote_compatible
+def test_ap_wds_sta(dev, apdev):
+ """WPA2-PSK AP with STA using 4addr mode"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wds_sta'] = "1"
+ params['wds_bridge'] = "wds-br0"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ dev[0].cmd_execute(['brctl', 'addbr', 'wds-br0'])
+ dev[0].cmd_execute(['brctl', 'setfd', 'wds-br0', '0'])
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'up'])
+ dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'on'])
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ ev = hapd.wait_event(["WDS-STA-INTERFACE-ADDED"], timeout=10)
+ if ev is None:
+ raise Exception("No WDS-STA-INTERFACE-ADDED event seen")
+ if "sta_addr=" + dev[0].own_addr() not in ev:
+ raise Exception("No sta_addr match in " + ev)
+ if "ifname=" + hapd.ifname + ".sta" not in ev:
+ raise Exception("No ifname match in " + ev)
+ sta = hapd.get_sta(dev[0].own_addr())
+ if "wds_sta_ifname" not in sta:
+ raise Exception("Missing wds_sta_ifname in STA data")
+ if "ifname=" + sta['wds_sta_ifname'] not in ev:
+ raise Exception("wds_sta_ifname %s not in event: %s" %
+ (sta['wds_sta_ifname'], ev))
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+ max_tries=15)
+ dev[0].request("REATTACH")
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+ max_tries=15)
+ dev[0].request("SET reassoc_same_bss_optim 1")
+ dev[0].request("REATTACH")
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+ max_tries=5, timeout=1)
+ finally:
+ dev[0].request("SET reassoc_same_bss_optim 0")
+ dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'off'])
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'down'])
+ dev[0].cmd_execute(['brctl', 'delbr', 'wds-br0'])
+
+def test_ap_wds_sta_eap(dev, apdev):
+ """WPA2-EAP AP with STA using 4addr mode"""
+ ssid = "test-wpa2-eap"
+ params = hostapd.wpa2_eap_params(ssid=ssid)
+ params['wds_sta'] = "1"
+ params['wds_bridge'] = "wds-br0"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ dev[0].cmd_execute(['brctl', 'addbr', 'wds-br0'])
+ dev[0].cmd_execute(['brctl', 'setfd', 'wds-br0', '0'])
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'up'])
+ dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'on'])
+ dev[0].connect(ssid, key_mgmt="WPA-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ ev = hapd.wait_event(["WDS-STA-INTERFACE-ADDED"], timeout=10)
+ if ev is None:
+ raise Exception("No WDS-STA-INTERFACE-ADDED event seen")
+ if "sta_addr=" + dev[0].own_addr() not in ev:
+ raise Exception("No sta_addr match in " + ev)
+ if "ifname=" + hapd.ifname + ".sta" not in ev:
+ raise Exception("No ifname match in " + ev)
+ sta = hapd.get_sta(dev[0].own_addr())
+ if "wds_sta_ifname" not in sta:
+ raise Exception("Missing wds_sta_ifname in STA data")
+ if "ifname=" + sta['wds_sta_ifname'] not in ev:
+ raise Exception("wds_sta_ifname %s not in event: %s" %
+ (sta['wds_sta_ifname'], ev))
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+ max_tries=15)
+ finally:
+ dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'off'])
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'down'])
+ dev[0].cmd_execute(['brctl', 'delbr', 'wds-br0'])
+
+def test_ap_wds_sta_open(dev, apdev):
+ """Open AP with STA using 4addr mode"""
+ ssid = "test-wds-open"
+ params = {}
+ params['ssid'] = ssid
+ params['wds_sta'] = "1"
+ params['wds_bridge'] = "wds-br0"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ dev[0].cmd_execute(['brctl', 'addbr', 'wds-br0'])
+ dev[0].cmd_execute(['brctl', 'setfd', 'wds-br0', '0'])
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'up'])
+ dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'on'])
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+ max_tries=15)
+ dev[0].request("REATTACH")
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+ max_tries=15)
+ dev[0].request("SET reassoc_same_bss_optim 1")
+ dev[0].request("REATTACH")
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+ max_tries=5, timeout=1)
+ finally:
+ dev[0].request("SET reassoc_same_bss_optim 0")
+ dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'off'])
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'down'])
+ dev[0].cmd_execute(['brctl', 'delbr', 'wds-br0'])
+
+def test_ap_wds_sta_wep(dev, apdev):
+ """WEP AP with STA using 4addr mode"""
+ check_wep_capa(dev[0])
+ ssid = "test-wds-wep"
+ params = {}
+ params['ssid'] = ssid
+ params["ieee80211n"] = "0"
+ params['wep_key0'] = '"hello"'
+ params['wds_sta'] = "1"
+ params['wds_bridge'] = "wds-br0"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ dev[0].cmd_execute(['brctl', 'addbr', 'wds-br0'])
+ dev[0].cmd_execute(['brctl', 'setfd', 'wds-br0', '0'])
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'up'])
+ dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'on'])
+ dev[0].connect(ssid, key_mgmt="NONE", wep_key0='"hello"',
+ scan_freq="2412")
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+ max_tries=15)
+ dev[0].request("REATTACH")
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+ max_tries=15)
+ dev[0].request("SET reassoc_same_bss_optim 1")
+ dev[0].request("REATTACH")
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "wds-br0",
+ max_tries=5, timeout=1)
+ finally:
+ dev[0].request("SET reassoc_same_bss_optim 0")
+ dev[0].cmd_execute(['iw', dev[0].ifname, 'set', '4addr', 'off'])
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'wds-br0', 'down'])
+ dev[0].cmd_execute(['brctl', 'delbr', 'wds-br0'])
+
+@remote_compatible
+def test_ap_inactivity_poll(dev, apdev):
+ """AP using inactivity poll"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['ap_max_inactivity'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("DISCONNECT")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT RX wait timed out for Deauth")
+ hapd.set("ext_mgmt_frame_handling", "0")
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=30)
+ if ev is None:
+ raise Exception("STA disconnection on inactivity was not reported")
+
+@remote_compatible
+def test_ap_inactivity_disconnect(dev, apdev):
+ """AP using inactivity disconnect"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['ap_max_inactivity'] = "1"
+ params['skip_inactivity_poll'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("DISCONNECT")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT RX wait timed out for Deauth")
+ hapd.set("ext_mgmt_frame_handling", "0")
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=30)
+ if ev is None:
+ raise Exception("STA disconnection on inactivity was not reported")
+
+@remote_compatible
+def test_ap_basic_rates(dev, apdev):
+ """Open AP with lots of basic rates"""
+ ssid = "basic rates"
+ params = {}
+ params['ssid'] = ssid
+ params['basic_rates'] = "10 20 55 110 60 90 120 180 240 360 480 540"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+@remote_compatible
+def test_ap_short_preamble(dev, apdev):
+ """Open AP with short preamble"""
+ ssid = "short preamble"
+ params = {}
+ params['ssid'] = ssid
+ params['preamble'] = "1"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+def test_ap_spectrum_management_required(dev, apdev):
+ """Open AP with spectrum management required"""
+ ssid = "spectrum mgmt"
+ params = {}
+ params['ssid'] = ssid
+ params["country_code"] = "JP"
+ params["hw_mode"] = "a"
+ params["channel"] = "36"
+ params["ieee80211d"] = "1"
+ params["local_pwr_constraint"] = "3"
+ params['spectrum_mgmt_required'] = "1"
+ try:
+ hapd = None
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="5180")
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ hostapd.cmd_execute(apdev[0], ['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+@remote_compatible
+def test_ap_max_listen_interval(dev, apdev):
+ """Open AP with maximum listen interval limit"""
+ ssid = "listen"
+ params = {}
+ params['ssid'] = ssid
+ params['max_listen_interval'] = "1"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+ if ev is None:
+ raise Exception("Association rejection not reported")
+ if "status_code=51" not in ev:
+ raise Exception("Unexpected ASSOC-REJECT reason")
+
+@remote_compatible
+def test_ap_max_num_sta(dev, apdev):
+ """Open AP with maximum STA count"""
+ ssid = "max"
+ params = {}
+ params['ssid'] = ssid
+ params['max_num_sta'] = "1"
+ hostapd.add_ap(apdev[0], params)
+ dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected association")
+
+def test_ap_max_num_sta_no_probe_resp(dev, apdev, params):
+ """Maximum STA count and limit on Probe Response frames"""
+ logdir = params['logdir']
+ dev[0].flush_scan_cache()
+ ssid = "max"
+ params = {}
+ params['ssid'] = ssid
+ params['beacon_int'] = "2000"
+ params['max_num_sta'] = "1"
+ params['no_probe_resp_if_max_sta'] = "1"
+ hostapd.add_ap(apdev[0], params)
+ dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ dev[0].scan(freq=2412, type="ONLY")
+ dev[0].scan(freq=2412, type="ONLY")
+ seen = dev[0].get_bss(apdev[0]['bssid']) != None
+ dev[1].scan(freq=2412, type="ONLY")
+ if seen:
+ out = run_tshark(os.path.join(logdir, "hwsim0.pcapng"),
+ "wlan.fc.type_subtype == 5", ["wlan.da"])
+ if out:
+ if dev[0].own_addr() not in out:
+ # Discovery happened through Beacon frame reception. That's not
+ # an error case.
+ seen = False
+ if dev[1].own_addr() not in out:
+ raise Exception("No Probe Response frames to dev[1] seen")
+ if seen:
+ raise Exception("AP found unexpectedly")
+
+@remote_compatible
+def test_ap_tx_queue_params(dev, apdev):
+ """Open AP with TX queue params set"""
+ ssid = "tx"
+ params = {}
+ params['ssid'] = ssid
+ params['tx_queue_data2_aifs'] = "4"
+ params['tx_queue_data2_cwmin'] = "7"
+ params['tx_queue_data2_cwmax'] = "1023"
+ params['tx_queue_data2_burst'] = "4.2"
+ params['tx_queue_data1_aifs'] = "4"
+ params['tx_queue_data1_cwmin'] = "7"
+ params['tx_queue_data1_cwmax'] = "1023"
+ params['tx_queue_data1_burst'] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_tx_queue_params_invalid(dev, apdev):
+ """Invalid TX queue params set (cwmin/cwmax)"""
+ ssid = "tx"
+ params = {}
+ params['ssid'] = ssid
+ params['tx_queue_data2_aifs'] = "4"
+ params['tx_queue_data2_cwmin'] = "7"
+ params['tx_queue_data2_cwmax'] = "1023"
+ params['tx_queue_data2_burst'] = "4.2"
+ params['wmm_ac_bk_cwmin'] = "4"
+ params['wmm_ac_bk_cwmax'] = "10"
+ params['wmm_ac_bk_aifs'] = "7"
+ params['wmm_ac_bk_txop_limit'] = "0"
+ params['wmm_ac_bk_acm'] = "0"
+
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # Valid WMM change
+ hapd.set("wmm_ac_be_cwmin", "3")
+
+ # "Invalid TX queue cwMin/cwMax values. cwMin(7) greater than cwMax(3)"
+ if "FAIL" not in hapd.request('SET tx_queue_data2_cwmax 3'):
+ raise Exception("TX cwMax < cwMin accepted")
+ # "Invalid WMM AC cwMin/cwMax values. cwMin(4) greater than cwMax(3)"
+ if "FAIL" not in hapd.request('SET wmm_ac_bk_cwmax 3'):
+ raise Exception("AC cwMax < cwMin accepted")
+
+ hapd.request("SET tx_queue_data2_cwmax 1023")
+ hapd.set("wmm_ac_bk_cwmax", "10")
+ # Invalid IEs to cause WMM parameter update failing
+ hapd.set("vendor_elements", "dd04112233")
+ hapd.set("wmm_ac_be_cwmin", "3")
+ # Valid IEs to cause WMM parameter update succeeding
+ hapd.set("vendor_elements", "dd0411223344")
+ hapd.set("wmm_ac_be_cwmin", "3")
+
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+def test_ap_beacon_rate_legacy(dev, apdev):
+ """Open AP with Beacon frame TX rate 5.5 Mbps"""
+ hapd = hostapd.add_ap(apdev[0], {'ssid': 'beacon-rate'})
+ res = hapd.get_driver_status_field('capa.flags')
+ if (int(res, 0) & 0x0000080000000000) == 0:
+ raise HwsimSkip("Setting Beacon frame TX rate not supported")
+ hapd.disable()
+ hapd.set('beacon_rate', '55')
+ hapd.enable()
+ dev[0].connect('beacon-rate', key_mgmt="NONE", scan_freq="2412")
+ time.sleep(0.5)
+
+def test_ap_beacon_rate_legacy2(dev, apdev):
+ """Open AP with Beacon frame TX rate 12 Mbps in VHT BSS"""
+ hapd = hostapd.add_ap(apdev[0], {'ssid': 'beacon-rate'})
+ res = hapd.get_driver_status_field('capa.flags')
+ if (int(res, 0) & 0x0000080000000000) == 0:
+ raise HwsimSkip("Setting Beacon frame TX rate not supported")
+ hapd.disable()
+ hapd.set('beacon_rate', '120')
+ hapd.set("country_code", "DE")
+ hapd.set("hw_mode", "a")
+ hapd.set("channel", "36")
+ hapd.set("ieee80211n", "1")
+ hapd.set("ieee80211ac", "1")
+ hapd.set("ht_capab", "[HT40+]")
+ hapd.set("vht_capab", "")
+ hapd.set("vht_oper_chwidth", "0")
+ hapd.set("vht_oper_centr_freq_seg0_idx", "0")
+ try:
+ hapd.enable()
+ dev[0].scan_for_bss(hapd.own_addr(), freq="5180")
+ dev[0].connect('beacon-rate', key_mgmt="NONE", scan_freq="5180")
+ time.sleep(0.5)
+ finally:
+ dev[0].request("DISCONNECT")
+ hapd.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+
+def test_ap_beacon_rate_ht(dev, apdev):
+ """Open AP with Beacon frame TX rate HT-MCS 0"""
+ hapd = hostapd.add_ap(apdev[0], {'ssid': 'beacon-rate'})
+ res = hapd.get_driver_status_field('capa.flags')
+ if (int(res, 0) & 0x0000100000000000) == 0:
+ raise HwsimSkip("Setting Beacon frame TX rate not supported")
+ hapd.disable()
+ hapd.set('beacon_rate', 'ht:0')
+ hapd.enable()
+ dev[0].connect('beacon-rate', key_mgmt="NONE", scan_freq="2412")
+ time.sleep(0.5)
+
+def test_ap_beacon_rate_ht2(dev, apdev):
+ """Open AP with Beacon frame TX rate HT-MCS 1 in VHT BSS"""
+ hapd = hostapd.add_ap(apdev[0], {'ssid': 'beacon-rate'})
+ res = hapd.get_driver_status_field('capa.flags')
+ if (int(res, 0) & 0x0000100000000000) == 0:
+ raise HwsimSkip("Setting Beacon frame TX rate not supported")
+ hapd.disable()
+ hapd.set('beacon_rate', 'ht:1')
+ hapd.set("country_code", "DE")
+ hapd.set("hw_mode", "a")
+ hapd.set("channel", "36")
+ hapd.set("ieee80211n", "1")
+ hapd.set("ieee80211ac", "1")
+ hapd.set("ht_capab", "[HT40+]")
+ hapd.set("vht_capab", "")
+ hapd.set("vht_oper_chwidth", "0")
+ hapd.set("vht_oper_centr_freq_seg0_idx", "0")
+ try:
+ hapd.enable()
+ dev[0].scan_for_bss(hapd.own_addr(), freq="5180")
+ dev[0].connect('beacon-rate', key_mgmt="NONE", scan_freq="5180")
+ time.sleep(0.5)
+ finally:
+ dev[0].request("DISCONNECT")
+ hapd.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+
+def test_ap_beacon_rate_vht(dev, apdev):
+ """Open AP with Beacon frame TX rate VHT-MCS 0"""
+ hapd = hostapd.add_ap(apdev[0], {'ssid': 'beacon-rate'})
+ res = hapd.get_driver_status_field('capa.flags')
+ if (int(res, 0) & 0x0000200000000000) == 0:
+ raise HwsimSkip("Setting Beacon frame TX rate not supported")
+ hapd.disable()
+ hapd.set('beacon_rate', 'vht:0')
+ hapd.set("country_code", "DE")
+ hapd.set("hw_mode", "a")
+ hapd.set("channel", "36")
+ hapd.set("ieee80211n", "1")
+ hapd.set("ieee80211ac", "1")
+ hapd.set("ht_capab", "[HT40+]")
+ hapd.set("vht_capab", "")
+ hapd.set("vht_oper_chwidth", "0")
+ hapd.set("vht_oper_centr_freq_seg0_idx", "0")
+ try:
+ hapd.enable()
+ dev[0].scan_for_bss(hapd.own_addr(), freq="5180")
+ dev[0].connect('beacon-rate', key_mgmt="NONE", scan_freq="5180")
+ time.sleep(0.5)
+ finally:
+ dev[0].request("DISCONNECT")
+ hapd.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+
+def test_ap_wep_to_wpa(dev, apdev):
+ """WEP to WPA2-PSK configuration change in hostapd"""
+ check_wep_capa(dev[0])
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wep-to-wpa",
+ "wep_key0": '"hello"'})
+ dev[0].flush_scan_cache()
+ dev[0].connect("wep-to-wpa", key_mgmt="NONE", wep_key0='"hello"',
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ hapd.disable()
+ hapd.set("wep_key0", "")
+ hapd.set("wpa_passphrase", "12345678")
+ hapd.set("wpa", "2")
+ hapd.set("wpa_key_mgmt", "WPA-PSK")
+ hapd.set("rsn_pairwise", "CCMP")
+ hapd.enable()
+
+ dev[0].connect("wep-to-wpa", psk="12345678", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_missing_psk(dev, apdev):
+ """WPA2-PSK AP and no PSK configured"""
+ ssid = "test-wpa2-psk"
+ params = hostapd.wpa2_params(ssid=ssid)
+ try:
+ # "WPA-PSK enabled, but PSK or passphrase is not configured."
+ hostapd.add_ap(apdev[0], params)
+ raise Exception("AP setup succeeded unexpectedly")
+ except Exception as e:
+ if "Failed to enable hostapd" in str(e):
+ pass
+ else:
+ raise
+
+def test_ap_eapol_version(dev, apdev):
+ """hostapd eapol_version configuration"""
+ passphrase = "asdfghjkl"
+ params = hostapd.wpa2_params(ssid="test1", passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ params = hostapd.wpa2_params(ssid="test2", passphrase=passphrase)
+ params['eapol_version'] = '1'
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].connect("test1", psk=passphrase, scan_freq="2412",
+ wait_connect=False)
+ ev1 = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev1 is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ hapd.request("SET ext_eapol_frame_io 0")
+
+ hapd2.request("SET ext_eapol_frame_io 1")
+ dev[1].connect("test2", psk=passphrase, scan_freq="2412",
+ wait_connect=False)
+ ev2 = hapd2.wait_event(["EAPOL-TX"], timeout=15)
+ if ev2 is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ hapd2.request("SET ext_eapol_frame_io 0")
+
+ dev[0].wait_connected()
+ dev[1].wait_connected()
+
+ ver1 = ev1.split(' ')[2][0:2]
+ ver2 = ev2.split(' ')[2][0:2]
+ if ver1 != "02":
+ raise Exception("Unexpected default eapol_version: " + ver1)
+ if ver2 != "01":
+ raise Exception("eapol_version did not match configuration: " + ver2)
+
+def test_ap_dtim_period(dev, apdev):
+ """DTIM period configuration"""
+ ssid = "dtim-period"
+ params = {'ssid': ssid, 'dtim_period': "10"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ for i in range(10):
+ dev[0].scan(freq="2412")
+ bss = dev[0].get_bss(bssid)
+ if 'beacon_ie' in bss:
+ break
+ time.sleep(0.2)
+ if 'beacon_ie' not in bss:
+ raise Exception("Did not find Beacon IEs")
+
+ ie = parse_ie(bss['beacon_ie'])
+ if 5 not in ie:
+ raise Exception("TIM element missing")
+ count, period = struct.unpack('BB', ie[5][0:2])
+ logger.info("DTIM count %d DTIM period %d" % (count, period))
+ if period != 10:
+ raise Exception("Unexpected DTIM period: %d" % period)
+ if count >= period:
+ raise Exception("Unexpected DTIM count: %d" % count)
+
+def test_ap_no_probe_resp(dev, apdev):
+ """AP with Probe Response frame sending from hostapd disabled"""
+ ssid = "no-probe-resp"
+ params = {'ssid': ssid, 'send_probe_response': "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].scan_for_bss(bssid, freq="2412", passive=True)
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ bss = dev[0].get_bss(bssid)
+ if 'ie' in bss and 'beacon_ie' in bss and \
+ len(bss['ie']) != len(bss['beacon_ie']):
+ raise Exception("Probe Response frames seen")
+
+def test_ap_long_preamble(dev, apdev):
+ """AP with long preamble"""
+ ssid = "long-preamble"
+ params = {'ssid': ssid, 'preamble': "0",
+ 'hw_mode': 'b', 'ieee80211n': '0',
+ 'supported_rates': '10', 'basic_rates': '10'}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wmm_uapsd(dev, apdev):
+ """AP with U-APSD advertisement"""
+ ssid = "uapsd"
+ params = {'ssid': ssid, 'uapsd_advertisement_enabled': "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wowlan_triggers(dev, apdev):
+ """AP with wowlan_triggers"""
+ ssid = "wowlan"
+ params = {'ssid': ssid, 'wowlan_triggers': "any"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_notify_mgmt_frames(dev, apdev):
+ """hostapd notify_mgmt_frames configuration enabled"""
+ ssid = "mgmt_frames"
+ params = {'ssid': ssid, 'notify_mgmt_frames': "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ ev = hapd.wait_event(["AP-MGMT-FRAME-RECEIVED"], timeout=5)
+ if ev is None:
+ raise Exception("AP-MGMT-FRAME-RECEIVED wait timed out")
+ if "buf=b0" not in ev:
+ raise Exception("Expected auth request in AP-MGMT-FRAME-RECEIVED")
+
+def test_ap_notify_mgmt_frames_disabled(dev, apdev):
+ """hostapd notify_mgmt_frames configuration disabled"""
+ ssid = "mgmt_frames"
+ params = {'ssid': ssid, 'notify_mgmt_frames': "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ ev = hapd.wait_event(["AP-MGMT-FRAME-RECEIVED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected AP-MGMT-FRAME-RECEIVED")
+
+def test_ap_airtime_policy_static(dev, apdev):
+ """Airtime policy - static"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['airtime_mode'] = "1"
+ params['airtime_update_interval'] = "200"
+ params['airtime_sta_weight'] = dev[0].own_addr() + " 512"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ time.sleep(1)
+
+def test_ap_airtime_policy_per_bss_dynamic(dev, apdev):
+ """Airtime policy - per-BSS dynamic"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['airtime_mode'] = "2"
+ params['airtime_update_interval'] = "200"
+ params['airtime_bss_weight'] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ time.sleep(1)
+
+def test_ap_airtime_policy_per_bss_limit(dev, apdev):
+ """Airtime policy - per-BSS limit"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['airtime_mode'] = "3"
+ params['airtime_update_interval'] = "200"
+ params['airtime_bss_weight'] = "2"
+ params['airtime_bss_limit'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ time.sleep(1)
+ hapd.set("force_backlog_bytes", "1")
+ time.sleep(1)
+
+def test_ap_airtime_policy_per_bss_limit_invalid(dev, apdev):
+ """Airtime policy - per-BSS limit (invalid)"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['airtime_mode'] = "3"
+ params['airtime_update_interval'] = "0"
+ params['airtime_bss_weight'] = "2"
+ params['airtime_bss_limit'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Invalid airtime policy configuration accepted")
+ hapd.set("airtime_update_interval", "200")
+ hapd.enable()
+ hapd.set("airtime_update_interval", "0")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ time.sleep(1)
diff --git a/contrib/wpa/tests/hwsim/test_ap_pmf.py b/contrib/wpa/tests/hwsim/test_ap_pmf.py
new file mode 100644
index 000000000000..6c2a58ac4df2
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_pmf.py
@@ -0,0 +1,1204 @@
+# Protected management frames tests
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import os
+import time
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import *
+from wlantest import Wlantest
+from wpasupplicant import WpaSupplicant
+
+@remote_compatible
+def test_ap_pmf_required(dev, apdev):
+ """WPA2-PSK AP with PMF required"""
+ ssid = "test-pmf-required"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "WPA-PSK-SHA256":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ if "[WPA2-PSK-SHA256-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing RSN element info")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[1].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[1], hapd)
+ if "OK" not in hapd.request("SA_QUERY " + dev[0].own_addr()):
+ raise Exception("SA_QUERY failed")
+ if "OK" not in hapd.request("SA_QUERY " + dev[1].own_addr()):
+ raise Exception("SA_QUERY failed")
+ if "FAIL" not in hapd.request("SA_QUERY foo"):
+ raise Exception("Invalid SA_QUERY accepted")
+ wt.require_ap_pmf_mandatory(apdev[0]['bssid'])
+ wt.require_sta_pmf(apdev[0]['bssid'], dev[0].p2p_interface_addr())
+ wt.require_sta_pmf_mandatory(apdev[0]['bssid'], dev[1].p2p_interface_addr())
+ time.sleep(0.1)
+ if wt.get_sta_counter("valid_saqueryresp_tx", apdev[0]['bssid'],
+ dev[0].p2p_interface_addr()) < 1:
+ raise Exception("STA did not reply to SA Query")
+ if wt.get_sta_counter("valid_saqueryresp_tx", apdev[0]['bssid'],
+ dev[1].p2p_interface_addr()) < 1:
+ raise Exception("STA did not reply to SA Query")
+
+def start_ocv_ap(apdev):
+ ssid = "test-pmf-required"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ params["ocv"] = "1"
+ try:
+ hapd = hostapd.add_ap(apdev, params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ return hapd, ssid, wt
+
+@remote_compatible
+def test_ocv_sa_query(dev, apdev):
+ """Test SA Query with OCV"""
+ hapd, ssid, wt = start_ocv_ap(apdev[0])
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1", ocv="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+
+ # Test that client can handle SA Query with OCI element
+ if "OK" not in hapd.request("SA_QUERY " + dev[0].own_addr()):
+ raise Exception("SA_QUERY failed")
+ ev = hapd.wait_event(["OCV-FAILURE"], timeout=0.1)
+ if ev:
+ raise Exception("Unexpected OCV failure reported")
+ if wt.get_sta_counter("valid_saqueryresp_tx", apdev[0]['bssid'],
+ dev[0].own_addr()) < 1:
+ raise Exception("STA did not reply to SA Query")
+
+ # Test that AP can handle SA Query with OCI element
+ if "OK" not in dev[0].request("UNPROT_DEAUTH"):
+ raise Exception("Triggering SA Query from the STA failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=3)
+ if ev is not None:
+ raise Exception("SA Query from the STA failed")
+
+@remote_compatible
+def test_ocv_sa_query_csa(dev, apdev):
+ """Test SA Query with OCV after channel switch"""
+ hapd, ssid, wt = start_ocv_ap(apdev[0])
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1", ocv="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+
+ hapd.request("CHAN_SWITCH 5 2437")
+ time.sleep(1)
+ if wt.get_sta_counter("valid_saqueryreq_tx", apdev[0]['bssid'],
+ dev[0].own_addr()) < 1:
+ raise Exception("STA did not start SA Query after channel switch")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=16)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+def test_ocv_sa_query_csa_no_resp(dev, apdev):
+ """Test SA Query with OCV after channel switch getting no response"""
+ hapd, ssid, wt = start_ocv_ap(apdev[0])
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1", ocv="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ hapd.request("CHAN_SWITCH 5 2437")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnection after CSA not reported")
+ if "locally_generated=1" not in ev:
+ raise Exception("Unexpectedly disconnected by AP: " + ev)
+
+def test_ocv_sa_query_csa_missing(dev, apdev):
+ """Test SA Query with OCV missing after channel switch"""
+ hapd, ssid, wt = start_ocv_ap(apdev[0])
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1", ocv="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ ev = hapd.wait_event(['MGMT-RX'], timeout=5)
+ if ev is None:
+ raise Exception("Deauthentication frame RX not reported")
+ hapd.set("ext_mgmt_frame_handling", "0")
+ hapd.request("CHAN_SWITCH 5 2437")
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("No disconnection event received from hostapd")
+
+@remote_compatible
+def test_ap_pmf_optional(dev, apdev):
+ """WPA2-PSK AP with PMF optional"""
+ ssid = "test-pmf-optional"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK"
+ params["ieee80211w"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[1].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[1], hapd)
+ wt.require_ap_pmf_optional(apdev[0]['bssid'])
+ wt.require_sta_pmf(apdev[0]['bssid'], dev[0].p2p_interface_addr())
+ wt.require_sta_pmf_mandatory(apdev[0]['bssid'], dev[1].p2p_interface_addr())
+
+@remote_compatible
+def test_ap_pmf_optional_2akm(dev, apdev):
+ """WPA2-PSK AP with PMF optional (2 AKMs)"""
+ ssid = "test-pmf-optional-2akm"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK WPA-PSK-SHA256"
+ params["ieee80211w"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[1].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[1], hapd)
+ wt.require_ap_pmf_optional(apdev[0]['bssid'])
+ wt.require_sta_pmf(apdev[0]['bssid'], dev[0].p2p_interface_addr())
+ wt.require_sta_key_mgmt(apdev[0]['bssid'], dev[0].p2p_interface_addr(),
+ "PSK-SHA256")
+ wt.require_sta_pmf_mandatory(apdev[0]['bssid'], dev[1].p2p_interface_addr())
+ wt.require_sta_key_mgmt(apdev[0]['bssid'], dev[1].p2p_interface_addr(),
+ "PSK-SHA256")
+
+@remote_compatible
+def test_ap_pmf_negative(dev, apdev):
+ """WPA2-PSK AP without PMF (negative test)"""
+ ssid = "test-pmf-negative"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ try:
+ dev[1].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[1], hapd)
+ raise Exception("PMF required STA connected to no PMF AP")
+ except Exception as e:
+ logger.debug("Ignore expected exception: " + str(e))
+ wt.require_ap_no_pmf(apdev[0]['bssid'])
+
+@remote_compatible
+def test_ap_pmf_assoc_comeback(dev, apdev):
+ """WPA2-PSK AP with PMF association comeback"""
+ ssid = "assoc-comeback"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=10)
+ hapd.set("ext_mgmt_frame_handling", "0")
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected(timeout=10, error="Timeout on re-connection")
+ if wt.get_sta_counter("assocresp_comeback", apdev[0]['bssid'],
+ dev[0].p2p_interface_addr()) < 1:
+ raise Exception("AP did not use association comeback request")
+
+@remote_compatible
+def test_ap_pmf_assoc_comeback2(dev, apdev):
+ """WPA2-PSK AP with PMF association comeback (using DROP_SA)"""
+ ssid = "assoc-comeback"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK"
+ params["ieee80211w"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ dev[0].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK", proto="WPA2", scan_freq="2412")
+ if "OK" not in dev[0].request("DROP_SA"):
+ raise Exception("DROP_SA failed")
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected(timeout=10, error="Timeout on re-connection")
+ if wt.get_sta_counter("reassocresp_comeback", apdev[0]['bssid'],
+ dev[0].p2p_interface_addr()) < 1:
+ raise Exception("AP did not use reassociation comeback request")
+
+@remote_compatible
+def test_ap_pmf_assoc_comeback_wps(dev, apdev):
+ """WPA2-PSK AP with PMF association comeback (WPS)"""
+ ssid = "assoc-comeback"
+ appin = "12345670"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ params["eap_server"] = "1"
+ params["wps_state"] = "2"
+ params["ap_pin"] = appin
+ hapd = hostapd.add_ap(apdev[0], params)
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=10)
+ hapd.set("ext_mgmt_frame_handling", "0")
+ dev[0].wps_reg(apdev[0]['bssid'], appin)
+ if wt.get_sta_counter("assocresp_comeback", apdev[0]['bssid'],
+ dev[0].p2p_interface_addr()) < 1:
+ raise Exception("AP did not use association comeback request")
+
+def test_ap_pmf_ap_dropping_sa(dev, apdev):
+ """WPA2-PSK PMF AP dropping SA"""
+ ssid = "pmf"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ dev[0].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+ addr0 = dev[0].own_addr()
+ dev[0].dump_monitor()
+ hapd.wait_sta()
+ # Drop SA and association at the AP locally without notifying the STA. This
+ # results in the STA getting unprotected Deauthentication frames when trying
+ # to transmit the next Class 3 frame.
+ if "OK" not in hapd.request("DEAUTHENTICATE " + addr0 + " tx=0"):
+ raise Exception("DEAUTHENTICATE command failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection event after DEAUTHENTICATE tx=0: " + ev)
+ dev[0].request("DATA_TEST_CONFIG 1")
+ dev[0].request("DATA_TEST_TX " + bssid + " " + addr0)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ dev[0].request("DATA_TEST_CONFIG 0")
+ if ev is None or "locally_generated=1" not in ev:
+ raise Exception("Locally generated disconnection not reported")
+
+def test_ap_pmf_valid_broadcast_deauth(dev, apdev):
+ """WPA2-PSK PMF AP sending valid broadcast deauth without dropping SA"""
+ run_ap_pmf_valid(dev, apdev, False, True)
+
+def test_ap_pmf_valid_broadcast_disassoc(dev, apdev):
+ """WPA2-PSK PMF AP sending valid broadcast disassoc without dropping SA"""
+ run_ap_pmf_valid(dev, apdev, True, True)
+
+def test_ap_pmf_valid_unicast_deauth(dev, apdev):
+ """WPA2-PSK PMF AP sending valid unicast deauth without dropping SA"""
+ run_ap_pmf_valid(dev, apdev, False, False)
+
+def test_ap_pmf_valid_unicast_disassoc(dev, apdev):
+ """WPA2-PSK PMF AP sending valid unicast disassoc without dropping SA"""
+ run_ap_pmf_valid(dev, apdev, True, False)
+
+def run_ap_pmf_valid(dev, apdev, disassociate, broadcast):
+ ssid = "pmf"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ dev[0].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+ addr0 = dev[0].own_addr()
+ dev[0].dump_monitor()
+ hapd.wait_sta()
+ cmd = "DISASSOCIATE " if disassociate else "DEAUTHENTICATE "
+ cmd += "ff:ff:ff:ff:ff:ff" if broadcast else addr0
+ cmd += " test=1"
+ if "OK" not in hapd.request(cmd):
+ raise Exception("hostapd command failed")
+ sta = hapd.get_sta(addr0)
+ if not sta:
+ raise Exception("STA entry lost")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnection not reported")
+ if "locally_generated=1" in ev:
+ raise Exception("Unexpected locally generated disconnection")
+
+ # Wait for SA Query procedure to fail and association comeback to succeed
+ dev[0].wait_connected()
+
+def start_wpas_ap(ssid):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="use_monitor=1")
+ id = wpas.add_network()
+ wpas.set_network(id, "mode", "2")
+ wpas.set_network_quoted(id, "ssid", ssid)
+ wpas.set_network(id, "proto", "WPA2")
+ wpas.set_network(id, "key_mgmt", "WPA-PSK-SHA256")
+ wpas.set_network(id, "ieee80211w", "2")
+ wpas.set_network_quoted(id, "psk", "12345678")
+ wpas.set_network(id, "pairwise", "CCMP")
+ wpas.set_network(id, "group", "CCMP")
+ wpas.set_network(id, "frequency", "2412")
+ wpas.set_network(id, "scan_freq", "2412")
+ wpas.connect_network(id)
+ wpas.dump_monitor()
+ return wpas
+
+def test_ap_pmf_sta_sa_query(dev, apdev):
+ """WPA2-PSK AP with station using SA Query"""
+ ssid = "assoc-comeback"
+ addr = dev[0].own_addr()
+
+ wpas = start_wpas_ap(ssid)
+ bssid = wpas.own_addr()
+
+ Wlantest.setup(wpas)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ wpas.dump_monitor()
+ wpas.request("DEAUTHENTICATE " + addr + " test=0")
+ wpas.dump_monitor()
+ wpas.request("DISASSOCIATE " + addr + " test=0")
+ wpas.dump_monitor()
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+ wpas.request("DEAUTHENTICATE " + addr + " reason=6 test=0")
+ wpas.dump_monitor()
+ wpas.request("DISASSOCIATE " + addr + " reason=7 test=0")
+ wpas.dump_monitor()
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ if wt.get_sta_counter("valid_saqueryreq_tx", bssid, addr) < 1:
+ raise Exception("STA did not send SA Query")
+ if wt.get_sta_counter("valid_saqueryresp_rx", bssid, addr) < 1:
+ raise Exception("AP did not reply to SA Query")
+ wpas.dump_monitor()
+
+def test_ap_pmf_sta_sa_query_no_response(dev, apdev):
+ """WPA2-PSK AP with station using SA Query and getting no response"""
+ ssid = "assoc-comeback"
+ addr = dev[0].own_addr()
+
+ wpas = start_wpas_ap(ssid)
+ bssid = wpas.own_addr()
+
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ wpas.dump_monitor()
+ wpas.request("DEAUTHENTICATE " + addr + " test=0")
+ wpas.dump_monitor()
+ wpas.request("DISASSOCIATE " + addr + " test=0")
+ wpas.dump_monitor()
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+ wpas.request("SET ext_mgmt_frame_handling 1")
+ wpas.request("DEAUTHENTICATE " + addr + " reason=6 test=0")
+ wpas.dump_monitor()
+ wpas.request("DISASSOCIATE " + addr + " reason=7 test=0")
+ wpas.dump_monitor()
+ dev[0].wait_disconnected()
+ wpas.dump_monitor()
+ wpas.request("SET ext_mgmt_frame_handling 0")
+ dev[0].wait_connected()
+ wpas.dump_monitor()
+
+def test_ap_pmf_sta_unprot_deauth_burst(dev, apdev):
+ """WPA2-PSK AP with station receiving burst of unprotected Deauthentication frames"""
+ ssid = "deauth-attack"
+ addr = dev[0].own_addr()
+
+ wpas = start_wpas_ap(ssid)
+ bssid = wpas.own_addr()
+
+ Wlantest.setup(wpas)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+
+ for i in range(0, 10):
+ wpas.request("DEAUTHENTICATE " + addr + " reason=6 test=0")
+ wpas.request("DISASSOCIATE " + addr + " reason=7 test=0")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ num_req = wt.get_sta_counter("valid_saqueryreq_tx", bssid, addr)
+ num_resp = wt.get_sta_counter("valid_saqueryresp_rx", bssid, addr)
+ if num_req < 1:
+ raise Exception("STA did not send SA Query")
+ if num_resp < 1:
+ raise Exception("AP did not reply to SA Query")
+ if num_req > 1:
+ raise Exception("STA initiated too many SA Query procedures (%d)" % num_req)
+
+ time.sleep(10)
+ for i in range(0, 5):
+ wpas.request("DEAUTHENTICATE " + addr + " reason=6 test=0")
+ wpas.request("DISASSOCIATE " + addr + " reason=7 test=0")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ num_req = wt.get_sta_counter("valid_saqueryreq_tx", bssid, addr)
+ num_resp = wt.get_sta_counter("valid_saqueryresp_rx", bssid, addr)
+ if num_req != 2 or num_resp != 2:
+ raise Exception("Unexpected number of SA Query procedures (req=%d resp=%d)" % (num_req, num_resp))
+
+def test_ap_pmf_sta_sa_query_oom(dev, apdev):
+ """WPA2-PSK AP with station using SA Query (OOM)"""
+ ssid = "assoc-comeback"
+ addr = dev[0].own_addr()
+ wpas = start_wpas_ap(ssid)
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ with alloc_fail(dev[0], 1, "=sme_sa_query_timer"):
+ wpas.request("DEAUTHENTICATE " + addr + " reason=6 test=0")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("DISCONNECT")
+ wpas.request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_ap_pmf_sta_sa_query_local_failure(dev, apdev):
+ """WPA2-PSK AP with station using SA Query (local failure)"""
+ ssid = "assoc-comeback"
+ addr = dev[0].own_addr()
+ wpas = start_wpas_ap(ssid)
+ dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ with fail_test(dev[0], 1, "os_get_random;sme_sa_query_timer"):
+ wpas.request("DEAUTHENTICATE " + addr + " reason=6 test=0")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("DISCONNECT")
+ wpas.request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_ap_pmf_sta_sa_query_hostapd(dev, apdev):
+ """WPA2-PSK AP with station using SA Query (hostapd)"""
+ ssid = "assoc-comeback"
+ passphrase = "12345678"
+ addr = dev[0].own_addr()
+
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase,
+ wpa_key_mgmt="WPA-PSK-SHA256",
+ ieee80211w="2")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect(ssid, psk=passphrase, ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hapd.wait_sta()
+ if "OK" not in hapd.request("DEAUTHENTICATE " + addr + " test=0") or \
+ "OK" not in hapd.request("DISASSOCIATE " + addr + " test=0"):
+ raise Exception("Failed to send unprotected disconnection messages")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+ if "OK" not in hapd.request("DEAUTHENTICATE " + addr + " reason=6 test=0") or \
+ "OK" not in hapd.request("DISASSOCIATE " + addr + " reason=7 test=0"):
+ raise Exception("Failed to send unprotected disconnection messages (2)")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ if wt.get_sta_counter("valid_saqueryreq_tx", bssid, addr) < 1:
+ raise Exception("STA did not send SA Query")
+ if wt.get_sta_counter("valid_saqueryresp_rx", bssid, addr) < 1:
+ raise Exception("AP did not reply to SA Query")
+
+def test_ap_pmf_sta_sa_query_no_response_hostapd(dev, apdev):
+ """WPA2-PSK AP with station using SA Query and getting no response (hostapd)"""
+ ssid = "assoc-comeback"
+ passphrase = "12345678"
+ addr = dev[0].own_addr()
+
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase,
+ wpa_key_mgmt="WPA-PSK-SHA256",
+ ieee80211w="2")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect(ssid, psk=passphrase, ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hapd.wait_sta()
+ hapd.set("ext_mgmt_frame_handling", "1")
+ if "OK" not in hapd.request("DEAUTHENTICATE " + addr + " reason=6 test=0") or \
+ "OK" not in hapd.request("DISASSOCIATE " + addr + " reason=7 test=0"):
+ raise Exception("Failed to send unprotected disconnection messages")
+ dev[0].wait_disconnected()
+ hapd.set("ext_mgmt_frame_handling", "0")
+ if wt.get_sta_counter("valid_saqueryreq_tx", bssid, addr) < 1:
+ raise Exception("STA did not send SA Query")
+ if wt.get_sta_counter("valid_saqueryresp_rx", bssid, addr) > 0:
+ raise Exception("AP replied to SA Query")
+ dev[0].wait_connected()
+
+def test_ap_pmf_sta_unprot_deauth_burst_hostapd(dev, apdev):
+ """WPA2-PSK AP with station receiving burst of unprotected Deauthentication frames (hostapd)"""
+ ssid = "deauth-attack"
+ passphrase = "12345678"
+ addr = dev[0].own_addr()
+
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase,
+ wpa_key_mgmt="WPA-PSK-SHA256",
+ ieee80211w="2")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect(ssid, psk=passphrase, ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hapd.wait_sta()
+ for i in range(10):
+ if "OK" not in hapd.request("DEAUTHENTICATE " + addr + " reason=6 test=0") or \
+ "OK" not in hapd.request("DISASSOCIATE " + addr + " reason=7 test=0"):
+ raise Exception("Failed to send unprotected disconnection messages")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ num_req = wt.get_sta_counter("valid_saqueryreq_tx", bssid, addr)
+ num_resp = wt.get_sta_counter("valid_saqueryresp_rx", bssid, addr)
+ if num_req < 1:
+ raise Exception("STA did not send SA Query")
+ if num_resp < 1:
+ raise Exception("AP did not reply to SA Query")
+ if num_req > 1:
+ raise Exception("STA initiated too many SA Query procedures (%d)" % num_req)
+
+ time.sleep(10)
+ for i in range(5):
+ if "OK" not in hapd.request("DEAUTHENTICATE " + addr + " reason=6 test=0") or \
+ "OK" not in hapd.request("DISASSOCIATE " + addr + " reason=7 test=0"):
+ raise Exception("Failed to send unprotected disconnection messages")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ num_req = wt.get_sta_counter("valid_saqueryreq_tx", bssid, addr)
+ num_resp = wt.get_sta_counter("valid_saqueryresp_rx", bssid, addr)
+ if num_req != 2 or num_resp != 2:
+ raise Exception("Unexpected number of SA Query procedures (req=%d resp=%d)" % (num_req, num_resp))
+
+def test_ap_pmf_required_eap(dev, apdev):
+ """WPA2-EAP AP with PMF required"""
+ ssid = "test-pmf-required-eap"
+ params = hostapd.wpa2_eap_params(ssid=ssid)
+ params["wpa_key_mgmt"] = "WPA-EAP-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "WPA-EAP-SHA256":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+ dev[0].connect("test-pmf-required-eap", key_mgmt="WPA-EAP-SHA256",
+ ieee80211w="2", eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ dev[1].connect("test-pmf-required-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ ieee80211w="1", eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+def test_ap_pmf_optional_eap(dev, apdev):
+ """WPA2EAP AP with PMF optional"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params["ieee80211w"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ ieee80211w="1", scan_freq="2412")
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP WPA-EAP-SHA256",
+ eap="TTLS", identity="pap user", anonymous_identity="ttls",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ ieee80211w="2", scan_freq="2412")
+
+@remote_compatible
+def test_ap_pmf_required_sha1(dev, apdev):
+ """WPA2-PSK AP with PMF required with SHA1 AKM"""
+ ssid = "test-pmf-required-sha1"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "WPA-PSK":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+ dev[0].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK", proto="WPA2", scan_freq="2412")
+ if "[WPA2-PSK-CCMP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing RSN element info")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_pmf_toggle(dev, apdev):
+ """WPA2-PSK AP with PMF optional and changing PMF on reassociation"""
+ try:
+ _test_ap_pmf_toggle(dev, apdev)
+ finally:
+ dev[0].request("SET reassoc_same_bss_optim 0")
+
+def _test_ap_pmf_toggle(dev, apdev):
+ ssid = "test-pmf-optional"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK"
+ params["ieee80211w"] = "1"
+ params["assoc_sa_query_max_timeout"] = "1"
+ params["assoc_sa_query_retry_timeout"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ bssid = apdev[0]['bssid']
+ addr = dev[0].own_addr()
+ dev[0].request("SET reassoc_same_bss_optim 1")
+ id = dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ wt.require_ap_pmf_optional(bssid)
+ wt.require_sta_pmf(bssid, addr)
+ sta = hapd.get_sta(addr)
+ if '[MFP]' not in sta['flags']:
+ raise Exception("MFP flag not present for STA")
+
+ dev[0].set_network(id, "ieee80211w", "0")
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected()
+ wt.require_sta_no_pmf(bssid, addr)
+ sta = hapd.get_sta(addr)
+ if '[MFP]' in sta['flags']:
+ raise Exception("MFP flag unexpectedly present for STA")
+ err, data = hapd.cmd_execute(['iw', 'dev', apdev[0]['ifname'], 'station',
+ 'get', addr])
+ if "yes" in [l for l in data.splitlines() if "MFP" in l][0]:
+ raise Exception("Kernel STA entry had MFP enabled")
+
+ dev[0].set_network(id, "ieee80211w", "1")
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected()
+ wt.require_sta_pmf(bssid, addr)
+ sta = hapd.get_sta(addr)
+ if '[MFP]' not in sta['flags']:
+ raise Exception("MFP flag not present for STA")
+ err, data = hapd.cmd_execute(['iw', 'dev', apdev[0]['ifname'], 'station',
+ 'get', addr])
+ if "yes" not in [l for l in data.splitlines() if "MFP" in l][0]:
+ raise Exception("Kernel STA entry did not have MFP enabled")
+
+@remote_compatible
+def test_ap_pmf_required_sta_no_pmf(dev, apdev):
+ """WPA2-PSK AP with PMF required and PMF disabled on STA"""
+ ssid = "test-pmf-required"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # Disable PMF on the station and try to connect
+ dev[0].connect(ssid, psk="12345678", ieee80211w="0",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND",
+ "CTRL-EVENT-ASSOC-REJECT"], timeout=2)
+ if ev is None:
+ raise Exception("No connection result")
+ if "CTRL-EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Tried to connect to PMF required AP without PMF enabled")
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_ap_pmf_inject_auth(dev, apdev):
+ """WPA2-PSK AP with PMF and Authentication frame injection"""
+ ssid = "test-pmf"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ bssid = hapd.own_addr().replace(':', '')
+ addr = dev[0].own_addr().replace(':', '')
+
+ # Inject an unprotected Authentication frame claiming to be from the
+ # associated STA, from another STA, from the AP's own address, from all
+ # zeros and all ones addresses, and from a multicast address.
+ hapd.request("SET ext_mgmt_frame_handling 1")
+ failed = False
+ addresses = [ addr, "021122334455", bssid, 6*"00", 6*"ff", 6*"01" ]
+ for a in addresses:
+ auth = "b0003a01" + bssid + a + bssid + '1000000001000000'
+ res = hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % auth)
+ if "OK" not in res:
+ failed = True
+ hapd.request("SET ext_mgmt_frame_handling 0")
+ if failed:
+ raise Exception("MGMT_RX_PROCESS failed")
+ time.sleep(0.1)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+ if ev:
+ raise Exception("Unexpected disconnection reported on the STA")
+
+ # Verify that original association is still functional.
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ # Inject an unprotected Association Request frame (with and without RSNE)
+ # claiming to be from the set of test addresses.
+ hapd.request("SET ext_mgmt_frame_handling 1")
+ for a in addresses:
+ assoc = "00003a01" + bssid + a + bssid + '2000' + '31040500' + '0008746573742d706d66' + '010802040b160c121824' + '301a0100000fac040100000fac040100000fac06c0000000000fac06'
+ res = hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % assoc)
+ if "OK" not in res:
+ failed = True
+
+ assoc = "00003a01" + bssid + a + bssid + '2000' + '31040500' + '0008746573742d706d66' + '010802040b160c121824' + '3000'
+ res = hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % assoc)
+ if "OK" not in res:
+ failed = True
+
+ assoc = "00003a01" + bssid + a + bssid + '2000' + '31040500' + '0008746573742d706d66' + '010802040b160c121824'
+ res = hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % assoc)
+ if "OK" not in res:
+ failed = True
+ hapd.request("SET ext_mgmt_frame_handling 0")
+ if failed:
+ raise Exception("MGMT_RX_PROCESS failed")
+ time.sleep(5)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+ if ev:
+ raise Exception("Unexpected disconnection reported on the STA")
+
+ # Verify that original association is still functional.
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_pmf_inject_data(dev, apdev):
+ """WPA2-PSK AP with PMF and Data frame injection"""
+ try:
+ run_ap_pmf_inject_data(dev, apdev)
+ finally:
+ stop_monitor(apdev[1]["ifname"])
+
+def run_ap_pmf_inject_data(dev, apdev):
+ ssid = "test-pmf"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ sock = start_monitor(apdev[1]["ifname"])
+ radiotap = radiotap_build()
+
+ bssid = hapd.own_addr().replace(':', '')
+ addr = dev[0].own_addr().replace(':', '')
+
+ # Inject Data frame with A2=broadcast, A2=multicast, A2=BSSID, A2=STA, and
+ # A2=unknown unicast
+ addresses = [ 6*"ff", 6*"01", bssid, addr, "020102030405" ]
+ for a in addresses:
+ frame = binascii.unhexlify("48010000" + bssid + a + bssid + "0000")
+ sock.send(radiotap + frame)
+
+ time.sleep(0.1)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+ if ev:
+ raise Exception("Unexpected disconnection reported on the STA")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_pmf_tkip_reject(dev, apdev):
+ """Mixed mode BSS and MFP-enabled AP rejecting TKIP"""
+ skip_without_tkip(dev[0])
+ params = hostapd.wpa2_params(ssid="test-pmf", passphrase="12345678")
+ params['wpa'] = '3'
+ params["ieee80211w"] = "1"
+ params["wpa_pairwise"] = "TKIP CCMP"
+ params["rsn_pairwise"] = "TKIP CCMP"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-pmf", psk="12345678", pairwise="CCMP", ieee80211w="2",
+ scan_freq="2412")
+ dev[0].dump_monitor()
+
+ dev[1].connect("test-pmf", psk="12345678", proto="WPA", pairwise="TKIP",
+ ieee80211w="0", scan_freq="2412")
+ dev[1].dump_monitor()
+
+ dev[2].connect("test-pmf", psk="12345678", pairwise="TKIP",
+ ieee80211w="2", scan_freq="2412", wait_connect=False)
+ ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("No connection result reported")
+ if "CTRL-EVENT-ASSOC-REJECT" not in ev:
+ raise Exception("MFP + TKIP connection was not rejected")
+ if "status_code=31" not in ev:
+ raise Exception("Unexpected status code in rejection: " + ev)
+ dev[2].request("DISCONNECT")
+ dev[2].dump_monitor()
+
+def test_ap_pmf_sa_query_timeout(dev, apdev):
+ """SA Query timeout"""
+ ssid = "test-pmf-required"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ if "OK" not in dev[0].request("UNPROT_DEAUTH"):
+ raise Exception("Triggering SA Query from the STA failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=2)
+ if ev is None:
+ raise Exception("No disconnection on SA Query timeout seen")
+ hapd.set("ext_mgmt_frame_handling", "0")
+ dev[0].wait_connected()
+ dev[0].dump_monitor()
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ if "OK" not in dev[0].request("UNPROT_DEAUTH"):
+ raise Exception("Triggering SA Query from the STA failed")
+ ev = hapd.mgmt_rx()
+ hapd.set("ext_mgmt_frame_handling", "0")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+ hapd.set("ext_mgmt_frame_handling", "1")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1.5)
+ if ev is not None:
+ raise Exception("Unexpected disconnection after reconnection seen")
+
+def mac80211_read_key(keydir):
+ vals = {}
+ for name in os.listdir(keydir):
+ try:
+ with open(os.path.join(keydir, name)) as f:
+ vals[name] = f.read().strip()
+ except OSError as e:
+ pass
+ return vals
+
+def check_mac80211_bigtk(dev, hapd):
+ sta_key = None
+ ap_key = None
+
+ phy = dev.get_driver_status_field("phyname")
+ keys = "/sys/kernel/debug/ieee80211/%s/keys" % phy
+ try:
+ for key in os.listdir(keys):
+ keydir = os.path.join(keys, key)
+ vals = mac80211_read_key(keydir)
+ keyidx = int(vals['keyidx'])
+ if keyidx == 6 or keyidx == 7:
+ sta_key = vals;
+ break
+ except OSError as e:
+ raise HwsimSkip("debugfs not supported in mac80211 (STA)")
+
+ phy = hapd.get_driver_status_field("phyname")
+ keys = "/sys/kernel/debug/ieee80211/%s/keys" % phy
+ try:
+ for key in os.listdir(keys):
+ keydir = os.path.join(keys, key)
+ vals = mac80211_read_key(keydir)
+ keyidx = int(vals['keyidx'])
+ if keyidx == 6 or keyidx == 7:
+ ap_key = vals;
+ break
+ except OSError as e:
+ raise HwsimSkip("debugfs not supported in mac80211 (AP)")
+
+ if not sta_key:
+ raise Exception("Could not find STA key information from debugfs")
+ logger.info("STA key: " + str(sta_key))
+
+ if not ap_key:
+ raise Exception("Could not find AP key information from debugfs")
+ logger.info("AP key: " + str(ap_key))
+
+ if sta_key['key'] != ap_key['key']:
+ raise Exception("AP and STA BIGTK mismatch")
+
+ if sta_key['keyidx'] != ap_key['keyidx']:
+ raise Exception("AP and STA BIGTK keyidx mismatch")
+
+ if sta_key['algorithm'] != ap_key['algorithm']:
+ raise Exception("AP and STA BIGTK algorithm mismatch")
+
+ replays = int(sta_key['replays'])
+ icverrors = int(sta_key['icverrors'])
+ if replays > 0 or icverrors > 0:
+ raise Exception("STA reported errors: replays=%d icverrors=%d" % replays, icverrors)
+
+ rx_spec = int(sta_key['rx_spec'], base=16)
+ if rx_spec < 3:
+ raise Exception("STA did not update BIGTK receive counter sufficiently")
+
+ tx_spec = int(ap_key['tx_spec'], base=16)
+ if tx_spec < 3:
+ raise Exception("AP did not update BIGTK BIPN sufficiently")
+
+def test_ap_pmf_beacon_protection_bip(dev, apdev):
+ """WPA2-PSK Beacon protection (BIP)"""
+ run_ap_pmf_beacon_protection(dev, apdev, "AES-128-CMAC")
+
+def test_ap_pmf_beacon_protection_bip_cmac_256(dev, apdev):
+ """WPA2-PSK Beacon protection (BIP-CMAC-256)"""
+ run_ap_pmf_beacon_protection(dev, apdev, "BIP-CMAC-256")
+
+def test_ap_pmf_beacon_protection_bip_gmac_128(dev, apdev):
+ """WPA2-PSK Beacon protection (BIP-GMAC-128)"""
+ run_ap_pmf_beacon_protection(dev, apdev, "BIP-GMAC-128")
+
+def test_ap_pmf_beacon_protection_bip_gmac_256(dev, apdev):
+ """WPA2-PSK Beacon protection (BIP-GMAC-256)"""
+ run_ap_pmf_beacon_protection(dev, apdev, "BIP-GMAC-256")
+
+def run_ap_pmf_beacon_protection(dev, apdev, cipher):
+ ssid = "test-beacon-prot"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ params["beacon_prot"] = "1"
+ params["group_mgmt_cipher"] = cipher
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except Exception as e:
+ if "Failed to enable hostapd interface" in str(e):
+ raise HwsimSkip("Beacon protection not supported")
+ raise
+
+ bssid = hapd.own_addr()
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ # STA with Beacon protection enabled
+ dev[0].connect(ssid, psk="12345678", ieee80211w="2", beacon_prot="1",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+
+ # STA with Beacon protection disabled
+ dev[1].connect(ssid, psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+
+ time.sleep(1)
+ check_mac80211_bigtk(dev[0], hapd)
+
+ valid_bip = wt.get_bss_counter('valid_bip_mmie', bssid)
+ invalid_bip = wt.get_bss_counter('invalid_bip_mmie', bssid)
+ missing_bip = wt.get_bss_counter('missing_bip_mmie', bssid)
+ logger.info("wlantest BIP counters: valid=%d invalid=%d missing=%d" % (valid_bip, invalid_bip, missing_bip))
+ if valid_bip < 0 or invalid_bip > 0 or missing_bip > 0:
+ raise Exception("Unexpected wlantest BIP counters: valid=%d invalid=%d missing=%d" % (valid_bip, invalid_bip, missing_bip))
+
+def test_ap_pmf_beacon_protection_mismatch(dev, apdev):
+ """WPA2-PSK Beacon protection MIC mismatch"""
+ run_ap_pmf_beacon_protection_mismatch(dev, apdev, False)
+
+def test_ap_pmf_beacon_protection_missing(dev, apdev):
+ """WPA2-PSK Beacon protection MME missing"""
+ run_ap_pmf_beacon_protection_mismatch(dev, apdev, True)
+
+def run_ap_pmf_beacon_protection_mismatch(dev, apdev, clear):
+ ssid = "test-beacon-prot"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ params["beacon_prot"] = "1"
+ params["group_mgmt_cipher"] = "AES-128-CMAC"
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except Exception as e:
+ if "Failed to enable hostapd interface" in str(e):
+ raise HwsimSkip("Beacon protection not supported")
+ raise
+
+ bssid = hapd.own_addr()
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ dev[0].connect(ssid, psk="12345678", ieee80211w="2", beacon_prot="1",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+
+ WPA_ALG_NONE = 0
+ WPA_ALG_IGTK = 4
+ KEY_FLAG_DEFAULT = 0x02
+ KEY_FLAG_TX = 0x08
+ KEY_FLAG_GROUP = 0x10
+ KEY_FLAG_GROUP_TX_DEFAULT = KEY_FLAG_GROUP | KEY_FLAG_TX | KEY_FLAG_DEFAULT
+
+ addr = "ff:ff:ff:ff:ff:ff"
+
+ if clear:
+ res = hapd.request("SET_KEY %d %s %d %d %s %s %d" % (WPA_ALG_NONE, addr, 6, 1, 6*"00", "", KEY_FLAG_GROUP))
+ else:
+ res = hapd.request("SET_KEY %d %s %d %d %s %s %d" % (WPA_ALG_IGTK, addr, 6, 1, 6*"00", 16*"00", KEY_FLAG_GROUP_TX_DEFAULT))
+ if "OK" not in res:
+ raise Exception("SET_KEY failed")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-UNPROT-BEACON"], timeout=5)
+ if ev is None:
+ raise Exception("Unprotected Beacon frame not reported")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-BEACON-LOSS"], timeout=5)
+ if ev is None:
+ raise Exception("Beacon loss not reported")
+
+ ev = hapd.wait_event(["CTRL-EVENT-UNPROT-BEACON"], timeout=5)
+ if ev is None:
+ raise Exception("WNM-Notification Request frame not reported")
+
+def test_ap_pmf_sta_global_require(dev, apdev):
+ """WPA2-PSK AP with PMF optional and wpa_supplicant pmf=2"""
+ ssid = "test-pmf-optional"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK"
+ params["ieee80211w"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ dev[0].set("pmf", "2")
+ dev[0].connect(ssid, psk="12345678",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ pmf = dev[0].get_status_field("pmf")
+ if pmf != "1":
+ raise Exception("Unexpected PMF state: " + str(pmf))
+ finally:
+ dev[0].set("pmf", "0")
+
+def test_ap_pmf_sta_global_require2(dev, apdev):
+ """WPA2-PSK AP with PMF optional and wpa_supplicant pmf=2 (2)"""
+ ssid = "test-pmf-optional"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK"
+ params["ieee80211w"] = "0"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ try:
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].set("pmf", "2")
+ dev[0].connect(ssid, psk="12345678",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("Connection result not reported")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ finally:
+ dev[0].set("pmf", "0")
diff --git a/contrib/wpa/tests/hwsim/test_ap_psk.py b/contrib/wpa/tests/hwsim/test_ap_psk.py
new file mode 100644
index 000000000000..b6048be13844
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_psk.py
@@ -0,0 +1,3553 @@
+# WPA2-Personal tests
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+from Crypto.Cipher import AES
+import hashlib
+import hmac
+import logging
+logger = logging.getLogger()
+import os
+import re
+import socket
+import struct
+import subprocess
+import time
+
+import hostapd
+from utils import *
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+from tshark import run_tshark
+from wlantest import WlantestCapture, Wlantest
+
+def check_mib(dev, vals):
+ mib = dev.get_mib()
+ for v in vals:
+ if mib[v[0]] != v[1]:
+ raise Exception("Unexpected {} = {} (expected {})".format(v[0], mib[v[0]], v[1]))
+
+@remote_compatible
+def test_ap_wpa2_psk(dev, apdev):
+ """WPA2-PSK AP with PSK instead of passphrase"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_psk'] = psk
+ hapd = hostapd.add_ap(apdev[0], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "WPA-PSK":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+ dev[0].connect(ssid, raw_psk=psk, scan_freq="2412")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ pkt = dev[0].request("PKTCNT_POLL").splitlines()
+ if "FREQUENCY=2412" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value: " + str(sig))
+ if "TXBAD=0" not in pkt:
+ raise Exception("Unexpected TXBAD value: " + str(pkt))
+
+def test_ap_wpa2_psk_file(dev, apdev):
+ """WPA2-PSK AP with PSK from a file"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_psk_file'] = 'hostapd.wpa_psk'
+ hostapd.add_ap(apdev[0], params)
+ dev[1].connect(ssid, psk="very secret", scan_freq="2412", wait_connect=False)
+ dev[2].connect(ssid, raw_psk=psk, scan_freq="2412")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[0].connect(ssid, psk="very secret", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[2].connect(ssid, psk="another passphrase for all STAs", scan_freq="2412")
+ dev[0].connect(ssid, psk="another passphrase for all STAs", scan_freq="2412")
+ ev = dev[1].wait_event(["WPA: 4-Way Handshake failed"], timeout=10)
+ if ev is None:
+ raise Exception("Timed out while waiting for failure report")
+ dev[1].request("REMOVE_NETWORK all")
+
+def check_no_keyid(hapd, dev):
+ addr = dev.own_addr()
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=1)
+ if ev is None:
+ raise Exception("No AP-STA-CONNECTED indicated")
+ if addr not in ev:
+ raise Exception("AP-STA-CONNECTED for unexpected STA")
+ if "keyid=" in ev:
+ raise Exception("Unexpected keyid indication")
+
+def check_keyid(hapd, dev, keyid):
+ addr = dev.own_addr()
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=1)
+ if ev is None:
+ raise Exception("No AP-STA-CONNECTED indicated")
+ if addr not in ev:
+ raise Exception("AP-STA-CONNECTED for unexpected STA")
+ if "keyid=" + keyid not in ev:
+ raise Exception("Incorrect keyid indication")
+ sta = hapd.get_sta(addr)
+ if 'keyid' not in sta or sta['keyid'] != keyid:
+ raise Exception("Incorrect keyid in STA output")
+ dev.request("REMOVE_NETWORK all")
+
+def check_disconnect(dev, expected):
+ for i in range(2):
+ if expected[i]:
+ dev[i].wait_disconnected()
+ dev[i].request("REMOVE_NETWORK all")
+ else:
+ ev = dev[i].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ dev[i].request("REMOVE_NETWORK all")
+ dev[i].wait_disconnected()
+
+def test_ap_wpa2_psk_file_keyid(dev, apdev, params):
+ """WPA2-PSK AP with PSK from a file (keyid and reload)"""
+ psk_file = os.path.join(params['logdir'], 'ap_wpa2_psk_file_keyid.wpa_psk')
+ with open(psk_file, 'w') as f:
+ f.write('00:00:00:00:00:00 secret passphrase\n')
+ f.write('02:00:00:00:00:00 very secret\n')
+ f.write('00:00:00:00:00:00 another passphrase for all STAs\n')
+ ssid = "test-wpa2-psk"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase='qwertyuiop')
+ params['wpa_psk_file'] = psk_file
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(ssid, psk="very secret", scan_freq="2412")
+ check_no_keyid(hapd, dev[0])
+
+ dev[1].connect(ssid, psk="another passphrase for all STAs",
+ scan_freq="2412")
+ check_no_keyid(hapd, dev[1])
+
+ dev[2].connect(ssid, psk="qwertyuiop", scan_freq="2412")
+ check_no_keyid(hapd, dev[2])
+
+ with open(psk_file, 'w') as f:
+ f.write('00:00:00:00:00:00 secret passphrase\n')
+ f.write('02:00:00:00:00:00 very secret\n')
+ f.write('00:00:00:00:00:00 changed passphrase\n')
+ if "OK" not in hapd.request("RELOAD_WPA_PSK"):
+ raise Exception("RELOAD_WPA_PSK failed")
+
+ check_disconnect(dev, [False, True, False])
+
+ with open(psk_file, 'w') as f:
+ f.write('00:00:00:00:00:00 secret passphrase\n')
+ f.write('keyid=foo 02:00:00:00:00:00 very secret\n')
+ f.write('keyid=bar 00:00:00:00:00:00 another passphrase for all STAs\n')
+ if "OK" not in hapd.request("RELOAD_WPA_PSK"):
+ raise Exception("RELOAD_WPA_PSK failed")
+
+ dev[0].connect(ssid, psk="very secret", scan_freq="2412")
+ check_keyid(hapd, dev[0], "foo")
+
+ dev[1].connect(ssid, psk="another passphrase for all STAs",
+ scan_freq="2412")
+ check_keyid(hapd, dev[1], "bar")
+
+ dev[2].connect(ssid, psk="qwertyuiop", scan_freq="2412")
+ check_no_keyid(hapd, dev[2])
+
+ dev[0].wait_disconnected()
+ dev[0].connect(ssid, psk="secret passphrase", scan_freq="2412")
+ check_no_keyid(hapd, dev[0])
+
+ with open(psk_file, 'w') as f:
+ f.write('# empty\n')
+ if "OK" not in hapd.request("RELOAD_WPA_PSK"):
+ raise Exception("RELOAD_WPA_PSK failed")
+
+ check_disconnect(dev, [True, True, False])
+
+ with open(psk_file, 'w') as f:
+ f.write('broken\n')
+ if "FAIL" not in hapd.request("RELOAD_WPA_PSK"):
+ raise Exception("RELOAD_WPA_PSK succeeded with invalid file")
+
+@remote_compatible
+def test_ap_wpa2_psk_mem(dev, apdev):
+ """WPA2-PSK AP with passphrase only in memory"""
+ try:
+ _test_ap_wpa2_psk_mem(dev, apdev)
+ finally:
+ dev[0].request("SCAN_INTERVAL 5")
+ dev[1].request("SCAN_INTERVAL 5")
+
+def _test_ap_wpa2_psk_mem(dev, apdev):
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_psk'] = psk
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(ssid, mem_only_psk="1", scan_freq="2412", wait_connect=False)
+ dev[0].request("SCAN_INTERVAL 1")
+ ev = dev[0].wait_event(["CTRL-REQ-PSK_PASSPHRASE"], timeout=10)
+ if ev is None:
+ raise Exception("Request for PSK/passphrase timed out")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-PSK_PASSPHRASE-" + id + ':"' + passphrase + '"')
+ dev[0].wait_connected(timeout=10)
+
+ dev[1].connect(ssid, mem_only_psk="1", scan_freq="2412", wait_connect=False)
+ dev[1].request("SCAN_INTERVAL 1")
+ ev = dev[1].wait_event(["CTRL-REQ-PSK_PASSPHRASE"], timeout=10)
+ if ev is None:
+ raise Exception("Request for PSK/passphrase timed out(2)")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[1].request("CTRL-RSP-PSK_PASSPHRASE-" + id + ':' + psk)
+ dev[1].wait_connected(timeout=10)
+
+@remote_compatible
+def test_ap_wpa2_ptk_rekey(dev, apdev):
+ """WPA2-PSK AP and PTK rekey enforced by station"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase(passphrase)
+
+ dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed",
+ "CTRL-EVENT-DISCONNECTED"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ raise Exception("Disconnect instead of rekey")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_ptk_rekey_blocked_ap(dev, apdev):
+ """WPA2-PSK AP and PTK rekey enforced by station and AP blocking it"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_deny_ptk0_rekey'] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ conf = hapd.request("GET_CONFIG").splitlines()
+ if "wpa_deny_ptk0_rekey=2" not in conf:
+ raise Exception("wpa_deny_ptk0_rekey value not in GET_CONFIG")
+ dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed",
+ "CTRL-EVENT-DISCONNECTED"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ if "WPA: Key negotiation completed" in ev:
+ raise Exception("No disconnect, PTK rekey succeeded")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=1)
+ if ev is None:
+ raise Exception("Reconnect too slow")
+
+def test_ap_wpa2_ptk_rekey_blocked_sta(dev, apdev):
+ """WPA2-PSK AP and PTK rekey enforced by station while also blocking it"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412",
+ wpa_deny_ptk0_rekey="2")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed",
+ "CTRL-EVENT-DISCONNECTED"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ if "WPA: Key negotiation completed" in ev:
+ raise Exception("No disconnect, PTK rekey succeeded")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=1)
+ if ev is None:
+ raise Exception("Reconnect too slow")
+
+def test_ap_wpa2_ptk_rekey_anonce(dev, apdev):
+ """WPA2-PSK AP and PTK rekey enforced by station and ANonce change"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412")
+ dev[0].dump_monitor()
+ anonce1 = dev[0].request("GET anonce")
+ if "OK" not in dev[0].request("KEY_REQUEST 0 1"):
+ raise Exception("KEY_REQUEST failed")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ anonce2 = dev[0].request("GET anonce")
+ if anonce1 == anonce2:
+ raise Exception("AP did not update ANonce in requested PTK rekeying")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_wpa2_ptk_rekey_ap(dev, apdev):
+ """WPA2-PSK AP and PTK rekey enforced by AP"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_ptk_rekey'] = '2'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_wpa2_sha256_ptk_rekey(dev, apdev):
+ """WPA2-PSK/SHA256 AKM AP and PTK rekey enforced by station"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
+ wpa_ptk_rekey="1", scan_freq="2412")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-6"),
+ ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-6")])
+
+@remote_compatible
+def test_ap_wpa2_sha256_ptk_rekey_ap(dev, apdev):
+ """WPA2-PSK/SHA256 AKM AP and PTK rekey enforced by AP"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params['wpa_ptk_rekey'] = '2'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
+ scan_freq="2412")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ check_mib(dev[0], [("dot11RSNAAuthenticationSuiteRequested", "00-0f-ac-6"),
+ ("dot11RSNAAuthenticationSuiteSelected", "00-0f-ac-6")])
+
+@remote_compatible
+def test_ap_wpa_ptk_rekey(dev, apdev):
+ """WPA-PSK/TKIP AP and PTK rekey enforced by station"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ ssid = "test-wpa-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1", scan_freq="2412")
+ if "[WPA-PSK-TKIP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing WPA element info")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_wpa_ptk_rekey_ap(dev, apdev):
+ """WPA-PSK/TKIP AP and PTK rekey enforced by AP"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ ssid = "test-wpa-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_ptk_rekey'] = '2'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=10)
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_wpa_ccmp(dev, apdev):
+ """WPA-PSK/CCMP"""
+ ssid = "test-wpa-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_pairwise'] = "CCMP"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ check_mib(dev[0], [("dot11RSNAConfigGroupCipherSize", "128"),
+ ("dot11RSNAGroupCipherRequested", "00-50-f2-4"),
+ ("dot11RSNAPairwiseCipherRequested", "00-50-f2-4"),
+ ("dot11RSNAAuthenticationSuiteRequested", "00-50-f2-2"),
+ ("dot11RSNAGroupCipherSelected", "00-50-f2-4"),
+ ("dot11RSNAPairwiseCipherSelected", "00-50-f2-4"),
+ ("dot11RSNAAuthenticationSuiteSelected", "00-50-f2-2"),
+ ("dot1xSuppSuppControlledPortStatus", "Authorized")])
+
+def test_ap_wpa2_psk_file_errors(dev, apdev):
+ """WPA2-PSK AP with various PSK file error and success cases"""
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ addr2 = dev[2].own_addr()
+ ssid = "psk"
+ pskfile = "/tmp/ap_wpa2_psk_file_errors.psk_file"
+ try:
+ os.remove(pskfile)
+ except:
+ pass
+
+ params = {"ssid": ssid, "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP", "wpa_psk_file": pskfile}
+
+ try:
+ # missing PSK file
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success")
+ hapd.request("DISABLE")
+
+ # invalid MAC address
+ with open(pskfile, "w") as f:
+ f.write("\n")
+ f.write("foo\n")
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success")
+ hapd.request("DISABLE")
+
+ # no PSK on line
+ with open(pskfile, "w") as f:
+ f.write("00:11:22:33:44:55\n")
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success")
+ hapd.request("DISABLE")
+
+ # invalid PSK
+ with open(pskfile, "w") as f:
+ f.write("00:11:22:33:44:55 1234567\n")
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success")
+ hapd.request("DISABLE")
+
+ # empty token at the end of the line
+ with open(pskfile, "w") as f:
+ f.write("=\n")
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE success")
+ hapd.request("DISABLE")
+
+ # valid PSK file
+ with open(pskfile, "w") as f:
+ f.write("00:11:22:33:44:55 12345678\n")
+ f.write(addr0 + " 123456789\n")
+ f.write(addr1 + " 123456789a\n")
+ f.write(addr2 + " 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef\n")
+ if "FAIL" in hapd.request("ENABLE"):
+ raise Exception("Unexpected ENABLE failure")
+
+ dev[0].connect(ssid, psk="123456789", scan_freq="2412")
+ dev[1].connect(ssid, psk="123456789a", scan_freq="2412")
+ dev[2].connect(ssid, raw_psk="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", scan_freq="2412")
+
+ finally:
+ try:
+ os.remove(pskfile)
+ except:
+ pass
+
+@remote_compatible
+def test_ap_wpa2_psk_wildcard_ssid(dev, apdev):
+ """WPA2-PSK AP and wildcard SSID configuration"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("", bssid=apdev[0]['bssid'], psk=passphrase,
+ scan_freq="2412")
+ dev[1].connect("", bssid=apdev[0]['bssid'], raw_psk=psk, scan_freq="2412")
+
+@remote_compatible
+def test_ap_wpa2_gtk_rekey(dev, apdev):
+ """WPA2-PSK AP and GTK rekey enforced by AP"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_group_rekey'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_gtk_rekey_request(dev, apdev):
+ """WPA2-PSK AP and GTK rekey by AP request"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ if "OK" not in hapd.request("REKEY_GTK"):
+ raise Exception("REKEY_GTK failed")
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_gtk_rekey_failure(dev, apdev):
+ """WPA2-PSK AP and GTK rekey failure"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ with fail_test(hapd, 1, "wpa_group_config_group_keys"):
+ if "OK" not in hapd.request("REKEY_GTK"):
+ raise Exception("REKEY_GTK failed")
+ wait_fail_trigger(hapd, "GET_FAIL")
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ dev[0].wait_disconnected()
+
+@remote_compatible
+def test_ap_wpa_gtk_rekey(dev, apdev):
+ """WPA-PSK/TKIP AP and GTK rekey enforced by AP"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ ssid = "test-wpa-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_group_rekey'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_wpa2_gmk_rekey(dev, apdev):
+ """WPA2-PSK AP and GMK and GTK rekey enforced by AP"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_group_rekey'] = '1'
+ params['wpa_gmk_rekey'] = '2'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ for i in range(0, 3):
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_wpa2_strict_rekey(dev, apdev):
+ """WPA2-PSK AP and strict GTK rekey enforced by AP"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_strict_rekey'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[1].request("DISCONNECT")
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_wpa2_bridge_fdb(dev, apdev):
+ """Bridge FDB entry removal"""
+ hapd = None
+ try:
+ ssid = "test-wpa2-psk"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['bridge'] = 'ap-br0'
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.cmd_execute(['brctl', 'setfd', 'ap-br0', '0'])
+ hapd.cmd_execute(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
+ bssid=apdev[0]['bssid'])
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412",
+ bssid=apdev[0]['bssid'])
+ hapd.wait_sta()
+ hapd.wait_sta()
+ addr0 = dev[0].p2p_interface_addr()
+ hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+ err, macs1 = hapd.cmd_execute(['brctl', 'showmacs', 'ap-br0'])
+ hapd.cmd_execute(['brctl', 'setageing', 'ap-br0', '1'])
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ time.sleep(1)
+ err, macs2 = hapd.cmd_execute(['brctl', 'showmacs', 'ap-br0'])
+
+ addr1 = dev[1].p2p_interface_addr()
+ if addr0 not in macs1 or addr1 not in macs1:
+ raise Exception("Bridge FDB entry missing")
+ if addr0 in macs2 or addr1 in macs2:
+ raise Exception("Bridge FDB entry was not removed")
+ finally:
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', 'ap-br0',
+ 'down'])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', 'ap-br0'])
+
+@remote_compatible
+def test_ap_wpa2_already_in_bridge(dev, apdev):
+ """hostapd behavior with interface already in bridge"""
+ ifname = apdev[0]['ifname']
+ br_ifname = 'ext-ap-br0'
+ try:
+ ssid = "test-wpa2-psk"
+ passphrase = "12345678"
+ hostapd.cmd_execute(apdev[0], ['brctl', 'addbr', br_ifname])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'setfd', br_ifname, '0'])
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
+ 'up'])
+ hostapd.cmd_execute(apdev[0], ['iw', ifname, 'set', 'type', '__ap'])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'addif', br_ifname, ifname])
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ if hapd.get_driver_status_field('brname') != br_ifname:
+ raise Exception("Bridge name not identified correctly")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ finally:
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
+ 'down'])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'delif', br_ifname, ifname])
+ hostapd.cmd_execute(apdev[0], ['iw', ifname, 'set', 'type', 'station'])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', br_ifname])
+
+@remote_compatible
+def test_ap_wpa2_in_different_bridge(dev, apdev):
+ """hostapd behavior with interface in different bridge"""
+ ifname = apdev[0]['ifname']
+ br_ifname = 'ext-ap-br0'
+ try:
+ ssid = "test-wpa2-psk"
+ passphrase = "12345678"
+ hostapd.cmd_execute(apdev[0], ['brctl', 'addbr', br_ifname])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'setfd', br_ifname, '0'])
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
+ 'up'])
+ hostapd.cmd_execute(apdev[0], ['iw', ifname, 'set', 'type', '__ap'])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'addif', br_ifname, ifname])
+ time.sleep(0.5)
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['bridge'] = 'ap-br0'
+ hapd = hostapd.add_ap(apdev[0], params)
+ hostapd.cmd_execute(apdev[0], ['brctl', 'setfd', 'ap-br0', '0'])
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', 'ap-br0',
+ 'up'])
+ brname = hapd.get_driver_status_field('brname')
+ if brname != 'ap-br0':
+ raise Exception("Incorrect bridge: " + brname)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "ap-br0")
+ if hapd.get_driver_status_field("added_bridge") != "1":
+ raise Exception("Unexpected added_bridge value")
+ if hapd.get_driver_status_field("added_if_into_bridge") != "1":
+ raise Exception("Unexpected added_if_into_bridge value")
+ dev[0].request("DISCONNECT")
+ hapd.disable()
+ finally:
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
+ 'down'])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'delif', br_ifname, ifname,
+ "2>", "/dev/null"], shell=True)
+ hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', br_ifname])
+
+@remote_compatible
+def test_ap_wpa2_ext_add_to_bridge(dev, apdev):
+ """hostapd behavior with interface added to bridge externally"""
+ ifname = apdev[0]['ifname']
+ br_ifname = 'ext-ap-br0'
+ try:
+ ssid = "test-wpa2-psk"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ hostapd.cmd_execute(apdev[0], ['brctl', 'addbr', br_ifname])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'setfd', br_ifname, '0'])
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
+ 'up'])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'addif', br_ifname, ifname])
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ if hapd.get_driver_status_field('brname') != br_ifname:
+ raise Exception("Bridge name not identified correctly")
+ finally:
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', br_ifname,
+ 'down'])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'delif', br_ifname, ifname])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', br_ifname])
+
+def setup_psk_ext(dev, apdev, wpa_ptk_rekey=None):
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_psk'] = psk
+ if wpa_ptk_rekey:
+ params['wpa_ptk_rekey'] = wpa_ptk_rekey
+ hapd = hostapd.add_ap(apdev, params)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev.request("SET ext_eapol_frame_io 1")
+ dev.connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False)
+ return hapd
+
+def ext_4way_hs(hapd, dev):
+ bssid = hapd.own_addr()
+ addr = dev.own_addr()
+ first = None
+ last = None
+ while True:
+ ev = hapd.wait_event(["EAPOL-TX", "AP-STA-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ if "AP-STA-CONNECTED" in ev:
+ dev.wait_connected(timeout=15)
+ break
+ if not first:
+ first = ev.split(' ')[2]
+ last = ev.split(' ')[2]
+ res = dev.request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+ ev = dev.wait_event(["EAPOL-TX", "CTRL-EVENT-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ break
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+ return first, last
+
+def test_ap_wpa2_psk_ext(dev, apdev):
+ """WPA2-PSK AP using external EAPOL I/O"""
+ hapd = setup_psk_ext(dev[0], apdev[0])
+ ext_4way_hs(hapd, dev[0])
+
+def test_ap_wpa2_psk_unexpected(dev, apdev):
+ """WPA2-PSK and supplicant receiving unexpected EAPOL-Key frames"""
+ hapd = setup_psk_ext(dev[0], apdev[0])
+ first, last = ext_4way_hs(hapd, dev[0])
+
+ # Not associated - Delay processing of received EAPOL frame (state=COMPLETED
+ # bssid=02:00:00:00:03:00)
+ other = "02:11:22:33:44:55"
+ res = dev[0].request("EAPOL_RX " + other + " " + first)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # WPA: EAPOL-Key Replay Counter did not increase - dropping packet
+ bssid = hapd.own_addr()
+ res = dev[0].request("EAPOL_RX " + bssid + " " + last)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # WPA: Invalid EAPOL-Key MIC - dropping packet
+ msg = last[0:18] + '01' + last[20:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=12)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+def test_ap_wpa2_psk_ext_retry_msg_3(dev, apdev):
+ """WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4"""
+ hapd = setup_psk_ext(dev[0], apdev[0])
+ bssid = apdev[0]['bssid']
+ addr = dev[0].p2p_interface_addr()
+
+ # EAPOL-Key msg 1/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ # EAPOL-Key msg 3/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ # Do not send to the AP
+ dev[0].wait_connected(timeout=15)
+
+ # EAPOL-Key msg 3/4 (retry)
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
+
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_psk_ext_retry_msg_3b(dev, apdev):
+ """WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4 (b)"""
+ hapd = setup_psk_ext(dev[0], apdev[0])
+ bssid = apdev[0]['bssid']
+ addr = dev[0].p2p_interface_addr()
+
+ # EAPOL-Key msg 1/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ # EAPOL-Key msg 3/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ # Do not send the first msg 3/4 to the STA yet; wait for retransmission
+ # from AP.
+ msg3_1 = ev
+
+ # EAPOL-Key msg 3/4 (retry)
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ msg3_2 = ev
+
+ # Send the first msg 3/4 to STA
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg3_1.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+ dev[0].wait_connected(timeout=15)
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
+
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ # Send the second msg 3/4 to STA
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg3_2.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ # Do not send the second msg 4/4 to the AP
+
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_psk_ext_retry_msg_3c(dev, apdev):
+ """WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4 (c)"""
+ hapd = setup_psk_ext(dev[0], apdev[0])
+ bssid = apdev[0]['bssid']
+ addr = dev[0].p2p_interface_addr()
+
+ # EAPOL-Key msg 1/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ msg1 = ev.split(' ')[2]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg1)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ # EAPOL-Key msg 3/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ msg4 = ev.split(' ')[2]
+ # Do not send msg 4/4 to hostapd to trigger retry
+
+ # STA believes everything is ready
+ dev[0].wait_connected()
+
+ # EAPOL-Key msg 3/4 (retry)
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ msg3 = ev.split(' ')[2]
+
+ # Send a forged msg 1/4 to STA (update replay counter)
+ msg1b = msg1[0:18] + msg3[18:34] + msg1[34:]
+ # and replace nonce (this results in "WPA: ANonce from message 1 of
+ # 4-Way Handshake differs from 3 of 4-Way Handshake - drop packet" when
+ # wpa_supplicant processed msg 3/4 afterwards)
+ #msg1b = msg1[0:18] + msg3[18:34] + 32*"ff" + msg1[98:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg1b)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=1)
+ if ev is None:
+ # wpa_supplicant seems to have ignored the forged message. This means
+ # the attack would fail.
+ logger.info("wpa_supplicant ignored forged EAPOL-Key msg 1/4")
+ return
+ # Do not send msg 2/4 to hostapd
+
+ # Send previously received msg 3/4 to STA
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg3)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
+
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_psk_ext_retry_msg_3d(dev, apdev):
+ """WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4 (d)"""
+ hapd = setup_psk_ext(dev[0], apdev[0])
+ bssid = apdev[0]['bssid']
+ addr = dev[0].p2p_interface_addr()
+
+ # EAPOL-Key msg 1/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ msg1 = ev.split(' ')[2]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg1)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ # EAPOL-Key msg 3/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ msg4 = ev.split(' ')[2]
+ # Do not send msg 4/4 to hostapd to trigger retry
+
+ # STA believes everything is ready
+ dev[0].wait_connected()
+
+ # EAPOL-Key msg 3/4 (retry)
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ msg3 = ev.split(' ')[2]
+
+ # Send a forged msg 1/4 to STA (update replay counter)
+ msg1b = msg1[0:18] + msg3[18:34] + msg1[34:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg1b)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=1)
+ if ev is None:
+ # wpa_supplicant seems to have ignored the forged message. This means
+ # the attack would fail.
+ logger.info("wpa_supplicant ignored forged EAPOL-Key msg 1/4")
+ return
+ # Do not send msg 2/4 to hostapd
+
+ # EAPOL-Key msg 3/4 (retry 2)
+ # New one needed to get the correct Replay Counter value
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ msg3 = ev.split(' ')[2]
+
+ # Send msg 3/4 to STA
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg3)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
+
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_psk_ext_retry_msg_3e(dev, apdev):
+ """WPA2-PSK AP using external EAPOL I/O and retry for EAPOL-Key msg 3/4 (e)"""
+ hapd = setup_psk_ext(dev[0], apdev[0])
+ bssid = apdev[0]['bssid']
+ addr = dev[0].p2p_interface_addr()
+
+ # EAPOL-Key msg 1/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ msg1 = ev.split(' ')[2]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg1)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ # EAPOL-Key msg 3/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ msg4 = ev.split(' ')[2]
+ # Do not send msg 4/4 to hostapd to trigger retry
+
+ # STA believes everything is ready
+ dev[0].wait_connected()
+
+ # EAPOL-Key msg 3/4 (retry)
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ msg3 = ev.split(' ')[2]
+
+ # Send a forged msg 1/4 to STA (update replay counter and replace ANonce)
+ msg1b = msg1[0:18] + msg3[18:34] + 32*"ff" + msg1[98:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg1b)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=1)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ # Do not send msg 2/4 to hostapd
+
+ # Send a forged msg 1/4 to STA (back to previously used ANonce)
+ msg1b = msg1[0:18] + msg3[18:34] + msg1[34:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg1b)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=1)
+ if ev is None:
+ # wpa_supplicant seems to have ignored the forged message. This means
+ # the attack would fail.
+ logger.info("wpa_supplicant ignored forged EAPOL-Key msg 1/4")
+ return
+ # Do not send msg 2/4 to hostapd
+
+ # EAPOL-Key msg 3/4 (retry 2)
+ # New one needed to get the correct Replay Counter value
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ msg3 = ev.split(' ')[2]
+
+ # Send msg 3/4 to STA
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg3)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
+
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_psk_ext_delayed_ptk_rekey(dev, apdev):
+ """WPA2-PSK AP using external EAPOL I/O and delayed PTK rekey exchange"""
+ hapd = setup_psk_ext(dev[0], apdev[0], wpa_ptk_rekey="3")
+ bssid = apdev[0]['bssid']
+ addr = dev[0].p2p_interface_addr()
+
+ # EAPOL-Key msg 1/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ msg2 = ev.split(' ')[2]
+ # Do not send this to the AP
+
+ # EAPOL-Key msg 1/4 (retry)
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ # EAPOL-Key msg 3/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ msg4 = ev.split(' ')[2]
+ # Do not send msg 4/4 to AP
+
+ # EAPOL-Key msg 3/4 (retry)
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ msg4b = ev.split(' ')[2]
+ # Do not send msg 4/4 to AP
+
+ # Send the previous EAPOL-Key msg 4/4 to AP
+ res = hapd.request("EAPOL_RX " + addr + " " + msg4)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
+
+ # Wait for PTK rekeying to be initialized
+ # EAPOL-Key msg 1/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+
+ # EAPOL-Key msg 2/4 from the previous 4-way handshake
+ # hostapd is expected to ignore this due to unexpected Replay Counter
+ res = hapd.request("EAPOL_RX " + addr + " " + msg2)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ # EAPOL-Key msg 3/4 (actually, this ends up being retransmitted 1/4)
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ keyinfo = ev.split(' ')[2][10:14]
+ if keyinfo != "008a":
+ raise Exception("Unexpected key info when expected msg 1/4:" + keyinfo)
+
+ # EAPOL-Key msg 4/4 from the previous 4-way handshake
+ # hostapd is expected to ignore this due to unexpected Replay Counter
+ res = hapd.request("EAPOL_RX " + addr + " " + msg4b)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ # Check if any more EAPOL-Key frames are seen. If the second 4-way handshake
+ # was accepted, there would be no more EAPOL-Key frames. If the Replay
+ # Counters were rejected, there would be a retransmitted msg 1/4 here.
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=1.1)
+ if ev is None:
+ raise Exception("Did not see EAPOL-TX from hostapd in the end (expected msg 1/4)")
+ keyinfo = ev.split(' ')[2][10:14]
+ if keyinfo != "008a":
+ raise Exception("Unexpected key info when expected msg 1/4:" + keyinfo)
+
+def parse_eapol(data):
+ (version, type, length) = struct.unpack('>BBH', data[0:4])
+ payload = data[4:]
+ if length > len(payload):
+ raise Exception("Invalid EAPOL length")
+ if length < len(payload):
+ payload = payload[0:length]
+ eapol = {}
+ eapol['version'] = version
+ eapol['type'] = type
+ eapol['length'] = length
+ eapol['payload'] = payload
+ if type == 3:
+ # EAPOL-Key
+ (eapol['descr_type'],) = struct.unpack('B', payload[0:1])
+ payload = payload[1:]
+ if eapol['descr_type'] == 2 or eapol['descr_type'] == 254:
+ # RSN EAPOL-Key
+ (key_info, key_len) = struct.unpack('>HH', payload[0:4])
+ eapol['rsn_key_info'] = key_info
+ eapol['rsn_key_len'] = key_len
+ eapol['rsn_replay_counter'] = payload[4:12]
+ eapol['rsn_key_nonce'] = payload[12:44]
+ eapol['rsn_key_iv'] = payload[44:60]
+ eapol['rsn_key_rsc'] = payload[60:68]
+ eapol['rsn_key_id'] = payload[68:76]
+ eapol['rsn_key_mic'] = payload[76:92]
+ payload = payload[92:]
+ (eapol['rsn_key_data_len'],) = struct.unpack('>H', payload[0:2])
+ payload = payload[2:]
+ eapol['rsn_key_data'] = payload
+ return eapol
+
+def build_eapol(msg):
+ data = struct.pack(">BBH", msg['version'], msg['type'], msg['length'])
+ if msg['type'] == 3:
+ data += struct.pack('>BHH', msg['descr_type'], msg['rsn_key_info'],
+ msg['rsn_key_len'])
+ data += msg['rsn_replay_counter']
+ data += msg['rsn_key_nonce']
+ data += msg['rsn_key_iv']
+ data += msg['rsn_key_rsc']
+ data += msg['rsn_key_id']
+ data += msg['rsn_key_mic']
+ data += struct.pack('>H', msg['rsn_key_data_len'])
+ data += msg['rsn_key_data']
+ else:
+ data += msg['payload']
+ return data
+
+def sha1_prf(key, label, data, outlen):
+ res = b''
+ counter = 0
+ while outlen > 0:
+ m = hmac.new(key, label.encode(), hashlib.sha1)
+ m.update(struct.pack('B', 0))
+ m.update(data)
+ m.update(struct.pack('B', counter))
+ counter += 1
+ hash = m.digest()
+ if outlen > len(hash):
+ res += hash
+ outlen -= len(hash)
+ else:
+ res += hash[0:outlen]
+ outlen = 0
+ return res
+
+def pmk_to_ptk(pmk, addr1, addr2, nonce1, nonce2):
+ if addr1 < addr2:
+ data = binascii.unhexlify(addr1.replace(':', '')) + binascii.unhexlify(addr2.replace(':', ''))
+ else:
+ data = binascii.unhexlify(addr2.replace(':', '')) + binascii.unhexlify(addr1.replace(':', ''))
+ if nonce1 < nonce2:
+ data += nonce1 + nonce2
+ else:
+ data += nonce2 + nonce1
+ label = "Pairwise key expansion"
+ ptk = sha1_prf(pmk, label, data, 48)
+ kck = ptk[0:16]
+ kek = ptk[16:32]
+ return (ptk, kck, kek)
+
+def eapol_key_mic(kck, msg):
+ msg['rsn_key_mic'] = binascii.unhexlify('00000000000000000000000000000000')
+ data = build_eapol(msg)
+ m = hmac.new(kck, data, hashlib.sha1)
+ msg['rsn_key_mic'] = m.digest()[0:16]
+
+def rsn_eapol_key_set(msg, key_info, key_len, nonce, data):
+ msg['rsn_key_info'] = key_info
+ msg['rsn_key_len'] = key_len
+ if nonce:
+ msg['rsn_key_nonce'] = nonce
+ else:
+ msg['rsn_key_nonce'] = binascii.unhexlify('0000000000000000000000000000000000000000000000000000000000000000')
+ if data:
+ msg['rsn_key_data_len'] = len(data)
+ msg['rsn_key_data'] = data
+ msg['length'] = 95 + len(data)
+ else:
+ msg['rsn_key_data_len'] = 0
+ msg['rsn_key_data'] = b''
+ msg['length'] = 95
+
+def recv_eapol(hapd):
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ eapol = binascii.unhexlify(ev.split(' ')[2])
+ return parse_eapol(eapol)
+
+def send_eapol(hapd, addr, data):
+ res = hapd.request("EAPOL_RX " + addr + " " + binascii.hexlify(data).decode())
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+def reply_eapol(info, hapd, addr, msg, key_info, nonce, data, kck):
+ logger.info("Send EAPOL-Key msg " + info)
+ rsn_eapol_key_set(msg, key_info, 0, nonce, data)
+ eapol_key_mic(kck, msg)
+ send_eapol(hapd, addr, build_eapol(msg))
+
+def eapol_test(apdev, dev, wpa2=True, ieee80211w=0):
+ bssid = apdev['bssid']
+ if wpa2:
+ ssid = "test-wpa2-psk"
+ else:
+ ssid = "test-wpa-psk"
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ pmk = binascii.unhexlify(psk)
+ if wpa2:
+ params = hostapd.wpa2_params(ssid=ssid)
+ else:
+ params = hostapd.wpa_params(ssid=ssid)
+ params['wpa_psk'] = psk
+ params['ieee80211w'] = str(ieee80211w)
+ hapd = hostapd.add_ap(apdev, params)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev.request("SET ext_eapol_frame_io 1")
+ dev.connect(ssid, raw_psk=psk, scan_freq="2412", wait_connect=False,
+ ieee80211w=str(ieee80211w))
+ addr = dev.p2p_interface_addr()
+ if wpa2:
+ if ieee80211w == 2:
+ rsne = binascii.unhexlify('30140100000fac040100000fac040100000fac02cc00')
+ else:
+ rsne = binascii.unhexlify('30140100000fac040100000fac040100000fac020000')
+ else:
+ rsne = binascii.unhexlify('dd160050f20101000050f20201000050f20201000050f202')
+ snonce = binascii.unhexlify('1111111111111111111111111111111111111111111111111111111111111111')
+ return (bssid, ssid, hapd, snonce, pmk, addr, rsne)
+
+@remote_compatible
+def test_ap_wpa2_psk_ext_eapol(dev, apdev):
+ """WPA2-PSK AP using external EAPOL supplicant"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ msg = recv_eapol(hapd)
+ anonce = msg['rsn_key_nonce']
+ logger.info("Replay same data back")
+ send_eapol(hapd, addr, build_eapol(msg))
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.info("Truncated Key Data in EAPOL-Key msg 2/4")
+ rsn_eapol_key_set(msg, 0x0101, 0, snonce, rsne)
+ msg['length'] = 95 + 22 - 1
+ send_eapol(hapd, addr, build_eapol(msg))
+
+ reply_eapol("2/4", hapd, addr, msg, 0x010a, snonce, rsne, kck)
+
+ msg = recv_eapol(hapd)
+ if anonce != msg['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+ logger.info("Replay same data back")
+ send_eapol(hapd, addr, build_eapol(msg))
+
+ reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+ hapd.wait_sta(timeout=15)
+
+@remote_compatible
+def test_ap_wpa2_psk_ext_eapol_retry1(dev, apdev):
+ """WPA2 4-way handshake with EAPOL-Key 1/4 retransmitted"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ msg1 = recv_eapol(hapd)
+ anonce = msg1['rsn_key_nonce']
+
+ msg2 = recv_eapol(hapd)
+ if anonce != msg2['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.info("Send EAPOL-Key msg 2/4")
+ msg = msg2
+ rsn_eapol_key_set(msg, 0x010a, 0, snonce, rsne)
+ eapol_key_mic(kck, msg)
+ send_eapol(hapd, addr, build_eapol(msg))
+
+ msg = recv_eapol(hapd)
+ if anonce != msg['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+
+ reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+ hapd.wait_sta(timeout=15)
+
+@remote_compatible
+def test_ap_wpa2_psk_ext_eapol_retry1b(dev, apdev):
+ """WPA2 4-way handshake with EAPOL-Key 1/4 and 2/4 retransmitted"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ msg1 = recv_eapol(hapd)
+ anonce = msg1['rsn_key_nonce']
+ msg2 = recv_eapol(hapd)
+ if anonce != msg2['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+ reply_eapol("2/4 (a)", hapd, addr, msg1, 0x010a, snonce, rsne, kck)
+ reply_eapol("2/4 (b)", hapd, addr, msg2, 0x010a, snonce, rsne, kck)
+
+ msg = recv_eapol(hapd)
+ if anonce != msg['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+
+ reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+ hapd.wait_sta(timeout=15)
+
+@remote_compatible
+def test_ap_wpa2_psk_ext_eapol_retry1c(dev, apdev):
+ """WPA2 4-way handshake with EAPOL-Key 1/4 and 2/4 retransmitted and SNonce changing"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ msg1 = recv_eapol(hapd)
+ anonce = msg1['rsn_key_nonce']
+
+ msg2 = recv_eapol(hapd)
+ if anonce != msg2['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+ reply_eapol("2/4 (a)", hapd, addr, msg1, 0x010a, snonce, rsne, kck)
+
+ snonce2 = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce2, anonce)
+ reply_eapol("2/4 (b)", hapd, addr, msg2, 0x010a, snonce2, rsne, kck)
+
+ msg = recv_eapol(hapd)
+ if anonce != msg['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+ reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+ hapd.wait_sta(timeout=15)
+
+@remote_compatible
+def test_ap_wpa2_psk_ext_eapol_retry1d(dev, apdev):
+ """WPA2 4-way handshake with EAPOL-Key 1/4 and 2/4 retransmitted and SNonce changing and older used"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ msg1 = recv_eapol(hapd)
+ anonce = msg1['rsn_key_nonce']
+ msg2 = recv_eapol(hapd)
+ if anonce != msg2['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+ reply_eapol("2/4 (a)", hapd, addr, msg1, 0x010a, snonce, rsne, kck)
+
+ snonce2 = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ (ptk2, kck2, kek2) = pmk_to_ptk(pmk, addr, bssid, snonce2, anonce)
+
+ reply_eapol("2/4 (b)", hapd, addr, msg2, 0x010a, snonce2, rsne, kck2)
+ msg = recv_eapol(hapd)
+ if anonce != msg['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+ reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+ hapd.wait_sta(timeout=15)
+
+@remote_compatible
+def test_ap_wpa2_psk_ext_eapol_type_diff(dev, apdev):
+ """WPA2 4-way handshake using external EAPOL supplicant"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ msg = recv_eapol(hapd)
+ anonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ # Incorrect descriptor type (frame dropped)
+ msg['descr_type'] = 253
+ rsn_eapol_key_set(msg, 0x010a, 0, snonce, rsne)
+ eapol_key_mic(kck, msg)
+ send_eapol(hapd, addr, build_eapol(msg))
+
+ # Incorrect descriptor type, but with a workaround (frame processed)
+ msg['descr_type'] = 254
+ rsn_eapol_key_set(msg, 0x010a, 0, snonce, rsne)
+ eapol_key_mic(kck, msg)
+ send_eapol(hapd, addr, build_eapol(msg))
+
+ msg = recv_eapol(hapd)
+ if anonce != msg['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+ logger.info("Replay same data back")
+ send_eapol(hapd, addr, build_eapol(msg))
+
+ reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+ hapd.wait_sta(timeout=15)
+
+@remote_compatible
+def test_ap_wpa_psk_ext_eapol(dev, apdev):
+ """WPA2-PSK AP using external EAPOL supplicant"""
+ skip_without_tkip(dev[0])
+ (bssid, ssid, hapd, snonce, pmk, addr, wpae) = eapol_test(apdev[0], dev[0],
+ wpa2=False)
+
+ msg = recv_eapol(hapd)
+ anonce = msg['rsn_key_nonce']
+ logger.info("Replay same data back")
+ send_eapol(hapd, addr, build_eapol(msg))
+ logger.info("Too short data")
+ send_eapol(hapd, addr, build_eapol(msg)[0:98])
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+ msg['descr_type'] = 2
+ reply_eapol("2/4(invalid type)", hapd, addr, msg, 0x010a, snonce, wpae, kck)
+ msg['descr_type'] = 254
+ reply_eapol("2/4", hapd, addr, msg, 0x010a, snonce, wpae, kck)
+
+ msg = recv_eapol(hapd)
+ if anonce != msg['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+ logger.info("Replay same data back")
+ send_eapol(hapd, addr, build_eapol(msg))
+
+ reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+ hapd.wait_sta(timeout=15)
+
+@remote_compatible
+def test_ap_wpa2_psk_ext_eapol_key_info(dev, apdev):
+ """WPA2-PSK 4-way handshake with strange key info values"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ msg = recv_eapol(hapd)
+ anonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+ rsn_eapol_key_set(msg, 0x0000, 0, snonce, rsne)
+ send_eapol(hapd, addr, build_eapol(msg))
+ rsn_eapol_key_set(msg, 0xffff, 0, snonce, rsne)
+ send_eapol(hapd, addr, build_eapol(msg))
+ # SMK M1
+ rsn_eapol_key_set(msg, 0x2802, 0, snonce, rsne)
+ send_eapol(hapd, addr, build_eapol(msg))
+ # SMK M3
+ rsn_eapol_key_set(msg, 0x2002, 0, snonce, rsne)
+ send_eapol(hapd, addr, build_eapol(msg))
+ # Request
+ rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
+ send_eapol(hapd, addr, build_eapol(msg))
+ # Request
+ rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
+ tmp_kck = binascii.unhexlify('00000000000000000000000000000000')
+ eapol_key_mic(tmp_kck, msg)
+ send_eapol(hapd, addr, build_eapol(msg))
+
+ reply_eapol("2/4", hapd, addr, msg, 0x010a, snonce, rsne, kck)
+
+ msg = recv_eapol(hapd)
+ if anonce != msg['rsn_key_nonce']:
+ raise Exception("ANonce changed")
+
+ # Request (valic MIC)
+ rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
+ eapol_key_mic(kck, msg)
+ send_eapol(hapd, addr, build_eapol(msg))
+ # Request (valid MIC, replayed counter)
+ rsn_eapol_key_set(msg, 0x0902, 0, snonce, rsne)
+ eapol_key_mic(kck, msg)
+ send_eapol(hapd, addr, build_eapol(msg))
+
+ reply_eapol("4/4", hapd, addr, msg, 0x030a, None, None, kck)
+ hapd.wait_sta(timeout=15)
+
+def build_eapol_key_1_4(anonce, replay_counter=1, key_data=b'', key_len=16):
+ msg = {}
+ msg['version'] = 2
+ msg['type'] = 3
+ msg['length'] = 95 + len(key_data)
+
+ msg['descr_type'] = 2
+ msg['rsn_key_info'] = 0x8a
+ msg['rsn_key_len'] = key_len
+ msg['rsn_replay_counter'] = struct.pack('>Q', replay_counter)
+ msg['rsn_key_nonce'] = anonce
+ msg['rsn_key_iv'] = binascii.unhexlify('00000000000000000000000000000000')
+ msg['rsn_key_rsc'] = binascii.unhexlify('0000000000000000')
+ msg['rsn_key_id'] = binascii.unhexlify('0000000000000000')
+ msg['rsn_key_mic'] = binascii.unhexlify('00000000000000000000000000000000')
+ msg['rsn_key_data_len'] = len(key_data)
+ msg['rsn_key_data'] = key_data
+ return msg
+
+def build_eapol_key_3_4(anonce, kck, key_data, replay_counter=2,
+ key_info=0x13ca, extra_len=0, descr_type=2, key_len=16):
+ msg = {}
+ msg['version'] = 2
+ msg['type'] = 3
+ msg['length'] = 95 + len(key_data) + extra_len
+
+ msg['descr_type'] = descr_type
+ msg['rsn_key_info'] = key_info
+ msg['rsn_key_len'] = key_len
+ msg['rsn_replay_counter'] = struct.pack('>Q', replay_counter)
+ msg['rsn_key_nonce'] = anonce
+ msg['rsn_key_iv'] = binascii.unhexlify('00000000000000000000000000000000')
+ msg['rsn_key_rsc'] = binascii.unhexlify('0000000000000000')
+ msg['rsn_key_id'] = binascii.unhexlify('0000000000000000')
+ msg['rsn_key_data_len'] = len(key_data)
+ msg['rsn_key_data'] = key_data
+ eapol_key_mic(kck, msg)
+ return msg
+
+def aes_wrap(kek, plain):
+ n = len(plain) // 8
+ a = 0xa6a6a6a6a6a6a6a6
+ enc = AES.new(kek).encrypt
+ r = [plain[i * 8:(i + 1) * 8] for i in range(0, n)]
+ for j in range(6):
+ for i in range(1, n + 1):
+ b = enc(struct.pack('>Q', a) + r[i - 1])
+ a = struct.unpack('>Q', b[:8])[0] ^ (n * j + i)
+ r[i - 1] = b[8:]
+ return struct.pack('>Q', a) + b''.join(r)
+
+def pad_key_data(plain):
+ pad_len = len(plain) % 8
+ if pad_len:
+ pad_len = 8 - pad_len
+ plain += b'\xdd'
+ pad_len -= 1
+ plain += pad_len * b'\x00'
+ return plain
+
+def test_ap_wpa2_psk_supp_proto(dev, apdev):
+ """WPA2-PSK 4-way handshake protocol testing for supplicant"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Invalid AES wrap data length 0")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'', replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Unsupported AES-WRAP len 0"])
+ if ev is None:
+ raise Exception("Unsupported AES-WRAP len 0 not reported")
+
+ logger.debug("Invalid AES wrap data length 1")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'1', replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Unsupported AES-WRAP len 1"])
+ if ev is None:
+ raise Exception("Unsupported AES-WRAP len 1 not reported")
+
+ logger.debug("Invalid AES wrap data length 9")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'123456789', replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Unsupported AES-WRAP len 9"])
+ if ev is None:
+ raise Exception("Unsupported AES-WRAP len 9 not reported")
+
+ logger.debug("Invalid AES wrap data payload")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter)
+ # do not increment counter to test replay protection
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: AES unwrap failed"])
+ if ev is None:
+ raise Exception("AES unwrap failure not reported")
+
+ logger.debug("Replay Count not increasing")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: EAPOL-Key Replay Counter did not increase"])
+ if ev is None:
+ raise Exception("Replay Counter replay not reported")
+
+ logger.debug("Missing Ack bit in key info")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
+ key_info=0x134a)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: No Ack bit in key_info"])
+ if ev is None:
+ raise Exception("Missing Ack bit not reported")
+
+ logger.debug("Unexpected Request bit in key info")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
+ key_info=0x1bca)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: EAPOL-Key with Request bit"])
+ if ev is None:
+ raise Exception("Request bit not reported")
+
+ logger.debug("Unsupported key descriptor version 0")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
+ replay_counter=counter, key_info=0x13c8)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Unsupported EAPOL-Key descriptor version 0"])
+ if ev is None:
+ raise Exception("Unsupported EAPOL-Key descriptor version 0 not reported")
+
+ logger.debug("Key descriptor version 1 not allowed with CCMP")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
+ replay_counter=counter, key_info=0x13c9)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: CCMP is used, but EAPOL-Key descriptor version (1) is not 2"])
+ if ev is None:
+ raise Exception("Not allowed EAPOL-Key descriptor version not reported")
+
+ logger.debug("Invalid AES wrap payload with key descriptor version 2")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
+ replay_counter=counter, key_info=0x13ca)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: AES unwrap failed"])
+ if ev is None:
+ raise Exception("AES unwrap failure not reported")
+
+ logger.debug("Key descriptor version 3 workaround")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
+ replay_counter=counter, key_info=0x13cb)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: CCMP is used, but EAPOL-Key descriptor version (3) is not 2"])
+ if ev is None:
+ raise Exception("CCMP key descriptor mismatch not reported")
+ ev = dev[0].wait_event(["WPA: Interoperability workaround"])
+ if ev is None:
+ raise Exception("AES-128-CMAC workaround not reported")
+ ev = dev[0].wait_event(["WPA: Invalid EAPOL-Key MIC - dropping packet"])
+ if ev is None:
+ raise Exception("MIC failure with AES-128-CMAC workaround not reported")
+
+ logger.debug("Unsupported key descriptor version 4")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
+ replay_counter=counter, key_info=0x13cc)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Unsupported EAPOL-Key descriptor version 4"])
+ if ev is None:
+ raise Exception("Unsupported EAPOL-Key descriptor version 4 not reported")
+
+ logger.debug("Unsupported key descriptor version 7")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'0123456789abcdef',
+ replay_counter=counter, key_info=0x13cf)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Unsupported EAPOL-Key descriptor version 7"])
+ if ev is None:
+ raise Exception("Unsupported EAPOL-Key descriptor version 7 not reported")
+
+ logger.debug("Too short EAPOL header length")
+ dev[0].dump_monitor()
+ msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
+ extra_len=-1)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Invalid EAPOL-Key frame - key_data overflow (8 > 7)"])
+ if ev is None:
+ raise Exception("Key data overflow not reported")
+
+ logger.debug("Too long EAPOL header length")
+ msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
+ extra_len=1)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+
+ logger.debug("Unsupported descriptor type 0")
+ msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
+ descr_type=0)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+
+ logger.debug("WPA descriptor type 0")
+ msg = build_eapol_key_3_4(anonce, kck, b'12345678', replay_counter=counter,
+ descr_type=254)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+
+ logger.debug("Non-zero key index for pairwise key")
+ dev[0].dump_monitor()
+ wrapped = aes_wrap(kek, 16*b'z')
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+ key_info=0x13ea)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Ignored EAPOL-Key (Pairwise) with non-zero key index"])
+ if ev is None:
+ raise Exception("Non-zero key index not reported")
+
+ logger.debug("Invalid Key Data plaintext payload --> disconnect")
+ dev[0].dump_monitor()
+ wrapped = aes_wrap(kek, 16*b'z')
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_no_ie(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: IE not included"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("No IEs in msg 3/4 --> disconnect")
+ dev[0].dump_monitor()
+ wrapped = aes_wrap(kek, 16*b'\x00')
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_ie_mismatch(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: IE mismatch"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Msg 3/4 with mismatching IE")
+ dev[0].dump_monitor()
+ wrapped = aes_wrap(kek, pad_key_data(binascii.unhexlify('30060100000fac04dd16000fac010100dc11188831bf4aa4a8678d2b41498618')))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_ok(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: success"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Valid EAPOL-Key msg 3/4")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ dev[0].wait_connected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_no_gtk(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: no GTK"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("EAPOL-Key msg 3/4 without GTK KDE")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected connection completion reported")
+
+def test_ap_wpa2_psk_supp_proto_anonce_change(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: ANonce change"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Valid EAPOL-Key msg 3/4")
+ dev[0].dump_monitor()
+ anonce2 = binascii.unhexlify('3333333333333333333333333333333333333333333333333333333333333333')
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce2, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: ANonce from message 1 of 4-Way Handshake differs from 3 of 4-Way Handshake"])
+ if ev is None:
+ raise Exception("ANonce change not reported")
+
+def test_ap_wpa2_psk_supp_proto_unexpected_group_msg(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: unexpected group message"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Group key 1/2 instead of msg 3/4")
+ dev[0].dump_monitor()
+ wrapped = aes_wrap(kek, binascii.unhexlify('dd16000fac010100dc11188831bf4aa4a8678d2b41498618'))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+ key_info=0x13c2)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Group Key Handshake started prior to completion of 4-way handshake"])
+ if ev is None:
+ raise Exception("Unexpected group key message not reported")
+ dev[0].wait_disconnected(timeout=1)
+
+@remote_compatible
+def test_ap_wpa2_psk_supp_proto_msg_1_invalid_kde(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: invalid KDE in msg 1/4"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4 with invalid KDE
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter,
+ key_data=binascii.unhexlify('5555'))
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_wrong_pairwise_key_len(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: wrong pairwise key length"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Valid EAPOL-Key msg 3/4")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+ key_len=15)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Invalid CCMP key length 15"])
+ if ev is None:
+ raise Exception("Invalid CCMP key length not reported")
+ dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_wrong_group_key_len(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: wrong group key length"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Valid EAPOL-Key msg 3/4")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd15000fac010100dc11188831bf4aa4a8678d2b414986')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Unsupported CCMP Group Cipher key length 15"])
+ if ev is None:
+ raise Exception("Invalid CCMP key length not reported")
+ dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_gtk_tx_bit_workaround(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: GTK TX bit workaround"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Valid EAPOL-Key msg 3/4")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010500dc11188831bf4aa4a8678d2b41498618')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Tx bit set for GTK, but pairwise keys are used - ignore Tx bit"])
+ if ev is None:
+ raise Exception("GTK Tx bit workaround not reported")
+ dev[0].wait_connected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_gtk_keyidx_0_and_3(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: GTK key index 0 and 3"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Valid EAPOL-Key msg 3/4 (GTK keyidx 0)")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010000dc11188831bf4aa4a8678d2b41498618')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ dev[0].wait_connected(timeout=1)
+
+ logger.debug("Valid EAPOL-Key group msg 1/2 (GTK keyidx 3)")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('dd16000fac010300dc11188831bf4aa4a8678d2b41498618')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+ key_info=0x13c2)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"])
+ if ev is None:
+ raise Exception("GTK rekeing not reported")
+
+ logger.debug("Unencrypted GTK KDE in group msg 1/2")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('dd16000fac010300dc11188831bf4aa4a8678d2b41498618')
+ msg = build_eapol_key_3_4(anonce, kck, plain, replay_counter=counter,
+ key_info=0x03c2)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: GTK IE in unencrypted key data"])
+ if ev is None:
+ raise Exception("Unencrypted GTK KDE not reported")
+ dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_no_gtk_in_group_msg(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: GTK KDE missing from group msg"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Valid EAPOL-Key msg 3/4 (GTK keyidx 0)")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010000dc11188831bf4aa4a8678d2b41498618')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ dev[0].wait_connected(timeout=1)
+
+ logger.debug("No GTK KDE in EAPOL-Key group msg 1/2")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('dd00dd00dd00dd00dd00dd00dd00dd00')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+ key_info=0x13c2)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: No GTK IE in Group Key msg 1/2"])
+ if ev is None:
+ raise Exception("Missing GTK KDE not reported")
+ dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_too_long_gtk_in_group_msg(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: too long GTK KDE in group msg"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Valid EAPOL-Key msg 3/4 (GTK keyidx 0)")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010000dc11188831bf4aa4a8678d2b41498618')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ dev[0].wait_connected(timeout=1)
+
+ logger.debug("EAPOL-Key group msg 1/2 with too long GTK KDE")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('dd27000fac010100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter,
+ key_info=0x13c2)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: Unsupported CCMP Group Cipher key length 33",
+ "RSN: Too long GTK in GTK KDE (len=33)"])
+ if ev is None:
+ raise Exception("Too long GTK KDE not reported")
+ dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_too_long_gtk_kde(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: too long GTK KDE"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("EAPOL-Key msg 3/4 with too short GTK KDE")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd27000fac010100ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff')
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ dev[0].wait_disconnected(timeout=1)
+
+def test_ap_wpa2_psk_supp_proto_gtk_not_encrypted(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: GTK KDE not encrypted"""
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0])
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("Valid EAPOL-Key msg 3/4")
+ dev[0].dump_monitor()
+ plain = binascii.unhexlify('30140100000fac040100000fac040100000fac020c00dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
+ msg = build_eapol_key_3_4(anonce, kck, plain, replay_counter=counter,
+ key_info=0x03ca)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ ev = dev[0].wait_event(["WPA: GTK IE in unencrypted key data"])
+ if ev is None:
+ raise Exception("Unencrypted GTK KDE not reported")
+ dev[0].wait_disconnected(timeout=1)
+
+def run_psk_supp_proto_pmf2(dev, apdev, igtk_kde=None, fail=False):
+ (bssid, ssid, hapd, snonce, pmk, addr, rsne) = eapol_test(apdev[0], dev[0],
+ ieee80211w=2)
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ msg = recv_eapol(hapd)
+ dev[0].dump_monitor()
+
+ # Build own EAPOL-Key msg 1/4
+ anonce = binascii.unhexlify('2222222222222222222222222222222222222222222222222222222222222222')
+ counter = 1
+ msg = build_eapol_key_1_4(anonce, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ msg = recv_eapol(dev[0])
+ snonce = msg['rsn_key_nonce']
+
+ (ptk, kck, kek) = pmk_to_ptk(pmk, addr, bssid, snonce, anonce)
+
+ logger.debug("EAPOL-Key msg 3/4")
+ dev[0].dump_monitor()
+ gtk_kde = binascii.unhexlify('dd16000fac010100dc11188831bf4aa4a8678d2b41498618')
+ plain = rsne + gtk_kde
+ if igtk_kde:
+ plain += igtk_kde
+ wrapped = aes_wrap(kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(anonce, kck, wrapped, replay_counter=counter)
+ counter += 1
+ send_eapol(dev[0], bssid, build_eapol(msg))
+ if fail:
+ dev[0].wait_disconnected(timeout=1)
+ return
+
+ dev[0].wait_connected(timeout=1)
+
+ # Verify that an unprotected broadcast Deauthentication frame is ignored
+ bssid = binascii.unhexlify(hapd.own_addr().replace(':', ''))
+ sock = start_monitor(apdev[1]["ifname"])
+ radiotap = radiotap_build()
+ frame = binascii.unhexlify("c0003a01")
+ frame += 6*b'\xff' + bssid + bssid
+ frame += binascii.unhexlify("1000" + "0300")
+ sock.send(radiotap + frame)
+ # And same with incorrect BIP protection
+ for keyid in ["0400", "0500", "0600", "0004", "0005", "0006", "ffff"]:
+ frame2 = frame + binascii.unhexlify("4c10" + keyid + "010000000000c0e5ca5f2b3b4de9")
+ sock.send(radiotap + frame2)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+def run_psk_supp_proto_pmf(dev, apdev, igtk_kde=None, fail=False):
+ try:
+ run_psk_supp_proto_pmf2(dev, apdev, igtk_kde=igtk_kde, fail=fail)
+ finally:
+ stop_monitor(apdev[1]["ifname"])
+
+def test_ap_wpa2_psk_supp_proto_no_igtk(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: no IGTK KDE"""
+ run_psk_supp_proto_pmf(dev, apdev, igtk_kde=None)
+
+def test_ap_wpa2_psk_supp_proto_igtk_ok(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: valid IGTK KDE"""
+ igtk_kde = binascii.unhexlify('dd1c' + '000fac09' + '0400' + 6*'00' + 16*'77')
+ run_psk_supp_proto_pmf(dev, apdev, igtk_kde=igtk_kde)
+
+def test_ap_wpa2_psk_supp_proto_igtk_keyid_swap(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: swapped IGTK KeyID"""
+ igtk_kde = binascii.unhexlify('dd1c' + '000fac09' + '0004' + 6*'00' + 16*'77')
+ run_psk_supp_proto_pmf(dev, apdev, igtk_kde=igtk_kde)
+
+def test_ap_wpa2_psk_supp_proto_igtk_keyid_too_large(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: too large IGTK KeyID"""
+ igtk_kde = binascii.unhexlify('dd1c' + '000fac09' + 'ffff' + 6*'00' + 16*'77')
+ run_psk_supp_proto_pmf(dev, apdev, igtk_kde=igtk_kde, fail=True)
+
+def test_ap_wpa2_psk_supp_proto_igtk_keyid_unexpected(dev, apdev):
+ """WPA2-PSK supplicant protocol testing: unexpected IGTK KeyID"""
+ igtk_kde = binascii.unhexlify('dd1c' + '000fac09' + '0006' + 6*'00' + 16*'77')
+ run_psk_supp_proto_pmf(dev, apdev, igtk_kde=igtk_kde, fail=True)
+
+def find_wpas_process(dev):
+ ifname = dev.ifname
+ err, data = dev.cmd_execute(['ps', 'ax'])
+ for l in data.splitlines():
+ if "wpa_supplicant" not in l:
+ continue
+ if "-i" + ifname not in l:
+ continue
+ return int(l.strip().split(' ')[0])
+ raise Exception("Could not find wpa_supplicant process")
+
+def read_process_memory(pid, key=None):
+ buf = bytes()
+ logger.info("Reading process memory (pid=%d)" % pid)
+ with open('/proc/%d/maps' % pid, 'r') as maps, \
+ open('/proc/%d/mem' % pid, 'rb') as mem:
+ for l in maps.readlines():
+ m = re.match(r'([0-9a-f]+)-([0-9a-f]+) ([-r][-w][-x][-p])', l)
+ if not m:
+ continue
+ start = int(m.group(1), 16)
+ end = int(m.group(2), 16)
+ perm = m.group(3)
+ if start > 0xffffffffffff:
+ continue
+ if end < start:
+ continue
+ if not perm.startswith('rw'):
+ continue
+ for name in ["[heap]", "[stack]"]:
+ if name in l:
+ logger.info("%s 0x%x-0x%x is at %d-%d" % (name, start, end, len(buf), len(buf) + (end - start)))
+ mem.seek(start)
+ data = mem.read(end - start)
+ buf += data
+ if key and key in data:
+ logger.info("Key found in " + l)
+ logger.info("Total process memory read: %d bytes" % len(buf))
+ return buf
+
+def verify_not_present(buf, key, fname, keyname):
+ pos = buf.find(key)
+ if pos < 0:
+ return
+
+ prefix = 2048 if pos > 2048 else pos
+ with open(fname + keyname, 'wb') as f:
+ f.write(buf[pos - prefix:pos + 2048])
+ raise Exception(keyname + " found after disassociation")
+
+def get_key_locations(buf, key, keyname):
+ count = 0
+ pos = 0
+ while True:
+ pos = buf.find(key, pos)
+ if pos < 0:
+ break
+ logger.info("Found %s at %d" % (keyname, pos))
+ context = 128
+ start = pos - context if pos > context else 0
+ before = binascii.hexlify(buf[start:pos])
+ context += len(key)
+ end = pos + context if pos < len(buf) - context else len(buf) - context
+ after = binascii.hexlify(buf[pos + len(key):end])
+ logger.debug("Memory context %d-%d: %s|%s|%s" % (start, end, before, binascii.hexlify(key), after))
+ count += 1
+ pos += len(key)
+ return count
+
+def test_wpa2_psk_key_lifetime_in_memory(dev, apdev, params):
+ """WPA2-PSK and PSK/PTK lifetime in memory"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ pmk = binascii.unhexlify(psk)
+ p = hostapd.wpa2_params(ssid=ssid)
+ p['wpa_psk'] = psk
+ hapd = hostapd.add_ap(apdev[0], p)
+
+ pid = find_wpas_process(dev[0])
+
+ id = dev[0].connect(ssid, raw_psk=psk, scan_freq="2412",
+ only_add_network=True)
+
+ logger.info("Checking keys in memory after network profile configuration")
+ buf = read_process_memory(pid, pmk)
+ get_key_locations(buf, pmk, "PMK")
+
+ dev[0].request("REMOVE_NETWORK all")
+ logger.info("Checking keys in memory after network profile removal")
+ buf = read_process_memory(pid, pmk)
+ get_key_locations(buf, pmk, "PMK")
+
+ id = dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
+ only_add_network=True)
+
+ logger.info("Checking keys in memory before connection")
+ buf = read_process_memory(pid, pmk)
+ get_key_locations(buf, pmk, "PMK")
+
+ dev[0].connect_network(id, timeout=20)
+ # The decrypted copy of GTK is freed only after the CTRL-EVENT-CONNECTED
+ # event has been delivered, so verify that wpa_supplicant has returned to
+ # eloop before reading process memory.
+ time.sleep(1)
+ dev[0].ping()
+
+ buf = read_process_memory(pid, pmk)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].relog()
+ ptk = None
+ gtk = None
+ with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+ for l in f.readlines():
+ if "WPA: PTK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ ptk = binascii.unhexlify(val)
+ if "WPA: Group Key - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ gtk = binascii.unhexlify(val)
+ if not pmk or not ptk or not gtk:
+ raise Exception("Could not find keys from debug log")
+ if len(gtk) != 16:
+ raise Exception("Unexpected GTK length")
+
+ kck = ptk[0:16]
+ kek = ptk[16:32]
+ tk = ptk[32:48]
+
+ logger.info("Checking keys in memory while associated")
+ get_key_locations(buf, pmk, "PMK")
+ if pmk not in buf:
+ raise HwsimSkip("PMK not found while associated")
+ if kck not in buf:
+ raise Exception("KCK not found while associated")
+ if kek not in buf:
+ raise Exception("KEK not found while associated")
+ #if tk in buf:
+ # raise Exception("TK found from memory")
+
+ logger.info("Checking keys in memory after disassociation")
+ buf = read_process_memory(pid, pmk)
+ get_key_locations(buf, pmk, "PMK")
+
+ # Note: PMK/PSK is still present in network configuration
+
+ fname = os.path.join(params['logdir'],
+ 'wpa2_psk_key_lifetime_in_memory.memctx-')
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ if gtk in buf:
+ get_key_locations(buf, gtk, "GTK")
+ verify_not_present(buf, gtk, fname, "GTK")
+
+ dev[0].request("REMOVE_NETWORK all")
+
+ logger.info("Checking keys in memory after network profile removal")
+ buf = read_process_memory(pid, pmk)
+ get_key_locations(buf, pmk, "PMK")
+
+ verify_not_present(buf, pmk, fname, "PMK")
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ verify_not_present(buf, gtk, fname, "GTK")
+
+@remote_compatible
+def test_ap_wpa2_psk_wep(dev, apdev):
+ """WPA2-PSK AP and WEP enabled"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ hapd.set('wep_key0', '"hello"')
+ raise Exception("WEP key accepted to WPA2 network")
+ except Exception:
+ pass
+
+def test_ap_wpa2_psk_wpas_in_bridge(dev, apdev):
+ """WPA2-PSK AP and wpas interface in a bridge"""
+ br_ifname = 'sta-br0'
+ ifname = 'wlan5'
+ try:
+ _test_ap_wpa2_psk_wpas_in_bridge(dev, apdev)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
+ subprocess.call(['brctl', 'delif', br_ifname, ifname])
+ subprocess.call(['brctl', 'delbr', br_ifname])
+ subprocess.call(['iw', ifname, 'set', '4addr', 'off'])
+
+def _test_ap_wpa2_psk_wpas_in_bridge(dev, apdev):
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ br_ifname = 'sta-br0'
+ ifname = 'wlan5'
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ subprocess.call(['brctl', 'addbr', br_ifname])
+ subprocess.call(['brctl', 'setfd', br_ifname, '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+ subprocess.call(['iw', ifname, 'set', '4addr', 'on'])
+ subprocess.check_call(['brctl', 'addif', br_ifname, ifname])
+ wpas.interface_add(ifname, br_ifname=br_ifname)
+ wpas.dump_monitor()
+
+ wpas.connect(ssid, psk=passphrase, scan_freq="2412")
+ wpas.dump_monitor()
+
+@remote_compatible
+def test_ap_wpa2_psk_ifdown(dev, apdev):
+ """AP with open mode and external ifconfig down"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ hapd.cmd_execute(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'down'])
+ ev = hapd.wait_event(["INTERFACE-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("No INTERFACE-DISABLED event")
+ # this wait tests beacon loss detection in mac80211
+ dev[0].wait_disconnected()
+ hapd.cmd_execute(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'up'])
+ ev = hapd.wait_event(["INTERFACE-ENABLED"], timeout=10)
+ if ev is None:
+ raise Exception("No INTERFACE-ENABLED event")
+ dev[0].wait_connected()
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_psk_drop_first_msg_4(dev, apdev):
+ """WPA2-PSK and first EAPOL-Key msg 4/4 dropped"""
+ hapd = setup_psk_ext(dev[0], apdev[0])
+ bssid = apdev[0]['bssid']
+ addr = dev[0].own_addr()
+
+ # EAPOL-Key msg 1/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ # EAPOL-Key msg 3/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ logger.info("Drop the first EAPOL-Key msg 4/4")
+
+ # wpa_supplicant believes now that 4-way handshake succeeded; hostapd
+ # doesn't. Use normal EAPOL TX/RX to handle retries.
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ dev[0].wait_connected()
+
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on AP-STA-CONNECTED from hostapd")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+ if ev is not None:
+ logger.info("Disconnection detected")
+ # The EAPOL-Key retries are supposed to allow the connection to be
+ # established without having to reassociate. However, this does not
+ # currently work since mac80211 ends up encrypting EAPOL-Key msg 4/4
+ # after the pairwise key has been configured and AP will drop those and
+ # disconnect the station after reaching retransmission limit. Connection
+ # is then established after reassociation. Once that behavior has been
+ # optimized to prevent EAPOL-Key frame encryption for retransmission
+ # case, this exception can be uncommented here.
+ #raise Exception("Unexpected disconnection")
+
+@remote_compatible
+def test_ap_wpa2_psk_disable_enable(dev, apdev):
+ """WPA2-PSK AP getting disabled and re-enabled"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_psk'] = psk
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, raw_psk=psk, scan_freq="2412")
+
+ for i in range(2):
+ hapd.request("DISABLE")
+ dev[0].wait_disconnected()
+ hapd.request("ENABLE")
+ dev[0].wait_connected()
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_wpa2_psk_incorrect_passphrase(dev, apdev):
+ """WPA2-PSK AP and station using incorrect passphrase"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk="incorrect passphrase", scan_freq="2412",
+ wait_connect=False)
+ ev = hapd.wait_event(["AP-STA-POSSIBLE-PSK-MISMATCH"], timeout=10)
+ if ev is None:
+ raise Exception("No AP-STA-POSSIBLE-PSK-MISMATCH reported")
+ dev[0].dump_monitor()
+
+ hapd.disable()
+ hapd.set("wpa_passphrase", "incorrect passphrase")
+ hapd.enable()
+
+ dev[0].wait_connected(timeout=20)
+
+@remote_compatible
+def test_ap_wpa_ie_parsing(dev, apdev):
+ """WPA IE parsing"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ ssid = "test-wpa-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ id = dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
+ only_add_network=True)
+
+ tests = ["dd040050f201",
+ "dd050050f20101",
+ "dd060050f2010100",
+ "dd060050f2010001",
+ "dd070050f201010000",
+ "dd080050f20101000050",
+ "dd090050f20101000050f2",
+ "dd0a0050f20101000050f202",
+ "dd0b0050f20101000050f20201",
+ "dd0c0050f20101000050f2020100",
+ "dd0c0050f20101000050f2020000",
+ "dd0c0050f20101000050f202ffff",
+ "dd0d0050f20101000050f202010000",
+ "dd0e0050f20101000050f20201000050",
+ "dd0f0050f20101000050f20201000050f2",
+ "dd100050f20101000050f20201000050f202",
+ "dd110050f20101000050f20201000050f20201",
+ "dd120050f20101000050f20201000050f2020100",
+ "dd120050f20101000050f20201000050f2020000",
+ "dd120050f20101000050f20201000050f202ffff",
+ "dd130050f20101000050f20201000050f202010000",
+ "dd140050f20101000050f20201000050f20201000050",
+ "dd150050f20101000050f20201000050f20201000050f2"]
+ for t in tests:
+ try:
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 " + t):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("Association rejection not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+ tests = ["dd170050f20101000050f20201000050f20201000050f202ff",
+ "dd180050f20101000050f20201000050f20201000050f202ffff",
+ "dd190050f20101000050f20201000050f20201000050f202ffffff"]
+ for t in tests:
+ try:
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 " + t):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(['CTRL-EVENT-CONNECTED',
+ 'WPA: 4-Way Handshake failed'], timeout=10)
+ if ev is None:
+ raise Exception("Association failed unexpectedly")
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+@remote_compatible
+def test_ap_wpa2_psk_no_random(dev, apdev):
+ """WPA2-PSK AP and no random numbers available"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_psk'] = psk
+ hapd = hostapd.add_ap(apdev[0], params)
+ with fail_test(hapd, 1, "wpa_gmk_to_gtk"):
+ id = dev[0].connect(ssid, raw_psk=psk, scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Disconnection event not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+
+@remote_compatible
+def test_rsn_ie_proto_psk_sta(dev, apdev):
+ """RSN element protocol testing for PSK cases on STA side"""
+ bssid = apdev[0]['bssid']
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ # This is the RSN element used normally by hostapd
+ params['own_ie_override'] = '30140100000fac040100000fac040100000fac020c00'
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("SET own_ie_override qwerty"):
+ raise Exception("Invalid own_ie_override value accepted")
+ id = dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+
+ tests = [('No RSN Capabilities field',
+ '30120100000fac040100000fac040100000fac02'),
+ ('Reserved RSN Capabilities bits set',
+ '30140100000fac040100000fac040100000fac023cff'),
+ ('Truncated RSN Capabilities field',
+ '30130100000fac040100000fac040100000fac023c'),
+ ('Extra pairwise cipher suite (unsupported)',
+ '30180100000fac040200ffffffff000fac040100000fac020c00'),
+ ('Extra AKM suite (unsupported)',
+ '30180100000fac040100000fac040200ffffffff000fac020c00'),
+ ('PMKIDCount field included',
+ '30160100000fac040100000fac040100000fac020c000000'),
+ ('Truncated PMKIDCount field',
+ '30150100000fac040100000fac040100000fac020c0000'),
+ ('Unexpected Group Management Cipher Suite with PMF disabled',
+ '301a0100000fac040100000fac040100000fac020c000000000fac06'),
+ ('Extra octet after defined fields (future extensibility)',
+ '301b0100000fac040100000fac040100000fac020c000000000fac0600')]
+ for txt, ie in tests:
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[0].request("NOTE " + txt)
+ logger.info(txt)
+ hapd.disable()
+ hapd.set('own_ie_override', ie)
+ hapd.enable()
+ dev[0].request("BSS_FLUSH 0")
+ dev[0].scan_for_bss(bssid, 2412, force_scan=True, only_new=True)
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+
+@remote_compatible
+def test_ap_cli_order(dev, apdev):
+ """hostapd configuration parameter SET ordering"""
+ ssid = "test-rsn-setup"
+ passphrase = 'zzzzzzzz'
+
+ hapd = hostapd.add_ap(apdev[0], {}, no_enable=True)
+ hapd.set('ssid', ssid)
+ hapd.set('wpa_passphrase', passphrase)
+ hapd.set('rsn_pairwise', 'CCMP')
+ hapd.set('wpa_key_mgmt', 'WPA-PSK')
+ hapd.set('wpa', '2')
+ hapd.enable()
+ cfg = hapd.get_config()
+ if cfg['group_cipher'] != 'CCMP':
+ raise Exception("Unexpected group_cipher: " + cfg['group_cipher'])
+ if cfg['rsn_pairwise_cipher'] != 'CCMP':
+ raise Exception("Unexpected rsn_pairwise_cipher: " + cfg['rsn_pairwise_cipher'])
+
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=30)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ if "AP-ENABLED" not in ev:
+ raise Exception("AP startup failed")
+
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+
+def set_test_assoc_ie(dev, ie):
+ if "OK" not in dev.request("TEST_ASSOC_IE " + ie):
+ raise Exception("Could not set TEST_ASSOC_IE")
+
+@remote_compatible
+def test_ap_wpa2_psk_assoc_rsn(dev, apdev):
+ """WPA2-PSK AP and association request RSN IE differences"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tests = [("Normal wpa_supplicant assoc req RSN IE",
+ "30140100000fac040100000fac040100000fac020000"),
+ ("RSN IE without RSN Capabilities",
+ "30120100000fac040100000fac040100000fac02")]
+ for title, ie in tests:
+ logger.info(title)
+ set_test_assoc_ie(dev[0], ie)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [("WPA IE instead of RSN IE and only RSN enabled on AP",
+ "dd160050f20101000050f20201000050f20201000050f202", 40),
+ ("Empty RSN IE", "3000", 40),
+ ("RSN IE with truncated Version", "300101", 40),
+ ("RSN IE with only Version", "30020100", 43)]
+ for title, ie, status in tests:
+ logger.info(title)
+ set_test_assoc_ie(dev[0], ie)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+ if ev is None:
+ raise Exception("Association rejection not reported")
+ if "status_code=" + str(status) not in ev:
+ raise Exception("Unexpected status code: " + ev)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def test_ap_wpa2_psk_ft_workaround(dev, apdev):
+ """WPA2-PSK+FT AP and workaround for incorrect STA behavior"""
+ ssid = "test-wpa2-psk-ft"
+ passphrase = 'qwertyuiop'
+
+ params = {"wpa": "2",
+ "wpa_key_mgmt": "FT-PSK WPA-PSK",
+ "rsn_pairwise": "CCMP",
+ "ssid": ssid,
+ "wpa_passphrase": passphrase}
+ params["mobility_domain"] = "a1b2"
+ params["r0_key_lifetime"] = "10000"
+ params["pmk_r1_push"] = "1"
+ params["reassociation_deadline"] = "1000"
+ params['nas_identifier'] = "nas1.w1.fi"
+ params['r1_key_holder'] = "000102030405"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # Include both WPA-PSK and FT-PSK AKMs in Association Request frame
+ set_test_assoc_ie(dev[0],
+ "30180100000fac040100000fac040200000fac02000fac040000")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa2_psk_assoc_rsn_pmkid(dev, apdev):
+ """WPA2-PSK AP and association request RSN IE with PMKID"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ set_test_assoc_ie(dev[0], "30260100000fac040100000fac040100000fac0200000100" + 16*'00')
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wpa_psk_rsn_pairwise(dev, apdev):
+ """WPA-PSK AP and only rsn_pairwise set"""
+ skip_without_tkip(dev[0])
+ params = {"ssid": "wpapsk", "wpa": "1", "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "TKIP", "wpa_passphrase": "1234567890"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("wpapsk", psk="1234567890", proto="WPA", pairwise="TKIP",
+ scan_freq="2412")
+
+def test_ap_wpa2_eapol_retry_limit(dev, apdev):
+ """WPA2-PSK EAPOL-Key retry limit configuration"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_ptk_rekey'] = '2'
+ params['wpa_group_update_count'] = '1'
+ params['wpa_pairwise_update_count'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+
+ if "FAIL" not in hapd.request("SET wpa_group_update_count 0"):
+ raise Exception("Invalid wpa_group_update_count value accepted")
+ if "FAIL" not in hapd.request("SET wpa_pairwise_update_count 0"):
+ raise Exception("Invalid wpa_pairwise_update_count value accepted")
+
+def test_ap_wpa2_disable_eapol_retry(dev, apdev):
+ """WPA2-PSK disable EAPOL-Key retry"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_disable_eapol_key_retries'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ logger.info("Verify working 4-way handshake without retries")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ addr = dev[0].own_addr()
+
+ logger.info("Verify no retransmission of message 3/4")
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False)
+
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX (M1) from hostapd")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX (M1 retry) from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX (M1) to wpa_supplicant failed")
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX (M2) from wpa_supplicant")
+ dev[0].dump_monitor()
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX (M2) to hostapd failed")
+
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX (M3) from hostapd")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=2)
+ if ev is not None:
+ raise Exception("Unexpected EAPOL-TX M3 retry from hostapd")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=3)
+ if ev is None:
+ raise Exception("Disconnection not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def test_ap_wpa2_disable_eapol_retry_group(dev, apdev):
+ """WPA2-PSK disable EAPOL-Key retry for group handshake"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_disable_eapol_key_retries'] = '1'
+ params['wpa_strict_rekey'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ id = dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ hapd.wait_sta()
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ hapd.wait_sta()
+ dev[0].dump_monitor()
+ addr = dev[0].own_addr()
+
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ dev[1].request("RECONNECT")
+ dev[1].wait_connected()
+ hapd.wait_sta()
+ dev[0].dump_monitor()
+
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+ dev[1].request("DISCONNECT")
+
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX (group M1) from hostapd")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=2)
+ if ev is not None:
+ raise Exception("Unexpected EAPOL-TX group M1 retry from hostapd")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=3)
+ if ev is None:
+ raise Exception("Disconnection not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def test_ap_wpa2_psk_mic_0(dev, apdev):
+ """WPA2-PSK/TKIP and MIC=0 in EAPOL-Key msg 3/4"""
+ skip_without_tkip(dev[0])
+ bssid = apdev[0]['bssid']
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['rsn_pairwise'] = "TKIP"
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False)
+ addr = dev[0].own_addr()
+
+ # EAPOL-Key msg 1/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+ dev[0].dump_monitor()
+
+ # EAPOL-Key msg 3/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ msg3 = ev.split(' ')[2]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg3)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 4/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ # Do not send to the AP
+
+ # EAPOL-Key msg 3/4 with MIC=0 and modifications
+ eapol_hdr = msg3[0:8]
+ key_type = msg3[8:10]
+ key_info = msg3[10:14]
+ key_length = msg3[14:18]
+ replay_counter = msg3[18:34]
+ key_nonce = msg3[34:98]
+ key_iv = msg3[98:130]
+ key_rsc = msg3[130:146]
+ key_id = msg3[146:162]
+ key_mic = msg3[162:194]
+ key_data_len = msg3[194:198]
+ key_data = msg3[198:]
+
+ msg3b = eapol_hdr + key_type
+ msg3b += "12c9" # Clear MIC bit from key_info (originally 13c9)
+ msg3b += key_length
+ msg3b += '0000000000000003'
+ msg3b += key_nonce + key_iv + key_rsc + key_id
+ msg3b += 32*'0' # Clear MIC value
+ msg3b += key_data_len + key_data
+ dev[0].dump_monitor()
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg3b)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+ ev = dev[0].wait_event(["EAPOL-TX", "WPA: Ignore EAPOL-Key"], timeout=2)
+ if ev is None:
+ raise Exception("No event from wpa_supplicant")
+ if "EAPOL-TX" in ev:
+ raise Exception("Unexpected EAPOL-Key message from wpa_supplicant")
+ dev[0].request("DISCONNECT")
+
+def test_ap_wpa2_psk_local_error(dev, apdev):
+ """WPA2-PSK and local error cases on supplicant"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params["wpa_key_mgmt"] = "WPA-PSK WPA-PSK-SHA256"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with fail_test(dev[0], 1, "sha1_prf;wpa_pmk_to_ptk"):
+ id = dev[0].connect(ssid, key_mgmt="WPA-PSK", psk=passphrase,
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnection event not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ with fail_test(dev[0], 1, "sha256_prf;wpa_pmk_to_ptk"):
+ id = dev[0].connect(ssid, key_mgmt="WPA-PSK-SHA256", psk=passphrase,
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnection event not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def test_ap_wpa2_psk_inject_assoc(dev, apdev, params):
+ """WPA2-PSK AP and Authentication and Association Request frame injection"""
+ prefix = "ap_wpa2_psk_inject_assoc"
+ ifname = apdev[0]["ifname"]
+ cap = os.path.join(params['logdir'], prefix + "." + ifname + ".pcap")
+
+ ssid = "test"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK"
+ hapd = hostapd.add_ap(apdev[0], params)
+ wt = WlantestCapture(ifname, cap)
+ time.sleep(1)
+
+ bssid = hapd.own_addr().replace(':', '')
+
+ hapd.request("SET ext_mgmt_frame_handling 1")
+ addr = "021122334455"
+ auth = "b0003a01" + bssid + addr + bssid + '1000000001000000'
+ res = hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % auth)
+ if "OK" not in res:
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("No TX status seen")
+ ev = ev.replace("ok=0", "ok=1")
+ cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
+ if "OK" not in hapd.request(cmd):
+ raise Exception("MGMT_TX_STATUS_PROCESS failed")
+
+ assoc = "00003a01" + bssid + addr + bssid + '2000' + '31040500' + '000474657374' + '010802040b160c121824' + '30140100000fac040100000fac040100000fac020000'
+ res = hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % assoc)
+ if "OK" not in res:
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("No TX status seen")
+ ev = ev.replace("ok=0", "ok=1")
+ cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
+ if "OK" not in hapd.request(cmd):
+ raise Exception("MGMT_TX_STATUS_PROCESS failed")
+ hapd.request("SET ext_mgmt_frame_handling 0")
+
+ dev[0].connect(ssid, psk="12345678", scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ time.sleep(1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ time.sleep(0.5)
+ wt.close()
+ time.sleep(0.5)
+
+ # Check for Layer 2 Update frame and unexpected frames from the station
+ # that did not fully complete authentication.
+ res = run_tshark(cap, "basicxid.llc.xid.format == 0x81",
+ ["eth.src"], wait=False)
+ real_sta_seen = False
+ unexpected_sta_seen = False
+ real_addr = dev[0].own_addr()
+ for l in res.splitlines():
+ if l == real_addr:
+ real_sta_seen = True
+ else:
+ unexpected_sta_seen = True
+ if unexpected_sta_seen:
+ raise Exception("Layer 2 Update frame from unexpected STA seen")
+ if not real_sta_seen:
+ raise Exception("Layer 2 Update frame from real STA not seen")
+
+ res = run_tshark(cap, "eth.src == 02:11:22:33:44:55", ["eth.src"],
+ wait=False)
+ if len(res) > 0:
+ raise Exception("Unexpected frame from unauthorized STA seen")
+
+def test_ap_wpa2_psk_no_control_port(dev, apdev):
+ """WPA2-PSK AP without nl80211 control port"""
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['driver_params'] = "control_port=0"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="control_port=0")
+ wpas.connect(ssid, psk=passphrase, scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(wpas, hapd)
+ if "OK" not in wpas.request("KEY_REQUEST 0 1"):
+ raise Exception("KEY_REQUEST failed")
+ ev = wpas.wait_event(["WPA: Key negotiation completed"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ hapd.wait_ptkinitdone(wpas.own_addr())
+ hwsim_utils.test_connectivity(wpas, hapd)
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+def test_ap_wpa2_psk_ap_control_port(dev, apdev):
+ """WPA2-PSK AP with nl80211 control port in AP mode"""
+ run_ap_wpa2_psk_ap_control_port(dev, apdev, ctrl_val=1)
+
+def test_ap_wpa2_psk_ap_control_port_disabled(dev, apdev):
+ """WPA2-PSK AP with nl80211 control port in AP mode disabled"""
+ run_ap_wpa2_psk_ap_control_port(dev, apdev, ctrl_val=0)
+
+def run_ap_wpa2_psk_ap_control_port(dev, apdev, ctrl_val):
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['driver_params'] = "control_port_ap=%d" % ctrl_val
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ flags = hapd.request("DRIVER_FLAGS").splitlines()[1:]
+ flags2 = hapd.request("DRIVER_FLAGS2").splitlines()[1:]
+ logger.info("AP driver flags: " + str(flags))
+ logger.info("AP driver flags2: " + str(flags2))
+ if 'CONTROL_PORT' not in flags or 'CONTROL_PORT_RX' not in flags2:
+ raise HwsimSkip("No AP driver support for CONTROL_PORT")
+
+ flags = dev[0].request("DRIVER_FLAGS").splitlines()[1:]
+ flags2 = dev[0].request("DRIVER_FLAGS2").splitlines()[1:]
+ logger.info("STA driver flags: " + str(flags))
+ logger.info("STA driver flags2: " + str(flags2))
+ if 'CONTROL_PORT' not in flags or 'CONTROL_PORT_RX' not in flags2:
+ raise HwsimSkip("No STA driver support for CONTROL_PORT")
+
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ if "OK" not in dev[0].request("KEY_REQUEST 0 1"):
+ raise Exception("KEY_REQUEST failed")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ hapd.wait_ptkinitdone(dev[0].own_addr())
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_wpa2_psk_rsne_mismatch_ap(dev, apdev):
+ """RSNE mismatch in EAPOL-Key msg 3/4"""
+ ie = "30140100000fac040100000fac040100000fac020c80"
+ run_ap_wpa2_psk_rsne_mismatch_ap(dev, apdev, ie)
+
+def test_ap_wpa2_psk_rsne_mismatch_ap2(dev, apdev):
+ """RSNE mismatch in EAPOL-Key msg 3/4"""
+ ie = "30150100000fac040100000fac040100000fac020c0000"
+ run_ap_wpa2_psk_rsne_mismatch_ap(dev, apdev, ie)
+
+def test_ap_wpa2_psk_rsne_mismatch_ap3(dev, apdev):
+ """RSNE mismatch in EAPOL-Key msg 3/4"""
+ run_ap_wpa2_psk_rsne_mismatch_ap(dev, apdev, "")
+
+def run_ap_wpa2_psk_rsne_mismatch_ap(dev, apdev, rsne):
+ params = hostapd.wpa2_params(ssid="psk", passphrase="12345678")
+ params['rsne_override_eapol'] = rsne
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("psk", psk="12345678", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["Associated with"], timeout=10)
+ if ev is None:
+ raise Exception("No indication of association seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=5)
+ dev[0].request("REMOVE_NETWORK all")
+ if ev is None:
+ raise Exception("No disconnection seen")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection")
+ if "reason=17 locally_generated=1" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+def test_ap_wpa2_psk_rsnxe_mismatch_ap(dev, apdev):
+ """RSNXE mismatch in EAPOL-Key msg 3/4"""
+ params = hostapd.wpa2_params(ssid="psk", passphrase="12345678")
+ params['rsnxe_override_eapol'] = "F40100"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("psk", psk="12345678", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["Associated with"], timeout=10)
+ if ev is None:
+ raise Exception("No indication of association seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=5)
+ dev[0].request("REMOVE_NETWORK all")
+ if ev is None:
+ raise Exception("No disconnection seen")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection")
+ if "reason=17 locally_generated=1" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+def test_ap_wpa2_psk_ext_key_id_ptk_rekey_ap0(dev, apdev):
+ """WPA2-PSK AP and PTK rekey by AP (disabled on STA)"""
+ run_ap_wpa2_psk_ext_key_id_ptk_rekey_ap(dev, apdev, 1, 0)
+
+def test_ap_wpa2_psk_ext_key_id_ptk_rekey_ap1(dev, apdev):
+ """WPA2-PSK AP and PTK rekey by AP (start with Key ID 0)"""
+ run_ap_wpa2_psk_ext_key_id_ptk_rekey_ap(dev, apdev, 1, 1)
+
+def test_ap_wpa2_psk_ext_key_id_ptk_rekey_ap2(dev, apdev):
+ """WPA2-PSK AP and PTK rekey by AP (start with Key ID 1)"""
+ run_ap_wpa2_psk_ext_key_id_ptk_rekey_ap(dev, apdev, 2, 1)
+
+def run_ap_wpa2_psk_ext_key_id_ptk_rekey_ap(dev, apdev, ap_ext_key_id,
+ sta_ext_key_id):
+ check_ext_key_id_capa(dev[0])
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['wpa_ptk_rekey'] = '2'
+ params['extended_key_id'] = str(ap_ext_key_id)
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_ext_key_id_capa(hapd)
+ try:
+ dev[0].set("extended_key_id", str(sta_ext_key_id))
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ idx = int(dev[0].request("GET last_tk_key_idx"))
+ expect_idx = 1 if ap_ext_key_id == 2 and sta_ext_key_id else 0
+ if idx != expect_idx:
+ raise Exception("Unexpected Key ID for the first TK: %d (expected %d)" % (idx, expect_idx))
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ idx = int(dev[0].request("GET last_tk_key_idx"))
+ expect_idx = 1 if ap_ext_key_id == 1 and sta_ext_key_id else 0
+ if idx != expect_idx:
+ raise Exception("Unexpected Key ID for the second TK: %d (expected %d)" % (idx, expect_idx))
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ dev[0].set("extended_key_id", "0")
+
+def test_ap_wpa2_psk_ext_key_id_ptk_rekey_sta0(dev, apdev):
+ """Extended Key ID and PTK rekey by station (Ext Key ID disabled on AP)"""
+ run_ap_wpa2_psk_ext_key_id_ptk_rekey_sta(dev, apdev, 0)
+
+def test_ap_wpa2_psk_ext_key_id_ptk_rekey_sta1(dev, apdev):
+ """Extended Key ID and PTK rekey by station (start with Key ID 0)"""
+ run_ap_wpa2_psk_ext_key_id_ptk_rekey_sta(dev, apdev, 1)
+
+def test_ap_wpa2_psk_ext_key_id_ptk_rekey_sta2(dev, apdev):
+ """Extended Key ID and PTK rekey by station (start with Key ID 1)"""
+ run_ap_wpa2_psk_ext_key_id_ptk_rekey_sta(dev, apdev, 2)
+
+def run_ap_wpa2_psk_ext_key_id_ptk_rekey_sta(dev, apdev, ext_key_id):
+ check_ext_key_id_capa(dev[0])
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['extended_key_id'] = str(ext_key_id)
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_ext_key_id_capa(hapd)
+
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase(passphrase)
+
+ try:
+ dev[0].set("extended_key_id", "1")
+ dev[0].connect(ssid, psk=passphrase, wpa_ptk_rekey="1",
+ scan_freq="2412")
+ idx = int(dev[0].request("GET last_tk_key_idx"))
+ expect_idx = 1 if ext_key_id == 2 else 0
+ if idx != expect_idx:
+ raise Exception("Unexpected Key ID for the first TK: %d (expected %d)" % (idx, expect_idx))
+ ev = dev[0].wait_event(["WPA: Key negotiation completed",
+ "CTRL-EVENT-DISCONNECTED"])
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ raise Exception("Disconnect instead of rekey")
+ idx = int(dev[0].request("GET last_tk_key_idx"))
+ expect_idx = 1 if ext_key_id == 1 else 0
+ if idx != expect_idx:
+ raise Exception("Unexpected Key ID for the second TK: %d (expected %d)" % (idx, expect_idx))
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ dev[0].set("extended_key_id", "0")
diff --git a/contrib/wpa/tests/hwsim/test_ap_qosmap.py b/contrib/wpa/tests/hwsim/test_ap_qosmap.py
new file mode 100644
index 000000000000..e4e940f0813f
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_qosmap.py
@@ -0,0 +1,169 @@
+# QoS Mapping tests
+# Copyright (c) 2013-2016, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import HwsimSkip, alloc_fail, fail_test
+from wlantest import Wlantest
+
+def check_qos_map(ap, hapd, dev, sta, dscp, tid, ap_tid=None):
+ if not ap_tid:
+ ap_tid = tid
+ bssid = ap['bssid']
+ wt = Wlantest()
+ wt.clear_sta_counters(bssid, sta)
+ hwsim_utils.test_connectivity(dev, hapd, dscp=dscp, config=False)
+ sleep_time = 0.02 if dev.hostname is None else 0.2
+ time.sleep(sleep_time)
+ tx = wt.get_tx_tid(bssid, sta, tid)
+ if tx == 0:
+ [tx, rx] = wt.get_tid_counters(bssid, sta)
+ logger.info("Expected TX DSCP " + str(dscp) + " with TID " + str(tid) + " but counters: " + str(tx))
+ raise Exception("No STA->AP data frame using the expected TID")
+ rx = wt.get_rx_tid(bssid, sta, ap_tid)
+ if rx == 0:
+ [tx, rx] = wt.get_tid_counters(bssid, sta)
+ logger.info("Expected RX DSCP " + str(dscp) + " with TID " + str(ap_tid) + " but counters: " + str(rx))
+ raise Exception("No AP->STA data frame using the expected TID")
+
+@remote_compatible
+def test_ap_qosmap(dev, apdev):
+ """QoS mapping"""
+ drv_flags = dev[0].get_driver_status_field("capa.flags")
+ if int(drv_flags, 0) & 0x40000000 == 0:
+ raise HwsimSkip("Driver does not support QoS Map")
+ ssid = "test-qosmap"
+ params = {"ssid": ssid}
+ params['qos_map_set'] = '53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,48,55'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ time.sleep(0.1)
+ addr = dev[0].p2p_interface_addr()
+ dev[0].request("DATA_TEST_CONFIG 1")
+ hapd.request("DATA_TEST_CONFIG 1")
+ Wlantest.setup(hapd)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 53, 2)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 22, 6)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 8, 0)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 15, 0)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 0, 1)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 7, 1)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 16, 3)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 31, 3)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 32, 4)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 39, 4)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 40, 6)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 47, 6)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 48, 7)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 55, 7)
+ hapd.request("SET_QOS_MAP_SET 22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,48,55")
+ hapd.request("SEND_QOS_MAP_CONF " + dev[0].get_status_field("address"))
+ check_qos_map(apdev[0], hapd, dev[0], addr, 53, 7)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 22, 6)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 48, 7)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 55, 7)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 56, 56 >> 3)
+ check_qos_map(apdev[0], hapd, dev[0], addr, 63, 63 >> 3)
+ dev[0].request("DATA_TEST_CONFIG 0")
+ hapd.request("DATA_TEST_CONFIG 0")
+
+@remote_compatible
+def test_ap_qosmap_default(dev, apdev):
+ """QoS mapping with default values"""
+ ssid = "test-qosmap-default"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].p2p_interface_addr()
+ dev[0].request("DATA_TEST_CONFIG 1")
+ hapd.request("DATA_TEST_CONFIG 1")
+ Wlantest.setup(hapd)
+ for dscp in [0, 7, 8, 15, 16, 23, 24, 31, 32, 39, 40, 47, 48, 55, 56, 63]:
+ check_qos_map(apdev[0], hapd, dev[0], addr, dscp, dscp >> 3)
+ dev[0].request("DATA_TEST_CONFIG 0")
+ hapd.request("DATA_TEST_CONFIG 0")
+
+@remote_compatible
+def test_ap_qosmap_default_acm(dev, apdev):
+ """QoS mapping with default values and ACM=1 for VO/VI"""
+ ssid = "test-qosmap-default"
+ params = {"ssid": ssid,
+ "wmm_ac_bk_aifs": "7",
+ "wmm_ac_bk_cwmin": "4",
+ "wmm_ac_bk_cwmax": "10",
+ "wmm_ac_bk_txop_limit": "0",
+ "wmm_ac_bk_acm": "0",
+ "wmm_ac_be_aifs": "3",
+ "wmm_ac_be_cwmin": "4",
+ "wmm_ac_be_cwmax": "10",
+ "wmm_ac_be_txop_limit": "0",
+ "wmm_ac_be_acm": "0",
+ "wmm_ac_vi_aifs": "2",
+ "wmm_ac_vi_cwmin": "3",
+ "wmm_ac_vi_cwmax": "4",
+ "wmm_ac_vi_txop_limit": "94",
+ "wmm_ac_vi_acm": "1",
+ "wmm_ac_vo_aifs": "2",
+ "wmm_ac_vo_cwmin": "2",
+ "wmm_ac_vo_cwmax": "2",
+ "wmm_ac_vo_txop_limit": "47",
+ "wmm_ac_vo_acm": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].p2p_interface_addr()
+ dev[0].request("DATA_TEST_CONFIG 1")
+ hapd.request("DATA_TEST_CONFIG 1")
+ Wlantest.setup(hapd)
+ for dscp in [0, 7, 8, 15, 16, 23, 24, 31, 32, 39, 40, 47, 48, 55, 56, 63]:
+ ap_tid = dscp >> 3
+ tid = ap_tid
+ # downgrade VI/VO to BE
+ if tid in [4, 5, 6, 7]:
+ tid = 3
+ check_qos_map(apdev[0], hapd, dev[0], addr, dscp, tid, ap_tid)
+ dev[0].request("DATA_TEST_CONFIG 0")
+ hapd.request("DATA_TEST_CONFIG 0")
+
+@remote_compatible
+def test_ap_qosmap_invalid(dev, apdev):
+ """QoS mapping ctrl_iface error handling"""
+ ssid = "test-qosmap"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("SEND_QOS_MAP_CONF 00:11:22:33:44:55"):
+ raise Exception("Unexpected SEND_QOS_MAP_CONF success")
+ if "FAIL" not in hapd.request("SET_QOS_MAP_SET "):
+ raise Exception("Unexpected SET_QOS_MAP_SET success")
+ if "FAIL" not in hapd.request("SET_QOS_MAP_SET 1,2,3"):
+ raise Exception("Unexpected SET_QOS_MAP_SET success")
+ if "FAIL" not in hapd.request("SET_QOS_MAP_SET 1,-2,3"):
+ raise Exception("Unexpected SET_QOS_MAP_SET success")
+ if "FAIL" not in hapd.request("SET_QOS_MAP_SET 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59"):
+ raise Exception("Unexpected SET_QOS_MAP_SET success")
+ if "FAIL" not in hapd.request("SET_QOS_MAP_SET 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21"):
+ raise Exception("Unexpected SET_QOS_MAP_SET success")
+
+ if "FAIL" in hapd.request("SET_QOS_MAP_SET 22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,48,55"):
+ raise Exception("Unexpected SET_QOS_MAP_SET failure")
+ if "FAIL" not in hapd.request("SEND_QOS_MAP_CONF 00:11:22:33:44:55"):
+ raise Exception("Unexpected SEND_QOS_MAP_CONF success")
+ if "FAIL" not in hapd.request("SEND_QOS_MAP_CONF 00:11:22:33:44"):
+ raise Exception("Unexpected SEND_QOS_MAP_CONF success")
+
+ with fail_test(hapd, 1, "hostapd_ctrl_iface_set_qos_map_set"):
+ if "FAIL" not in hapd.request("SET_QOS_MAP_SET 22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,48,55"):
+ raise Exception("SET_QOS_MAP_SET accepted during forced driver failure")
+
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ with alloc_fail(hapd, 1,
+ "wpabuf_alloc;hostapd_ctrl_iface_send_qos_map_conf"):
+ if "FAIL" not in hapd.request("SEND_QOS_MAP_CONF " + dev[0].own_addr()):
+ raise Exception("SEND_QOS_MAP_CONF accepted during OOM")
diff --git a/contrib/wpa/tests/hwsim/test_ap_roam.py b/contrib/wpa/tests/hwsim/test_ap_roam.py
new file mode 100644
index 000000000000..0bc54b391e86
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_roam.py
@@ -0,0 +1,395 @@
+# Roaming tests
+# Copyright (c) 2013-2021, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+
+@remote_compatible
+def test_ap_roam_open(dev, apdev):
+ """Roam between two open APs"""
+ hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE")
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+ hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open"})
+ dev[0].scan(type="ONLY")
+ dev[0].roam(apdev[1]['bssid'])
+ hwsim_utils.test_connectivity(dev[0], hapd1)
+ dev[0].roam(apdev[0]['bssid'])
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+
+def test_ap_ignore_bssid_all(dev, apdev, params):
+ """Ensure we clear the ignore BSSID list if all visible APs reject"""
+ hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open", "max_num_sta": "0"})
+ hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open", "max_num_sta": "0"})
+ bss0 = hapd0.own_addr()
+ bss1 = hapd1.own_addr()
+
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False, bssid=bss0)
+ if not dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10):
+ raise Exception("AP 0 didn't reject us")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False, bssid=bss1)
+ if not dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10):
+ raise Exception("AP 1 didn't reject us")
+ ignore_list = get_bssid_ignore_list(dev[0])
+ logger.info("ignore list: " + str(ignore_list))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ hapd0.set("max_num_sta", "1")
+ # All visible APs were ignored; we should clear the ignore list and find
+ # the AP that now accepts us.
+ dev[0].scan_for_bss(bss0, freq=2412)
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412", bssid=bss0)
+
+@remote_compatible
+def test_ap_roam_open_failed(dev, apdev):
+ """Roam failure due to rejected authentication"""
+ hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+ params = {"ssid": "test-open", "max_num_sta": "0"}
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid = hapd1.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("ROAM " + bssid):
+ raise Exception("ROAM failed")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], 1)
+ if not ev:
+ raise Exception("CTRL-EVENT-AUTH-REJECT was not seen")
+
+ dev[0].wait_connected(timeout=5)
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+
+def test_ap_roam_open_failed_ssid_mismatch(dev, apdev):
+ """Roam failure due to SSID mismatch"""
+ hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ bssid0 = hapd0.own_addr()
+ hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open2"})
+ bssid1 = hapd1.own_addr()
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(bssid0, freq=2412)
+ dev[0].scan_for_bss(bssid1, freq=2412)
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+ hapd0.wait_sta()
+ bssid = dev[0].get_status_field("bssid")
+ if bssid != bssid0:
+ raise Exception("Unexpected BSSID reported after initial connection: " + bssid)
+ if "FAIL" not in dev[0].request("ROAM " + bssid1):
+ raise Exception("ROAM succeed unexpectedly")
+ bssid = dev[0].get_status_field("bssid")
+ if bssid != bssid0:
+ raise Exception("Unexpected BSSID reported after failed roam attempt: " + bssid)
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+
+@remote_compatible
+def test_ap_roam_wpa2_psk(dev, apdev):
+ """Roam between two WPA2-PSK APs"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-psk", psk="12345678")
+ hapd0.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ dev[0].scan(type="ONLY")
+ dev[0].roam(apdev[1]['bssid'])
+ hapd1.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd1)
+ dev[0].roam(apdev[0]['bssid'])
+ hapd0.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+
+def test_ap_roam_wpa2_psk_pmf_mismatch(dev, apdev):
+ """Roam between two WPA2-PSK APs - PMF mismatch"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ params['ieee80211w'] = '1'
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+ params['ieee80211w'] = '0'
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(bssid0, freq=2412)
+ dev[0].scan_for_bss(bssid1, freq=2412)
+ dev[0].connect("test-wpa2-psk", psk="12345678", ieee80211w='2')
+ hapd0.wait_sta()
+ bssid = dev[0].get_status_field("bssid")
+ if bssid != bssid0:
+ raise Exception("Unexpected BSSID reported after initial connection: " + bssid)
+ if "OK" not in dev[0].request("ROAM " + apdev[1]['bssid']):
+ raise Exception("ROAM failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected connection reported")
+ bssid = dev[0].get_status_field("bssid")
+ if bssid != bssid0:
+ raise Exception("Unexpected BSSID reported after failed roam attempt: " + bssid)
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+
+def get_bssid_ignore_list(dev):
+ return dev.request("BSSID_IGNORE").splitlines()
+
+def test_ap_reconnect_auth_timeout(dev, apdev, params):
+ """Reconnect to 2nd AP and authentication times out"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5",
+ drv_params="force_connect_cmd=1,force_bss_selection=1")
+
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ wpas.scan_for_bss(bssid0, freq=2412)
+ id = wpas.connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+ hapd0.wait_sta()
+ hwsim_utils.test_connectivity(wpas, hapd0)
+
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+
+ wpas.request("BSSID_IGNORE " + bssid0)
+
+ wpas.scan_for_bss(bssid1, freq=2412)
+ wpas.request("DISCONNECT")
+ if "OK" not in wpas.request("SET ignore_auth_resp 1"):
+ raise Exception("SET ignore_auth_resp failed")
+ if "OK" not in wpas.request("ENABLE_NETWORK " + str(id)):
+ raise Exception("ENABLE_NETWORK failed")
+ if "OK" not in wpas.request("SELECT_NETWORK " + str(id)):
+ raise Exception("SELECT_NETWORK failed")
+
+ logger.info("Wait ~10s for auth timeout...")
+ time.sleep(10)
+ ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"], 12)
+ if not ev:
+ raise Exception("CTRL-EVENT-SCAN-STARTED not seen")
+
+ b = get_bssid_ignore_list(wpas)
+ if '00:00:00:00:00:00' in b:
+ raise Exception("Unexpected ignore list contents: " + str(b))
+ if bssid1 not in b:
+ raise Exception("Unexpected ignore list contents: " + str(b))
+
+def test_ap_roam_with_reassoc_auth_timeout(dev, apdev, params):
+ """Roam using reassoc between two APs and authentication times out"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5",
+ drv_params="force_connect_cmd=1,force_bss_selection=1")
+
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ bssid0 = hapd0.own_addr()
+
+ id = wpas.connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+ hapd0.wait_sta()
+ hwsim_utils.test_connectivity(wpas, hapd0)
+
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = hapd1.own_addr()
+ wpas.scan_for_bss(bssid1, freq=2412)
+
+ if "OK" not in wpas.request("SET_NETWORK " + str(id) + " bssid " + bssid1):
+ raise Exception("SET_NETWORK failed")
+ if "OK" not in wpas.request("SET ignore_auth_resp 1"):
+ raise Exception("SET ignore_auth_resp failed")
+ if "OK" not in wpas.request("REASSOCIATE"):
+ raise Exception("REASSOCIATE failed")
+
+ logger.info("Wait ~10s for auth timeout...")
+ time.sleep(10)
+ ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"], 12)
+ if not ev:
+ raise Exception("CTRL-EVENT-SCAN-STARTED not seen")
+
+ b = get_bssid_ignore_list(wpas)
+ if bssid0 in b:
+ raise Exception("Unexpected ignore list contents: " + str(b))
+
+def test_ap_roam_wpa2_psk_failed(dev, apdev, params):
+ """Roam failure with WPA2-PSK AP due to wrong passphrase"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ id = dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+ hapd0.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+ params['wpa_passphrase'] = "22345678"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid = hapd1.own_addr()
+ dev[0].scan_for_bss(bssid, freq=2412)
+
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("ROAM " + bssid):
+ raise Exception("ROAM failed")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED",
+ "CTRL-EVENT-CONNECTED"], 5)
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Got unexpected CTRL-EVENT-CONNECTED")
+ if "CTRL-EVENT-SSID-TEMP-DISABLED" not in ev:
+ raise Exception("CTRL-EVENT-SSID-TEMP-DISABLED not seen")
+
+ if "OK" not in dev[0].request("SELECT_NETWORK id=" + str(id)):
+ raise Exception("SELECT_NETWORK failed")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SSID-REENABLED"], 3)
+ if not ev:
+ raise Exception("CTRL-EVENT-SSID-REENABLED not seen")
+
+ dev[0].wait_connected(timeout=5)
+ hapd0.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+
+@remote_compatible
+def test_ap_reassociation_to_same_bss(dev, apdev):
+ """Reassociate to the same BSS"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE")
+ hapd.wait_sta()
+
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected(timeout=10, error="Reassociation timed out")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[0].request("REATTACH")
+ dev[0].wait_connected(timeout=10, error="Reattach timed out")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ # Wait for previous scan results to expire to trigger new scan
+ time.sleep(5)
+ dev[0].request("REATTACH")
+ dev[0].wait_connected(timeout=10, error="Reattach timed out")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_ap_roam_set_bssid(dev, apdev):
+ """Roam control"""
+ hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ hostapd.add_ap(apdev[1], {"ssid": "test-open"})
+ id = dev[0].connect("test-open", key_mgmt="NONE", bssid=apdev[1]['bssid'],
+ scan_freq="2412")
+ if dev[0].get_status_field('bssid') != apdev[1]['bssid']:
+ raise Exception("Unexpected BSS")
+ # for now, these are just verifying that the code path to indicate
+ # within-ESS roaming changes can be executed; the actual results of those
+ # operations are not currently verified (that would require a test driver
+ # that does BSS selection)
+ dev[0].set_network(id, "bssid", "")
+ dev[0].set_network(id, "bssid", apdev[0]['bssid'])
+ dev[0].set_network(id, "bssid", apdev[1]['bssid'])
+
+@remote_compatible
+def test_ap_roam_wpa2_psk_race(dev, apdev):
+ """Roam between two WPA2-PSK APs and try to hit a disconnection race"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+ hapd0.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+
+ params['channel'] = '2'
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq=2417)
+ dev[0].roam(apdev[1]['bssid'])
+ hapd1.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd1)
+ dev[0].roam(apdev[0]['bssid'])
+ hapd0.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+ # Wait at least two seconds to trigger the previous issue with the
+ # disconnection callback.
+ for i in range(3):
+ time.sleep(0.8)
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+
+def test_ap_roam_signal_level_override(dev, apdev):
+ """Roam between two APs based on driver signal level override"""
+ hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ bssid0 = apdev[0]['bssid']
+ hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open"})
+ bssid1 = apdev[1]['bssid']
+ dev[0].scan_for_bss(bssid0, freq=2412)
+ dev[0].scan_for_bss(bssid1, freq=2412)
+
+ dev[0].connect("test-open", key_mgmt="NONE")
+ bssid = dev[0].get_status_field('bssid')
+ if bssid == bssid0:
+ dst = bssid1
+ src = bssid0
+ else:
+ dst = bssid0
+ src = bssid1
+
+ dev[0].scan(freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], 0.5)
+ if ev is not None:
+ raise Exception("Unexpected roam")
+
+ orig_res = dev[0].request("SIGNAL_POLL")
+ dev[0].set("driver_signal_override", src + " -1 -2 -3 -4 -5")
+ res = dev[0].request("SIGNAL_POLL").splitlines()
+ if "RSSI=-1" not in res or \
+ "AVG_RSSI=-2" not in res or \
+ "AVG_BEACON_RSSI=-3" not in res or \
+ "NOISE=-4" not in res:
+ raise Exception("SIGNAL_POLL override did not work: " + str(res))
+
+ dev[0].set("driver_signal_override", src)
+ new_res = dev[0].request("SIGNAL_POLL")
+ if orig_res != new_res:
+ raise Exception("SIGNAL_POLL restore did not work: " + new_res)
+
+ tests = [("-30 -30 -30 -95 -30", "-30 -30 -30 -95 -30"),
+ ("-30 -30 -30 -95 -30", "-20 -20 -20 -95 -20"),
+ ("-90 -90 -90 -95 -90", "-89 -89 -89 -95 -89"),
+ ("-90 -90 -90 -95 -95", "-89 -89 -89 -95 -89")]
+ for src_override, dst_override in tests:
+ dev[0].set("driver_signal_override", src + " " + src_override)
+ dev[0].set("driver_signal_override", dst + " " + dst_override)
+ dev[0].scan(freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], 0.1)
+ if ev is not None:
+ raise Exception("Unexpected roam")
+ dev[0].dump_monitor()
+
+ dev[0].set("driver_signal_override", src + " -90 -90 -90 -95 -90")
+ dev[0].set("driver_signal_override", dst + " -80 -80 -80 -95 -80")
+ dev[0].scan(freq=2412)
+ dev[0].wait_connected()
+ if dst != dev[0].get_status_field('bssid'):
+ raise Exception("Unexpected AP after roam")
+ dev[0].dump_monitor()
+
+def test_ap_roam_during_scan(dev, apdev):
+ """Roam command during a scan operation"""
+ hapd0 = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].scan_for_bss(hapd0.own_addr(), freq=2412)
+ dev[0].connect("test-open", key_mgmt="NONE")
+ hapd1 = hostapd.add_ap(apdev[1], {"ssid": "test-open"})
+ dev[0].scan_for_bss(hapd1.own_addr(), freq=2412)
+ if "OK" not in dev[0].request("SCAN"):
+ raise Exception("Failed to start scan")
+ if "OK" not in dev[0].request("ROAM " + hapd1.own_addr()):
+ raise Exception("Failed to issue ROAM")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection not reported after ROAM")
+ if hapd1.own_addr() not in ev:
+ raise Exception("Connected to unexpected AP")
diff --git a/contrib/wpa/tests/hwsim/test_ap_tdls.py b/contrib/wpa/tests/hwsim/test_ap_tdls.py
new file mode 100644
index 000000000000..8cdd00235567
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_tdls.py
@@ -0,0 +1,652 @@
+# TDLS tests
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import logging
+logger = logging.getLogger()
+import subprocess
+
+import hwsim_utils
+from hostapd import HostapdGlobal
+from hostapd import Hostapd
+import hostapd
+from utils import *
+from wlantest import Wlantest
+
+def start_ap_wpa2_psk(ap):
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ return hostapd.add_ap(ap, params)
+
+def connectivity(dev, hapd):
+ hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ hwsim_utils.test_connectivity(dev[1], hapd)
+
+def connect_2sta(dev, ssid, hapd, sae=False):
+ key_mgmt = "SAE" if sae else "WPA-PSK"
+ ieee80211w = "2" if sae else "1"
+ dev[0].connect(ssid, key_mgmt=key_mgmt, psk="12345678",
+ ieee80211w=ieee80211w, scan_freq="2412")
+ dev[1].connect(ssid, key_mgmt=key_mgmt, psk="12345678",
+ ieee80211w=ieee80211w, scan_freq="2412")
+ hapd.wait_sta()
+ hapd.wait_sta()
+ connectivity(dev, hapd)
+
+def connect_2sta_wpa2_psk(dev, hapd):
+ connect_2sta(dev, "test-wpa2-psk", hapd)
+
+def connect_2sta_wpa_psk(dev, hapd):
+ connect_2sta(dev, "test-wpa-psk", hapd)
+
+def connect_2sta_wpa_psk_mixed(dev, hapd):
+ dev[0].connect("test-wpa-mixed-psk", psk="12345678", proto="WPA",
+ scan_freq="2412")
+ dev[1].connect("test-wpa-mixed-psk", psk="12345678", proto="WPA2",
+ scan_freq="2412")
+ hapd.wait_sta()
+ hapd.wait_sta()
+ connectivity(dev, hapd)
+
+def connect_2sta_wep(dev, hapd):
+ dev[0].connect("test-wep", key_mgmt="NONE", wep_key0='"hello"',
+ scan_freq="2412")
+ dev[1].connect("test-wep", key_mgmt="NONE", wep_key0='"hello"',
+ scan_freq="2412")
+ hapd.wait_sta()
+ hapd.wait_sta()
+ connectivity(dev, hapd)
+
+def connect_2sta_open(dev, hapd, scan_freq="2412"):
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq=scan_freq)
+ dev[1].connect("test-open", key_mgmt="NONE", scan_freq=scan_freq)
+ hapd.wait_sta()
+ hapd.wait_sta()
+ connectivity(dev, hapd)
+
+def wlantest_setup(hapd):
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ wt.add_wepkey("68656c6c6f")
+
+def wlantest_tdls_packet_counters(bssid, addr0, addr1):
+ wt = Wlantest()
+ dl = wt.get_tdls_counter("valid_direct_link", bssid, addr0, addr1)
+ inv_dl = wt.get_tdls_counter("invalid_direct_link", bssid, addr0, addr1)
+ ap = wt.get_tdls_counter("valid_ap_path", bssid, addr0, addr1)
+ inv_ap = wt.get_tdls_counter("invalid_ap_path", bssid, addr0, addr1)
+ return [dl, inv_dl, ap, inv_ap]
+
+def tdls_check_dl(sta0, sta1, bssid, addr0, addr1):
+ wt = Wlantest()
+ wt.tdls_clear(bssid, addr0, addr1)
+ hwsim_utils.test_connectivity_sta(sta0, sta1)
+ [dl, inv_dl, ap, inv_ap] = wlantest_tdls_packet_counters(bssid, addr0, addr1)
+ if dl == 0:
+ raise Exception("No valid frames through direct link")
+ if inv_dl > 0:
+ raise Exception("Invalid frames through direct link")
+ if ap > 0:
+ raise Exception("Unexpected frames through AP path")
+ if inv_ap > 0:
+ raise Exception("Invalid frames through AP path")
+
+def tdls_check_ap(sta0, sta1, bssid, addr0, addr1):
+ wt = Wlantest()
+ wt.tdls_clear(bssid, addr0, addr1)
+ hwsim_utils.test_connectivity_sta(sta0, sta1)
+ [dl, inv_dl, ap, inv_ap] = wlantest_tdls_packet_counters(bssid, addr0, addr1)
+ if dl > 0:
+ raise Exception("Unexpected frames through direct link")
+ if inv_dl > 0:
+ raise Exception("Invalid frames through direct link")
+ if ap == 0:
+ raise Exception("No valid frames through AP path")
+ if inv_ap > 0:
+ raise Exception("Invalid frames through AP path")
+
+def check_connectivity(sta0, sta1, hapd):
+ hwsim_utils.test_connectivity_sta(sta0, sta1)
+ hwsim_utils.test_connectivity(sta0, hapd)
+ hwsim_utils.test_connectivity(sta1, hapd)
+
+def setup_tdls(sta0, sta1, hapd, reverse=False, expect_fail=False, sae=False):
+ logger.info("Setup TDLS")
+ check_connectivity(sta0, sta1, hapd)
+ bssid = hapd.own_addr()
+ addr0 = sta0.p2p_interface_addr()
+ addr1 = sta1.p2p_interface_addr()
+ wt = Wlantest()
+ wt.tdls_clear(bssid, addr0, addr1)
+ wt.tdls_clear(bssid, addr1, addr0)
+ sta0.tdls_setup(addr1)
+ time.sleep(1)
+ if expect_fail:
+ if not sae:
+ tdls_check_ap(sta0, sta1, bssid, addr0, addr1)
+ return
+ if reverse:
+ addr1 = sta0.p2p_interface_addr()
+ addr0 = sta1.p2p_interface_addr()
+ if not sae:
+ conf = wt.get_tdls_counter("setup_conf_ok", bssid, addr0, addr1)
+ if conf == 0:
+ raise Exception("No TDLS Setup Confirm (success) seen")
+ tdls_check_dl(sta0, sta1, bssid, addr0, addr1)
+ check_connectivity(sta0, sta1, hapd)
+
+def teardown_tdls(sta0, sta1, hapd, responder=False, wildcard=False, sae=False):
+ logger.info("Teardown TDLS")
+ check_connectivity(sta0, sta1, hapd)
+ bssid = hapd.own_addr()
+ addr0 = sta0.p2p_interface_addr()
+ addr1 = sta1.p2p_interface_addr()
+ if responder:
+ sta1.tdls_teardown(addr0)
+ elif wildcard:
+ sta0.tdls_teardown("*")
+ else:
+ sta0.tdls_teardown(addr1)
+ time.sleep(1)
+ if not sae:
+ wt = Wlantest()
+ teardown = wt.get_tdls_counter("teardown", bssid, addr0, addr1)
+ if teardown == 0:
+ raise Exception("No TDLS Setup Teardown seen")
+ tdls_check_ap(sta0, sta1, bssid, addr0, addr1)
+ check_connectivity(sta0, sta1, hapd)
+
+def check_tdls_link(sta0, sta1, connected=True):
+ addr0 = sta0.own_addr()
+ addr1 = sta1.own_addr()
+ status0 = sta0.tdls_link_status(addr1).rstrip()
+ status1 = sta1.tdls_link_status(addr0).rstrip()
+ logger.info("%s: %s" % (sta0.ifname, status0))
+ logger.info("%s: %s" % (sta1.ifname, status1))
+ if status0 != status1:
+ raise Exception("TDLS link status differs between stations")
+ if "status: connected" in status0:
+ if not connected:
+ raise Exception("Expected TDLS link status NOT to be connected")
+ else:
+ if connected:
+ raise Exception("Expected TDLS link status to be connected")
+
+@remote_compatible
+def test_ap_tdls_discovery(dev, apdev):
+ """WPA2-PSK AP and two stations using TDLS discovery"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[0].request("TDLS_DISCOVER " + dev[1].p2p_interface_addr())
+ time.sleep(0.2)
+
+def test_ap_wpa2_tdls(dev, apdev):
+ """WPA2-PSK AP and two stations using TDLS"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ setup_tdls(dev[0], dev[1], hapd)
+ teardown_tdls(dev[0], dev[1], hapd)
+ setup_tdls(dev[1], dev[0], hapd)
+ #teardown_tdls(dev[0], dev[1], hapd)
+
+def test_ap_wpa2_tdls_concurrent_init(dev, apdev):
+ """Concurrent TDLS setup initiation"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[0].request("SET tdls_testing 0x80")
+ setup_tdls(dev[1], dev[0], hapd, reverse=True)
+
+def test_ap_wpa2_tdls_concurrent_init2(dev, apdev):
+ """Concurrent TDLS setup initiation (reverse)"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[1].request("SET tdls_testing 0x80")
+ setup_tdls(dev[0], dev[1], hapd)
+
+def test_ap_wpa2_tdls_decline_resp(dev, apdev):
+ """Decline TDLS Setup Response"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[1].request("SET tdls_testing 0x200")
+ setup_tdls(dev[1], dev[0], hapd, expect_fail=True)
+
+def test_ap_wpa2_tdls_long_lifetime(dev, apdev):
+ """TDLS with long TPK lifetime"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[1].request("SET tdls_testing 0x40")
+ setup_tdls(dev[1], dev[0], hapd)
+
+def test_ap_wpa2_tdls_long_frame(dev, apdev):
+ """TDLS with long setup/teardown frames"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[0].request("SET tdls_testing 0x1")
+ dev[1].request("SET tdls_testing 0x1")
+ setup_tdls(dev[1], dev[0], hapd)
+ teardown_tdls(dev[1], dev[0], hapd)
+ setup_tdls(dev[0], dev[1], hapd)
+
+def test_ap_wpa2_tdls_reneg(dev, apdev):
+ """Renegotiate TDLS link"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ setup_tdls(dev[1], dev[0], hapd)
+ setup_tdls(dev[0], dev[1], hapd)
+
+def test_ap_wpa2_tdls_wrong_lifetime_resp(dev, apdev):
+ """Incorrect TPK lifetime in TDLS Setup Response"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[1].request("SET tdls_testing 0x10")
+ setup_tdls(dev[0], dev[1], hapd, expect_fail=True)
+
+def test_ap_wpa2_tdls_diff_rsnie(dev, apdev):
+ """TDLS with different RSN IEs"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[1].request("SET tdls_testing 0x2")
+ setup_tdls(dev[1], dev[0], hapd)
+ teardown_tdls(dev[1], dev[0], hapd)
+
+def test_ap_wpa2_tdls_wrong_tpk_m2_mic(dev, apdev):
+ """Incorrect MIC in TDLS Setup Response"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[0].request("SET tdls_testing 0x800")
+ addr0 = dev[0].p2p_interface_addr()
+ dev[1].tdls_setup(addr0)
+ time.sleep(1)
+
+def test_ap_wpa2_tdls_wrong_tpk_m3_mic(dev, apdev):
+ """Incorrect MIC in TDLS Setup Confirm"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[1].request("SET tdls_testing 0x800")
+ addr0 = dev[0].p2p_interface_addr()
+ dev[1].tdls_setup(addr0)
+ time.sleep(1)
+
+def test_ap_wpa2_tdls_double_tpk_m2(dev, apdev):
+ """Double TPK M2 during TDLS setup initiation"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ dev[0].request("SET tdls_testing 0x1000")
+ setup_tdls(dev[1], dev[0], hapd)
+
+def test_ap_wpa_tdls(dev, apdev):
+ """WPA-PSK AP and two stations using TDLS"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ skip_without_tkip(dev[1])
+ hapd = hostapd.add_ap(apdev[0],
+ hostapd.wpa_params(ssid="test-wpa-psk",
+ passphrase="12345678"))
+ wlantest_setup(hapd)
+ connect_2sta_wpa_psk(dev, hapd)
+ setup_tdls(dev[0], dev[1], hapd)
+ teardown_tdls(dev[0], dev[1], hapd)
+ setup_tdls(dev[1], dev[0], hapd)
+
+def test_ap_wpa_mixed_tdls(dev, apdev):
+ """WPA+WPA2-PSK AP and two stations using TDLS"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ hapd = hostapd.add_ap(apdev[0],
+ hostapd.wpa_mixed_params(ssid="test-wpa-mixed-psk",
+ passphrase="12345678"))
+ wlantest_setup(hapd)
+ connect_2sta_wpa_psk_mixed(dev, hapd)
+ setup_tdls(dev[0], dev[1], hapd)
+ teardown_tdls(dev[0], dev[1], hapd)
+ setup_tdls(dev[1], dev[0], hapd)
+
+def test_ap_wep_tdls(dev, apdev):
+ """WEP AP and two stations using TDLS"""
+ check_wep_capa(dev[0])
+ check_wep_capa(dev[1])
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "test-wep", "wep_key0": '"hello"'})
+ wlantest_setup(hapd)
+ connect_2sta_wep(dev, hapd)
+ setup_tdls(dev[0], dev[1], hapd)
+ teardown_tdls(dev[0], dev[1], hapd)
+ setup_tdls(dev[1], dev[0], hapd)
+
+def test_ap_open_tdls(dev, apdev):
+ """Open AP and two stations using TDLS"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ wlantest_setup(hapd)
+ connect_2sta_open(dev, hapd)
+ setup_tdls(dev[0], dev[1], hapd)
+ teardown_tdls(dev[0], dev[1], hapd)
+ setup_tdls(dev[1], dev[0], hapd)
+ teardown_tdls(dev[1], dev[0], hapd, wildcard=True)
+
+def test_ap_wpa2_tdls_bssid_mismatch(dev, apdev):
+ """TDLS failure due to BSSID mismatch"""
+ try:
+ ssid = "test-wpa2-psk"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['bridge'] = 'ap-br0'
+ hapd = hostapd.add_ap(apdev[0], params)
+ hostapd.add_ap(apdev[1], params)
+ wlantest_setup(hapd)
+ subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412",
+ bssid=apdev[0]['bssid'])
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412",
+ bssid=apdev[1]['bssid'])
+ hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "ap-br0")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "ap-br0")
+
+ addr0 = dev[0].p2p_interface_addr()
+ dev[1].tdls_setup(addr0)
+ time.sleep(1)
+ hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'])
+ subprocess.call(['brctl', 'delbr', 'ap-br0'])
+
+def test_ap_wpa2_tdls_responder_teardown(dev, apdev):
+ """TDLS teardown from responder with WPA2-PSK AP"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ setup_tdls(dev[0], dev[1], hapd)
+ teardown_tdls(dev[0], dev[1], hapd, responder=True)
+
+def tdls_clear_reg(hapd, dev):
+ if hapd:
+ hapd.request("DISABLE")
+ dev[1].request("DISCONNECT")
+ dev[0].disconnect_and_stop_scan()
+ dev[1].disconnect_and_stop_scan()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_ap_open_tdls_vht(dev, apdev):
+ """Open AP and two stations using TDLS"""
+ params = {"ssid": "test-open",
+ "country_code": "DE",
+ "hw_mode": "a",
+ "channel": "36",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ht_capab": "",
+ "vht_capab": "",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "0"}
+ hapd = None
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ wlantest_setup(hapd)
+ connect_2sta_open(dev, hapd, scan_freq="5180")
+ setup_tdls(dev[0], dev[1], hapd)
+ teardown_tdls(dev[0], dev[1], hapd)
+ setup_tdls(dev[1], dev[0], hapd)
+ teardown_tdls(dev[1], dev[0], hapd, wildcard=True)
+ finally:
+ tdls_clear_reg(hapd, dev)
+
+def test_ap_open_tdls_vht80(dev, apdev):
+ """Open AP and two stations using TDLS with VHT 80"""
+ params = {"ssid": "test-open",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "[VHT160]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_capab": "",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42"}
+ try:
+ hapd = None
+ hapd = hostapd.add_ap(apdev[0], params)
+ wlantest_setup(hapd)
+ connect_2sta_open(dev, hapd, scan_freq="5180")
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ setup_tdls(dev[0], dev[1], hapd)
+ for i in range(10):
+ check_connectivity(dev[0], dev[1], hapd)
+ for i in range(2):
+ cmd = subprocess.Popen(['iw', dev[0].ifname, 'station', 'dump'],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read()
+ cmd.stdout.close()
+ logger.info("Station dump on dev[%d]:\n%s" % (i, res.decode()))
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ tdls_clear_reg(hapd, dev)
+
+def test_ap_open_tdls_vht80plus80(dev, apdev):
+ """Open AP and two stations using TDLS with VHT 80+80"""
+ params = {"ssid": "test-open",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_capab": "[VHT160-80PLUS80]",
+ "vht_oper_chwidth": "3",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "vht_oper_centr_freq_seg1_idx": "155"}
+ try:
+ hapd = None
+ hapd = hostapd.add_ap(apdev[0], params)
+ wlantest_setup(hapd)
+ connect_2sta_open(dev, hapd, scan_freq="5180")
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80+80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "CENTER_FRQ1=5210" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+ if "CENTER_FRQ2=5775" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(4): " + str(sig))
+ setup_tdls(dev[0], dev[1], hapd)
+ for i in range(10):
+ check_connectivity(dev[0], dev[1], hapd)
+ for i in range(2):
+ cmd = subprocess.Popen(['iw', dev[0].ifname, 'station', 'dump'],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read()
+ cmd.stdout.close()
+ logger.info("Station dump on dev[%d]:\n%s" % (i, res.decode()))
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ tdls_clear_reg(hapd, dev)
+
+def test_ap_open_tdls_vht160(dev, apdev):
+ """Open AP and two stations using TDLS with VHT 160"""
+ params = {"ssid": "test-open",
+ "country_code": "ZA",
+ "hw_mode": "a",
+ "channel": "104",
+ "ht_capab": "[HT40-]",
+ "vht_capab": "[VHT160]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "114"}
+ try:
+ hapd = None
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=2)
+ if not ev:
+ cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
+ reg = cmd.stdout.readlines()
+ for r in reg:
+ if "5490" in r and "DFS" in r:
+ raise HwsimSkip("ZA regulatory rule did not have DFS requirement removed")
+ raise Exception("AP setup timed out")
+ wlantest_setup(hapd)
+ connect_2sta_open(dev, hapd, scan_freq="5520")
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=160 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ setup_tdls(dev[0], dev[1], hapd)
+ for i in range(10):
+ check_connectivity(dev[0], dev[1], hapd)
+ for i in range(2):
+ cmd = subprocess.Popen(['iw', dev[0].ifname, 'station', 'dump'],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read()
+ cmd.stdout.close()
+ logger.info("Station dump on dev[%d]:\n%s" % (i, res.decode()))
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ tdls_clear_reg(hapd, dev)
+
+def test_tdls_chan_switch(dev, apdev):
+ """Open AP and two stations using TDLS"""
+ flags = int(dev[0].get_driver_status_field('capa.flags'), 16)
+ if flags & 0x800000000 == 0:
+ raise HwsimSkip("Driver does not support TDLS channel switching")
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ wlantest_setup(hapd)
+ connect_2sta_open(dev, hapd)
+ setup_tdls(dev[0], dev[1], hapd)
+ if "OK" not in dev[0].request("TDLS_CHAN_SWITCH " + dev[1].own_addr() + " 81 2462"):
+ raise Exception("Failed to enable TDLS channel switching")
+ if "OK" not in dev[0].request("TDLS_CANCEL_CHAN_SWITCH " + dev[1].own_addr()):
+ raise Exception("Could not disable TDLS channel switching")
+ if "FAIL" not in dev[0].request("TDLS_CANCEL_CHAN_SWITCH " + dev[1].own_addr()):
+ raise Exception("TDLS_CANCEL_CHAN_SWITCH accepted even though channel switching was already disabled")
+ if "FAIL" not in dev[0].request("TDLS_CHAN_SWITCH foo 81 2462"):
+ raise Exception("Invalid TDLS channel switching command accepted")
+
+def test_ap_tdls_link_status(dev, apdev):
+ """Check TDLS link status between two stations"""
+ hapd = start_ap_wpa2_psk(apdev[0])
+ wlantest_setup(hapd)
+ connect_2sta_wpa2_psk(dev, hapd)
+ check_tdls_link(dev[0], dev[1], connected=False)
+ setup_tdls(dev[0], dev[1], hapd)
+ check_tdls_link(dev[0], dev[1], connected=True)
+ teardown_tdls(dev[0], dev[1], hapd)
+ check_tdls_link(dev[0], dev[1], connected=False)
+ if "FAIL" not in dev[0].request("TDLS_LINK_STATUS foo"):
+ raise Exception("Unexpected TDLS_LINK_STATUS response for invalid argument")
+
+def test_ap_tdls_prohibit(dev, apdev):
+ """Open AP and TDLS prohibited"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open",
+ "tdls_prohibit": "1"})
+ connect_2sta_open(dev, hapd)
+ if "FAIL" not in dev[0].request("TDLS_SETUP " + dev[1].own_addr()):
+ raise Exception("TDLS_SETUP accepted unexpectedly")
+
+def test_ap_tdls_chan_switch_prohibit(dev, apdev):
+ """Open AP and TDLS channel switch prohibited"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open",
+ "tdls_prohibit_chan_switch": "1"})
+ wlantest_setup(hapd)
+ connect_2sta_open(dev, hapd)
+ setup_tdls(dev[0], dev[1], hapd)
+
+def test_ap_open_tdls_external_control(dev, apdev):
+ """TDLS and tdls_external_control"""
+ try:
+ _test_ap_open_tdls_external_control(dev, apdev)
+ finally:
+ dev[0].set("tdls_external_control", "0")
+
+def _test_ap_open_tdls_external_control(dev, apdev):
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+
+ dev[0].set("tdls_external_control", "1")
+ if "FAIL" in dev[0].request("TDLS_SETUP " + addr1):
+ # tdls_external_control not supported; try without it
+ dev[0].set("tdls_external_control", "0")
+ if "FAIL" in dev[0].request("TDLS_SETUP " + addr1):
+ raise Exception("TDLS_SETUP failed")
+ connected = False
+ for i in range(50):
+ res0 = dev[0].request("TDLS_LINK_STATUS " + addr1)
+ res1 = dev[1].request("TDLS_LINK_STATUS " + addr0)
+ if "TDLS link status: connected" in res0 and "TDLS link status: connected" in res1:
+ connected = True
+ break
+ time.sleep(0.1)
+ if not connected:
+ raise Exception("TDLS setup did not complete")
+
+ dev[0].set("tdls_external_control", "1")
+ if "FAIL" in dev[0].request("TDLS_TEARDOWN " + addr1):
+ # tdls_external_control not supported; try without it
+ dev[0].set("tdls_external_control", "0")
+ if "FAIL" in dev[0].request("TDLS_TEARDOWN " + addr1):
+ raise Exception("TDLS_TEARDOWN failed")
+ for i in range(50):
+ res0 = dev[0].request("TDLS_LINK_STATUS " + addr1)
+ res1 = dev[1].request("TDLS_LINK_STATUS " + addr0)
+ if "TDLS link status: connected" not in res0 and "TDLS link status: connected" not in res1:
+ connected = False
+ break
+ time.sleep(0.1)
+ if connected:
+ raise Exception("TDLS teardown did not complete")
+
+def test_ap_sae_tdls(dev, apdev):
+ """SAE AP and two stations using TDLS"""
+ check_sae_capab(dev[0])
+ check_sae_capab(dev[1])
+ dev[0].request("SET sae_groups ")
+ dev[1].request("SET sae_groups ")
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ wlantest_setup(hapd)
+ connect_2sta(dev, "test-wpa2-psk", hapd, sae=True)
+ setup_tdls(dev[0], dev[1], hapd, sae=True)
+ teardown_tdls(dev[0], dev[1], hapd, sae=True)
+ setup_tdls(dev[1], dev[0], hapd, sae=True)
diff --git a/contrib/wpa/tests/hwsim/test_ap_track.py b/contrib/wpa/tests/hwsim/test_ap_track.py
new file mode 100644
index 000000000000..ba8f3eb252cd
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_track.py
@@ -0,0 +1,437 @@
+# Test cases for hostapd tracking unconnected stations
+# Copyright (c) 2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import parse_ie, disable_hapd, clear_regdom_dev
+
+def test_ap_track_sta(dev, apdev):
+ """Dualband AP tracking unconnected stations"""
+
+ try:
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "g",
+ "channel": "6",
+ "track_sta_max_num": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "40",
+ "track_sta_max_num": "100",
+ "track_sta_max_age": "1"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ _test_ap_track_sta(dev, hapd, apdev[0]['bssid'], hapd2,
+ apdev[1]['bssid'])
+ finally:
+ disable_hapd(hapd)
+ disable_hapd(hapd2)
+ clear_regdom_dev(dev, 3)
+
+def _test_ap_track_sta(dev, hapd, bssid, hapd2, bssid2):
+ for i in range(2):
+ dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+ dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
+ dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
+ dev[2].scan_for_bss(bssid2, freq=5200, force_scan=True)
+
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ addr2 = dev[2].own_addr()
+
+ track = hapd.request("TRACK_STA_LIST")
+ if addr0 not in track or addr1 not in track:
+ raise Exception("Station missing from 2.4 GHz tracking")
+ if addr2 in track:
+ raise Exception("Unexpected station included in 2.4 GHz tracking")
+
+ track = hapd2.request("TRACK_STA_LIST")
+ if addr0 not in track or addr2 not in track:
+ raise Exception("Station missing from 5 GHz tracking")
+ if addr1 in track:
+ raise Exception("Unexpected station included in 5 GHz tracking")
+
+ # Test expiration
+ time.sleep(1.1)
+ track = hapd.request("TRACK_STA_LIST")
+ if addr0 not in track or addr1 not in track:
+ raise Exception("Station missing from 2.4 GHz tracking (expiration)")
+ track = hapd2.request("TRACK_STA_LIST")
+ if addr0 in track or addr2 in track:
+ raise Exception("Station not expired from 5 GHz tracking")
+
+ # Test maximum list length
+ dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+ dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
+ dev[2].scan_for_bss(bssid, freq=2437, force_scan=True)
+ track = hapd.request("TRACK_STA_LIST")
+ if len(track.splitlines()) != 2:
+ raise Exception("Unexpected number of entries: %d" % len(track.splitlines()))
+ if addr1 not in track or addr2 not in track:
+ raise Exception("Station missing from 2.4 GHz tracking (max limit)")
+
+def test_ap_track_sta_no_probe_resp(dev, apdev):
+ """Dualband AP not replying to probes from dualband STA on 2.4 GHz"""
+ try:
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "g",
+ "channel": "6",
+ "beacon_int": "10000",
+ "no_probe_resp_if_seen_on": apdev[1]['ifname']}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "40",
+ "track_sta_max_num": "100"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ _test_ap_track_sta_no_probe_resp(dev, apdev[0]['bssid'],
+ apdev[1]['bssid'])
+ finally:
+ disable_hapd(hapd)
+ disable_hapd(hapd2)
+ clear_regdom_dev(dev, 2)
+
+def _test_ap_track_sta_no_probe_resp(dev, bssid, bssid2):
+ dev[0].flush_scan_cache()
+
+ dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
+ dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
+ dev[0].scan(freq=2437, type="ONLY")
+ dev[0].scan(freq=2437, type="ONLY")
+
+ bss = dev[0].get_bss(bssid)
+ if bss:
+ ie = parse_ie(bss['ie'])
+ # Check whether this is from a Beacon frame (TIM element included) since
+ # it is possible that a Beacon frame was received during the active
+ # scan. This test should fail only if a Probe Response frame was
+ # received.
+ if 5 not in ie:
+ raise Exception("2.4 GHz AP found unexpectedly")
+
+def test_ap_track_sta_no_auth(dev, apdev):
+ """Dualband AP rejecting authentication from dualband STA on 2.4 GHz"""
+ try:
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "g",
+ "channel": "6",
+ "track_sta_max_num": "100",
+ "no_auth_if_seen_on": apdev[1]['ifname']}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "40",
+ "track_sta_max_num": "100"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ _test_ap_track_sta_no_auth(dev, apdev[0]['bssid'], apdev[1]['bssid'])
+ finally:
+ disable_hapd(hapd)
+ disable_hapd(hapd2)
+ clear_regdom_dev(dev, 2)
+
+def _test_ap_track_sta_no_auth(dev, bssid, bssid2):
+ dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+ dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
+ dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
+
+ dev[1].connect("track", key_mgmt="NONE", scan_freq="2437")
+
+ dev[0].connect("track", key_mgmt="NONE", scan_freq="2437",
+ freq_list="2437", wait_connect=False)
+ dev[1].request("DISCONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-AUTH-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("Unknown connection result")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ if "status_code=82" not in ev:
+ raise Exception("Unexpected rejection reason: " + ev)
+ if "ie=34" not in ev:
+ raise Exception("No Neighbor Report element: " + ev)
+ dev[0].request("DISCONNECT")
+
+def test_ap_track_sta_no_auth_passive(dev, apdev):
+ """AP rejecting authentication from dualband STA on 2.4 GHz (passive)"""
+ try:
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "g",
+ "channel": "6",
+ "no_auth_if_seen_on": apdev[1]['ifname']}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "40",
+ "interworking": "1",
+ "venue_name": "eng:Venue",
+ "track_sta_max_num": "100"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ _test_ap_track_sta_no_auth_passive(dev, apdev[0]['bssid'],
+ apdev[1]['bssid'])
+ finally:
+ disable_hapd(hapd)
+ disable_hapd(hapd2)
+ clear_regdom_dev(dev)
+
+def _test_ap_track_sta_no_auth_passive(dev, bssid, bssid2):
+ dev[0].flush_scan_cache()
+
+ dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+ for i in range(10):
+ dev[0].request("SCAN freq=5200 passive=1")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=5)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ if dev[0].get_bss(bssid2):
+ break
+ if i == 9:
+ raise Exception("AP not found with passive scans")
+
+ if "OK" not in dev[0].request("ANQP_GET " + bssid2 + " 258"):
+ raise Exception("ANQP_GET command failed")
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+ if ev is None or "Venue Name" not in ev:
+ raise Exception("Did not receive Venue Name")
+
+ dev[0].connect("track", key_mgmt="NONE", scan_freq="2437",
+ freq_list="2437", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-AUTH-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("Unknown connection result")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ if "status_code=82" not in ev:
+ raise Exception("Unexpected rejection reason: " + ev)
+ dev[0].request("DISCONNECT")
+
+def test_ap_track_sta_force_5ghz(dev, apdev):
+ """Dualband AP forcing dualband STA to connect on 5 GHz"""
+ try:
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "g",
+ "channel": "6",
+ "no_probe_resp_if_seen_on": apdev[1]['ifname'],
+ "no_auth_if_seen_on": apdev[1]['ifname']}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "40",
+ "track_sta_max_num": "100"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ _test_ap_track_sta_force_5ghz(dev, apdev[0]['bssid'], apdev[1]['bssid'])
+ finally:
+ disable_hapd(hapd)
+ disable_hapd(hapd2)
+ clear_regdom_dev(dev)
+
+def _test_ap_track_sta_force_5ghz(dev, bssid, bssid2):
+ dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+ dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
+
+ dev[0].connect("track", key_mgmt="NONE", scan_freq="2437 5200")
+ freq = dev[0].get_status_field('freq')
+ if freq != '5200':
+ raise Exception("Unexpected operating channel")
+ dev[0].request("DISCONNECT")
+
+def test_ap_track_sta_force_2ghz(dev, apdev):
+ """Dualband AP forcing dualband STA to connect on 2.4 GHz"""
+ try:
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "g",
+ "channel": "6",
+ "track_sta_max_num": "100"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "40",
+ "no_probe_resp_if_seen_on": apdev[0]['ifname'],
+ "no_auth_if_seen_on": apdev[0]['ifname']}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ _test_ap_track_sta_force_2ghz(dev, apdev[0]['bssid'], apdev[1]['bssid'])
+ finally:
+ disable_hapd(hapd)
+ disable_hapd(hapd2)
+ clear_regdom_dev(dev)
+
+def _test_ap_track_sta_force_2ghz(dev, bssid, bssid2):
+ dev[0].scan_for_bss(bssid2, freq=5200, force_scan=True)
+ dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+
+ dev[0].connect("track", key_mgmt="NONE", scan_freq="2437 5200")
+ freq = dev[0].get_status_field('freq')
+ if freq != '2437':
+ raise Exception("Unexpected operating channel")
+ dev[0].request("DISCONNECT")
+
+def test_ap_track_taxonomy(dev, apdev):
+ """AP tracking STA taxonomy"""
+ try:
+ _test_ap_track_taxonomy(dev, apdev)
+ finally:
+ dev[1].request("SET p2p_disabled 0")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ dev[2].flush_scan_cache()
+
+def _test_ap_track_taxonomy(dev, apdev):
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "g",
+ "channel": "6",
+ "track_sta_max_num": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq=2437, force_scan=True)
+ addr0 = dev[0].own_addr()
+ dev[0].connect("track", key_mgmt="NONE", scan_freq="2437")
+
+ dev[1].request("SET p2p_disabled 1")
+ dev[1].scan_for_bss(bssid, freq=2437, force_scan=True)
+ addr1 = dev[1].own_addr()
+ dev[1].connect("track", key_mgmt="NONE", scan_freq="2437")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.request("SET model_name track test")
+ wpas.scan_for_bss(bssid, freq=2437, force_scan=True)
+ addr = wpas.own_addr()
+ wpas.connect("track", key_mgmt="NONE", scan_freq="2437")
+
+ if "FAIL" not in hapd.request("SIGNATURE abc"):
+ raise Exception("SIGNATURE failure not reported (1)")
+ if "FAIL" not in hapd.request("SIGNATURE 22:33:44:55:66:77"):
+ raise Exception("SIGNATURE failure not reported (2)")
+
+ res = hapd.request("SIGNATURE " + addr0)
+ logger.info("sta0: " + res)
+ if not res.startswith("wifi4|probe:"):
+ raise Exception("Unexpected SIGNATURE prefix")
+ if "|assoc:" not in res:
+ raise Exception("Missing assoc info in SIGNATURE")
+ if "wps:track_test" in res:
+ raise Exception("Unexpected WPS model name")
+
+ res = hapd.request("SIGNATURE " + addr1)
+ logger.info("sta1: " + res)
+ if not res.startswith("wifi4|probe:"):
+ raise Exception("Unexpected SIGNATURE prefix")
+ if "|assoc:" not in res:
+ raise Exception("Missing assoc info in SIGNATURE")
+ if "wps:" in res:
+ raise Exception("Unexpected WPS info")
+ if ",221(0050f2,4)," in res:
+ raise Exception("Unexpected WPS IE info")
+ if ",221(506f9a,9)," in res:
+ raise Exception("Unexpected P2P IE info")
+
+ res = hapd.request("SIGNATURE " + addr)
+ logger.info("sta: " + res)
+ if not res.startswith("wifi4|probe:"):
+ raise Exception("Unexpected SIGNATURE prefix")
+ if "|assoc:" not in res:
+ raise Exception("Missing assoc info in SIGNATURE")
+ if "wps:track_test" not in res:
+ raise Exception("Missing WPS model name")
+ if ",221(0050f2,4)," not in res:
+ raise Exception("Missing WPS IE info")
+ if ",221(506f9a,9)," not in res:
+ raise Exception("Missing P2P IE info")
+
+ addr2 = dev[2].own_addr()
+ res = hapd.request("SIGNATURE " + addr2)
+ if "FAIL" not in res:
+ raise Exception("Unexpected SIGNATURE success for sta2 (1)")
+
+ for i in range(10):
+ dev[2].request("SCAN freq=2437 passive=1")
+ ev = dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ if dev[2].get_bss(bssid):
+ break
+
+ res = hapd.request("SIGNATURE " + addr2)
+ if "FAIL" not in res:
+ raise Exception("Unexpected SIGNATURE success for sta2 (2)")
+
+ dev[2].connect("track", key_mgmt="NONE", scan_freq="2437")
+
+ res = hapd.request("SIGNATURE " + addr2)
+ if "FAIL" not in res and len(res) > 0:
+ raise Exception("Unexpected SIGNATURE success for sta2 (3)")
+
+ dev[2].scan_for_bss(bssid, freq=2437, force_scan=True)
+
+ res = hapd.request("SIGNATURE " + addr2)
+ logger.info("sta2: " + res)
+ if not res.startswith("wifi4|probe:"):
+ raise Exception("Unexpected SIGNATURE prefix")
+ if "|assoc:" not in res:
+ raise Exception("Missing assoc info in SIGNATURE")
+
+def test_ap_track_taxonomy_5g(dev, apdev):
+ """AP tracking STA taxonomy (5 GHz)"""
+ try:
+ _test_ap_track_taxonomy_5g(dev, apdev)
+ finally:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+
+def _test_ap_track_taxonomy_5g(dev, apdev):
+ params = {"ssid": "track",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "40",
+ "track_sta_max_num": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq=5200, force_scan=True)
+ addr0 = dev[0].own_addr()
+ dev[0].connect("track", key_mgmt="NONE", scan_freq="5200")
+
+ res = hapd.request("SIGNATURE " + addr0)
+ logger.info("sta0: " + res)
+ if not res.startswith("wifi4|probe:"):
+ raise Exception("Unexpected SIGNATURE prefix")
+ if "|assoc:" not in res:
+ raise Exception("Missing assoc info in SIGNATURE")
+ if ",htcap:" not in res:
+ raise Exception("Missing HT info in SIGNATURE")
+ if ",vhtcap:" not in res:
+ raise Exception("Missing VHT info in SIGNATURE")
diff --git a/contrib/wpa/tests/hwsim/test_ap_vht.py b/contrib/wpa/tests/hwsim/test_ap_vht.py
new file mode 100644
index 000000000000..0123697f4813
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_vht.py
@@ -0,0 +1,1333 @@
+# Test cases for VHT operations with hostapd
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+# Copyright (c) 2013, Intel Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os
+import subprocess, time
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+from test_dfs import wait_dfs_event
+
+def test_ap_vht80(dev, apdev):
+ """VHT with 80 MHz channel width"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ est = dev[0].get_bss(bssid)['est_throughput']
+ if est != "390001":
+ raise Exception("Unexpected BSS est_throughput: " + est)
+ status = dev[0].get_status()
+ if status["ieee80211ac"] != "1":
+ raise Exception("Unexpected STATUS ieee80211ac value (STA)")
+ status = hapd.get_status()
+ logger.info("hostapd STATUS: " + str(status))
+ if status["ieee80211n"] != "1":
+ raise Exception("Unexpected STATUS ieee80211n value")
+ if status["ieee80211ac"] != "1":
+ raise Exception("Unexpected STATUS ieee80211ac value")
+ if status["secondary_channel"] != "1":
+ raise Exception("Unexpected STATUS secondary_channel value")
+ if status["vht_oper_chwidth"] != "1":
+ raise Exception("Unexpected STATUS vht_oper_chwidth value")
+ if status["vht_oper_centr_freq_seg0_idx"] != "42":
+ raise Exception("Unexpected STATUS vht_oper_centr_freq_seg0_idx value")
+ if "vht_caps_info" not in status:
+ raise Exception("Missing vht_caps_info")
+
+ sta = hapd.get_sta(dev[0].own_addr())
+ logger.info("hostapd STA: " + str(sta))
+ if "[HT]" not in sta['flags']:
+ raise Exception("Missing STA flag: HT")
+ if "[VHT]" not in sta['flags']:
+ raise Exception("Missing STA flag: VHT")
+ if 'supp_op_classes' not in sta or len(sta['supp_op_classes']) < 2:
+ raise Exception("No Supported Operating Classes information for STA")
+ opclass = int(sta['supp_op_classes'][0:2], 16)
+ if opclass != 128:
+ raise Exception("Unexpected Current Operating Class from STA: %d" % opclass)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_ap_vht_wifi_generation(dev, apdev):
+ """VHT and wifi_generation"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5180")
+ status = dev[0].get_status()
+ if 'wifi_generation' not in status:
+ # For now, assume this is because of missing kernel support
+ raise HwsimSkip("Association Request IE reporting not supported")
+ #raise Exception("Missing wifi_generation information")
+ if status['wifi_generation'] != "5":
+ raise Exception("Unexpected wifi_generation value: " + status['wifi_generation'])
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("vht", key_mgmt="NONE", scan_freq="5180")
+ status = wpas.get_status()
+ if 'wifi_generation' not in status:
+ # For now, assume this is because of missing kernel support
+ raise HwsimSkip("Association Request IE reporting not supported")
+ #raise Exception("Missing wifi_generation information (connect)")
+ if status['wifi_generation'] != "5":
+ raise Exception("Unexpected wifi_generation value (connect): " + status['wifi_generation'])
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def vht80_test(apdev, dev, channel, ht_capab):
+ clear_scan_cache(apdev)
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": str(channel),
+ "ht_capab": ht_capab,
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev, params)
+ bssid = apdev['bssid']
+
+ dev[0].connect("vht", key_mgmt="NONE",
+ scan_freq=str(5000 + 5 * channel))
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_vht80b(dev, apdev):
+ """VHT with 80 MHz channel width (HT40- channel 40)"""
+ vht80_test(apdev[0], dev, 40, "[HT40-]")
+
+def test_ap_vht80c(dev, apdev):
+ """VHT with 80 MHz channel width (HT40+ channel 44)"""
+ vht80_test(apdev[0], dev, 44, "[HT40+]")
+
+def test_ap_vht80d(dev, apdev):
+ """VHT with 80 MHz channel width (HT40- channel 48)"""
+ vht80_test(apdev[0], dev, 48, "[HT40-]")
+
+def test_ap_vht80_params(dev, apdev):
+ """VHT with 80 MHz channel width and number of optional features enabled"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+][SHORT-GI-40][DSS_CCK-40]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_capab": "[MAX-MPDU-11454][RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-STBC-1][MAX-A-MPDU-LEN-EXP0]",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "require_vht": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[1].connect("vht", key_mgmt="NONE", scan_freq="5180",
+ disable_vht="1", wait_connect=False)
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5180")
+ dev[2].connect("vht", key_mgmt="NONE", scan_freq="5180",
+ disable_sgi="1")
+ ev = dev[1].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+ if ev is None:
+ raise Exception("Association rejection timed out")
+ if "status_code=104" not in ev:
+ raise Exception("Unexpected rejection status code")
+ dev[1].request("DISCONNECT")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sta0 = hapd.get_sta(dev[0].own_addr())
+ sta2 = hapd.get_sta(dev[2].own_addr())
+ capab0 = int(sta0['vht_caps_info'], base=16)
+ capab2 = int(sta2['vht_caps_info'], base=16)
+ if capab0 & 0x60 == 0:
+ raise Exception("dev[0] did not support SGI")
+ if capab2 & 0x60 != 0:
+ raise Exception("dev[2] claimed support for SGI")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev, count=3)
+
+def test_ap_vht80_invalid(dev, apdev):
+ """VHT with invalid 80 MHz channel configuration (seg1)"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "vht_oper_centr_freq_seg1_idx": "155",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ # This fails due to unexpected seg1 configuration
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("AP-DISABLED not reported")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_vht80_invalid2(dev, apdev):
+ """VHT with invalid 80 MHz channel configuration (seg0)"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "46",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ # This fails due to invalid seg0 configuration
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("AP-DISABLED not reported")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_vht_20(devs, apdevs):
+ """VHT and 20 MHz channel"""
+ dev = devs[0]
+ ap = apdevs[0]
+ try:
+ hapd = None
+ params = {"ssid": "test-vht20",
+ "country_code": "DE",
+ "hw_mode": "a",
+ "channel": "36",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ht_capab": "",
+ "vht_capab": "",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "0",
+ "supported_rates": "60 120 240 360 480 540",
+ "require_vht": "1"}
+ hapd = hostapd.add_ap(ap, params)
+ dev.connect("test-vht20", scan_freq="5180", key_mgmt="NONE")
+ hwsim_utils.test_connectivity(dev, hapd)
+
+ sta = hapd.get_sta(dev.own_addr())
+ if 'supp_op_classes' not in sta or len(sta['supp_op_classes']) < 2:
+ raise Exception("No Supported Operating Classes information for STA")
+ opclass = int(sta['supp_op_classes'][0:2], 16)
+ if opclass != 115:
+ raise Exception("Unexpected Current Operating Class from STA: %d" % opclass)
+ finally:
+ dev.request("DISCONNECT")
+ clear_regdom(hapd, devs)
+
+def test_ap_vht_40(devs, apdevs):
+ """VHT and 40 MHz channel"""
+ dev = devs[0]
+ ap = apdevs[0]
+ try:
+ hapd = None
+ params = {"ssid": "test-vht40",
+ "country_code": "DE",
+ "hw_mode": "a",
+ "channel": "36",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "0"}
+ hapd = hostapd.add_ap(ap, params)
+ dev.connect("test-vht40", scan_freq="5180", key_mgmt="NONE")
+ hwsim_utils.test_connectivity(dev, hapd)
+
+ sta = hapd.get_sta(dev.own_addr())
+ if 'supp_op_classes' not in sta or len(sta['supp_op_classes']) < 2:
+ raise Exception("No Supported Operating Classes information for STA")
+ opclass = int(sta['supp_op_classes'][0:2], 16)
+ if opclass != 116:
+ raise Exception("Unexpected Current Operating Class from STA: %d" % opclass)
+ finally:
+ dev.request("DISCONNECT")
+ clear_regdom(hapd, devs)
+
+def test_ap_vht_capab_not_supported(dev, apdev):
+ """VHT configuration with driver not supporting all vht_capab entries"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+][SHORT-GI-40][DSS_CCK-40]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_capab": "[MAX-MPDU-7991][MAX-MPDU-11454][VHT160][VHT160-80PLUS80][RXLDPC][SHORT-GI-80][SHORT-GI-160][TX-STBC-2BY1][RX-STBC-1][RX-STBC-12][RX-STBC-123][RX-STBC-1234][SU-BEAMFORMER][SU-BEAMFORMEE][BF-ANTENNA-2][BF-ANTENNA-3][BF-ANTENNA-4][SOUNDING-DIMENSION-2][SOUNDING-DIMENSION-3][SOUNDING-DIMENSION-4][MU-BEAMFORMER][VHT-TXOP-PS][HTC-VHT][MAX-A-MPDU-LEN-EXP0][MAX-A-MPDU-LEN-EXP7][VHT-LINK-ADAPT2][VHT-LINK-ADAPT3][RX-ANTENNA-PATTERN][TX-ANTENNA-PATTERN]",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "require_vht": "1"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("Startup failure not reported")
+ for i in range(1, 7):
+ if "OK" not in hapd.request("SET vht_capab [MAX-A-MPDU-LEN-EXP%d]" % i):
+ raise Exception("Unexpected SET failure")
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_vht160(dev, apdev):
+ """VHT with 160 MHz channel width (1)"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "[VHT160]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "50",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ if "DFS-CAC-START" not in ev:
+ raise Exception("Unexpected DFS event")
+
+ state = hapd.get_status_field("state")
+ if state != "DFS":
+ if state == "DISABLED" and not os.path.exists("dfs"):
+ # Not all systems have recent enough CRDA version and
+ # wireless-regdb changes to support 160 MHz and DFS. For now,
+ # do not report failures for this test case.
+ raise HwsimSkip("CRDA or wireless-regdb did not support 160 MHz")
+ raise Exception("Unexpected interface state: " + state)
+
+ logger.info("Waiting for CAC to complete")
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected DFS freq result")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state")
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5180")
+ dev[0].wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=160 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+
+ sta = hapd.get_sta(dev[0].own_addr())
+ if 'supp_op_classes' not in sta or len(sta['supp_op_classes']) < 2:
+ raise Exception("No Supported Operating Classes information for STA")
+ opclass = int(sta['supp_op_classes'][0:2], 16)
+ if opclass != 129:
+ raise Exception("Unexpected Current Operating Class from STA: %d" % opclass)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def test_ap_vht160b(dev, apdev):
+ """VHT with 160 MHz channel width (2)"""
+ try:
+ hapd = None
+
+ params = {"ssid": "vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "104",
+ "ht_capab": "[HT40-]",
+ "vht_capab": "[VHT160]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "114",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[1], params, wait_enabled=False)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ if "DFS-CAC-START" not in ev:
+ raise Exception("Unexpected DFS event(2)")
+
+ state = hapd.get_status_field("state")
+ if state != "DFS":
+ if state == "DISABLED" and not os.path.exists("dfs"):
+ # Not all systems have recent enough CRDA version and
+ # wireless-regdb changes to support 160 MHz and DFS. For now,
+ # do not report failures for this test case.
+ raise HwsimSkip("CRDA or wireless-regdb did not support 160 MHz")
+ raise Exception("Unexpected interface state: " + state)
+
+ logger.info("Waiting for CAC to complete")
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed(2)")
+ if "freq=5520" not in ev:
+ raise Exception("Unexpected DFS freq result(2)")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out(2)")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state(2)")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "5520":
+ raise Exception("Unexpected frequency(2)")
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5520")
+ dev[0].wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5520" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=160 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def test_ap_vht160_no_dfs_100_plus(dev, apdev):
+ """VHT with 160 MHz channel width and no DFS (100 plus)"""
+ run_ap_vht160_no_dfs(dev, apdev, "100", "[HT40+]")
+
+def test_ap_vht160_no_dfs(dev, apdev):
+ """VHT with 160 MHz channel width and no DFS (104 minus)"""
+ run_ap_vht160_no_dfs(dev, apdev, "104", "[HT40-]")
+
+def test_ap_vht160_no_dfs_108_plus(dev, apdev):
+ """VHT with 160 MHz channel width and no DFS (108 plus)"""
+ run_ap_vht160_no_dfs(dev, apdev, "108", "[HT40+]")
+
+def test_ap_vht160_no_dfs_112_minus(dev, apdev):
+ """VHT with 160 MHz channel width and no DFS (112 minus)"""
+ run_ap_vht160_no_dfs(dev, apdev, "112", "[HT40-]")
+
+def test_ap_vht160_no_dfs_116_plus(dev, apdev):
+ """VHT with 160 MHz channel width and no DFS (116 plus)"""
+ run_ap_vht160_no_dfs(dev, apdev, "116", "[HT40+]")
+
+def test_ap_vht160_no_dfs_120_minus(dev, apdev):
+ """VHT with 160 MHz channel width and no DFS (120 minus)"""
+ run_ap_vht160_no_dfs(dev, apdev, "120", "[HT40-]")
+
+def test_ap_vht160_no_dfs_124_plus(dev, apdev):
+ """VHT with 160 MHz channel width and no DFS (124 plus)"""
+ run_ap_vht160_no_dfs(dev, apdev, "124", "[HT40+]")
+
+def test_ap_vht160_no_dfs_128_minus(dev, apdev):
+ """VHT with 160 MHz channel width and no DFS (128 minus)"""
+ run_ap_vht160_no_dfs(dev, apdev, "128", "[HT40-]")
+
+def run_ap_vht160_no_dfs(dev, apdev, channel, ht_capab):
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "ZA",
+ "hw_mode": "a",
+ "channel": channel,
+ "ht_capab": ht_capab,
+ "vht_capab": "[VHT160]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "114",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=2)
+ if not ev:
+ cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
+ reg = cmd.stdout.readlines()
+ for r in reg:
+ if b"5490" in r and b"DFS" in r:
+ raise HwsimSkip("ZA regulatory rule did not have DFS requirement removed")
+ raise Exception("AP setup timed out")
+
+ freq = str(int(channel) * 5 + 5000)
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq=freq)
+ dev[0].wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=" + freq not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=160 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_vht160_no_ht40(dev, apdev):
+ """VHT with 160 MHz channel width and HT40 disabled"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "ZA",
+ "hw_mode": "a",
+ "channel": "108",
+ "ht_capab": "",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "114",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=2)
+ if not ev:
+ cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
+ reg = cmd.stdout.readlines()
+ for r in reg:
+ if "5490" in r and "DFS" in r:
+ raise HwsimSkip("ZA regulatory rule did not have DFS requirement removed")
+ raise Exception("AP setup timed out")
+ if "AP-ENABLED" in ev:
+ # This was supposed to fail due to sec_channel_offset == 0
+ raise Exception("Unexpected AP-ENABLED")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_vht80plus80(dev, apdev):
+ """VHT with 80+80 MHz channel width"""
+ try:
+ hapd = None
+ hapd2 = None
+ params = {"ssid": "vht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "52",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "[VHT160-80PLUS80]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "3",
+ "vht_oper_centr_freq_seg0_idx": "58",
+ "vht_oper_centr_freq_seg1_idx": "155",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ # This will actually fail since DFS on 80+80 is not yet supported
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+ # ignore result to avoid breaking the test once 80+80 DFS gets enabled
+
+ params = {"ssid": "vht2",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "[VHT160-80PLUS80]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "3",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "vht_oper_centr_freq_seg1_idx": "155"}
+ hapd2 = hostapd.add_ap(apdev[1], params, wait_enabled=False)
+
+ ev = hapd2.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out(2)")
+ if "AP-DISABLED" in ev:
+ # Assume this failed due to missing regulatory update for now
+ raise HwsimSkip("80+80 MHz channel not supported in regulatory information")
+
+ state = hapd2.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state(2)")
+
+ dev[1].connect("vht2", key_mgmt="NONE", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[1], hapd2)
+ sig = dev[1].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80+80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "CENTER_FRQ1=5210" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+ if "CENTER_FRQ2=5775" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(4): " + str(sig))
+
+ sta = hapd2.get_sta(dev[1].own_addr())
+ if 'supp_op_classes' not in sta or len(sta['supp_op_classes']) < 2:
+ raise Exception("No Supported Operating Classes information for STA")
+ opclass = int(sta['supp_op_classes'][0:2], 16)
+ if opclass != 130:
+ raise Exception("Unexpected Current Operating Class from STA: %d" % opclass)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ if hapd2:
+ hapd2.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_ap_vht80plus80_invalid(dev, apdev):
+ """VHT with invalid 80+80 MHz channel"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "3",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "vht_oper_centr_freq_seg1_idx": "0",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ # This fails due to missing(invalid) seg1 configuration
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("AP-DISABLED not reported")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_vht80_csa(dev, apdev):
+ """VHT with 80 MHz channel width and CSA"""
+ csa_supported(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "149",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "155"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5745")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5180 ht vht blocktx center_freq1=5210 sec_channel_offset=1 bandwidth=80")
+ ev = hapd.wait_event(["CTRL-EVENT-STARTED-CHANNEL-SWITCH"], timeout=10)
+ if ev is None:
+ raise Exception("Channel switch start event not seen")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected channel in CS started")
+ ev = hapd.wait_event(["CTRL-EVENT-CHANNEL-SWITCH"], timeout=10)
+ if ev is None:
+ raise Exception("Channel switch completion event not seen")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected channel in CS completed")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ time.sleep(0.5)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5745")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5745" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ time.sleep(0.5)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ # This CSA to same channel will fail in kernel, so use this only for
+ # extra code coverage.
+ hapd.request("CHAN_SWITCH 5 5745")
+ hapd.wait_event(["AP-CSA-FINISHED"], timeout=1)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_ap_vht_csa_vht80p80(dev, apdev):
+ """VHT CSA with VHT80+80 getting enabled"""
+ csa_supported(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "149",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5745")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ #if "OK" not in hapd.request("CHAN_SWITCH 5 5765 sec_channel_offset=-1 center_freq1=5775 center_freq2=5210 bandwidth=80 vht"):
+ if "OK" not in hapd.request("CHAN_SWITCH 5 5180 sec_channel_offset=1 center_freq1=5210 center_freq2=5775 bandwidth=80 vht"):
+ raise Exception("CHAN_SWITCH command failed")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ ev = dev[0].wait_event(["CTRL-EVENT-CHANNEL-SWITCH"], timeout=5)
+ if ev is None:
+ raise Exception("Channel switch event not seen")
+ if "freq=5180" not in ev:
+ raise Exception("Channel mismatch: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected disconnection event from station")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[1].connect("vht", key_mgmt="NONE", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[1], hapd)
+
+ if dev[1].get_status_field("ieee80211ac") != '1':
+ raise Exception("VHT not enabled as part of channel switch")
+ sig = dev[1].request("SIGNAL_POLL").splitlines()
+ logger.info("SIGNAL_POLL(1): " + str(sig))
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Correct FREQUENCY missing from SIGNAL_POLL")
+ if "WIDTH=80+80 MHz" not in sig:
+ raise Exception("Correct WIDTH missing from SIGNAL_POLL")
+ if "CENTER_FRQ1=5210" not in sig:
+ raise Exception("Correct CENTER_FRQ1 missing from SIGNAL_POLL")
+ if "CENTER_FRQ2=5775" not in sig:
+ raise Exception("Correct CENTER_FRQ1 missing from SIGNAL_POLL")
+
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ logger.info("SIGNAL_POLL(0): " + str(sig))
+ finally:
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_ap_vht_csa_vht40(dev, apdev):
+ """VHT CSA with VHT40 getting enabled"""
+ csa_supported(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "149",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5745")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5765 sec_channel_offset=-1 center_freq1=5755 bandwidth=40 vht")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5765" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ ev = dev[0].wait_event(["CTRL-EVENT-CHANNEL-SWITCH"], timeout=5)
+ if ev is None:
+ raise Exception("Channel switch event not seen")
+ if "freq=5765" not in ev:
+ raise Exception("Channel mismatch: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected disconnection event from station")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[1].connect("vht", key_mgmt="NONE", scan_freq="5765")
+ hwsim_utils.test_connectivity(dev[1], hapd)
+
+ if dev[1].get_status_field("ieee80211ac") != '1':
+ raise Exception("VHT not enabled as part of channel switch")
+ finally:
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_ap_vht_csa_vht20(dev, apdev):
+ """VHT CSA with VHT20 getting enabled"""
+ csa_supported(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "149",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5745")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5200 center_freq1=5200 bandwidth=20 vht")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5200" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ time.sleep(0.5)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[1].connect("vht", key_mgmt="NONE", scan_freq="5200")
+ hwsim_utils.test_connectivity(dev[1], hapd)
+
+ if dev[1].get_status_field("ieee80211ac") != '1':
+ raise Exception("VHT not enabled as part of channel switch")
+ finally:
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_ap_vht_csa_vht40_disable(dev, apdev):
+ """VHT CSA with VHT40 getting disabled"""
+ csa_supported(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "149",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_capab": "",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5200 5745")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5200 center_freq1=5210 sec_channel_offset=1 bandwidth=40 ht")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5200" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ ev = dev[0].wait_event(["CTRL-EVENT-CHANNEL-SWITCH"], timeout=5)
+ if ev is None:
+ raise Exception("Channel switch event not seen")
+ if "freq=5200" not in ev:
+ raise Exception("Channel mismatch: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev:
+ # mac80211 does not support CSA to disable VHT, so the channel
+ # switch will be followed by disconnection and attempt to reconnect.
+ # Wait for that here to avoid failing the test case based on how
+ # example the connectivity test would get timed compared to getting
+ # disconnected or reconnected.
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[1].connect("vht", key_mgmt="NONE", scan_freq="5200")
+ hwsim_utils.test_connectivity(dev[1], hapd)
+
+ if dev[1].get_status_field("ieee80211ac") == '1':
+ raise Exception("VHT not disabled as part of channel switch")
+ finally:
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_ap_vht_on_24ghz(dev, apdev):
+ """Subset of VHT features on 2.4 GHz"""
+ hapd = None
+ params = {"ssid": "test-vht-2g",
+ "hw_mode": "g",
+ "channel": "1",
+ "ieee80211n": "1",
+ "vendor_vht": "1",
+ "vht_capab": "[MAX-MPDU-11454]",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 dd1300904c0400bf0c3240820feaff0000eaff0000"):
+ raise Exception("Failed to add vendor element")
+ dev[0].connect("test-vht-2g", scan_freq="2412", key_mgmt="NONE")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sta = hapd.get_sta(dev[0].own_addr())
+ if '[VENDOR_VHT]' not in sta['flags']:
+ raise Exception("No VENDOR_VHT STA flag")
+
+ dev[1].connect("test-vht-2g", scan_freq="2412", key_mgmt="NONE")
+ hapd.wait_sta()
+ sta = hapd.get_sta(dev[1].own_addr())
+ if '[VENDOR_VHT]' in sta['flags']:
+ raise Exception("Unexpected VENDOR_VHT STA flag")
+
+ status = dev[0].get_status()
+ if 'wifi_generation' in status:
+ if status['wifi_generation'] != "4":
+ raise Exception("Unexpected wifi_generation value: " + status['wifi_generation'])
+
+ status = dev[1].get_status()
+ if 'wifi_generation' in status:
+ if status['wifi_generation'] != "4":
+ raise Exception("Unexpected wifi_generation value(2): " + status['wifi_generation'])
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def test_ap_vht_on_24ghz_2(dev, apdev):
+ """Subset of VHT features on 2.4 GHz (2)"""
+ hapd = None
+ params = {"ssid": "test-vht-2g",
+ "hw_mode": "g",
+ "channel": "1",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vendor_vht": "1",
+ "vht_capab": "[MAX-MPDU-11454]",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 bf0cfa048003aaaa0000aaaa0000dd1300904c0400bf0c3240820feaff0000eaff0000"):
+ raise Exception("Failed to add vendor element")
+ dev[0].connect("test-vht-2g", scan_freq="2412", key_mgmt="NONE")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sta = hapd.get_sta(dev[0].own_addr())
+ if '[VHT]' not in sta['flags']:
+ raise Exception("No VHT STA flag")
+
+ dev[1].connect("test-vht-2g", scan_freq="2412", key_mgmt="NONE")
+ hapd.wait_sta()
+ sta = hapd.get_sta(dev[1].own_addr())
+ if '[VENDOR_VHT]' in sta['flags']:
+ raise Exception("Unexpected VENDOR_VHT STA flag")
+ if '[VHT]' in sta['flags']:
+ raise Exception("Unexpected VHT STA flag")
+
+ status = dev[0].get_status()
+ if 'wifi_generation' in status:
+ if status['wifi_generation'] != "4":
+ raise Exception("Unexpected wifi_generation value: " + status['wifi_generation'])
+
+ status = dev[1].get_status()
+ if 'wifi_generation' in status:
+ if status['wifi_generation'] != "4":
+ raise Exception("Unexpected wifi_generation value(2): " + status['wifi_generation'])
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def test_prefer_vht40(dev, apdev):
+ """Preference on VHT40 over HT40"""
+ try:
+ hapd = None
+ hapd2 = None
+
+ params = {"ssid": "test",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ieee80211n": "1",
+ "ht_capab": "[HT40+]"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ params = {"ssid": "test",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "0"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq=5180)
+ dev[0].scan_for_bss(bssid2, freq=5180)
+ dev[0].connect("test", scan_freq="5180", key_mgmt="NONE")
+ if dev[0].get_status_field('bssid') != bssid2:
+ raise Exception("Unexpected BSS selected")
+
+ est = dev[0].get_bss(bssid)['est_throughput']
+ if est != "135000":
+ raise Exception("Unexpected BSS0 est_throughput: " + est)
+
+ est = dev[0].get_bss(bssid2)['est_throughput']
+ if est != "180001":
+ raise Exception("Unexpected BSS1 est_throughput: " + est)
+ finally:
+ dev[0].request("DISCONNECT")
+ disable_hapd(hapd)
+ disable_hapd(hapd2)
+ clear_regdom_dev(dev)
+
+def test_ap_vht80_pwr_constraint(dev, apdev):
+ """VHT with 80 MHz channel width and local power constraint"""
+ hapd = None
+ try:
+ params = {"ssid": "vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211d": "1",
+ "local_pwr_constraint": "3",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5180")
+ dev[0].wait_regdom(country_ie=True)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def test_ap_vht_use_sta_nsts(dev, apdev):
+ """VHT with 80 MHz channel width and use_sta_nsts=1"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "use_sta_nsts": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_ap_vht_tkip(dev, apdev):
+ """VHT and TKIP"""
+ skip_without_tkip(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "wpa": "1",
+ "wpa_key_mgmt": "WPA-PSK",
+ "wpa_pairwise": "TKIP",
+ "wpa_passphrase": "12345678",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("vht", psk="12345678", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=20 MHz (no HT)" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ status = hapd.get_status()
+ logger.info("hostapd STATUS: " + str(status))
+ if status["ieee80211n"] != "0":
+ raise Exception("Unexpected STATUS ieee80211n value")
+ if status["ieee80211ac"] != "0":
+ raise Exception("Unexpected STATUS ieee80211ac value")
+ if status["secondary_channel"] != "0":
+ raise Exception("Unexpected STATUS secondary_channel value")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_ap_vht_40_fallback_to_20(devs, apdevs):
+ """VHT and 40 MHz channel configuration falling back to 20 MHz"""
+ dev = devs[0]
+ ap = apdevs[0]
+ try:
+ hapd = None
+ params = {"ssid": "test-vht40",
+ "country_code": "US",
+ "hw_mode": "a",
+ "basic_rates": "60 120 240",
+ "channel": "161",
+ "ieee80211d": "1",
+ "ieee80211h": "1",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ht_capab": "[HT40+][SHORT-GI-20][SHORT-GI-40][DSSS_CCK-40]",
+ "vht_capab": "[RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-STBC1][MAX-MPDU-11454][MAX-A-MPDU-LEN-EXP7]",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "155"}
+ hapd = hostapd.add_ap(ap, params)
+ dev.connect("test-vht40", scan_freq="5805", key_mgmt="NONE")
+ dev.wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev, hapd)
+ finally:
+ clear_regdom(hapd, devs)
+
+def test_ap_vht80_to_24g_ht(dev, apdev):
+ """VHT with 80 MHz channel width reconfigured to 2.4 GHz HT"""
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_capab": "[MAX-MPDU-11454]",
+ "vht_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ hapd.disable()
+ hapd.set("ieee80211ac", "0")
+ hapd.set("hw_mode", "g")
+ hapd.set("channel", "1")
+ hapd.set("ht_capab", "")
+ hapd.set("vht_capab", "")
+ hapd.enable()
+
+ dev[0].connect("vht", key_mgmt="NONE", scan_freq="2412")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_ap_vht_csa_invalid(dev, apdev):
+ """VHT CSA with invalid parameters"""
+ csa_supported(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "vht",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "149",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tests = ["5 5765 center_freq1=5180",
+ "5 5765 bandwidth=40",
+ "5 5765 bandwidth=40 center_freq2=5180",
+ "5 5765 bandwidth=40 sec_channel_offset=1 center_freq1=5180",
+ "5 5765 bandwidth=40 sec_channel_offset=-1 center_freq1=5180",
+ "5 5765 bandwidth=40 sec_channel_offset=2 center_freq1=5180",
+ "5 5765 bandwidth=80",
+ "5 5765 bandwidth=80 sec_channel_offset=-1",
+ "5 5765 bandwidth=80 center_freq1=5755",
+ "5 5765 bandwidth=80 sec_channel_offset=1 center_freq1=5180",
+ "5 5765 bandwidth=80 sec_channel_offset=-1 center_freq1=5180",
+ "5 5765 bandwidth=80 sec_channel_offset=2 center_freq1=5180",
+ "5 5765 bandwidth=80 sec_channel_offset=-1 center_freq1=5775 center_freq2=5775",
+ "5 5765 bandwidth=160",
+ "5 5765 bandwidth=160 center_freq1=5755",
+ "5 5765 bandwidth=160 center_freq1=5755 center_freq2=5755",
+ "5 5765 bandwidth=160 center_freq1=5755 center_freq2=5755 sec_channel_offset=-1",
+ "5 5765 bandwidth=160 center_freq1=5754 sec_channel_offset=1",
+ "5 5765 bandwidth=160 center_freq1=5755 sec_channel_offset=2",
+ "5 5765 sec_channel_offset=-1"]
+ for t in tests:
+ if "FAIL" not in hapd.request("CHAN_SWITCH " + t):
+ raise Exception("Invalid CHAN_SWITCH accepted: " + t)
+
+ hapd.request("CHAN_SWITCH 5 5765 bandwidth=160 center_freq1=5755 sec_channel_offset=1")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on AP-CSA-FINISHED")
+
+ hapd.request("CHAN_SWITCH 5 5765 bandwidth=160 center_freq1=5775 sec_channel_offset=-1")
+ time.sleep(1)
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
diff --git a/contrib/wpa/tests/hwsim/test_ap_vlan.py b/contrib/wpa/tests/hwsim/test_ap_vlan.py
new file mode 100644
index 000000000000..29f8f53225ef
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_vlan.py
@@ -0,0 +1,807 @@
+#!/usr/bin/python
+#
+# Test cases for AP VLAN
+# Copyright (c) 2013-2016, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import subprocess
+import logging
+logger = logging.getLogger(__name__)
+
+try:
+ import netifaces
+ netifaces_imported = True
+except ImportError:
+ netifaces_imported = False
+
+import hwsim_utils
+import hostapd
+from utils import iface_is_in_bridge, HwsimSkip, alloc_fail
+import os
+from tshark import run_tshark
+
+def test_ap_vlan_open(dev, apdev):
+ """AP VLAN with open network"""
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ params = {"ssid": "test-vlan-open",
+ "dynamic_vlan": "1",
+ "accept_mac_file": filename}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ dev[2].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+ hwsim_utils.test_connectivity(dev[2], hapd)
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_vlan_file_open(dev, apdev):
+ """AP VLAN with open network and vlan_file mapping"""
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ params = {"ssid": "test-vlan-open",
+ "dynamic_vlan": "1",
+ "vlan_file": "hostapd.vlan",
+ "accept_mac_file": filename}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ dev[2].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+ hwsim_utils.test_connectivity(dev[2], hapd)
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_vlan_file_open2(dev, apdev):
+ """AP VLAN with open network and vlan_file mapping (2)"""
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept2')
+ hostapd.send_file(apdev[0], filename, filename)
+ params = {"ssid": "test-vlan-open",
+ "dynamic_vlan": "1",
+ "vlan_file": "hostapd.vlan2",
+ "accept_mac_file": filename}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ dev[2].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+ hwsim_utils.test_connectivity_iface(dev[2], hapd, "hwsimbr3")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_vlan_file_parsing(dev, apdev, params):
+ """hostapd vlan_file/mac_file parsing"""
+ tmp = os.path.join(params['logdir'], 'ap_vlan_file_parsing.tmp')
+ params = {"ssid": "test-vlan-open", "dynamic_vlan": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tests = ["#\n\n0\t11\n",
+ "1 netdev br\n1",
+ "* ",
+ "1 netdev12345678901234567890"]
+ for t in tests:
+ with open(tmp, "w") as f:
+ f.write(t)
+ if "FAIL" not in hapd.request("SET vlan_file " + tmp):
+ raise Exception("Invalid vlan_file accepted")
+
+ with open(tmp, "w") as f:
+ f.write("1\tvlan\n")
+ with alloc_fail(hapd, 1, "=hostapd_config_read_vlan_file"):
+ if "FAIL" not in hapd.request("SET vlan_file " + tmp):
+ raise Exception("vlan_file accepted during OOM")
+
+ tests = ["#\n\n0\tvlan\n",
+ "4095\tvlan\n",
+ "vlan\n",
+ "1\t1234567890abcdef1234567890\n",
+ "1\n"]
+ for t in tests:
+ with open(tmp, "w") as f:
+ f.write(t)
+ if "FAIL" not in hapd.request("SET accept_mac_file " + tmp):
+ raise Exception("Invalid accept_mac_file accepted")
+
+ with open(tmp, "w") as f:
+ f.write("00:11:22:33:44:55\n")
+ with alloc_fail(hapd, 1, "hostapd_config_read_maclist"):
+ if "FAIL" not in hapd.request("SET accept_mac_file " + tmp):
+ raise Exception("accept_mac_file accepted during OOM")
+
+def test_ap_vlan_wpa2(dev, apdev):
+ """AP VLAN with WPA2-PSK"""
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ params = hostapd.wpa2_params(ssid="test-vlan",
+ passphrase="12345678")
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan", psk="12345678", scan_freq="2412")
+ dev[1].connect("test-vlan", psk="12345678", scan_freq="2412")
+ dev[2].connect("test-vlan", psk="12345678", scan_freq="2412")
+ hapd.wait_sta()
+ hapd.wait_sta()
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+ hwsim_utils.test_connectivity(dev[2], hapd)
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_vlan_wpa2_radius(dev, apdev):
+ """AP VLAN with WPA2-Enterprise and RADIUS attributes"""
+ params = hostapd.wpa2_eap_params(ssid="test-vlan")
+ params['dynamic_vlan'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="vlan1",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ dev[1].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="vlan2",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ dev[2].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ hapd.wait_sta()
+ hapd.wait_sta()
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+ hwsim_utils.test_connectivity(dev[2], hapd)
+
+def test_ap_vlan_wpa2_radius_2(dev, apdev):
+ """AP VLAN with WPA2-Enterprise and RADIUS EGRESS_VLANID attributes"""
+ params = hostapd.wpa2_eap_params(ssid="test-vlan")
+ params['dynamic_vlan'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="vlan1b",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+
+def test_ap_vlan_wpa2_radius_local(dev, apdev):
+ """AP VLAN with WPA2-Enterprise and local file setting VLAN IDs"""
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ params = hostapd.wpa2_eap_params(ssid="test-vlan")
+ params['dynamic_vlan'] = "0"
+ params['vlan_file'] = "hostapd.vlan"
+ params['vlan_bridge'] = "test_br_vlan"
+ params['accept_mac_file'] = filename
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ dev[1].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ dev[2].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ hapd.wait_sta()
+ hapd.wait_sta()
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "test_br_vlan1")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "test_br_vlan2")
+ hwsim_utils.test_connectivity(dev[2], hapd)
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_vlan_wpa2_radius_id_change(dev, apdev):
+ """AP VLAN with WPA2-Enterprise and RADIUS attributes changing VLANID"""
+ generic_ap_vlan_wpa2_radius_id_change(dev, apdev, False)
+
+def test_ap_vlan_tagged_wpa2_radius_id_change(dev, apdev):
+ """AP tagged VLAN with WPA2-Enterprise and RADIUS attributes changing VLANID"""
+ ifname1 = 'wlan0.1'
+ ifname2 = 'wlan0.2'
+ try:
+ # Create tagged interface for wpa_supplicant
+ subprocess.call(['ip', 'link', 'add', 'link', dev[0].ifname,
+ 'name', ifname1, 'type', 'vlan', 'id', '1'])
+ subprocess.call(['ifconfig', ifname1, 'up'])
+
+ subprocess.call(['ip', 'link', 'add', 'link', dev[0].ifname,
+ 'name', ifname2, 'type', 'vlan', 'id', '2'])
+ subprocess.call(['ifconfig', ifname2, 'up'])
+
+ generic_ap_vlan_wpa2_radius_id_change(dev, apdev, True)
+ finally:
+ subprocess.call(['ifconfig', ifname1, 'down'])
+ subprocess.call(['ifconfig', ifname2, 'down'])
+ subprocess.call(['ip', 'link', 'del', ifname1])
+ subprocess.call(['ip', 'link', 'del', ifname2])
+
+def generic_ap_vlan_wpa2_radius_id_change(dev, apdev, tagged):
+ as_params = {"ssid": "as",
+ "beacon_int": "2000",
+ "radius_server_clients": "auth_serv/radius_clients.conf",
+ "radius_server_auth_port": '18128',
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key"}
+ authserv = hostapd.add_ap(apdev[1], as_params)
+
+ params = hostapd.wpa2_eap_params(ssid="test-vlan")
+ params['dynamic_vlan'] = "1"
+ params['auth_server_port'] = "18128"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ identity = "vlan1tagged" if tagged else "vlan1"
+
+ dev[0].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity=identity,
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ hapd.wait_sta()
+ if tagged:
+ hwsim_utils.run_connectivity_test(dev[0], hapd, 0, ifname1="wlan0.1",
+ ifname2="brvlan1")
+ else:
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+
+ logger.info("VLAN-ID -> 2")
+
+ authserv.disable()
+ authserv.set('eap_user_file', "auth_serv/eap_user_vlan.conf")
+ authserv.enable()
+
+ dev[0].dump_monitor()
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP reauthentication timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=5)
+ if ev is None:
+ raise Exception("4-way handshake after reauthentication timed out")
+ state = dev[0].get_status_field('wpa_state')
+ if state != "COMPLETED":
+ raise Exception("Unexpected state after reauth: " + state)
+ sta = hapd.get_sta(dev[0].own_addr())
+ if 'vlan_id' not in sta:
+ raise Exception("No VLAN ID in STA info")
+ if (not tagged) and (sta['vlan_id'] != '2'):
+ raise Exception("Unexpected VLAN ID: " + sta['vlan_id'])
+ if tagged:
+ hwsim_utils.run_connectivity_test(dev[0], hapd, 0, ifname1="wlan0.2",
+ ifname2="brvlan2")
+ else:
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan2")
+
+ logger.info("VLAN-ID -> 1")
+ time.sleep(1)
+
+ authserv.disable()
+ authserv.set('eap_user_file', "auth_serv/eap_user.conf")
+ authserv.enable()
+
+ dev[0].dump_monitor()
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP reauthentication timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=5)
+ if ev is None:
+ raise Exception("4-way handshake after reauthentication timed out")
+ state = dev[0].get_status_field('wpa_state')
+ if state != "COMPLETED":
+ raise Exception("Unexpected state after reauth: " + state)
+ sta = hapd.get_sta(dev[0].own_addr())
+ if 'vlan_id' not in sta:
+ raise Exception("No VLAN ID in STA info")
+ if (not tagged) and (sta['vlan_id'] != '1'):
+ raise Exception("Unexpected VLAN ID: " + sta['vlan_id'])
+ time.sleep(0.2)
+ try:
+ if tagged:
+ hwsim_utils.run_connectivity_test(dev[0], hapd, 0,
+ ifname1="wlan0.1",
+ ifname2="brvlan1")
+ else:
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ except Exception as e:
+ # It is possible for new bridge setup to not be ready immediately, so
+ # try again to avoid reporting issues related to that.
+ logger.info("First VLAN-ID 1 data test failed - try again")
+ if tagged:
+ hwsim_utils.run_connectivity_test(dev[0], hapd, 0,
+ ifname1="wlan0.1",
+ ifname2="brvlan1")
+ else:
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+
+def test_ap_vlan_wpa2_radius_required(dev, apdev):
+ """AP VLAN with WPA2-Enterprise and RADIUS attributes required"""
+ params = hostapd.wpa2_eap_params(ssid="test-vlan")
+ params['dynamic_vlan'] = "2"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="vlan1",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ dev[2].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Timeout on connection attempt")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected success without tunnel parameters")
+
+def test_ap_vlan_tagged(dev, apdev):
+ """AP VLAN with tagged interface"""
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ params = {"ssid": "test-vlan-open",
+ "dynamic_vlan": "1",
+ "vlan_tagged_interface": "lo",
+ "accept_mac_file": filename}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ dev[2].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brlo.1")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "brlo.2")
+ hwsim_utils.test_connectivity(dev[2], hapd)
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def ap_vlan_iface_cleanup_multibss_cleanup():
+ subprocess.call(['ifconfig', 'dummy0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ ifnames = ['wlan3.1', 'wlan3.2', 'wlan3-2.1', 'wlan3-2.2', 'dummy0.2',
+ 'dummy0.1', 'dummy0', 'brvlan1', 'brvlan2']
+ for ifname in ifnames:
+ subprocess.call(['ip', 'link', 'del', ifname],
+ stderr=open('/dev/null', 'w'))
+
+def ap_vlan_iface_test_and_prepare_environ():
+ ifaces = netifaces.interfaces()
+ if "dummy0" in ifaces:
+ raise Exception("dummy0 already exists before")
+ ifaces = netifaces.interfaces()
+ if "dummy0.1" in ifaces:
+ raise Exception("dummy0.1 already exists before")
+
+ subprocess.call(['ip', 'link', 'add', 'dummy0', 'type', 'dummy'])
+ subprocess.call(['ifconfig', 'dummy0', 'up'])
+
+ ifaces = netifaces.interfaces()
+ if "dummy0" not in ifaces:
+ raise HwsimSkip("failed to add dummy0 - missing kernel config DUMMY ?")
+
+ subprocess.call(['ip', 'link', 'add', 'link', 'dummy0', 'name', 'dummy0.1',
+ 'type', 'vlan', 'id', '1'])
+
+ ifaces = netifaces.interfaces()
+ if "dummy0.1" not in ifaces:
+ raise HwsimSkip("failed to add dummy0.1 - missing kernel config VLAN_8021Q ?")
+
+ subprocess.call(['ip', 'link', 'del', 'dummy0.1'])
+
+ ifaces = netifaces.interfaces()
+ if "dummy0.1" in ifaces:
+ raise Exception("dummy0.1 was not removed before testing")
+
+def test_ap_vlan_iface_cleanup_multibss(dev, apdev):
+ """AP VLAN operation in multi-BSS multi-VLAN case"""
+ ap_vlan_iface_cleanup_multibss(dev, apdev, 'multi-bss-iface.conf')
+
+def ap_vlan_iface_cleanup_multibss(dev, apdev, cfgfile):
+ # AP VLAN with WPA2-Enterprise and RADIUS attributes changing VLANID
+ # check that multiple bss do not interfere with each other with respect
+ # to deletion of bridge and tagged interface.
+
+ if not netifaces_imported:
+ raise HwsimSkip("python module netifaces not available")
+
+ try:
+ ap_vlan_iface_cleanup_multibss_cleanup()
+ ap_vlan_iface_test_and_prepare_environ()
+
+ as_params = {"ssid": "as",
+ "beacon_int": "2000",
+ "radius_server_clients": "auth_serv/radius_clients.conf",
+ "radius_server_auth_port": '18128',
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key",
+ "vlan_naming": "1"}
+ authserv = hostapd.add_ap(apdev[1], as_params)
+
+ # start the actual test
+ hapd = hostapd.add_iface(apdev[0], cfgfile)
+ hapd1 = hostapd.Hostapd("wlan3-2", 1)
+ hapd1.enable()
+
+ ifaces = netifaces.interfaces()
+ if "brvlan1" in ifaces:
+ raise Exception("bridge brvlan1 already exists before")
+ if "brvlan2" in ifaces:
+ raise Exception("bridge brvlan2 already exists before")
+
+ dev[0].connect("bss-1", key_mgmt="WPA-EAP", eap="PAX",
+ identity="vlan1",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ hapd.wait_sta()
+
+ ifaces = netifaces.interfaces()
+ if "brvlan1" not in ifaces:
+ raise Exception("bridge brvlan1 was not created")
+
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ if not iface_is_in_bridge("brvlan1", "dummy0.1"):
+ raise Exception("dummy0.1 not in brvlan1")
+
+ dev[1].connect("bss-2", key_mgmt="WPA-EAP", eap="PAX",
+ identity="vlan1",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+ hapd1.wait_sta()
+ hwsim_utils.test_connectivity_iface(dev[1], hapd1, "brvlan1")
+ if not iface_is_in_bridge("brvlan1", "dummy0.1"):
+ raise Exception("dummy0.1 not in brvlan1")
+
+ authserv.disable()
+ authserv.set('eap_user_file', "auth_serv/eap_user_vlan.conf")
+ authserv.enable()
+
+ logger.info("wlan0 -> VLAN 2")
+
+ dev[0].dump_monitor()
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP reauthentication timed out")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=5)
+ if ev is None:
+ raise Exception("4-way handshake after reauthentication timed out")
+ state = dev[0].get_status_field('wpa_state')
+ if state != "COMPLETED":
+ raise Exception("Unexpected state after reauth: " + state)
+
+ ifaces = netifaces.interfaces()
+ if "brvlan1" not in ifaces:
+ raise Exception("bridge brvlan1 has been removed too early")
+
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan2",
+ max_tries=5)
+
+ if not iface_is_in_bridge("brvlan2", "dummy0.2"):
+ raise Exception("dummy0.2 not in brvlan2")
+
+ logger.info("test wlan1 == VLAN 1")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd1, "brvlan1")
+ if not iface_is_in_bridge("brvlan1", "dummy0.1"):
+ raise Exception("dummy0.1 not in brvlan1")
+
+ logger.info("wlan1 -> VLAN 2")
+
+ dev[1].dump_monitor()
+ dev[1].request("REAUTHENTICATE")
+ ev = dev[1].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP reauthentication timed out")
+ ev = dev[1].wait_event(["WPA: Key negotiation completed"], timeout=5)
+ if ev is None:
+ raise Exception("4-way handshake after reauthentication timed out")
+ state = dev[1].get_status_field('wpa_state')
+ if state != "COMPLETED":
+ raise Exception("Unexpected state after reauth: " + state)
+
+ # it can take some time for data connectivity to be updated
+ hwsim_utils.test_connectivity_iface(dev[1], hapd1, "brvlan2",
+ max_tries=5)
+ logger.info("test wlan0 == VLAN 2")
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan2")
+
+ if not iface_is_in_bridge("brvlan2", "dummy0.2"):
+ raise Exception("dummy0.2 not in brvlan2")
+
+ ifaces = netifaces.interfaces()
+ if "brvlan1" in ifaces:
+ raise Exception("bridge brvlan1 has not been cleaned up")
+
+ # disconnect dev0 first to test a corner case
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+
+ # station removal needs some time
+ for i in range(15):
+ time.sleep(1)
+ ifaces = netifaces.interfaces()
+ if "brvlan2" not in ifaces:
+ break
+
+ ifaces = netifaces.interfaces()
+ if "brvlan2" in ifaces:
+ raise Exception("bridge brvlan2 has not been cleaned up")
+
+ hapd.request("DISABLE")
+ finally:
+ ap_vlan_iface_cleanup_multibss_cleanup()
+
+def test_ap_vlan_iface_cleanup_multibss_per_sta_vif(dev, apdev):
+ """AP VLAN operation in multi-BSS multi-VLAN case with per-sta-vif set"""
+
+ # AP VLAN with WPA2-Enterprise and RADIUS attributes changing VLANID
+ # check that multiple bss do not interfere with each other with respect
+ # to deletion of bridge and tagged interface. per_sta_vif is enabled.
+ ap_vlan_iface_cleanup_multibss(dev, apdev,
+ 'multi-bss-iface-per_sta_vif.conf')
+
+def test_ap_vlan_without_station(dev, apdev, p):
+ """AP VLAN with WPA2-PSK and no station"""
+ try:
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ subprocess.call(['brctl', 'addbr', 'brvlan1'])
+ subprocess.call(['brctl', 'setfd', 'brvlan1', '0'])
+ subprocess.call(['ifconfig', 'brvlan1', 'up'])
+ # use a passphrase wlantest does not know, so it cannot
+ # inject decrypted frames into pcap
+ params = hostapd.wpa2_params(ssid="test-vlan",
+ passphrase="12345678x")
+ params['dynamic_vlan'] = "1"
+ params['vlan_file'] = 'hostapd.wlan3.vlan'
+ params['accept_mac_file'] = filename
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # inject some traffic
+ sa = hapd.own_addr()
+ da = "ff:ff:ff:ff:ff:00"
+ hapd.request('DATA_TEST_CONFIG 1 ifname=brvlan1')
+ hapd.request('DATA_TEST_TX {} {} 0'.format(da, sa))
+ hapd.request('DATA_TEST_CONFIG 0')
+ time.sleep(.1)
+
+ dev[0].connect("test-vlan", psk="12345678x", scan_freq="2412")
+
+ # inject some traffic
+ sa = hapd.own_addr()
+ da = "ff:ff:ff:ff:ff:01"
+ hapd.request('DATA_TEST_CONFIG 1 ifname=brvlan1')
+ hapd.request('DATA_TEST_TX {} {} 0'.format(da, sa))
+ hapd.request('DATA_TEST_CONFIG 0')
+
+ # let the AP send couple of Beacon frames
+ time.sleep(1)
+ out = run_tshark(os.path.join(p['logdir'], "hwsim0.pcapng"),
+ "wlan.da == ff:ff:ff:ff:ff:00",
+ ["wlan.fc.protected"])
+
+ if out is not None:
+ lines = out.splitlines()
+ if len(lines) < 1:
+ # Newer kernel versions filter out frames when there are no
+ # authorized stations on an AP/AP_VLAN interface, so do not
+ # trigger an error here.
+ logger.info("first frame not observed")
+ state = 1
+ for l in lines:
+ is_protected = int(l, 16)
+ if is_protected != 1:
+ state = 0
+ if state != 1:
+ raise Exception("Broadcast packets were not encrypted when no station was connected")
+ else:
+ raise Exception("first frame not observed")
+
+ out = run_tshark(os.path.join(p['logdir'], "hwsim0.pcapng"),
+ "wlan.da == ff:ff:ff:ff:ff:01",
+ ["wlan.fc.protected"])
+
+ if out is not None:
+ lines = out.splitlines()
+ if len(lines) < 1:
+ raise Exception("second frame not observed")
+ state = 1
+ for l in lines:
+ is_protected = int(l, 16)
+ if is_protected != 1:
+ state = 0
+ if state != 1:
+ raise Exception("Broadcast packets were not encrypted when station was connected")
+ else:
+ raise Exception("second frame not observed")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', 'brvlan1', 'down'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'wlan3.1', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delif', 'brvlan1', 'wlan3.1'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'brvlan1'])
+
+@remote_compatible
+def test_ap_open_per_sta_vif(dev, apdev):
+ """AP VLAN with open network"""
+ params = {"ssid": "test-vlan-open",
+ "per_sta_vif": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity_iface(dev[0], hapd,
+ apdev[0]['ifname'] + ".4096")
+
+@remote_compatible
+def test_ap_vlan_open_per_sta_vif(dev, apdev):
+ """AP VLAN (dynamic) with open network"""
+ params = {"ssid": "test-vlan-open",
+ "per_sta_vif": "1",
+ "dynamic_vlan": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity_iface(dev[0], hapd,
+ apdev[0]['ifname'] + ".4096")
+
+def test_ap_vlan_wpa2_radius_tagged(dev, apdev):
+ """AP VLAN with WPA2-Enterprise and RADIUS EGRESS_VLANID attributes"""
+ ifname = 'wlan0.1'
+ try:
+ params = hostapd.wpa2_eap_params(ssid="test-vlan")
+ params['dynamic_vlan'] = "1"
+ params["vlan_naming"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="vlan1tagged",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+ # Create tagged interface for wpa_supplicant
+ subprocess.call(['ip', 'link', 'add', 'link', dev[0].ifname,
+ 'name', ifname, 'type', 'vlan', 'id', '1'])
+ subprocess.call(['ifconfig', ifname, 'up'])
+
+ hwsim_utils.run_connectivity_test(dev[0], hapd, 0, ifname1=ifname,
+ ifname2="brvlan1")
+ finally:
+ subprocess.call(['ifconfig', ifname, 'down'])
+ subprocess.call(['ip', 'link', 'del', ifname])
+
+def test_ap_vlan_wpa2_radius_mixed(dev, apdev):
+ """AP VLAN with WPA2-Enterprise and tagged+untagged VLANs"""
+ ifname = 'wlan0.1'
+ try:
+ params = hostapd.wpa2_eap_params(ssid="test-vlan")
+ params['dynamic_vlan'] = "1"
+ params["vlan_naming"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-vlan", key_mgmt="WPA-EAP", eap="PAX",
+ identity="vlan12mixed",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+ # Add tagged VLAN interface to wpa_supplicant interface for testing
+ subprocess.call(['ip', 'link', 'add', 'link', dev[0].ifname,
+ 'name', ifname, 'type', 'vlan', 'id', '1'])
+ subprocess.call(['ifconfig', ifname, 'up'])
+
+ logger.info("Test connectivity in untagged VLAN 2")
+ hwsim_utils.run_connectivity_test(dev[0], hapd, 0,
+ ifname1=dev[0].ifname,
+ ifname2="brvlan2")
+ logger.info("Test connectivity in tagged VLAN 1")
+ hwsim_utils.run_connectivity_test(dev[0], hapd, 0, ifname1=ifname,
+ ifname2="brvlan1")
+ finally:
+ subprocess.call(['ifconfig', ifname, 'down'])
+ subprocess.call(['ip', 'link', 'del', ifname])
+
+def test_ap_vlan_reconnect(dev, apdev):
+ """AP VLAN with WPA2-PSK connect, disconnect, connect"""
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ hostapd.send_file(apdev[0], filename, filename)
+ params = hostapd.wpa2_params(ssid="test-vlan",
+ passphrase="12345678")
+ params['dynamic_vlan'] = "1"
+ params['accept_mac_file'] = filename
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ logger.info("connect sta")
+ dev[0].connect("test-vlan", psk="12345678", scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ logger.info("disconnect sta")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=10)
+ time.sleep(1)
+ logger.info("reconnect sta")
+ dev[0].connect("test-vlan", psk="12345678", scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_ap_vlan_psk(dev, apdev, params):
+ """AP VLAN based on PSK/passphrase"""
+ psk_file = os.path.join(params['logdir'], 'ap_vlan_psk.wpa_psk')
+ with open(psk_file, 'w') as f:
+ f.write('vlanid=1 00:00:00:00:00:00 passphrase-for-vlan-1\n')
+ f.write('vlanid=2 00:00:00:00:00:00 passphrase-for-vlan-2\n')
+ f.write('vlanid=3 00:00:00:00:00:00 passphrase-for-vlan-3\n')
+
+ ssid = 'test-vlan-rsn'
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['dynamic_vlan'] = "1"
+ params['wpa_psk_file'] = psk_file
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(ssid, psk="passphrase-for-vlan-1", scan_freq="2412")
+ dev[1].connect(ssid, psk="passphrase-for-vlan-2", scan_freq="2412")
+ dev[2].connect(ssid, psk="passphrase-for-vlan-3", scan_freq="2412")
+ hapd.wait_sta()
+ hapd.wait_sta()
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+ hwsim_utils.test_connectivity_iface(dev[2], hapd, "brvlan3")
+
+def test_ap_vlan_sae(dev, apdev, params):
+ """AP VLAN based on SAE Password Identifier"""
+ for i in range(3):
+ if "SAE" not in dev[i].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ params = hostapd.wpa2_params(ssid="test-sae-vlan")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = ['pw1|vlanid=1|id=id1',
+ 'pw2|mac=ff:ff:ff:ff:ff:ff|vlanid=2|id=id2',
+ 'pw3|vlanid=3|id=id3']
+ params['dynamic_vlan'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ for i in range(3):
+ dev[i].request("SET sae_groups ")
+ dev[i].connect("test-sae-vlan", sae_password="pw%d" % (i + 1),
+ sae_password_id="id%d" % (i + 1),
+ key_mgmt="SAE", scan_freq="2412")
+ hapd.wait_sta()
+
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, "brvlan1")
+ hwsim_utils.test_connectivity_iface(dev[1], hapd, "brvlan2")
+ hwsim_utils.test_connectivity_iface(dev[2], hapd, "brvlan3")
diff --git a/contrib/wpa/tests/hwsim/test_ap_wps.py b/contrib/wpa/tests/hwsim/test_ap_wps.py
new file mode 100644
index 000000000000..a07ed60b8218
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ap_wps.py
@@ -0,0 +1,10568 @@
+# WPS tests
+# Copyright (c) 2013-2017, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+from tshark import run_tshark
+import base64
+import binascii
+from Crypto.Cipher import AES
+import hashlib
+import hmac
+import os
+import time
+import sys
+import stat
+import subprocess
+import logging
+logger = logging.getLogger()
+import re
+import socket
+import struct
+try:
+ from http.client import HTTPConnection
+ from urllib.request import urlopen
+ from urllib.parse import urlparse, urljoin
+ from urllib.error import HTTPError
+ from io import StringIO
+ from socketserver import StreamRequestHandler, TCPServer
+except ImportError:
+ from httplib import HTTPConnection
+ from urllib import urlopen
+ from urlparse import urlparse, urljoin
+ from urllib2 import build_opener, ProxyHandler, HTTPError
+ from StringIO import StringIO
+ from SocketServer import StreamRequestHandler, TCPServer
+import urllib
+import xml.etree.ElementTree as ET
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+from test_ap_eap import int_eap_server_params
+
+def wps_start_ap(apdev, ssid="test-wps-conf", extra_cred=None):
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
+ if extra_cred:
+ params['extra_cred'] = extra_cred
+ return hostapd.add_ap(apdev, params)
+
+@remote_compatible
+def test_ap_wps_init(dev, apdev):
+ """Initial AP configuration with first WPS Enrollee"""
+ skip_without_tkip(dev[0])
+ ssid = "test-wps"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1"})
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", "home")
+ dev[0].set_network_quoted(id, "psk", "12345678")
+ dev[0].request("ENABLE_NETWORK %s no-connect" % id)
+
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", "home2")
+ dev[0].set_network(id, "bssid", "00:11:22:33:44:55")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].request("ENABLE_NETWORK %s no-connect" % id)
+
+ dev[0].request("WPS_PBC")
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+ if status['ssid'] != ssid:
+ raise Exception("Unexpected SSID")
+ if status['pairwise_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+ status = hapd.request("WPS_GET_STATUS")
+ if "PBC Status: Disabled" not in status:
+ raise Exception("PBC status not shown correctly")
+ if "Last WPS result: Success" not in status:
+ raise Exception("Last WPS result not shown correctly")
+ if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
+ raise Exception("Peer address not shown correctly")
+ conf = hapd.request("GET_CONFIG")
+ if "wps_state=configured" not in conf:
+ raise Exception("AP not in WPS configured state")
+ if "wpa=2" in conf:
+ if "rsn_pairwise_cipher=CCMP" not in conf:
+ raise Exception("Unexpected rsn_pairwise_cipher")
+ if "group_cipher=CCMP" not in conf:
+ raise Exception("Unexpected group_cipher")
+ else:
+ if "wpa=3" not in conf:
+ raise Exception("AP not in WPA+WPA2 configuration")
+ if "rsn_pairwise_cipher=CCMP TKIP" not in conf:
+ raise Exception("Unexpected rsn_pairwise_cipher")
+ if "wpa_pairwise_cipher=CCMP TKIP" not in conf:
+ raise Exception("Unexpected wpa_pairwise_cipher")
+ if "group_cipher=TKIP" not in conf:
+ raise Exception("Unexpected group_cipher")
+
+ if len(dev[0].list_networks()) != 3:
+ raise Exception("Unexpected number of network blocks")
+
+def test_ap_wps_init_2ap_pbc(dev, apdev):
+ """Initial two-radio AP configuration with first WPS PBC Enrollee"""
+ skip_without_tkip(dev[0])
+ ssid = "test-wps"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hostapd.add_ap(apdev[1], params)
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if "[WPS-PBC]" not in bss['flags']:
+ raise Exception("WPS-PBC flag missing from AP1")
+ bss = dev[0].get_bss(apdev[1]['bssid'])
+ if "[WPS-PBC]" not in bss['flags']:
+ raise Exception("WPS-PBC flag missing from AP2")
+ dev[0].dump_monitor()
+ dev[0].request("SET wps_cred_processing 2")
+ dev[0].request("WPS_PBC")
+ ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=30)
+ dev[0].request("SET wps_cred_processing 0")
+ if ev is None:
+ raise Exception("WPS cred event not seen")
+ if "100e" not in ev:
+ raise Exception("WPS attributes not included in the cred event")
+ dev[0].wait_connected(timeout=30)
+
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[1].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ bss = dev[1].get_bss(apdev[0]['bssid'])
+ if "[WPS-PBC]" in bss['flags']:
+ raise Exception("WPS-PBC flag not cleared from AP1")
+ bss = dev[1].get_bss(apdev[1]['bssid'])
+ if "[WPS-PBC]" in bss['flags']:
+ raise Exception("WPS-PBC flag not cleared from AP2")
+
+def test_ap_wps_init_2ap_pin(dev, apdev):
+ """Initial two-radio AP configuration with first WPS PIN Enrollee"""
+ skip_without_tkip(dev[0])
+ ssid = "test-wps"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hostapd.add_ap(apdev[1], params)
+ logger.info("WPS provisioning step")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if "[WPS-AUTH]" not in bss['flags']:
+ raise Exception("WPS-AUTH flag missing from AP1")
+ bss = dev[0].get_bss(apdev[1]['bssid'])
+ if "[WPS-AUTH]" not in bss['flags']:
+ raise Exception("WPS-AUTH flag missing from AP2")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PIN any " + pin)
+ dev[0].wait_connected(timeout=30)
+
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[1].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ bss = dev[1].get_bss(apdev[0]['bssid'])
+ if "[WPS-AUTH]" in bss['flags']:
+ raise Exception("WPS-AUTH flag not cleared from AP1")
+ bss = dev[1].get_bss(apdev[1]['bssid'])
+ if "[WPS-AUTH]" in bss['flags']:
+ raise Exception("WPS-AUTH flag not cleared from AP2")
+
+@remote_compatible
+def test_ap_wps_init_through_wps_config(dev, apdev):
+ """Initial AP configuration using wps_config command"""
+ ssid = "test-wps-init-config"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1"})
+ if "FAIL" in hapd.request("WPS_CONFIG " + binascii.hexlify(ssid.encode()).decode() + " WPA2PSK CCMP " + binascii.hexlify(b"12345678").decode()):
+ raise Exception("WPS_CONFIG command failed")
+ ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on WPS-NEW-AP-SETTINGS events")
+ # It takes some time for the AP to update Beacon and Probe Response frames,
+ # so wait here before requesting the scan to be started to avoid adding
+ # extra five second wait to the test due to fetching obsolete scan results.
+ hapd.ping()
+ time.sleep(0.2)
+ dev[0].connect(ssid, psk="12345678", scan_freq="2412", proto="WPA2",
+ pairwise="CCMP", group="CCMP")
+
+ if "FAIL" not in hapd.request("WPS_CONFIG foo"):
+ raise Exception("Invalid WPS_CONFIG accepted")
+
+@remote_compatible
+def test_ap_wps_init_through_wps_config_2(dev, apdev):
+ """AP configuration using wps_config and wps_cred_processing=2"""
+ ssid = "test-wps-init-config"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1",
+ "wps_cred_processing": "2"})
+ if "FAIL" in hapd.request("WPS_CONFIG " + binascii.hexlify(ssid.encode()).decode() + " WPA2PSK CCMP " + binascii.hexlify(b"12345678").decode()):
+ raise Exception("WPS_CONFIG command failed")
+ ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on WPS-NEW-AP-SETTINGS events")
+ if "100e" not in ev:
+ raise Exception("WPS-NEW-AP-SETTINGS did not include Credential")
+
+@remote_compatible
+def test_ap_wps_invalid_wps_config_passphrase(dev, apdev):
+ """AP configuration using wps_config command with invalid passphrase"""
+ ssid = "test-wps-init-config"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1"})
+ if "FAIL" not in hapd.request("WPS_CONFIG " + binascii.hexlify(ssid.encode()).decode() + " WPA2PSK CCMP " + binascii.hexlify(b"1234567").decode()):
+ raise Exception("Invalid WPS_CONFIG command accepted")
+
+def test_ap_wps_conf(dev, apdev):
+ """WPS PBC provisioning with configured AP"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].set("device_name", "Device A")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED':
+ raise Exception("Not fully connected")
+ if status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Unexpected BSSID")
+ if status['ssid'] != ssid:
+ raise Exception("Unexpected SSID")
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+ sta = hapd.get_sta(dev[0].p2p_interface_addr())
+ if 'wpsDeviceName' not in sta or sta['wpsDeviceName'] != "Device A":
+ raise Exception("Device name not available in STA command")
+
+def test_ap_wps_conf_5ghz(dev, apdev):
+ """WPS PBC provisioning with configured AP on 5 GHz band"""
+ try:
+ hapd = None
+ ssid = "test-wps-conf"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "country_code": "FI", "hw_mode": "a", "channel": "36"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].set("device_name", "Device A")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="5180")
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+
+ sta = hapd.get_sta(dev[0].p2p_interface_addr())
+ if 'wpsDeviceName' not in sta or sta['wpsDeviceName'] != "Device A":
+ raise Exception("Device name not available in STA command")
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_ap_wps_conf_chan14(dev, apdev):
+ """WPS PBC provisioning with configured AP on channel 14"""
+ try:
+ hapd = None
+ ssid = "test-wps-conf"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "country_code": "JP", "hw_mode": "b", "channel": "14"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].set("device_name", "Device A")
+ dev[0].request("WPS_PBC")
+ dev[0].wait_connected(timeout=30)
+
+ sta = hapd.get_sta(dev[0].p2p_interface_addr())
+ if 'wpsDeviceName' not in sta or sta['wpsDeviceName'] != "Device A":
+ raise Exception("Device name not available in STA command")
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+@remote_compatible
+def test_ap_wps_twice(dev, apdev):
+ """WPS provisioning with twice to change passphrase"""
+ ssid = "test-wps-twice"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ dev[0].request("DISCONNECT")
+
+ logger.info("Restart AP with different passphrase and re-run WPS")
+ hostapd.remove_bss(apdev[0])
+ params['wpa_passphrase'] = 'another passphrase'
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ networks = dev[0].list_networks()
+ if len(networks) > 1:
+ raise Exception("Unexpected duplicated network block present")
+
+@remote_compatible
+def test_ap_wps_incorrect_pin(dev, apdev):
+ """WPS PIN provisioning with incorrect PIN"""
+ ssid = "test-wps-incorrect-pin"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+
+ logger.info("WPS provisioning attempt 1")
+ hapd.request("WPS_PIN any 12345670")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PIN %s 55554444" % apdev[0]['bssid'])
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=30)
+ if ev is None:
+ raise Exception("WPS operation timed out")
+ if "config_error=18" not in ev:
+ raise Exception("Incorrect config_error reported")
+ if "msg=8" not in ev:
+ raise Exception("PIN error detected on incorrect message")
+ dev[0].wait_disconnected(timeout=10)
+ dev[0].request("WPS_CANCEL")
+ # if a scan was in progress, wait for it to complete before trying WPS again
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+
+ status = hapd.request("WPS_GET_STATUS")
+ if "Last WPS result: Failed" not in status:
+ raise Exception("WPS failure result not shown correctly")
+
+ logger.info("WPS provisioning attempt 2")
+ hapd.request("WPS_PIN any 12345670")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PIN %s 12344444" % apdev[0]['bssid'])
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=30)
+ if ev is None:
+ raise Exception("WPS operation timed out")
+ if "config_error=18" not in ev:
+ raise Exception("Incorrect config_error reported")
+ if "msg=10" not in ev:
+ raise Exception("PIN error detected on incorrect message")
+ dev[0].wait_disconnected(timeout=10)
+
+@remote_compatible
+def test_ap_wps_conf_pin(dev, apdev):
+ """WPS PIN provisioning with configured AP"""
+ ssid = "test-wps-conf-pin"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ logger.info("WPS provisioning step")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+ if status['ssid'] != ssid:
+ raise Exception("Unexpected SSID")
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ bss = dev[1].get_bss(apdev[0]['bssid'])
+ if "[WPS-AUTH]" in bss['flags']:
+ raise Exception("WPS-AUTH flag not cleared")
+ logger.info("Try to connect from another station using the same PIN")
+ pin = dev[1].request("WPS_PIN " + apdev[0]['bssid'])
+ ev = dev[1].wait_event(["WPS-M2D", "CTRL-EVENT-CONNECTED"], timeout=30)
+ if ev is None:
+ raise Exception("Operation timed out")
+ if "WPS-M2D" not in ev:
+ raise Exception("Unexpected WPS operation started")
+ hapd.request("WPS_PIN any " + pin)
+ dev[1].wait_connected(timeout=30)
+
+def test_ap_wps_conf_pin_mixed_mode(dev, apdev):
+ """WPS PIN provisioning with configured AP (WPA+WPA2)"""
+ skip_without_tkip(dev[0])
+ ssid = "test-wps-conf-pin-mixed"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "3",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wpa_pairwise": "TKIP"})
+
+ logger.info("WPS provisioning step")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP' or status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected encryption/key_mgmt configuration: pairwise=%s group=%s key_mgmt=%s" % (status['pairwise_cipher'], status['group_cipher'], status['key_mgmt']))
+
+ logger.info("WPS provisioning step (auth_types=0x1b)")
+ if "OK" not in dev[0].request("SET wps_force_auth_types 0x1b"):
+ raise Exception("Failed to set wps_force_auth_types 0x1b")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP' or status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected encryption/key_mgmt configuration: pairwise=%s group=%s key_mgmt=%s" % (status['pairwise_cipher'], status['group_cipher'], status['key_mgmt']))
+
+ logger.info("WPS provisioning step (auth_types=0 encr_types=0)")
+ if "OK" not in dev[0].request("SET wps_force_auth_types 0"):
+ raise Exception("Failed to set wps_force_auth_types 0")
+ if "OK" not in dev[0].request("SET wps_force_encr_types 0"):
+ raise Exception("Failed to set wps_force_encr_types 0")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP' or status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected encryption/key_mgmt configuration: pairwise=%s group=%s key_mgmt=%s" % (status['pairwise_cipher'], status['group_cipher'], status['key_mgmt']))
+
+ dev[0].request("SET wps_force_auth_types ")
+ dev[0].request("SET wps_force_encr_types ")
+
+@remote_compatible
+def test_ap_wps_conf_pin_v1(dev, apdev):
+ """WPS PIN provisioning with configured WPS v1.0 AP"""
+ ssid = "test-wps-conf-pin-v1"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ logger.info("WPS provisioning step")
+ pin = dev[0].wps_read_pin()
+ hapd.request("SET wps_version_number 0x10")
+ hapd.request("WPS_PIN any " + pin)
+ found = False
+ for i in range(0, 10):
+ dev[0].scan(freq="2412")
+ if "[WPS-PIN]" in dev[0].request("SCAN_RESULTS"):
+ found = True
+ break
+ if not found:
+ hapd.request("SET wps_version_number 0x20")
+ raise Exception("WPS-PIN flag not seen in scan results")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_connected(timeout=30)
+ hapd.request("SET wps_version_number 0x20")
+
+@remote_compatible
+def test_ap_wps_conf_pin_2sta(dev, apdev):
+ """Two stations trying to use WPS PIN at the same time"""
+ ssid = "test-wps-conf-pin2"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ logger.info("WPS provisioning step")
+ pin = "12345670"
+ pin2 = "55554444"
+ hapd.request("WPS_PIN " + dev[0].get_status_field("uuid") + " " + pin)
+ hapd.request("WPS_PIN " + dev[1].get_status_field("uuid") + " " + pin)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_connected(timeout=30)
+ dev[1].wait_connected(timeout=30)
+
+@remote_compatible
+def test_ap_wps_conf_pin_timeout(dev, apdev):
+ """WPS PIN provisioning with configured AP timing out PIN"""
+ ssid = "test-wps-conf-pin"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ addr = dev[0].p2p_interface_addr()
+ pin = dev[0].wps_read_pin()
+ if "FAIL" not in hapd.request("WPS_PIN "):
+ raise Exception("Unexpected success on invalid WPS_PIN")
+ hapd.request("WPS_PIN any " + pin + " 1")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ time.sleep(1.1)
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = hapd.wait_event(["WPS-PIN-NEEDED"], timeout=20)
+ if ev is None:
+ raise Exception("WPS-PIN-NEEDED event timed out")
+ ev = dev[0].wait_event(["WPS-M2D"])
+ if ev is None:
+ raise Exception("M2D not reported")
+ dev[0].request("WPS_CANCEL")
+
+ hapd.request("WPS_PIN any " + pin + " 20 " + addr)
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_connected(timeout=30)
+
+def test_ap_wps_reg_connect(dev, apdev):
+ """WPS registrar using AP PIN to connect"""
+ ssid = "test-wps-reg-ap-pin"
+ appin = "12345670"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "ap_pin": appin})
+ logger.info("WPS provisioning step")
+ dev[0].dump_monitor()
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], appin)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+ if status['ssid'] != ssid:
+ raise Exception("Unexpected SSID")
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+def test_ap_wps_reg_connect_zero_len_ap_pin(dev, apdev):
+ """hostapd with zero length ap_pin parameter"""
+ ssid = "test-wps-reg-ap-pin"
+ appin = ""
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "ap_pin": appin})
+ logger.info("WPS provisioning step")
+ dev[0].dump_monitor()
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], appin, no_wait=True)
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("No WPS-FAIL reported")
+ if "msg=5 config_error=15" not in ev:
+ raise Exception("Unexpected WPS-FAIL: " + ev)
+
+def test_ap_wps_reg_connect_mixed_mode(dev, apdev):
+ """WPS registrar using AP PIN to connect (WPA+WPA2)"""
+ skip_without_tkip(dev[0])
+ ssid = "test-wps-reg-ap-pin"
+ appin = "12345670"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "3",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wpa_pairwise": "TKIP", "ap_pin": appin})
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], appin)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+ if status['ssid'] != ssid:
+ raise Exception("Unexpected SSID")
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+def test_ap_wps_reg_override_ap_settings(dev, apdev):
+ """WPS registrar and ap_settings override"""
+ ap_settings = "/tmp/ap_wps_reg_override_ap_settings"
+ try:
+ os.remove(ap_settings)
+ except:
+ pass
+ # Override AP Settings with values that point to another AP
+ data = build_wsc_attr(ATTR_NETWORK_INDEX, b'\x01')
+ data += build_wsc_attr(ATTR_SSID, b"test")
+ data += build_wsc_attr(ATTR_AUTH_TYPE, b'\x00\x01')
+ data += build_wsc_attr(ATTR_ENCR_TYPE, b'\x00\x01')
+ data += build_wsc_attr(ATTR_NETWORK_KEY, b'')
+ data += build_wsc_attr(ATTR_MAC_ADDR, binascii.unhexlify(apdev[1]['bssid'].replace(':', '')))
+ with open(ap_settings, "wb") as f:
+ f.write(data)
+ ssid = "test-wps-reg-ap-pin"
+ appin = "12345670"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "ap_pin": appin, "ap_settings": ap_settings})
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "test"})
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], appin)
+ ev = hapd2.wait_event(['AP-STA-CONNECTED'], timeout=10)
+ os.remove(ap_settings)
+ if ev is None:
+ raise Exception("No connection with the other AP")
+
+def check_wps_reg_failure(dev, ap, appin):
+ dev.request("WPS_REG " + ap['bssid'] + " " + appin)
+ ev = dev.wait_event(["WPS-SUCCESS", "WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS operation timed out")
+ if "WPS-SUCCESS" in ev:
+ raise Exception("WPS operation succeeded unexpectedly")
+ if "config_error=15" not in ev:
+ raise Exception("WPS setup locked state was not reported correctly")
+
+def test_ap_wps_random_ap_pin(dev, apdev):
+ """WPS registrar using random AP PIN"""
+ ssid = "test-wps-reg-random-ap-pin"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "uuid": ap_uuid, "upnp_iface": "lo"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ appin = hapd.request("WPS_AP_PIN random")
+ if "FAIL" in appin:
+ raise Exception("Could not generate random AP PIN")
+ if appin not in hapd.request("WPS_AP_PIN get"):
+ raise Exception("Could not fetch current AP PIN")
+ logger.info("WPS provisioning step")
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], appin)
+
+ hapd.request("WPS_AP_PIN disable")
+ logger.info("WPS provisioning step with AP PIN disabled")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ check_wps_reg_failure(dev[1], apdev[0], appin)
+
+ logger.info("WPS provisioning step with AP PIN reset")
+ appin = "12345670"
+ hapd.request("WPS_AP_PIN set " + appin)
+ dev[1].wps_reg(apdev[0]['bssid'], appin)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=10)
+ dev[1].wait_disconnected(timeout=10)
+
+ logger.info("WPS provisioning step after AP PIN timeout")
+ hapd.request("WPS_AP_PIN disable")
+ appin = hapd.request("WPS_AP_PIN random 1")
+ time.sleep(1.1)
+ if "FAIL" not in hapd.request("WPS_AP_PIN get"):
+ raise Exception("AP PIN unexpectedly still enabled")
+ check_wps_reg_failure(dev[0], apdev[0], appin)
+
+ logger.info("WPS provisioning step after AP PIN timeout(2)")
+ hapd.request("WPS_AP_PIN disable")
+ appin = "12345670"
+ hapd.request("WPS_AP_PIN set " + appin + " 1")
+ time.sleep(1.1)
+ if "FAIL" not in hapd.request("WPS_AP_PIN get"):
+ raise Exception("AP PIN unexpectedly still enabled")
+ check_wps_reg_failure(dev[1], apdev[0], appin)
+
+ with fail_test(hapd, 1, "os_get_random;wps_generate_pin"):
+ hapd.request("WPS_AP_PIN random 1")
+ hapd.request("WPS_AP_PIN disable")
+
+ with alloc_fail(hapd, 1, "upnp_wps_set_ap_pin"):
+ hapd.request("WPS_AP_PIN set 12345670")
+ hapd.request("WPS_AP_PIN disable")
+
+ if "FAIL" not in hapd.request("WPS_AP_PIN set"):
+ raise Exception("Invalid WPS_AP_PIN accepted")
+ if "FAIL" not in hapd.request("WPS_AP_PIN foo"):
+ raise Exception("Invalid WPS_AP_PIN accepted")
+ if "FAIL" not in hapd.request("WPS_AP_PIN set " + 9*'1'):
+ raise Exception("Invalid WPS_AP_PIN accepted")
+
+def test_ap_wps_reg_config(dev, apdev):
+ """WPS registrar configuring an AP using AP PIN"""
+ ssid = "test-wps-init-ap-pin"
+ appin = "12345670"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "ap_pin": appin})
+ logger.info("WPS configuration step")
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].dump_monitor()
+ new_ssid = "wps-new-ssid"
+ new_passphrase = "1234567890"
+ dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
+ new_passphrase)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+ if status['ssid'] != new_ssid:
+ raise Exception("Unexpected SSID")
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+ logger.info("Re-configure back to open")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].flush_scan_cache()
+ dev[0].dump_monitor()
+ dev[0].wps_reg(apdev[0]['bssid'], appin, "wps-open", "OPEN", "NONE", "")
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+ if status['ssid'] != "wps-open":
+ raise Exception("Unexpected SSID")
+ if status['key_mgmt'] != 'NONE':
+ raise Exception("Unexpected key_mgmt")
+
+def test_ap_wps_reg_config_ext_processing(dev, apdev):
+ """WPS registrar configuring an AP with external config processing"""
+ ssid = "test-wps-init-ap-pin"
+ appin = "12345670"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wps_cred_processing": "1", "ap_pin": appin}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ new_ssid = "wps-new-ssid"
+ new_passphrase = "1234567890"
+ dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
+ new_passphrase, no_wait=True)
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS registrar operation timed out")
+ ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS configuration timed out")
+ if "1026" not in ev:
+ raise Exception("AP Settings missing from event")
+ hapd.request("SET wps_cred_processing 0")
+ if "FAIL" in hapd.request("WPS_CONFIG " + binascii.hexlify(new_ssid.encode()).decode() + " WPA2PSK CCMP " + binascii.hexlify(new_passphrase.encode()).decode()):
+ raise Exception("WPS_CONFIG command failed")
+ dev[0].wait_connected(timeout=15)
+
+def test_ap_wps_reg_config_tkip(dev, apdev):
+ """WPS registrar configuring AP to use TKIP and AP upgrading to TKIP+CCMP"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ ssid = "test-wps-init-ap"
+ appin = "12345670"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1",
+ "ap_pin": appin})
+ logger.info("WPS configuration step")
+ dev[0].flush_scan_cache()
+ dev[0].request("SET wps_version_number 0x10")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].dump_monitor()
+ new_ssid = "wps-new-ssid-with-tkip"
+ new_passphrase = "1234567890"
+ dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPAPSK", "TKIP",
+ new_passphrase)
+ logger.info("Re-connect to verify WPA2 mixed mode")
+ dev[0].request("DISCONNECT")
+ id = 0
+ dev[0].set_network(id, "pairwise", "CCMP")
+ dev[0].set_network(id, "proto", "RSN")
+ dev[0].connect_network(id)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected: wpa_state={} bssid={}".format(status['wpa_state'], status['bssid']))
+ if status['ssid'] != new_ssid:
+ raise Exception("Unexpected SSID")
+ if status['pairwise_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration")
+ if status['group_cipher'] != 'TKIP':
+ conf = hapd.request("GET_CONFIG")
+ if "group_cipher=CCMP" not in conf or status['group_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+def test_ap_wps_setup_locked(dev, apdev):
+ """WPS registrar locking up AP setup on AP PIN failures"""
+ ssid = "test-wps-incorrect-ap-pin"
+ appin = "12345670"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "ap_pin": appin})
+ new_ssid = "wps-new-ssid-test"
+ new_passphrase = "1234567890"
+
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ ap_setup_locked = False
+ for pin in ["55554444", "1234", "12345678", "00000000", "11111111"]:
+ dev[0].dump_monitor()
+ logger.info("Try incorrect AP PIN - attempt " + pin)
+ dev[0].wps_reg(apdev[0]['bssid'], pin, new_ssid, "WPA2PSK",
+ "CCMP", new_passphrase, no_wait=True)
+ ev = dev[0].wait_event(["WPS-FAIL", "CTRL-EVENT-CONNECTED"])
+ if ev is None:
+ raise Exception("Timeout on receiving WPS operation failure event")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ if "config_error=15" in ev:
+ logger.info("AP Setup Locked")
+ ap_setup_locked = True
+ elif "config_error=18" not in ev:
+ raise Exception("config_error=18 not reported")
+ dev[0].wait_disconnected(timeout=10)
+ time.sleep(0.1)
+ if not ap_setup_locked:
+ raise Exception("AP setup was not locked")
+ dev[0].request("WPS_CANCEL")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412, force_scan=True,
+ only_new=True)
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if 'wps_ap_setup_locked' not in bss or bss['wps_ap_setup_locked'] != '1':
+ logger.info("BSS: " + str(bss))
+ raise Exception("AP Setup Locked not indicated in scan results")
+
+ status = hapd.request("WPS_GET_STATUS")
+ if "Last WPS result: Failed" not in status:
+ raise Exception("WPS failure result not shown correctly")
+ if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
+ raise Exception("Peer address not shown correctly")
+
+ time.sleep(0.5)
+ dev[0].dump_monitor()
+ logger.info("WPS provisioning step")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=30)
+ if ev is None:
+ raise Exception("WPS success was not reported")
+ dev[0].wait_connected(timeout=30)
+
+ appin = hapd.request("WPS_AP_PIN random")
+ if "FAIL" in appin:
+ raise Exception("Could not generate random AP PIN")
+ ev = hapd.wait_event(["WPS-AP-SETUP-UNLOCKED"], timeout=10)
+ if ev is None:
+ raise Exception("Failed to unlock AP PIN")
+
+def test_ap_wps_setup_locked_timeout(dev, apdev):
+ """WPS re-enabling AP PIN after timeout"""
+ ssid = "test-wps-incorrect-ap-pin"
+ appin = "12345670"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "ap_pin": appin})
+ new_ssid = "wps-new-ssid-test"
+ new_passphrase = "1234567890"
+
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ ap_setup_locked = False
+ for pin in ["55554444", "1234", "12345678", "00000000", "11111111"]:
+ dev[0].dump_monitor()
+ logger.info("Try incorrect AP PIN - attempt " + pin)
+ dev[0].wps_reg(apdev[0]['bssid'], pin, new_ssid, "WPA2PSK",
+ "CCMP", new_passphrase, no_wait=True)
+ ev = dev[0].wait_event(["WPS-FAIL", "CTRL-EVENT-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on receiving WPS operation failure event")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ if "config_error=15" in ev:
+ logger.info("AP Setup Locked")
+ ap_setup_locked = True
+ break
+ elif "config_error=18" not in ev:
+ raise Exception("config_error=18 not reported")
+ dev[0].wait_disconnected(timeout=10)
+ time.sleep(0.1)
+ if not ap_setup_locked:
+ raise Exception("AP setup was not locked")
+ ev = hapd.wait_event(["WPS-AP-SETUP-UNLOCKED"], timeout=80)
+ if ev is None:
+ raise Exception("AP PIN did not get unlocked on 60 second timeout")
+
+def test_ap_wps_setup_locked_2(dev, apdev):
+ """WPS AP configured for special ap_setup_locked=2 mode"""
+ ssid = "test-wps-ap-pin"
+ appin = "12345670"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "ap_pin": appin, "ap_setup_locked": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ new_ssid = "wps-new-ssid-test"
+ new_passphrase = "1234567890"
+
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], appin)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+ dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK",
+ "CCMP", new_passphrase, no_wait=True)
+
+ ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
+ if ev is None:
+ raise Exception("hostapd did not report WPS failure")
+ if "msg=12 config_error=15" not in ev:
+ raise Exception("Unexpected failure reason (AP): " + ev)
+
+ ev = dev[0].wait_event(["WPS-FAIL", "CTRL-EVENT-CONNECTED"])
+ if ev is None:
+ raise Exception("Timeout on receiving WPS operation failure event")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ if "config_error=15" not in ev:
+ raise Exception("Unexpected failure reason (STA): " + ev)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+
+def setup_ap_wps_pbc_overlap_2ap(apdev):
+ params = {"ssid": "wps1", "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wps_independent": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ params = {"ssid": "wps2", "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "123456789", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wps_independent": "1"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ hapd.request("WPS_PBC")
+ hapd2.request("WPS_PBC")
+ return hapd, hapd2
+
+@remote_compatible
+def test_ap_wps_pbc_overlap_2ap(dev, apdev):
+ """WPS PBC session overlap with two active APs"""
+ hapd, hapd2 = setup_ap_wps_pbc_overlap_2ap(apdev)
+ logger.info("WPS provisioning step")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ dev[0].request("WPS_PBC")
+ ev = dev[0].wait_event(["WPS-OVERLAP-DETECTED"], timeout=15)
+ hapd.request("DISABLE")
+ hapd2.request("DISABLE")
+ dev[0].flush_scan_cache()
+ if ev is None:
+ raise Exception("PBC session overlap not detected")
+
+@remote_compatible
+def test_ap_wps_pbc_overlap_2ap_specific_bssid(dev, apdev):
+ """WPS PBC session overlap with two active APs (specific BSSID selected)"""
+ hapd, hapd2 = setup_ap_wps_pbc_overlap_2ap(apdev)
+ logger.info("WPS provisioning step")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ ev = dev[0].wait_event(["WPS-OVERLAP-DETECTED",
+ "CTRL-EVENT-CONNECTED"], timeout=15)
+ dev[0].request("DISCONNECT")
+ hapd.request("DISABLE")
+ hapd2.request("DISABLE")
+ dev[0].flush_scan_cache()
+ if ev is None:
+ raise Exception("PBC session overlap result not reported")
+ if "CTRL-EVENT-CONNECTED" not in ev:
+ raise Exception("Connection did not complete")
+
+@remote_compatible
+def test_ap_wps_pbc_overlap_2sta(dev, apdev):
+ """WPS PBC session overlap with two active STAs"""
+ ssid = "test-wps-pbc-overlap"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[1].dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+ ev = dev[0].wait_event(["WPS-M2D"], timeout=15)
+ if ev is None:
+ raise Exception("PBC session overlap not detected (dev0)")
+ if "config_error=12" not in ev:
+ raise Exception("PBC session overlap not correctly reported (dev0)")
+ dev[0].request("WPS_CANCEL")
+ dev[0].request("DISCONNECT")
+ ev = dev[1].wait_event(["WPS-M2D"], timeout=15)
+ if ev is None:
+ raise Exception("PBC session overlap not detected (dev1)")
+ if "config_error=12" not in ev:
+ raise Exception("PBC session overlap not correctly reported (dev1)")
+ dev[1].request("WPS_CANCEL")
+ dev[1].request("DISCONNECT")
+ ev = hapd.wait_event(["WPS-OVERLAP-DETECTED"], timeout=1)
+ if ev is None:
+ raise Exception("PBC session overlap not detected (AP)")
+ if "PBC Status: Overlap" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+ hapd.request("WPS_CANCEL")
+ ret = hapd.request("WPS_PBC")
+ if "FAIL" not in ret:
+ raise Exception("PBC mode allowed to be started while PBC overlap still active")
+ hapd.request("DISABLE")
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_ap_wps_pbc_session_workaround(dev, apdev):
+ """WPS PBC session overlap workaround"""
+ ssid = "test-wps-pbc-overlap"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ bssid = hapd.own_addr()
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("WPS_PBC " + bssid)
+ dev[0].wait_connected(timeout=30)
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=30)
+ dev[0].dump_monitor()
+ # Trigger AP/Registrar to ignore PBC activation immediately after
+ # successfully completed provisioning
+ dev[0].request("WPS_PBC " + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("No scan results reported")
+ dev[0].request("WPS_CANCEL")
+ dev[0].dump_monitor()
+
+ # Verify that PBC session overlap does not prevent connection
+ hapd.request("WPS_PBC")
+ dev[1].scan_for_bss(bssid, freq="2412")
+ dev[1].request("WPS_PBC " + bssid)
+ dev[1].wait_connected()
+ dev[1].request("REMOVE_NETWORK all")
+ dev[1].wait_disconnected()
+
+ hapd.request("DISABLE")
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+@remote_compatible
+def test_ap_wps_cancel(dev, apdev):
+ """WPS AP cancelling enabled config method"""
+ ssid = "test-wps-ap-cancel"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ bssid = apdev[0]['bssid']
+
+ logger.info("Verify PBC enable/cancel")
+ hapd.request("WPS_PBC")
+ dev[0].scan(freq="2412")
+ dev[0].scan(freq="2412")
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if "[WPS-PBC]" not in bss['flags']:
+ raise Exception("WPS-PBC flag missing")
+ if "FAIL" in hapd.request("WPS_CANCEL"):
+ raise Exception("WPS_CANCEL failed")
+ dev[0].scan(freq="2412")
+ dev[0].scan(freq="2412")
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if "[WPS-PBC]" in bss['flags']:
+ raise Exception("WPS-PBC flag not cleared")
+
+ logger.info("Verify PIN enable/cancel")
+ hapd.request("WPS_PIN any 12345670")
+ dev[0].scan(freq="2412")
+ dev[0].scan(freq="2412")
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if "[WPS-AUTH]" not in bss['flags']:
+ raise Exception("WPS-AUTH flag missing")
+ if "FAIL" in hapd.request("WPS_CANCEL"):
+ raise Exception("WPS_CANCEL failed")
+ dev[0].scan(freq="2412")
+ dev[0].scan(freq="2412")
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if "[WPS-AUTH]" in bss['flags']:
+ raise Exception("WPS-AUTH flag not cleared")
+
+def test_ap_wps_er_add_enrollee(dev, apdev):
+ """WPS ER configuring AP and adding a new enrollee using PIN"""
+ try:
+ _test_ap_wps_er_add_enrollee(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_add_enrollee(dev, apdev):
+ ssid = "wps-er-add-enrollee"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ 'friendly_name': "WPS AP - <>&'\" - TEST",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+ logger.info("WPS configuration step")
+ new_passphrase = "1234567890"
+ dev[0].dump_monitor()
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin, ssid, "WPA2PSK", "CCMP",
+ new_passphrase)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+ if status['ssid'] != ssid:
+ raise Exception("Unexpected SSID")
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+ logger.info("Start ER")
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+ if "|WPS AP - &lt;&gt;&amp;&apos;&quot; - TEST|Company|" not in ev:
+ raise Exception("Expected friendly name not found")
+
+ logger.info("Learn AP configuration through UPnP")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+ ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+ if ev is None:
+ raise Exception("AP learn timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not in settings")
+ if "ssid=" + ssid not in ev:
+ raise Exception("Expected SSID not in settings")
+ if "key=" + new_passphrase not in ev:
+ raise Exception("Expected passphrase not in settings")
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL after AP learn timed out")
+ time.sleep(0.1)
+
+ logger.info("Add Enrollee using ER")
+ pin = dev[1].wps_read_pin()
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_PIN any " + pin + " " + dev[1].p2p_interface_addr())
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[1].dump_monitor()
+ dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=30)
+ if ev is None:
+ raise Exception("Enrollee did not report success")
+ dev[1].wait_connected(timeout=15)
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS ER did not report success")
+ hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+
+ logger.info("Add a specific Enrollee using ER")
+ pin = dev[2].wps_read_pin()
+ addr2 = dev[2].p2p_interface_addr()
+ dev[0].dump_monitor()
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[2].dump_monitor()
+ dev[2].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
+ if ev is None:
+ raise Exception("Enrollee not seen")
+ if addr2 not in ev:
+ raise Exception("Unexpected Enrollee MAC address")
+ dev[0].request("WPS_ER_PIN " + addr2 + " " + pin + " " + addr2)
+ dev[2].wait_connected(timeout=30)
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS ER did not report success")
+
+ logger.info("Verify registrar selection behavior")
+ dev[0].request("WPS_ER_PIN any " + pin + " " + dev[1].p2p_interface_addr())
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected(timeout=10)
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[1].scan(freq="2412")
+ bss = dev[1].get_bss(apdev[0]['bssid'])
+ if "[WPS-AUTH]" not in bss['flags']:
+ # It is possible for scan to miss an update especially when running
+ # tests under load with multiple VMs, so allow another attempt.
+ dev[1].scan(freq="2412")
+ bss = dev[1].get_bss(apdev[0]['bssid'])
+ if "[WPS-AUTH]" not in bss['flags']:
+ raise Exception("WPS-AUTH flag missing")
+
+ logger.info("Stop ER")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_STOP")
+ ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"])
+ if ev is None:
+ raise Exception("WPS ER unsubscription timed out")
+ # It takes some time for the UPnP UNSUBSCRIBE command to go through, so wait
+ # a bit before verifying that the scan results have changed.
+ time.sleep(0.2)
+
+ for i in range(0, 10):
+ dev[1].request("BSS_FLUSH 0")
+ dev[1].scan(freq="2412", only_new=True)
+ bss = dev[1].get_bss(apdev[0]['bssid'])
+ if bss and 'flags' in bss and "[WPS-AUTH]" not in bss['flags']:
+ break
+ logger.debug("WPS-AUTH flag was still in place - wait a bit longer")
+ time.sleep(0.1)
+ if "[WPS-AUTH]" in bss['flags']:
+ raise Exception("WPS-AUTH flag not removed")
+
+def test_ap_wps_er_add_enrollee_uuid(dev, apdev):
+ """WPS ER adding a new enrollee identified by UUID"""
+ try:
+ _test_ap_wps_er_add_enrollee_uuid(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_add_enrollee_uuid(dev, apdev):
+ ssid = "wps-er-add-enrollee"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+ logger.info("WPS configuration step")
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+
+ logger.info("Start ER")
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+
+ logger.info("Learn AP configuration through UPnP")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+ ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+ if ev is None:
+ raise Exception("AP learn timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not in settings")
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL after AP learn timed out")
+ time.sleep(0.1)
+
+ logger.info("Add a specific Enrollee using ER (PBC/UUID)")
+ addr1 = dev[1].p2p_interface_addr()
+ dev[0].dump_monitor()
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[1].dump_monitor()
+ dev[1].request("WPS_PBC %s" % apdev[0]['bssid'])
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
+ if ev is None:
+ raise Exception("Enrollee not seen")
+ if addr1 not in ev:
+ raise Exception("Unexpected Enrollee MAC address")
+ uuid = ev.split(' ')[1]
+ dev[0].request("WPS_ER_PBC " + uuid)
+ dev[1].wait_connected(timeout=30)
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS ER did not report success")
+
+ logger.info("Add a specific Enrollee using ER (PIN/UUID)")
+ pin = dev[2].wps_read_pin()
+ addr2 = dev[2].p2p_interface_addr()
+ dev[0].dump_monitor()
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[2].dump_monitor()
+ dev[2].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
+ if ev is None:
+ raise Exception("Enrollee not seen")
+ if addr2 not in ev:
+ raise Exception("Unexpected Enrollee MAC address")
+ uuid = ev.split(' ')[1]
+ dev[0].request("WPS_ER_PIN " + uuid + " " + pin)
+ dev[2].wait_connected(timeout=30)
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS ER did not report success")
+
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-REMOVE"], timeout=15)
+ if ev is None:
+ raise Exception("No Enrollee STA entry timeout seen")
+
+ logger.info("Stop ER")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_multi_add_enrollee(dev, apdev):
+ """Multiple WPS ERs adding a new enrollee using PIN"""
+ try:
+ _test_ap_wps_er_multi_add_enrollee(dev, apdev)
+ finally:
+ for i in range(2):
+ dev[i].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_multi_add_enrollee(dev, apdev):
+ ssid = "wps-er-add-enrollee"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ 'friendly_name': "WPS AP",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+
+ for i in range(2):
+ dev[i].flush_scan_cache()
+ dev[i].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[i].wps_reg(apdev[0]['bssid'], ap_pin)
+ for i in range(2):
+ dev[i].request("WPS_ER_START ifname=lo")
+ for i in range(2):
+ ev = dev[i].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ dev[i].dump_monitor()
+ for i in range(2):
+ dev[i].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+ for i in range(2):
+ ev = dev[i].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+ if ev is None:
+ raise Exception("AP learn timed out")
+ ev = dev[i].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL after AP learn timed out")
+
+ time.sleep(0.1)
+
+ pin = dev[2].wps_read_pin()
+ addr = dev[2].own_addr()
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_PIN any " + pin + " " + addr)
+ dev[1].dump_monitor()
+ dev[1].request("WPS_ER_PIN any " + pin + " " + addr)
+
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[2].dump_monitor()
+ dev[2].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = dev[2].wait_event(["WPS-SUCCESS"], timeout=30)
+ if ev is None:
+ raise Exception("Enrollee did not report success")
+ dev[2].wait_connected(timeout=15)
+
+def test_ap_wps_er_add_enrollee_pbc(dev, apdev):
+ """WPS ER connected to AP and adding a new enrollee using PBC"""
+ try:
+ _test_ap_wps_er_add_enrollee_pbc(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_add_enrollee_pbc(dev, apdev):
+ ssid = "wps-er-add-enrollee-pbc"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+ logger.info("Learn AP configuration")
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].dump_monitor()
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+
+ logger.info("Start ER")
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+
+ enrollee = dev[1].p2p_interface_addr()
+
+ if "FAIL-UNKNOWN-UUID" not in dev[0].request("WPS_ER_PBC " + enrollee):
+ raise Exception("Unknown UUID not reported")
+
+ logger.info("Add Enrollee using ER and PBC")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[1].request("WPS_PBC")
+
+ for i in range(0, 2):
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("Enrollee discovery timed out")
+ if enrollee in ev:
+ break
+ if i == 1:
+ raise Exception("Expected Enrollee not found")
+ if "FAIL-NO-AP-SETTINGS" not in dev[0].request("WPS_ER_PBC " + enrollee):
+ raise Exception("Unknown UUID not reported")
+ logger.info("Use learned network configuration on ER")
+ dev[0].request("WPS_ER_SET_CONFIG " + ap_uuid + " 0")
+ if "OK" not in dev[0].request("WPS_ER_PBC " + enrollee):
+ raise Exception("WPS_ER_PBC failed")
+
+ ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("Enrollee did not report success")
+ dev[1].wait_connected(timeout=15)
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS ER did not report success")
+ hwsim_utils.test_connectivity_sta(dev[0], dev[1])
+
+def test_ap_wps_er_pbc_overlap(dev, apdev):
+ """WPS ER connected to AP and PBC session overlap"""
+ try:
+ _test_ap_wps_er_pbc_overlap(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_pbc_overlap(dev, apdev):
+ ssid = "wps-er-add-enrollee-pbc"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].dump_monitor()
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ # avoid leaving dev 1 or 2 as the last Probe Request to the AP
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412, force_scan=True)
+
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_START ifname=lo")
+
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+
+ # verify BSSID selection of the AP instead of UUID
+ if "FAIL" in dev[0].request("WPS_ER_SET_CONFIG " + apdev[0]['bssid'] + " 0"):
+ raise Exception("Could not select AP based on BSSID")
+
+ dev[0].dump_monitor()
+ dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[2].request("WPS_PBC " + apdev[0]['bssid'])
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("PBC scan failed")
+ ev = dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("PBC scan failed")
+ found1 = False
+ found2 = False
+ addr1 = dev[1].own_addr()
+ addr2 = dev[2].own_addr()
+ for i in range(3):
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("Enrollee discovery timed out")
+ if addr1 in ev:
+ found1 = True
+ if found2:
+ break
+ if addr2 in ev:
+ found2 = True
+ if found1:
+ break
+ if dev[0].request("WPS_ER_PBC " + ap_uuid) != "FAIL-PBC-OVERLAP\n":
+ raise Exception("PBC overlap not reported")
+ dev[1].request("WPS_CANCEL")
+ dev[2].request("WPS_CANCEL")
+ if dev[0].request("WPS_ER_PBC foo") != "FAIL\n":
+ raise Exception("Invalid WPS_ER_PBC accepted")
+
+def test_ap_wps_er_v10_add_enrollee_pin(dev, apdev):
+ """WPS v1.0 ER connected to AP and adding a new enrollee using PIN"""
+ try:
+ _test_ap_wps_er_v10_add_enrollee_pin(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_v10_add_enrollee_pin(dev, apdev):
+ ssid = "wps-er-add-enrollee-pbc"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+ logger.info("Learn AP configuration")
+ dev[0].request("SET wps_version_number 0x10")
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].dump_monitor()
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+
+ logger.info("Start ER")
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+
+ logger.info("Use learned network configuration on ER")
+ dev[0].request("WPS_ER_SET_CONFIG " + ap_uuid + " 0")
+
+ logger.info("Add Enrollee using ER and PIN")
+ enrollee = dev[1].p2p_interface_addr()
+ pin = dev[1].wps_read_pin()
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_PIN any " + pin + " " + enrollee)
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[1].dump_monitor()
+ dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[1].wait_connected(timeout=30)
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS ER did not report success")
+
+@remote_compatible
+def test_ap_wps_er_config_ap(dev, apdev):
+ """WPS ER configuring AP over UPnP"""
+ try:
+ _test_ap_wps_er_config_ap(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_config_ap(dev, apdev):
+ ssid = "wps-er-ap-config"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+
+ logger.info("Connect ER to the AP")
+ dev[0].connect(ssid, psk="12345678", scan_freq="2412")
+
+ logger.info("WPS configuration step")
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+ new_passphrase = "1234567890"
+ dev[0].request("WPS_ER_CONFIG " + apdev[0]['bssid'] + " " + ap_pin + " " +
+ binascii.hexlify(ssid.encode()).decode() + " WPA2PSK CCMP " +
+ binascii.hexlify(new_passphrase.encode()).decode())
+ ev = dev[0].wait_event(["WPS-SUCCESS"])
+ if ev is None:
+ raise Exception("WPS ER configuration operation timed out")
+ dev[0].wait_disconnected(timeout=10)
+ dev[0].connect(ssid, psk="1234567890", scan_freq="2412")
+
+ logger.info("WPS ER restart")
+ dev[0].request("WPS_ER_START")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out on ER restart")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found on ER restart")
+ if "OK" not in dev[0].request("WPS_ER_STOP"):
+ raise Exception("WPS_ER_STOP failed")
+ if "OK" not in dev[0].request("WPS_ER_STOP"):
+ raise Exception("WPS_ER_STOP failed")
+
+@remote_compatible
+def test_ap_wps_er_cache_ap_settings(dev, apdev):
+ """WPS ER caching AP settings"""
+ try:
+ _test_ap_wps_er_cache_ap_settings(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_cache_ap_settings(dev, apdev):
+ ssid = "wps-er-add-enrollee"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+ id = int(dev[0].list_networks()[0]['id'])
+ dev[0].set_network(id, "scan_freq", "2412")
+
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+ ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+ if ev is None:
+ raise Exception("AP learn timed out")
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL after AP learn timed out")
+ time.sleep(0.1)
+
+ hapd.disable()
+
+ for i in range(2):
+ ev = dev[0].wait_event(["WPS-ER-AP-REMOVE", "CTRL-EVENT-DISCONNECTED"],
+ timeout=15)
+ if ev is None:
+ raise Exception("AP removal or disconnection timed out")
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ for i in range(2):
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD", "CTRL-EVENT-CONNECTED"],
+ timeout=15)
+ if ev is None:
+ raise Exception("AP discovery or connection timed out")
+
+ pin = dev[1].wps_read_pin()
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_PIN any " + pin + " " + dev[1].p2p_interface_addr())
+
+ time.sleep(0.2)
+
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[1].dump_monitor()
+ dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=30)
+ if ev is None:
+ raise Exception("Enrollee did not report success")
+ dev[1].wait_connected(timeout=15)
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS ER did not report success")
+
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_cache_ap_settings_oom(dev, apdev):
+ """WPS ER caching AP settings (OOM)"""
+ try:
+ _test_ap_wps_er_cache_ap_settings_oom(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_cache_ap_settings_oom(dev, apdev):
+ ssid = "wps-er-add-enrollee"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+ id = int(dev[0].list_networks()[0]['id'])
+ dev[0].set_network(id, "scan_freq", "2412")
+
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+ ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+ if ev is None:
+ raise Exception("AP learn timed out")
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL after AP learn timed out")
+ time.sleep(0.1)
+
+ with alloc_fail(dev[0], 1, "=wps_er_ap_use_cached_settings"):
+ hapd.disable()
+
+ for i in range(2):
+ ev = dev[0].wait_event(["WPS-ER-AP-REMOVE",
+ "CTRL-EVENT-DISCONNECTED"],
+ timeout=15)
+ if ev is None:
+ raise Exception("AP removal or disconnection timed out")
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ for i in range(2):
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD", "CTRL-EVENT-CONNECTED"],
+ timeout=15)
+ if ev is None:
+ raise Exception("AP discovery or connection timed out")
+
+ dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_cache_ap_settings_oom2(dev, apdev):
+ """WPS ER caching AP settings (OOM 2)"""
+ try:
+ _test_ap_wps_er_cache_ap_settings_oom2(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_cache_ap_settings_oom2(dev, apdev):
+ ssid = "wps-er-add-enrollee"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+ id = int(dev[0].list_networks()[0]['id'])
+ dev[0].set_network(id, "scan_freq", "2412")
+
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+ ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+ if ev is None:
+ raise Exception("AP learn timed out")
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL after AP learn timed out")
+ time.sleep(0.1)
+
+ with alloc_fail(dev[0], 1, "=wps_er_ap_cache_settings"):
+ hapd.disable()
+
+ for i in range(2):
+ ev = dev[0].wait_event(["WPS-ER-AP-REMOVE",
+ "CTRL-EVENT-DISCONNECTED"],
+ timeout=15)
+ if ev is None:
+ raise Exception("AP removal or disconnection timed out")
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ for i in range(2):
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD", "CTRL-EVENT-CONNECTED"],
+ timeout=15)
+ if ev is None:
+ raise Exception("AP discovery or connection timed out")
+
+ dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_subscribe_oom(dev, apdev):
+ """WPS ER subscribe OOM"""
+ try:
+ _test_ap_wps_er_subscribe_oom(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_subscribe_oom(dev, apdev):
+ ssid = "wps-er-add-enrollee"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+ id = int(dev[0].list_networks()[0]['id'])
+ dev[0].set_network(id, "scan_freq", "2412")
+
+ with alloc_fail(dev[0], 1, "http_client_addr;wps_er_subscribe"):
+ dev[0].request("WPS_ER_START ifname=lo")
+ for i in range(50):
+ res = dev[0].request("GET_ALLOC_FAIL")
+ if res.startswith("0:"):
+ break
+ time.sleep(0.1)
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=0)
+ if ev:
+ raise Exception("Unexpected AP discovery during OOM")
+
+ dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_er_set_sel_reg_oom(dev, apdev):
+ """WPS ER SetSelectedRegistrar OOM"""
+ try:
+ _test_ap_wps_er_set_sel_reg_oom(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_set_sel_reg_oom(dev, apdev):
+ ssid = "wps-er-add-enrollee"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
+ if ev is None:
+ raise Exception("AP not discovered")
+
+ dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+ ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+ if ev is None:
+ raise Exception("AP learn timed out")
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL timed out")
+ time.sleep(0.1)
+
+ for func in ["http_client_url_parse;wps_er_send_set_sel_reg",
+ "wps_er_soap_hdr;wps_er_send_set_sel_reg",
+ "http_client_addr;wps_er_send_set_sel_reg",
+ "wpabuf_alloc;wps_er_set_sel_reg"]:
+ with alloc_fail(dev[0], 1, func):
+ if "OK" not in dev[0].request("WPS_ER_PBC " + ap_uuid):
+ raise Exception("WPS_ER_PBC failed")
+ ev = dev[0].wait_event(["WPS-PBC-ACTIVE"], timeout=3)
+ if ev is None:
+ raise Exception("WPS-PBC-ACTIVE not seen")
+
+ dev[0].request("WPS_ER_STOP")
+
+@remote_compatible
+def test_ap_wps_er_learn_oom(dev, apdev):
+ """WPS ER learn OOM"""
+ try:
+ _test_ap_wps_er_learn_oom(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_learn_oom(dev, apdev):
+ ssid = "wps-er-add-enrollee"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], ap_pin)
+
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
+ if ev is None:
+ raise Exception("AP not discovered")
+
+ for func in ["wps_er_http_put_message_cb",
+ "xml_get_base64_item;wps_er_http_put_message_cb",
+ "http_client_url_parse;wps_er_ap_put_message",
+ "wps_er_soap_hdr;wps_er_ap_put_message",
+ "http_client_addr;wps_er_ap_put_message"]:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+ ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=1)
+ if ev is not None:
+ raise Exception("AP learn succeeded during OOM")
+
+ dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+ ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=10)
+ if ev is None:
+ raise Exception("AP learn did not succeed")
+
+ if "FAIL" not in dev[0].request("WPS_ER_LEARN 00000000-9e5c-4e73-bd82-f89cbcd10d7e " + ap_pin):
+ raise Exception("WPS_ER_LEARN for unknown AP accepted")
+
+ dev[0].request("WPS_ER_STOP")
+
+def test_ap_wps_fragmentation(dev, apdev):
+ """WPS with fragmentation in EAP-WSC and mixed mode WPA+WPA2"""
+ skip_without_tkip(dev[0])
+ ssid = "test-wps-fragmentation"
+ appin = "12345670"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "3",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wpa_pairwise": "TKIP", "ap_pin": appin,
+ "fragment_size": "50"})
+ logger.info("WPS provisioning step (PBC)")
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].dump_monitor()
+ dev[0].request("SET wps_fragment_size 50")
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED':
+ raise Exception("Not fully connected")
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+ logger.info("WPS provisioning step (PIN)")
+ pin = dev[1].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[1].request("SET wps_fragment_size 50")
+ dev[1].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[1].wait_connected(timeout=30)
+ status = dev[1].get_status()
+ if status['wpa_state'] != 'COMPLETED':
+ raise Exception("Not fully connected")
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+ logger.info("WPS connection as registrar")
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[2].request("SET wps_fragment_size 50")
+ dev[2].wps_reg(apdev[0]['bssid'], appin)
+ status = dev[2].get_status()
+ if status['wpa_state'] != 'COMPLETED':
+ raise Exception("Not fully connected")
+ if status['pairwise_cipher'] != 'CCMP' or status['group_cipher'] != 'TKIP':
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+@remote_compatible
+def test_ap_wps_new_version_sta(dev, apdev):
+ """WPS compatibility with new version number on the station"""
+ ssid = "test-wps-ver"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("SET wps_version_number 0x43")
+ dev[0].request("SET wps_vendor_ext_m1 000137100100020001")
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+
+@remote_compatible
+def test_ap_wps_new_version_ap(dev, apdev):
+ """WPS compatibility with new version number on the AP"""
+ ssid = "test-wps-ver"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ logger.info("WPS provisioning step")
+ if "FAIL" in hapd.request("SET wps_version_number 0x43"):
+ raise Exception("Failed to enable test functionality")
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ hapd.request("SET wps_version_number 0x20")
+
+@remote_compatible
+def test_ap_wps_check_pin(dev, apdev):
+ """Verify PIN checking through control interface"""
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wps", "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ for t in [("12345670", "12345670"),
+ ("12345678", "FAIL-CHECKSUM"),
+ ("12345", "FAIL"),
+ ("123456789", "FAIL"),
+ ("1234-5670", "12345670"),
+ ("1234 5670", "12345670"),
+ ("1-2.3:4 5670", "12345670")]:
+ res = hapd.request("WPS_CHECK_PIN " + t[0]).rstrip('\n')
+ res2 = dev[0].request("WPS_CHECK_PIN " + t[0]).rstrip('\n')
+ if res != res2:
+ raise Exception("Unexpected difference in WPS_CHECK_PIN responses")
+ if res != t[1]:
+ raise Exception("Incorrect WPS_CHECK_PIN response {} (expected {})".format(res, t[1]))
+
+ if "FAIL" not in hapd.request("WPS_CHECK_PIN 12345"):
+ raise Exception("Unexpected WPS_CHECK_PIN success")
+ if "FAIL" not in hapd.request("WPS_CHECK_PIN 123456789"):
+ raise Exception("Unexpected WPS_CHECK_PIN success")
+
+ for i in range(0, 10):
+ pin = dev[0].request("WPS_PIN get")
+ rpin = dev[0].request("WPS_CHECK_PIN " + pin).rstrip('\n')
+ if pin != rpin:
+ raise Exception("Random PIN validation failed for " + pin)
+
+def test_ap_wps_pin_get_failure(dev, apdev):
+ """PIN generation failure"""
+ with fail_test(dev[0], 1,
+ "os_get_random;wpa_supplicant_ctrl_iface_wps_pin"):
+ if "FAIL" not in dev[0].request("WPS_PIN get"):
+ raise Exception("WPS_PIN did not report failure")
+
+def test_ap_wps_wep_config(dev, apdev):
+ """WPS 2.0 AP rejecting WEP configuration"""
+ ssid = "test-wps-config"
+ appin = "12345670"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "ap_pin": appin})
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], appin, "wps-new-ssid-wep", "OPEN", "WEP",
+ "hello", no_wait=True)
+ ev = hapd.wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL timed out")
+ if "reason=2" not in ev:
+ raise Exception("Unexpected reason code in WPS-FAIL")
+ status = hapd.request("WPS_GET_STATUS")
+ if "Last WPS result: Failed" not in status:
+ raise Exception("WPS failure result not shown correctly")
+ if "Failure Reason: WEP Prohibited" not in status:
+ raise Exception("Failure reason not reported correctly")
+ if "Peer Address: " + dev[0].p2p_interface_addr() not in status:
+ raise Exception("Peer address not shown correctly")
+
+def test_ap_wps_wep_enroll(dev, apdev):
+ """WPS 2.0 STA rejecting WEP configuration"""
+ ssid = "test-wps-wep"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "skip_cred_build": "1", "extra_cred": "wps-wep-cred"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL event timed out")
+ if "msg=12" not in ev or "reason=2 (WEP Prohibited)" not in ev:
+ raise Exception("Unexpected WPS-FAIL event: " + ev)
+
+@remote_compatible
+def test_ap_wps_ie_fragmentation(dev, apdev):
+ """WPS AP using fragmented WPS IE"""
+ ssid = "test-wps-ie-fragmentation"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "1234567890abcdef1234567890abcdef",
+ "manufacturer": "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
+ "model_name": "1234567890abcdef1234567890abcdef",
+ "model_number": "1234567890abcdef1234567890abcdef",
+ "serial_number": "1234567890abcdef1234567890abcdef"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if "wps_device_name" not in bss or bss['wps_device_name'] != "1234567890abcdef1234567890abcdef":
+ logger.info("Device Name not received correctly")
+ logger.info(bss)
+ # This can fail if Probe Response frame is missed and Beacon frame was
+ # used to fill in the BSS entry. This can happen, e.g., during heavy
+ # load every now and then and is not really an error, so try to
+ # workaround by runnign another scan.
+ dev[0].scan(freq="2412", only_new=True)
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if not bss or "wps_device_name" not in bss or bss['wps_device_name'] != "1234567890abcdef1234567890abcdef":
+ logger.info(bss)
+ raise Exception("Device Name not received correctly")
+ if len(re.findall("dd..0050f204", bss['ie'])) != 2:
+ raise Exception("Unexpected number of WPS IEs")
+
+def get_psk(pskfile):
+ psks = {}
+ with open(pskfile, "r") as f:
+ lines = f.read().splitlines()
+ for l in lines:
+ if l == "# WPA PSKs":
+ continue
+ vals = l.split(' ')
+ if len(vals) != 3 or vals[0] != "wps=1":
+ continue
+ addr = vals[1]
+ psk = vals[2]
+ psks[addr] = psk
+ return psks
+
+def test_ap_wps_per_station_psk(dev, apdev):
+ """WPS PBC provisioning with per-station PSK"""
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ addr2 = dev[2].own_addr()
+ ssid = "wps"
+ appin = "12345670"
+ pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
+ try:
+ os.remove(pskfile)
+ except:
+ pass
+
+ hapd = None
+ try:
+ with open(pskfile, "w") as f:
+ f.write("# WPA PSKs\n")
+
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP", "ap_pin": appin,
+ "wpa_psk_file": pskfile}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ logger.info("First enrollee")
+ hapd.request("WPS_PBC")
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+
+ logger.info("Second enrollee")
+ hapd.request("WPS_PBC")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[1].wait_connected(timeout=30)
+
+ logger.info("External registrar")
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[2].wps_reg(apdev[0]['bssid'], appin)
+
+ logger.info("Verifying PSK results")
+ psks = get_psk(pskfile)
+ if addr0 not in psks:
+ raise Exception("No PSK recorded for sta0")
+ if addr1 not in psks:
+ raise Exception("No PSK recorded for sta1")
+ if addr2 not in psks:
+ raise Exception("No PSK recorded for sta2")
+ if psks[addr0] == psks[addr1]:
+ raise Exception("Same PSK recorded for sta0 and sta1")
+ if psks[addr0] == psks[addr2]:
+ raise Exception("Same PSK recorded for sta0 and sta2")
+ if psks[addr1] == psks[addr2]:
+ raise Exception("Same PSK recorded for sta1 and sta2")
+
+ dev[0].request("REMOVE_NETWORK all")
+ logger.info("Second external registrar")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].wps_reg(apdev[0]['bssid'], appin)
+ psks2 = get_psk(pskfile)
+ if addr0 not in psks2:
+ raise Exception("No PSK recorded for sta0(reg)")
+ if psks[addr0] == psks2[addr0]:
+ raise Exception("Same PSK recorded for sta0(enrollee) and sta0(reg)")
+ finally:
+ os.remove(pskfile)
+ if hapd:
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ dev[2].request("DISCONNECT")
+ hapd.disable()
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ dev[2].flush_scan_cache()
+
+def test_ap_wps_per_station_psk_preset(dev, apdev):
+ """WPS PIN provisioning with per-station PSK preset"""
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ addr2 = dev[2].own_addr()
+ ssid = "wps"
+ appin = "12345670"
+ pskfile = "/tmp/ap_wps_per_enrollee_psk_preset.psk_file"
+ try:
+ os.remove(pskfile)
+ except:
+ pass
+
+ hapd = None
+ try:
+ with open(pskfile, "w") as f:
+ f.write("# WPA PSKs\n")
+ f.write("wps=1 " + addr0 + " preset-passphrase-0\n")
+ f.write("wps=1 " + addr2 + " preset-passphrase-2\n")
+
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP", "ap_pin": appin,
+ "wpa_psk_file": pskfile}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ logger.info("First enrollee")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("WPS_PIN %s %s" % (bssid, pin))
+ dev[0].wait_connected(timeout=30)
+
+ logger.info("Second enrollee")
+ pin = dev[1].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[1].scan_for_bss(bssid, freq=2412)
+ dev[1].request("WPS_PIN %s %s" % (bssid, pin))
+ dev[1].wait_connected(timeout=30)
+
+ logger.info("External registrar")
+ dev[2].scan_for_bss(bssid, freq=2412)
+ dev[2].wps_reg(bssid, appin)
+
+ logger.info("Verifying PSK results")
+ psks = get_psk(pskfile)
+ if addr0 not in psks:
+ raise Exception("No PSK recorded for sta0")
+ if addr1 not in psks:
+ raise Exception("No PSK recorded for sta1")
+ if addr2 not in psks:
+ raise Exception("No PSK recorded for sta2")
+ logger.info("PSK[0]: " + psks[addr0])
+ logger.info("PSK[1]: " + psks[addr1])
+ logger.info("PSK[2]: " + psks[addr2])
+ if psks[addr0] == psks[addr1]:
+ raise Exception("Same PSK recorded for sta0 and sta1")
+ if psks[addr0] == psks[addr2]:
+ raise Exception("Same PSK recorded for sta0 and sta2")
+ if psks[addr1] == psks[addr2]:
+ raise Exception("Same PSK recorded for sta1 and sta2")
+ pmk0 = hapd.request("GET_PMK " + addr0)
+ pmk1 = hapd.request("GET_PMK " + addr1)
+ pmk2 = hapd.request("GET_PMK " + addr2)
+ logger.info("PMK[0]: " + pmk0)
+ logger.info("PMK[1]: " + pmk1)
+ logger.info("PMK[2]: " + pmk2)
+ if pmk0 != "565faec21ff04702d9d17c464e1301efd36c8a3ea46bb866b4bec7fed4384579":
+ raise Exception("PSK[0] mismatch")
+ if psks[addr1] != pmk1:
+ raise Exception("PSK[1] mismatch")
+ if psks[addr2] != pmk2:
+ raise Exception("PSK[2] mismatch")
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ logger.info("First enrollee again")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("WPS_PIN %s %s" % (bssid, pin))
+ dev[0].wait_connected(timeout=30)
+ psks2 = get_psk(pskfile)
+ if addr0 not in psks2:
+ raise Exception("No PSK recorded for sta0 (2)")
+ if psks[addr0] != psks2[addr0]:
+ raise Exception("Different PSK recorded for sta0(enrollee) and sta0(enrollee 2)")
+ finally:
+ os.remove(pskfile)
+
+def test_ap_wps_per_station_psk_failure(dev, apdev):
+ """WPS PBC provisioning with per-station PSK (file not writable)"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ ssid = "wps"
+ appin = "12345670"
+ pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
+ try:
+ os.remove(pskfile)
+ except:
+ pass
+
+ hapd = None
+ try:
+ with open(pskfile, "w") as f:
+ f.write("# WPA PSKs\n")
+
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP", "ap_pin": appin,
+ "wpa_psk_file": pskfile}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" in hapd.request("SET wpa_psk_file /tmp/does/not/exists/ap_wps_per_enrollee_psk_failure.psk_file"):
+ raise Exception("Failed to set wpa_psk_file")
+
+ logger.info("First enrollee")
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+
+ logger.info("Second enrollee")
+ hapd.request("WPS_PBC")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[1].wait_connected(timeout=30)
+
+ logger.info("External registrar")
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[2].wps_reg(apdev[0]['bssid'], appin)
+
+ logger.info("Verifying PSK results")
+ psks = get_psk(pskfile)
+ if len(psks) > 0:
+ raise Exception("PSK recorded unexpectedly")
+ finally:
+ if hapd:
+ for i in range(3):
+ dev[i].request("DISCONNECT")
+ hapd.disable()
+ for i in range(3):
+ dev[i].flush_scan_cache()
+ os.remove(pskfile)
+
+def test_ap_wps_pin_request_file(dev, apdev):
+ """WPS PIN provisioning with configured AP"""
+ ssid = "wps"
+ pinfile = "/tmp/ap_wps_pin_request_file.log"
+ if os.path.exists(pinfile):
+ os.remove(pinfile)
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wps_pin_requests": pinfile,
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ uuid = dev[0].get_status_field("uuid")
+ pin = dev[0].wps_read_pin()
+ try:
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = hapd.wait_event(["WPS-PIN-NEEDED"], timeout=15)
+ if ev is None:
+ raise Exception("PIN needed event not shown")
+ if uuid not in ev:
+ raise Exception("UUID mismatch")
+ dev[0].request("WPS_CANCEL")
+ success = False
+ with open(pinfile, "r") as f:
+ lines = f.readlines()
+ for l in lines:
+ if uuid in l:
+ success = True
+ break
+ if not success:
+ raise Exception("PIN request entry not in the log file")
+ finally:
+ try:
+ os.remove(pinfile)
+ except:
+ pass
+
+def test_ap_wps_auto_setup_with_config_file(dev, apdev):
+ """WPS auto-setup with configuration file"""
+ skip_without_tkip(dev[0])
+ conffile = "/tmp/ap_wps_auto_setup_with_config_file.conf"
+ ifname = apdev[0]['ifname']
+ try:
+ with open(conffile, "w") as f:
+ f.write("driver=nl80211\n")
+ f.write("hw_mode=g\n")
+ f.write("channel=1\n")
+ f.write("ieee80211n=1\n")
+ f.write("interface=%s\n" % ifname)
+ f.write("ctrl_interface=/var/run/hostapd\n")
+ f.write("ssid=wps\n")
+ f.write("eap_server=1\n")
+ f.write("wps_state=1\n")
+ hapd = hostapd.add_bss(apdev[0], ifname, conffile)
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ with open(conffile, "r") as f:
+ lines = f.read().splitlines()
+ vals = dict()
+ for l in lines:
+ try:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ except ValueError as e:
+ if "# WPS configuration" in l:
+ pass
+ else:
+ raise Exception("Unexpected configuration line: " + l)
+ if vals['ieee80211n'] != '1' or vals['wps_state'] != '2' or "WPA-PSK" not in vals['wpa_key_mgmt']:
+ raise Exception("Incorrect configuration: " + str(vals))
+ finally:
+ try:
+ os.remove(conffile)
+ except:
+ pass
+
+@long_duration_test
+def test_ap_wps_pbc_timeout(dev, apdev):
+ """wpa_supplicant PBC walk time and WPS ER SelReg timeout"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hapd = add_ssdp_ap(apdev[0], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+ urls = upnp_get_urls(location)
+ eventurl = urlparse(urls['event_sub_url'])
+ ctrlurl = urlparse(urls['control_url'])
+
+ url = urlparse(location)
+ conn = HTTPConnection(url.netloc)
+
+ class WPSERHTTPServer(StreamRequestHandler):
+ def handle(self):
+ data = self.rfile.readline().strip()
+ logger.debug(data)
+ self.wfile.write(gen_wps_event())
+
+ server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
+ server.timeout = 1
+
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ sid = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid)
+
+ msg = '''<?xml version="1.0"?>
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+<s:Body>
+<u:SetSelectedRegistrar xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">
+<NewMessage>EEoAARAQQQABARASAAIAABBTAAIxSBBJAA4ANyoAASABBv///////xBIABA2LbR7pTpRkYj7
+VFi5hrLk
+</NewMessage>
+</u:SetSelectedRegistrar>
+</s:Body>
+</s:Envelope>'''
+ headers = {"Content-type": 'text/xml; charset="utf-8"'}
+ headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % "SetSelectedRegistrar"
+ conn.request("POST", ctrlurl.path, msg, headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ server.handle_request()
+
+ logger.info("Start WPS_PBC and wait for PBC walk time expiration")
+ if "OK" not in dev[0].request("WPS_PBC"):
+ raise Exception("WPS_PBC failed")
+
+ start = os.times()[4]
+
+ server.handle_request()
+ dev[1].request("BSS_FLUSH 0")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True,
+ only_new=True)
+ bss = dev[1].get_bss(apdev[0]['bssid'])
+ logger.debug("BSS: " + str(bss))
+ if '[WPS-AUTH]' not in bss['flags']:
+ raise Exception("WPS not indicated authorized")
+
+ server.handle_request()
+
+ wps_timeout_seen = False
+
+ while True:
+ hapd.dump_monitor()
+ dev[1].dump_monitor()
+ if not wps_timeout_seen:
+ ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=0)
+ if ev is not None:
+ logger.info("PBC timeout seen")
+ wps_timeout_seen = True
+ else:
+ dev[0].dump_monitor()
+ now = os.times()[4]
+ if now - start > 130:
+ raise Exception("Selected registration information not removed")
+ dev[1].request("BSS_FLUSH 0")
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True,
+ only_new=True)
+ bss = dev[1].get_bss(apdev[0]['bssid'])
+ logger.debug("BSS: " + str(bss))
+ if '[WPS-AUTH]' not in bss['flags']:
+ break
+ server.handle_request()
+
+ server.server_close()
+
+ if wps_timeout_seen:
+ return
+
+ now = os.times()[4]
+ if now < start + 150:
+ dur = start + 150 - now
+ else:
+ dur = 1
+ logger.info("Continue waiting for PBC timeout (%d sec)" % dur)
+ ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=dur)
+ if ev is None:
+ raise Exception("WPS-TIMEOUT not reported")
+
+def add_ssdp_ap(ap, ap_uuid):
+ ssid = "wps-ssdp"
+ ap_pin = "12345670"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo",
+ "friendly_name": "WPS Access Point",
+ "manufacturer_url": "http://www.example.com/",
+ "model_description": "Wireless Access Point",
+ "model_url": "http://www.example.com/model/",
+ "upc": "123456789012"}
+ return hostapd.add_ap(ap, params)
+
+def ssdp_send(msg, no_recv=False):
+ socket.setdefaulttimeout(1)
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+ sock.bind(("127.0.0.1", 0))
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+ if no_recv:
+ return None
+ return sock.recv(1000).decode()
+
+def ssdp_send_msearch(st, no_recv=False):
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX: 1',
+ 'MAN: "ssdp:discover"',
+ 'ST: ' + st,
+ '', ''])
+ return ssdp_send(msg, no_recv=no_recv)
+
+def test_ap_wps_ssdp_msearch(dev, apdev):
+ """WPS AP and SSDP M-SEARCH messages"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0], ap_uuid)
+
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'Host: 239.255.255.250:1900',
+ 'Mx: 1',
+ 'Man: "ssdp:discover"',
+ 'St: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ ssdp_send(msg)
+
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'host:\t239.255.255.250:1900\t\t\t\t \t\t',
+ 'mx: \t1\t\t ',
+ 'man: \t \t "ssdp:discover" ',
+ 'st: urn:schemas-wifialliance-org:device:WFADevice:1\t\t',
+ '', ''])
+ ssdp_send(msg)
+
+ ssdp_send_msearch("ssdp:all")
+ ssdp_send_msearch("upnp:rootdevice")
+ ssdp_send_msearch("uuid:" + ap_uuid)
+ ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1")
+ ssdp_send_msearch("urn:schemas-wifialliance-org:device:WFADevice:1")
+
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST:\t239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 130',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ ssdp_send(msg, no_recv=True)
+
+def test_ap_wps_ssdp_invalid_msearch(dev, apdev):
+ """WPS AP and invalid SSDP M-SEARCH messages"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0], ap_uuid)
+
+ socket.setdefaulttimeout(1)
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+ sock.bind(("127.0.0.1", 0))
+
+ logger.debug("Missing MX")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ logger.debug("Negative MX")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX: -1',
+ 'MAN: "ssdp:discover"',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ logger.debug("Invalid MX")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX; 1',
+ 'MAN: "ssdp:discover"',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ logger.debug("Missing MAN")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ logger.debug("Invalid MAN")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX: 1',
+ 'MAN: foo',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MX: 1',
+ 'MAN; "ssdp:discover"',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ logger.debug("Missing HOST")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ logger.debug("Missing ST")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ logger.debug("Mismatching ST")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: uuid:16d5f8a9-4ee4-4f5e-81f9-cc6e2f47f42d',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: foo:bar',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: foobar',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ logger.debug("Invalid ST")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST; urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ logger.debug("Invalid M-SEARCH")
+ msg = '\r\n'.join([
+ 'M+SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+ msg = '\r\n'.join([
+ 'M-SEARCH-* HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ logger.debug("Invalid message format")
+ sock.sendto(b"NOTIFY * HTTP/1.1", ("239.255.255.250", 1900))
+ msg = '\r'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ try:
+ r = sock.recv(1000)
+ raise Exception("Unexpected M-SEARCH response: " + r)
+ except socket.timeout:
+ pass
+
+ logger.debug("Valid M-SEARCH")
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+
+ try:
+ r = sock.recv(1000)
+ pass
+ except socket.timeout:
+ raise Exception("No SSDP response")
+
+def test_ap_wps_ssdp_burst(dev, apdev):
+ """WPS AP and SSDP burst"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0], ap_uuid)
+
+ msg = '\r\n'.join([
+ 'M-SEARCH * HTTP/1.1',
+ 'HOST: 239.255.255.250:1900',
+ 'MAN: "ssdp:discover"',
+ 'MX: 1',
+ 'ST: urn:schemas-wifialliance-org:device:WFADevice:1',
+ '', ''])
+ socket.setdefaulttimeout(1)
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+ sock.bind(("127.0.0.1", 0))
+ for i in range(0, 25):
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+ resp = 0
+ while True:
+ try:
+ r = sock.recv(1000).decode()
+ if not r.startswith("HTTP/1.1 200 OK\r\n"):
+ raise Exception("Unexpected message: " + r)
+ resp += 1
+ except socket.timeout:
+ break
+ if resp < 20:
+ raise Exception("Too few SSDP responses")
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+ sock.bind(("127.0.0.1", 0))
+ for i in range(0, 25):
+ sock.sendto(msg.encode(), ("239.255.255.250", 1900))
+ while True:
+ try:
+ r = sock.recv(1000).decode()
+ if ap_uuid in r:
+ break
+ except socket.timeout:
+ raise Exception("No SSDP response")
+
+def ssdp_get_location(uuid):
+ res = ssdp_send_msearch("uuid:" + uuid)
+ location = None
+ for l in res.splitlines():
+ if l.lower().startswith("location:"):
+ location = l.split(':', 1)[1].strip()
+ break
+ if location is None:
+ raise Exception("No UPnP location found")
+ return location
+
+def upnp_get_urls(location):
+ if sys.version_info[0] > 2:
+ conn = urlopen(location)
+ else:
+ conn = urlopen(location, proxies={})
+ tree = ET.parse(conn)
+ root = tree.getroot()
+ urn = '{urn:schemas-upnp-org:device-1-0}'
+ service = root.find("./" + urn + "device/" + urn + "serviceList/" + urn + "service")
+ res = {}
+ res['scpd_url'] = urljoin(location, service.find(urn + 'SCPDURL').text)
+ res['control_url'] = urljoin(location,
+ service.find(urn + 'controlURL').text)
+ res['event_sub_url'] = urljoin(location,
+ service.find(urn + 'eventSubURL').text)
+ return res
+
+def upnp_soap_action(conn, path, action, include_soap_action=True,
+ soap_action_override=None, newmsg=None, neweventtype=None,
+ neweventmac=None):
+ soapns = 'http://schemas.xmlsoap.org/soap/envelope/'
+ wpsns = 'urn:schemas-wifialliance-org:service:WFAWLANConfig:1'
+ ET.register_namespace('soapenv', soapns)
+ ET.register_namespace('wfa', wpsns)
+ attrib = {}
+ attrib['{%s}encodingStyle' % soapns] = 'http://schemas.xmlsoap.org/soap/encoding/'
+ root = ET.Element("{%s}Envelope" % soapns, attrib=attrib)
+ body = ET.SubElement(root, "{%s}Body" % soapns)
+ act = ET.SubElement(body, "{%s}%s" % (wpsns, action))
+ if newmsg:
+ msg = ET.SubElement(act, "NewMessage")
+ msg.text = base64.b64encode(newmsg.encode()).decode()
+ if neweventtype:
+ msg = ET.SubElement(act, "NewWLANEventType")
+ msg.text = neweventtype
+ if neweventmac:
+ msg = ET.SubElement(act, "NewWLANEventMAC")
+ msg.text = neweventmac
+
+ headers = {"Content-type": 'text/xml; charset="utf-8"'}
+ if include_soap_action:
+ headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % action
+ elif soap_action_override:
+ headers["SOAPAction"] = soap_action_override
+ decl = b'<?xml version=\'1.0\' encoding=\'utf8\'?>\n'
+ conn.request("POST", path, decl + ET.tostring(root), headers)
+ return conn.getresponse()
+
+def test_ap_wps_upnp(dev, apdev):
+ """WPS AP and UPnP operations"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+ urls = upnp_get_urls(location)
+
+ if sys.version_info[0] > 2:
+ conn = urlopen(urls['scpd_url'])
+ else:
+ conn = urlopen(urls['scpd_url'], proxies={})
+ scpd = conn.read()
+
+ if sys.version_info[0] > 2:
+ try:
+ conn = urlopen(urljoin(location, "unknown.html"))
+ raise Exception("Unexpected HTTP response to GET unknown URL")
+ except HTTPError as e:
+ if e.code != 404:
+ raise Exception("Unexpected HTTP response to GET unknown URL")
+ else:
+ conn = urlopen(urljoin(location, "unknown.html"), proxies={})
+ if conn.getcode() != 404:
+ raise Exception("Unexpected HTTP response to GET unknown URL")
+
+ url = urlparse(location)
+ conn = HTTPConnection(url.netloc)
+ #conn.set_debuglevel(1)
+ headers = {"Content-type": 'text/xml; charset="utf-8"',
+ "SOAPAction": '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#GetDeviceInfo"'}
+ conn.request("POST", "hello", "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 404:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ conn.request("UNKNOWN", "hello", "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 501:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ headers = {"Content-type": 'text/xml; charset="utf-8"',
+ "SOAPAction": '"urn:some-unknown-action#GetDeviceInfo"'}
+ ctrlurl = urlparse(urls['control_url'])
+ conn.request("POST", ctrlurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 401:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("GetDeviceInfo without SOAPAction header")
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo",
+ include_soap_action=False)
+ if resp.status != 401:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("GetDeviceInfo with invalid SOAPAction header")
+ for act in ["foo",
+ "urn:schemas-wifialliance-org:service:WFAWLANConfig:1#GetDeviceInfo",
+ '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1"',
+ '"urn:schemas-wifialliance-org:service:WFAWLANConfig:123#GetDevice']:
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo",
+ include_soap_action=False,
+ soap_action_override=act)
+ if resp.status != 401:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ dev = resp.read().decode()
+ if "NewDeviceInfo" not in dev:
+ raise Exception("Unexpected GetDeviceInfo response")
+
+ logger.debug("PutMessage without required parameters")
+ resp = upnp_soap_action(conn, ctrlurl.path, "PutMessage")
+ if resp.status != 600:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("PutWLANResponse without required parameters")
+ resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse")
+ if resp.status != 600:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("SetSelectedRegistrar from unregistered ER")
+ resp = upnp_soap_action(conn, ctrlurl.path, "SetSelectedRegistrar")
+ if resp.status != 501:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Unknown action")
+ resp = upnp_soap_action(conn, ctrlurl.path, "Unknown")
+ if resp.status != 401:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+def test_ap_wps_upnp_subscribe(dev, apdev):
+ """WPS AP and UPnP event subscription"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hapd = add_ssdp_ap(apdev[0], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+ urls = upnp_get_urls(location)
+ eventurl = urlparse(urls['event_sub_url'])
+
+ url = urlparse(location)
+ conn = HTTPConnection(url.netloc)
+ #conn.set_debuglevel(1)
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", "hello", "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ headers = {"NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:foobar",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Valid subscription")
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ sid = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid)
+
+ logger.debug("Invalid re-subscription")
+ headers = {"NT": "upnp:event",
+ "sid": "123456734567854",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Invalid re-subscription")
+ headers = {"NT": "upnp:event",
+ "sid": "uuid:123456734567854",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Invalid re-subscription")
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "sid": sid,
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("SID mismatch in re-subscription")
+ headers = {"NT": "upnp:event",
+ "sid": "uuid:4c2bca79-1ff4-4e43-85d4-952a2b8a51fb",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Valid re-subscription")
+ headers = {"NT": "upnp:event",
+ "sid": sid,
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ sid2 = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid2)
+
+ if sid != sid2:
+ raise Exception("Unexpected SID change")
+
+ logger.debug("Valid re-subscription")
+ headers = {"NT": "upnp:event",
+ "sid": "uuid: \t \t" + sid.split(':')[1],
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Invalid unsubscription")
+ headers = {"sid": sid}
+ conn.request("UNSUBSCRIBE", "/hello", "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ headers = {"foo": "bar"}
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Valid unsubscription")
+ headers = {"sid": sid}
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Unsubscription for not existing SID")
+ headers = {"sid": sid}
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 412:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Invalid unsubscription")
+ headers = {"sid": " \t \tfoo"}
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Invalid unsubscription")
+ headers = {"sid": "uuid:\t \tfoo"}
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Invalid unsubscription")
+ headers = {"NT": "upnp:event",
+ "sid": sid}
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "sid": sid}
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 400:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.debug("Valid subscription with multiple callbacks")
+ headers = {"callback": '<http://127.0.0.1:12345/event> <http://127.0.0.1:12345/event>\t<http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event><http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ sid = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid)
+
+ # Force subscription to be deleted due to errors
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ with alloc_fail(hapd, 1, "event_build_message"):
+ for i in range(10):
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+ dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[1].request("WPS_CANCEL")
+ dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[2].request("WPS_CANCEL")
+ if i % 4 == 1:
+ time.sleep(1)
+ else:
+ time.sleep(0.1)
+ time.sleep(0.2)
+
+ headers = {"sid": sid}
+ conn.request("UNSUBSCRIBE", eventurl.path, "", headers)
+ resp = conn.getresponse()
+ if resp.status != 200 and resp.status != 412:
+ raise Exception("Unexpected HTTP response for UNSUBSCRIBE: %d" % resp.status)
+
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ with alloc_fail(hapd, 1, "http_client_addr;event_send_start"):
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response for SUBSCRIBE: %d" % resp.status)
+ sid = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid)
+
+ headers = {"sid": sid}
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response for UNSUBSCRIBE: %d" % resp.status)
+
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ sid = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid)
+
+ with alloc_fail(hapd, 1, "=wps_upnp_event_add"):
+ for i in range(2):
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+ dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[1].request("WPS_CANCEL")
+ dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[2].request("WPS_CANCEL")
+ if i == 0:
+ time.sleep(1)
+ else:
+ time.sleep(0.1)
+
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ with alloc_fail(hapd, 1, "wpabuf_dup;wps_upnp_event_add"):
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+ dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[1].request("WPS_CANCEL")
+ dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[2].request("WPS_CANCEL")
+ time.sleep(0.1)
+
+ with fail_test(hapd, 1, "os_get_random;uuid_make;subscription_start"):
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 500:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ with alloc_fail(hapd, 1, "=subscription_start"):
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 500:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ headers = {"callback": '',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 500:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ headers = {"callback": ' <',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 500:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ with alloc_fail(hapd, 1, "wpabuf_alloc;subscription_first_event"):
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 500:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ with alloc_fail(hapd, 1, "wps_upnp_event_add;subscription_first_event"):
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 500:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ with alloc_fail(hapd, 1, "subscr_addr_add_url"):
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 500:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ with alloc_fail(hapd, 2, "subscr_addr_add_url"):
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 500:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ for i in range(6):
+ headers = {"callback": '<http://127.0.0.1:%d/event>' % (12345 + i),
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ with alloc_fail(hapd, 1, "=upnp_wps_device_send_wlan_event"):
+ dev[1].dump_monitor()
+ dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[1].request("WPS_CANCEL")
+ time.sleep(0.1)
+
+ with alloc_fail(hapd, 1, "wpabuf_alloc;upnp_wps_device_send_event"):
+ dev[1].dump_monitor()
+ dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[1].request("WPS_CANCEL")
+ time.sleep(0.1)
+
+ with alloc_fail(hapd, 1,
+ "base64_gen_encode;?base64_encode;upnp_wps_device_send_wlan_event"):
+ dev[1].dump_monitor()
+ dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[1].request("WPS_CANCEL")
+ time.sleep(0.1)
+
+ hapd.disable()
+ with alloc_fail(hapd, 1, "get_netif_info"):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE succeeded during OOM")
+
+def test_ap_wps_upnp_subscribe_events(dev, apdev):
+ """WPS AP and UPnP event subscription and many events"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hapd = add_ssdp_ap(apdev[0], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+ urls = upnp_get_urls(location)
+ eventurl = urlparse(urls['event_sub_url'])
+
+ class WPSERHTTPServer(StreamRequestHandler):
+ def handle(self):
+ data = self.rfile.readline().strip()
+ logger.debug(data)
+ self.wfile.write(gen_wps_event())
+
+ server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
+ server.timeout = 1
+
+ url = urlparse(location)
+ conn = HTTPConnection(url.netloc)
+
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ sid = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid)
+
+ # Fetch the first event message
+ server.handle_request()
+
+ # Force subscription event queue to reach the maximum length by generating
+ # new proxied events without the ER fetching any of the pending events.
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ for i in range(16):
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+ dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[2].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[1].request("WPS_CANCEL")
+ dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ dev[2].request("WPS_CANCEL")
+ if i % 4 == 1:
+ time.sleep(1)
+ else:
+ time.sleep(0.1)
+
+ hapd.request("WPS_PIN any 12345670")
+ dev[1].dump_monitor()
+ dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("WPS success not reported")
+
+ # Close the WPS ER HTTP server without fetching all the pending events.
+ # This tests hostapd code path that clears subscription and the remaining
+ # event queue when the interface is deinitialized.
+ server.handle_request()
+ server.server_close()
+
+ dev[1].wait_connected()
+
+def test_ap_wps_upnp_http_proto(dev, apdev):
+ """WPS AP and UPnP/HTTP protocol testing"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+
+ url = urlparse(location)
+ conn = HTTPConnection(url.netloc, timeout=0.2)
+ #conn.set_debuglevel(1)
+
+ conn.request("HEAD", "hello")
+ resp = conn.getresponse()
+ if resp.status != 501:
+ raise Exception("Unexpected response to HEAD: " + str(resp.status))
+ conn.close()
+
+ for cmd in ["PUT", "DELETE", "TRACE", "CONNECT", "M-SEARCH", "M-POST"]:
+ try:
+ conn.request(cmd, "hello")
+ resp = conn.getresponse()
+ except Exception as e:
+ pass
+ conn.close()
+
+ headers = {"Content-Length": 'abc'}
+ conn.request("HEAD", "hello", "\r\n\r\n", headers)
+ try:
+ resp = conn.getresponse()
+ except Exception as e:
+ pass
+ conn.close()
+
+ headers = {"Content-Length": '-10'}
+ conn.request("HEAD", "hello", "\r\n\r\n", headers)
+ try:
+ resp = conn.getresponse()
+ except Exception as e:
+ pass
+ conn.close()
+
+ headers = {"Content-Length": '10000000000000'}
+ conn.request("HEAD", "hello", "\r\n\r\nhello", headers)
+ try:
+ resp = conn.getresponse()
+ except Exception as e:
+ pass
+ conn.close()
+
+ headers = {"Transfer-Encoding": 'abc'}
+ conn.request("HEAD", "hello", "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 501:
+ raise Exception("Unexpected response to HEAD: " + str(resp.status))
+ conn.close()
+
+ headers = {"Transfer-Encoding": 'chunked'}
+ conn.request("HEAD", "hello", "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 501:
+ raise Exception("Unexpected response to HEAD: " + str(resp.status))
+ conn.close()
+
+ # Too long a header
+ conn.request("HEAD", 5000 * 'A')
+ try:
+ resp = conn.getresponse()
+ except Exception as e:
+ pass
+ conn.close()
+
+ # Long URL but within header length limits
+ conn.request("HEAD", 3000 * 'A')
+ resp = conn.getresponse()
+ if resp.status != 501:
+ raise Exception("Unexpected response to HEAD: " + str(resp.status))
+ conn.close()
+
+ headers = {"Content-Length": '20'}
+ conn.request("POST", "hello", 10 * 'A' + "\r\n\r\n", headers)
+ try:
+ resp = conn.getresponse()
+ except Exception as e:
+ pass
+ conn.close()
+
+ conn.request("POST", "hello", 5000 * 'A' + "\r\n\r\n")
+ resp = conn.getresponse()
+ if resp.status != 404:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ conn.close()
+
+ conn.request("POST", "hello", 60000 * 'A' + "\r\n\r\n")
+ try:
+ resp = conn.getresponse()
+ except Exception as e:
+ pass
+ conn.close()
+
+def test_ap_wps_upnp_http_proto_chunked(dev, apdev):
+ """WPS AP and UPnP/HTTP protocol testing for chunked encoding"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+
+ url = urlparse(location)
+ conn = HTTPConnection(url.netloc)
+ #conn.set_debuglevel(1)
+
+ headers = {"Transfer-Encoding": 'chunked'}
+ conn.request("POST", "hello",
+ "a\r\nabcdefghij\r\n" + "2\r\nkl\r\n" + "0\r\n\r\n",
+ headers)
+ resp = conn.getresponse()
+ if resp.status != 404:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ conn.close()
+
+ conn.putrequest("POST", "hello")
+ conn.putheader('Transfer-Encoding', 'chunked')
+ conn.endheaders()
+ conn.send(b"a\r\nabcdefghij\r\n")
+ time.sleep(0.1)
+ conn.send(b"2\r\nkl\r\n")
+ conn.send(b"0\r\n\r\n")
+ resp = conn.getresponse()
+ if resp.status != 404:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ conn.close()
+
+ conn.putrequest("POST", "hello")
+ conn.putheader('Transfer-Encoding', 'chunked')
+ conn.endheaders()
+ completed = False
+ try:
+ for i in range(20000):
+ conn.send(b"1\r\nZ\r\n")
+ conn.send(b"0\r\n\r\n")
+ resp = conn.getresponse()
+ completed = True
+ except Exception as e:
+ pass
+ conn.close()
+ if completed:
+ raise Exception("Too long chunked request did not result in connection reset")
+
+ headers = {"Transfer-Encoding": 'chunked'}
+ conn.request("POST", "hello", "80000000\r\na", headers)
+ try:
+ resp = conn.getresponse()
+ except Exception as e:
+ pass
+ conn.close()
+
+ conn.request("POST", "hello", "10000000\r\na", headers)
+ try:
+ resp = conn.getresponse()
+ except Exception as e:
+ pass
+ conn.close()
+
+@remote_compatible
+def test_ap_wps_disabled(dev, apdev):
+ """WPS operations while WPS is disabled"""
+ ssid = "test-wps-disabled"
+ hapd = hostapd.add_ap(apdev[0], {"ssid": ssid})
+ if "FAIL" not in hapd.request("WPS_PBC"):
+ raise Exception("WPS_PBC succeeded unexpectedly")
+ if "FAIL" not in hapd.request("WPS_CANCEL"):
+ raise Exception("WPS_CANCEL succeeded unexpectedly")
+
+def test_ap_wps_mixed_cred(dev, apdev):
+ """WPS 2.0 STA merging mixed mode WPA/WPA2 credentials"""
+ skip_without_tkip(dev[0])
+ ssid = "test-wps-wep"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "skip_cred_build": "1", "extra_cred": "wps-mixed-cred"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=30)
+ if ev is None:
+ raise Exception("WPS-SUCCESS event timed out")
+ nets = dev[0].list_networks()
+ if len(nets) != 1:
+ raise Exception("Unexpected number of network blocks")
+ id = nets[0]['id']
+ proto = dev[0].get_network(id, "proto")
+ if proto != "WPA RSN":
+ raise Exception("Unexpected merged proto field value: " + proto)
+ pairwise = dev[0].get_network(id, "pairwise")
+ p = pairwise.split()
+ if "CCMP" not in p or "TKIP" not in p:
+ raise Exception("Unexpected merged pairwise field value: " + pairwise)
+
+@remote_compatible
+def test_ap_wps_while_connected(dev, apdev):
+ """WPS PBC provisioning while connected to another AP"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+
+ hostapd.add_ap(apdev[1], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Unexpected BSSID")
+
+@remote_compatible
+def test_ap_wps_while_connected_no_autoconnect(dev, apdev):
+ """WPS PBC provisioning while connected to another AP and STA_AUTOCONNECT disabled"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+
+ hostapd.add_ap(apdev[1], {"ssid": "open"})
+
+ try:
+ dev[0].request("STA_AUTOCONNECT 0")
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+ logger.info("WPS provisioning step")
+ hapd.request("WPS_PBC")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Unexpected BSSID")
+ finally:
+ dev[0].request("STA_AUTOCONNECT 1")
+
+@remote_compatible
+def test_ap_wps_from_event(dev, apdev):
+ """WPS PBC event on AP to enable PBC"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+
+ ev = hapd.wait_event(['WPS-ENROLLEE-SEEN'], timeout=15)
+ if ev is None:
+ raise Exception("No WPS-ENROLLEE-SEEN event on AP")
+ vals = ev.split(' ')
+ if vals[1] != dev[0].p2p_interface_addr():
+ raise Exception("Unexpected enrollee address: " + vals[1])
+ if vals[5] != '4':
+ raise Exception("Unexpected Device Password Id: " + vals[5])
+ hapd.request("WPS_PBC")
+ dev[0].wait_connected(timeout=30)
+
+def test_ap_wps_ap_scan_2(dev, apdev):
+ """AP_SCAN 2 for WPS"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ hapd.request("WPS_PBC")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.dump_monitor()
+
+ if "OK" not in wpas.request("AP_SCAN 2"):
+ raise Exception("Failed to set AP_SCAN 2")
+
+ wpas.flush_scan_cache()
+ wpas.scan_for_bss(apdev[0]['bssid'], freq="2412")
+ wpas.dump_monitor()
+ wpas.request("WPS_PBC " + apdev[0]['bssid'])
+ ev = wpas.wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-SUCCESS event timed out")
+ wpas.wait_connected(timeout=30)
+ wpas.dump_monitor()
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ id = wpas.list_networks()[0]['id']
+ pairwise = wpas.get_network(id, "pairwise")
+ if "CCMP" not in pairwise.split():
+ raise Exception("Unexpected pairwise parameter value: " + pairwise)
+ group = wpas.get_network(id, "group")
+ if "CCMP" not in group.split():
+ raise Exception("Unexpected group parameter value: " + group)
+ # Need to select a single cipher for ap_scan=2 testing
+ wpas.set_network(id, "pairwise", "CCMP")
+ wpas.set_network(id, "group", "CCMP")
+ wpas.request("BSS_FLUSH 0")
+ wpas.dump_monitor()
+ wpas.request("REASSOCIATE")
+ wpas.wait_connected(timeout=30)
+ wpas.dump_monitor()
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.flush_scan_cache()
+
+@remote_compatible
+def test_ap_wps_eapol_workaround(dev, apdev):
+ """EAPOL workaround code path for 802.1X header length mismatch"""
+ ssid = "test-wps"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1"})
+ bssid = apdev[0]['bssid']
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+ hapd.request("WPS_PBC")
+ dev[0].request("WPS_PBC")
+
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+
+ res = dev[0].request("EAPOL_RX " + bssid + " 020000040193000501FFFF")
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+def test_ap_wps_iteration(dev, apdev):
+ """WPS PIN and iterate through APs without selected registrar"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+
+ ssid2 = "test-wps-conf2"
+ hapd2 = hostapd.add_ap(apdev[1],
+ {"ssid": ssid2, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ pin = dev[0].request("WPS_PIN any")
+
+ # Wait for iteration through all WPS APs to happen before enabling any
+ # Registrar.
+ for i in range(2):
+ ev = dev[0].wait_event(["Associated with"], timeout=30)
+ if ev is None:
+ raise Exception("No association seen")
+ ev = dev[0].wait_event(["WPS-M2D"], timeout=10)
+ if ev is None:
+ raise Exception("No M2D from AP")
+ dev[0].wait_disconnected()
+
+ # Verify that each AP requested PIN
+ ev = hapd.wait_event(["WPS-PIN-NEEDED"], timeout=1)
+ if ev is None:
+ raise Exception("No WPS-PIN-NEEDED event from AP")
+ ev = hapd2.wait_event(["WPS-PIN-NEEDED"], timeout=1)
+ if ev is None:
+ raise Exception("No WPS-PIN-NEEDED event from AP2")
+
+ # Provide PIN to one of the APs and verify that connection gets formed
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].wait_connected(timeout=30)
+
+def test_ap_wps_iteration_error(dev, apdev):
+ """WPS AP iteration on no Selected Registrar and error case with an AP"""
+ ssid = "test-wps-conf-pin"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wps_independent": "1"})
+ hapd.request("SET ext_eapol_frame_io 1")
+ bssid = apdev[0]['bssid']
+ pin = dev[0].wps_read_pin()
+ dev[0].request("WPS_PIN any " + pin)
+
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("No EAPOL-TX (EAP-Request/Identity) from hostapd")
+ dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("No EAPOL-TX (EAP-WSC/Start) from hostapd")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("No CTRL-EVENT-EAP-STARTED")
+
+ # Do not forward any more EAPOL frames to test wpa_supplicant behavior for
+ # a case with an incorrectly behaving WPS AP.
+
+ # Start the real target AP and activate registrar on it.
+ hapd2 = hostapd.add_ap(apdev[1],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wps_independent": "1"})
+ hapd2.request("WPS_PIN any " + pin)
+
+ dev[0].wait_disconnected(timeout=15)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("No CTRL-EVENT-EAP-STARTED for the second AP")
+ ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=15)
+ if ev is None:
+ raise Exception("No WPS-CRED-RECEIVED for the second AP")
+ dev[0].wait_connected(timeout=15)
+
+@remote_compatible
+def test_ap_wps_priority(dev, apdev):
+ """WPS PIN provisioning with configured AP and wps_priority"""
+ ssid = "test-wps-conf-pin"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ logger.info("WPS provisioning step")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ try:
+ dev[0].request("SET wps_priority 6")
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_connected(timeout=30)
+ netw = dev[0].list_networks()
+ prio = dev[0].get_network(netw[0]['id'], 'priority')
+ if prio != '6':
+ raise Exception("Unexpected network priority: " + prio)
+ finally:
+ dev[0].request("SET wps_priority 0")
+
+@remote_compatible
+def test_ap_wps_and_non_wps(dev, apdev):
+ """WPS and non-WPS AP in single hostapd process"""
+ params = {"ssid": "wps", "eap_server": "1", "wps_state": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "no wps"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ appin = hapd.request("WPS_AP_PIN random")
+ if "FAIL" in appin:
+ raise Exception("Could not generate random AP PIN")
+ if appin not in hapd.request("WPS_AP_PIN get"):
+ raise Exception("Could not fetch current AP PIN")
+
+ if "FAIL" in hapd.request("WPS_PBC"):
+ raise Exception("WPS_PBC failed")
+ if "FAIL" in hapd.request("WPS_CANCEL"):
+ raise Exception("WPS_CANCEL failed")
+
+def test_ap_wps_init_oom(dev, apdev):
+ """Initial AP configuration and OOM during PSK generation"""
+ ssid = "test-wps"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with alloc_fail(hapd, 1, "base64_gen_encode;?base64_encode;wps_build_cred"):
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_disconnected()
+
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].wait_connected(timeout=30)
+
+@remote_compatible
+def test_ap_wps_er_oom(dev, apdev):
+ """WPS ER OOM in XML processing"""
+ try:
+ _test_ap_wps_er_oom(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+ dev[1].request("WPS_CANCEL")
+ dev[0].request("DISCONNECT")
+
+def _test_ap_wps_er_oom(dev, apdev):
+ ssid = "wps-er-ap-config"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
+
+ dev[0].connect(ssid, psk="12345678", scan_freq="2412")
+
+ with alloc_fail(dev[0], 1,
+ "base64_gen_decode;?base64_decode;xml_get_base64_item"):
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=3)
+ if ev is not None:
+ raise Exception("Unexpected AP discovery")
+
+ dev[0].request("WPS_ER_STOP")
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ with alloc_fail(dev[0], 1,
+ "base64_gen_decode;?base64_decode;xml_get_base64_item"):
+ dev[1].request("WPS_PBC " + apdev[0]['bssid'])
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("PBC scan failed")
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("Enrollee discovery timed out")
+
+@remote_compatible
+def test_ap_wps_er_init_oom(dev, apdev):
+ """WPS ER and OOM during init"""
+ try:
+ _test_ap_wps_er_init_oom(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_init_oom(dev, apdev):
+ with alloc_fail(dev[0], 1, "wps_er_init"):
+ if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
+ raise Exception("WPS_ER_START succeeded during OOM")
+ with alloc_fail(dev[0], 1, "http_server_init"):
+ if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
+ raise Exception("WPS_ER_START succeeded during OOM")
+ with alloc_fail(dev[0], 2, "http_server_init"):
+ if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
+ raise Exception("WPS_ER_START succeeded during OOM")
+ with alloc_fail(dev[0], 1, "eloop_sock_table_add_sock;?eloop_register_sock;wps_er_ssdp_init"):
+ if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
+ raise Exception("WPS_ER_START succeeded during OOM")
+ with fail_test(dev[0], 1, "os_get_random;wps_er_init"):
+ if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo"):
+ raise Exception("WPS_ER_START succeeded during os_get_random failure")
+
+@remote_compatible
+def test_ap_wps_er_init_fail(dev, apdev):
+ """WPS ER init failure"""
+ if "FAIL" not in dev[0].request("WPS_ER_START ifname=does-not-exist"):
+ dev[0].request("WPS_ER_STOP")
+ raise Exception("WPS_ER_START with non-existing ifname succeeded")
+
+def test_ap_wps_wpa_cli_action(dev, apdev, test_params):
+ """WPS events and wpa_cli action script"""
+ logdir = os.path.abspath(test_params['logdir'])
+ pidfile = os.path.join(logdir, 'ap_wps_wpa_cli_action.wpa_cli.pid')
+ logfile = os.path.join(logdir, 'ap_wps_wpa_cli_action.wpa_cli.res')
+ actionfile = os.path.join(logdir, 'ap_wps_wpa_cli_action.wpa_cli.action.sh')
+
+ with open(actionfile, 'w') as f:
+ f.write('#!/bin/sh\n')
+ f.write('echo $* >> %s\n' % logfile)
+ # Kill the process and wait some time before returning to allow all the
+ # pending events to be processed with some of this happening after the
+ # eloop SIGALRM signal has been scheduled.
+ f.write('if [ $2 = "WPS-SUCCESS" -a -r %s ]; then kill `cat %s`; sleep 1; fi\n' % (pidfile, pidfile))
+
+ os.chmod(actionfile, stat.S_IREAD | stat.S_IWRITE | stat.S_IEXEC |
+ stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH)
+
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+
+ prg = os.path.join(test_params['logdir'],
+ 'alt-wpa_supplicant/wpa_supplicant/wpa_cli')
+ if not os.path.exists(prg):
+ prg = '../../wpa_supplicant/wpa_cli'
+ arg = [prg, '-P', pidfile, '-B', '-i', dev[0].ifname, '-a', actionfile]
+ subprocess.call(arg)
+
+ arg = ['ps', 'ax']
+ cmd = subprocess.Popen(arg, stdout=subprocess.PIPE)
+ out = cmd.communicate()[0].decode()
+ cmd.wait()
+ logger.debug("Processes:\n" + out)
+ if "wpa_cli -P %s -B -i %s" % (pidfile, dev[0].ifname) not in out:
+ raise Exception("Did not see wpa_cli running")
+
+ hapd.request("WPS_PIN any 12345670")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ dev[0].wait_connected(timeout=30)
+
+ for i in range(30):
+ if not os.path.exists(pidfile):
+ break
+ time.sleep(0.1)
+
+ if not os.path.exists(logfile):
+ raise Exception("wpa_cli action results file not found")
+ with open(logfile, 'r') as f:
+ res = f.read()
+ if "WPS-SUCCESS" not in res:
+ raise Exception("WPS-SUCCESS event not seen in action file")
+
+ arg = ['ps', 'ax']
+ cmd = subprocess.Popen(arg, stdout=subprocess.PIPE)
+ out = cmd.communicate()[0].decode()
+ cmd.wait()
+ logger.debug("Remaining processes:\n" + out)
+ if "wpa_cli -P %s -B -i %s" % (pidfile, dev[0].ifname) in out:
+ raise Exception("wpa_cli still running")
+
+ if os.path.exists(pidfile):
+ raise Exception("PID file not removed")
+
+def test_ap_wps_er_ssdp_proto(dev, apdev):
+ """WPS ER SSDP protocol testing"""
+ try:
+ _test_ap_wps_er_ssdp_proto(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_ssdp_proto(dev, apdev):
+ socket.setdefaulttimeout(1)
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind(("239.255.255.250", 1900))
+ if "FAIL" not in dev[0].request("WPS_ER_START ifname=lo foo"):
+ raise Exception("Invalid filter accepted")
+ if "OK" not in dev[0].request("WPS_ER_START ifname=lo 1.2.3.4"):
+ raise Exception("WPS_ER_START with filter failed")
+ (msg, addr) = sock.recvfrom(1000)
+ msg = msg.decode()
+ logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
+ if "M-SEARCH" not in msg:
+ raise Exception("Not an M-SEARCH")
+ sock.sendto(b"FOO", addr)
+ time.sleep(0.1)
+ dev[0].request("WPS_ER_STOP")
+
+ dev[0].request("WPS_ER_START ifname=lo")
+ (msg, addr) = sock.recvfrom(1000)
+ msg = msg.decode()
+ logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
+ if "M-SEARCH" not in msg:
+ raise Exception("Not an M-SEARCH")
+ sock.sendto(b"FOO", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nFOO\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nNTS:foo\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nNTS:ssdp:byebye\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\ncache-control: foo=1\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\ncache-control: max-age=1\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nusn:\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nusn:foo\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nusn: uuid:\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nusn: uuid: \r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nusn: uuid: foo\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nNTS:ssdp:byebye\r\n\r\n", addr)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\n\r\n", addr)
+ with alloc_fail(dev[0], 1, "wps_er_ap_add"):
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
+ time.sleep(0.1)
+ with alloc_fail(dev[0], 2, "wps_er_ap_add"):
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
+ time.sleep(0.1)
+
+ # Add an AP with bogus URL
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
+ # Update timeout on AP without updating URL
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1:12345/foo.xml\r\ncache-control:max-age=1\r\n\r\n", addr)
+ ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=5)
+ if ev is None:
+ raise Exception("No WPS-ER-AP-REMOVE event on max-age timeout")
+
+ # Add an AP with a valid URL (but no server listing to it)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1:12345/foo.xml\r\ncache-control:max-age=1\r\n\r\n", addr)
+ ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=5)
+ if ev is None:
+ raise Exception("No WPS-ER-AP-REMOVE event on max-age timeout")
+
+ sock.close()
+
+wps_event_url = None
+
+def gen_upnp_info(eventSubURL='wps_event', controlURL='wps_control',
+ udn='uuid:27ea801a-9e5c-4e73-bd82-f89cbcd10d7e'):
+ payload = '''<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+<specVersion>
+<major>1</major>
+<minor>0</minor>
+</specVersion>
+<device>
+<deviceType>urn:schemas-wifialliance-org:device:WFADevice:1</deviceType>
+<friendlyName>WPS Access Point</friendlyName>
+<manufacturer>Company</manufacturer>
+<modelName>WAP</modelName>
+<modelNumber>123</modelNumber>
+<serialNumber>12345</serialNumber>
+'''
+ if udn:
+ payload += '<UDN>' + udn + '</UDN>'
+ payload += '''<serviceList>
+<service>
+<serviceType>urn:schemas-wifialliance-org:service:WFAWLANConfig:1</serviceType>
+<serviceId>urn:wifialliance-org:serviceId:WFAWLANConfig1</serviceId>
+<SCPDURL>wps_scpd.xml</SCPDURL>
+'''
+ if controlURL:
+ payload += '<controlURL>' + controlURL + '</controlURL>\n'
+ if eventSubURL:
+ payload += '<eventSubURL>' + eventSubURL + '</eventSubURL>\n'
+ payload += '''</service>
+</serviceList>
+</device>
+</root>
+'''
+ hdr = 'HTTP/1.1 200 OK\r\n' + \
+ 'Content-Type: text/xml; charset="utf-8"\r\n' + \
+ 'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+ 'Connection: close\r\n' + \
+ 'Content-Length: ' + str(len(payload)) + '\r\n' + \
+ 'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+ return (hdr + payload).encode()
+
+def gen_wps_control(payload_override=None):
+ payload = '''<?xml version="1.0"?>
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+<s:Body>
+<u:GetDeviceInfoResponse xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">
+<NewDeviceInfo>EEoAARAQIgABBBBHABAn6oAanlxOc72C+Jy80Q1+ECAABgIAAAADABAaABCJZ7DPtbU3Ust9
+Z3wJF07WEDIAwH45D3i1OqB7eJGwTzqeapS71h3KyXncK2xJZ+xqScrlorNEg6LijBJzG2Ca
++FZli0iliDJd397yAx/jk4nFXco3q5ylBSvSw9dhJ5u1xBKSnTilKGlUHPhLP75PUqM3fot9
+7zwtFZ4bx6x1sBA6oEe2d0aUJmLumQGCiKEIWlnxs44zego/2tAe81bDzdPBM7o5HH/FUhD+
+KoGzFXp51atP+1n9Vta6AkI0Vye99JKLcC6Md9dMJltSVBgd4Xc4lRAEAAIAIxAQAAIADRAN
+AAEBEAgAAgAEEEQAAQIQIQAHQ29tcGFueRAjAANXQVAQJAADMTIzEEIABTEyMzQ1EFQACAAG
+AFDyBAABEBEAC1dpcmVsZXNzIEFQEDwAAQEQAgACAAAQEgACAAAQCQACAAAQLQAEgQIDABBJ
+AAYANyoAASA=
+</NewDeviceInfo>
+</u:GetDeviceInfoResponse>
+</s:Body>
+</s:Envelope>
+'''
+ if payload_override:
+ payload = payload_override
+ hdr = 'HTTP/1.1 200 OK\r\n' + \
+ 'Content-Type: text/xml; charset="utf-8"\r\n' + \
+ 'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+ 'Connection: close\r\n' + \
+ 'Content-Length: ' + str(len(payload)) + '\r\n' + \
+ 'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+ return (hdr + payload).encode()
+
+def gen_wps_event(sid='uuid:7eb3342a-8a5f-47fe-a585-0785bfec6d8a'):
+ payload = ""
+ hdr = 'HTTP/1.1 200 OK\r\n' + \
+ 'Content-Type: text/xml; charset="utf-8"\r\n' + \
+ 'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+ 'Connection: close\r\n' + \
+ 'Content-Length: ' + str(len(payload)) + '\r\n'
+ if sid:
+ hdr += 'SID: ' + sid + '\r\n'
+ hdr += 'Timeout: Second-1801\r\n' + \
+ 'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+ return (hdr + payload).encode()
+
+class WPSAPHTTPServer(StreamRequestHandler):
+ def handle(self):
+ data = self.rfile.readline().decode().strip()
+ logger.info("HTTP server received: " + data)
+ while True:
+ hdr = self.rfile.readline().decode().strip()
+ if len(hdr) == 0:
+ break
+ logger.info("HTTP header: " + hdr)
+ if "CALLBACK:" in hdr:
+ global wps_event_url
+ wps_event_url = hdr.split(' ')[1].strip('<>')
+
+ if "GET /foo.xml" in data:
+ self.handle_upnp_info()
+ elif "POST /wps_control" in data:
+ self.handle_wps_control()
+ elif "SUBSCRIBE /wps_event" in data:
+ self.handle_wps_event()
+ else:
+ self.handle_others(data)
+
+ def handle_upnp_info(self):
+ self.wfile.write(gen_upnp_info())
+
+ def handle_wps_control(self):
+ self.wfile.write(gen_wps_control())
+
+ def handle_wps_event(self):
+ self.wfile.write(gen_wps_event())
+
+ def handle_others(self, data):
+ logger.info("Ignore HTTP request: " + data)
+
+class MyTCPServer(TCPServer):
+ def __init__(self, addr, handler):
+ self.allow_reuse_address = True
+ TCPServer.__init__(self, addr, handler)
+
+def wps_er_start(dev, http_server, max_age=1, wait_m_search=False,
+ location_url=None):
+ socket.setdefaulttimeout(1)
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind(("239.255.255.250", 1900))
+ dev.request("WPS_ER_START ifname=lo")
+ for i in range(100):
+ (msg, addr) = sock.recvfrom(1000)
+ msg = msg.decode()
+ logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
+ if "M-SEARCH" in msg:
+ break
+ if not wait_m_search:
+ raise Exception("Not an M-SEARCH")
+ if i == 99:
+ raise Exception("No M-SEARCH seen")
+
+ # Add an AP with a valid URL and server listing to it
+ server = MyTCPServer(("127.0.0.1", 12345), http_server)
+ if not location_url:
+ location_url = 'http://127.0.0.1:12345/foo.xml'
+ sock.sendto(("HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:%s\r\ncache-control:max-age=%d\r\n\r\n" % (location_url, max_age)).encode(), addr)
+ server.timeout = 1
+ return server, sock
+
+def wps_er_stop(dev, sock, server, on_alloc_fail=False):
+ sock.close()
+ server.server_close()
+
+ if on_alloc_fail:
+ done = False
+ for i in range(50):
+ res = dev.request("GET_ALLOC_FAIL")
+ if res.startswith("0:"):
+ done = True
+ break
+ time.sleep(0.1)
+ if not done:
+ raise Exception("No allocation failure reported")
+ else:
+ ev = dev.wait_event(["WPS-ER-AP-REMOVE"], timeout=5)
+ if ev is None:
+ raise Exception("No WPS-ER-AP-REMOVE event on max-age timeout")
+ dev.request("WPS_ER_STOP")
+
+def run_wps_er_proto_test(dev, handler, no_event_url=False, location_url=None,
+ max_age=1):
+ try:
+ uuid = '27ea801a-9e5c-4e73-bd82-f89cbcd10d7e'
+ server, sock = wps_er_start(dev, handler, location_url=location_url,
+ max_age=max_age)
+ global wps_event_url
+ wps_event_url = None
+ server.handle_request()
+ server.handle_request()
+ server.handle_request()
+ server.server_close()
+ if no_event_url:
+ if wps_event_url:
+ raise Exception("Received event URL unexpectedly")
+ return
+ if wps_event_url is None:
+ raise Exception("Did not get event URL")
+ logger.info("Event URL: " + wps_event_url)
+ finally:
+ dev.request("WPS_ER_STOP")
+
+def send_wlanevent(url, uuid, data, no_response=False):
+ conn = HTTPConnection(url.netloc)
+ payload = '''<?xml version="1.0" encoding="utf-8"?>
+<e:propertyset xmlns:e="urn:schemas-upnp-org:event-1-0">
+<e:property><STAStatus>1</STAStatus></e:property>
+<e:property><APStatus>1</APStatus></e:property>
+<e:property><WLANEvent>'''
+ payload += base64.b64encode(data).decode()
+ payload += '</WLANEvent></e:property></e:propertyset>'
+ headers = {"Content-type": 'text/xml; charset="utf-8"',
+ "Server": "Unspecified, UPnP/1.0, Unspecified",
+ "HOST": url.netloc,
+ "NT": "upnp:event",
+ "SID": "uuid:" + uuid,
+ "SEQ": "0",
+ "Content-Length": str(len(payload))}
+ conn.request("NOTIFY", url.path, payload, headers)
+ if no_response:
+ try:
+ conn.getresponse()
+ except Exception as e:
+ pass
+ return
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+def test_ap_wps_er_http_proto(dev, apdev):
+ """WPS ER HTTP protocol testing"""
+ try:
+ _test_ap_wps_er_http_proto(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_http_proto(dev, apdev):
+ uuid = '27ea801a-9e5c-4e73-bd82-f89cbcd10d7e'
+ server, sock = wps_er_start(dev[0], WPSAPHTTPServer, max_age=15)
+ global wps_event_url
+ wps_event_url = None
+ server.handle_request()
+ server.handle_request()
+ server.handle_request()
+ server.server_close()
+ if wps_event_url is None:
+ raise Exception("Did not get event URL")
+ logger.info("Event URL: " + wps_event_url)
+
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=10)
+ if ev is None:
+ raise Exception("No WPS-ER-AP-ADD event")
+ if uuid not in ev:
+ raise Exception("UUID mismatch")
+
+ sock.close()
+
+ logger.info("Valid Probe Request notification")
+ url = urlparse(wps_event_url)
+ conn = HTTPConnection(url.netloc)
+ payload = '''<?xml version="1.0" encoding="utf-8"?>
+<e:propertyset xmlns:e="urn:schemas-upnp-org:event-1-0">
+<e:property><STAStatus>1</STAStatus></e:property>
+<e:property><APStatus>1</APStatus></e:property>
+<e:property><WLANEvent>ATAyOjAwOjAwOjAwOjAwOjAwEEoAARAQOgABAhAIAAIxSBBHABA2LbR7pTpRkYj7VFi5hrLk
+EFQACAAAAAAAAAAAEDwAAQMQAgACAAAQCQACAAAQEgACAAAQIQABIBAjAAEgECQAASAQEQAI
+RGV2aWNlIEEQSQAGADcqAAEg
+</WLANEvent></e:property>
+</e:propertyset>
+'''
+ headers = {"Content-type": 'text/xml; charset="utf-8"',
+ "Server": "Unspecified, UPnP/1.0, Unspecified",
+ "HOST": url.netloc,
+ "NT": "upnp:event",
+ "SID": "uuid:" + uuid,
+ "SEQ": "0",
+ "Content-Length": str(len(payload))}
+ conn.request("NOTIFY", url.path, payload, headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=5)
+ if ev is None:
+ raise Exception("No WPS-ER-ENROLLEE-ADD event")
+ if "362db47b-a53a-5191-88fb-5458b986b2e4" not in ev:
+ raise Exception("No Enrollee UUID match")
+
+ logger.info("Incorrect event URL AP id")
+ conn = HTTPConnection(url.netloc)
+ conn.request("NOTIFY", url.path + '123', payload, headers)
+ resp = conn.getresponse()
+ if resp.status != 404:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.info("Missing AP id")
+ conn = HTTPConnection(url.netloc)
+ conn.request("NOTIFY", '/event/' + url.path.split('/')[2],
+ payload, headers)
+ time.sleep(0.1)
+
+ logger.info("Incorrect event URL event id")
+ conn = HTTPConnection(url.netloc)
+ conn.request("NOTIFY", '/event/123456789/123', payload, headers)
+ time.sleep(0.1)
+
+ logger.info("Incorrect event URL prefix")
+ conn = HTTPConnection(url.netloc)
+ conn.request("NOTIFY", '/foobar/123456789/123', payload, headers)
+ resp = conn.getresponse()
+ if resp.status != 404:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.info("Unsupported request")
+ conn = HTTPConnection(url.netloc)
+ conn.request("FOOBAR", '/foobar/123456789/123', payload, headers)
+ resp = conn.getresponse()
+ if resp.status != 501:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ logger.info("Unsupported request and OOM")
+ with alloc_fail(dev[0], 1, "wps_er_http_req"):
+ conn = HTTPConnection(url.netloc)
+ conn.request("FOOBAR", '/foobar/123456789/123', payload, headers)
+ time.sleep(0.5)
+
+ logger.info("Too short WLANEvent")
+ data = b'\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("Invalid WLANEventMAC")
+ data = b'\x00qwertyuiopasdfghjklzxcvbnm'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("Unknown WLANEventType")
+ data = b'\xff02:00:00:00:00:00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("Probe Request notification without any attributes")
+ data = b'\x0102:00:00:00:00:00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("Probe Request notification with invalid attribute")
+ data = b'\x0102:00:00:00:00:00\xff'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message without any attributes")
+ data = b'\x0202:00:00:00:00:00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message with invalid attribute")
+ data = b'\x0202:00:00:00:00:00\xff'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message from new STA and not M1")
+ data = b'\x0202:ff:ff:ff:ff:ff' + b'\x10\x22\x00\x01\x05'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1")
+ data = b'\x0202:00:00:00:00:00'
+ data += b'\x10\x22\x00\x01\x04'
+ data += b'\x10\x47\x00\x10' + 16 * b'\x00'
+ data += b'\x10\x20\x00\x06\x02\x00\x00\x00\x00\x00'
+ data += b'\x10\x1a\x00\x10' + 16 * b'\x00'
+ data += b'\x10\x32\x00\xc0' + 192 * b'\x00'
+ data += b'\x10\x04\x00\x02\x00\x00'
+ data += b'\x10\x10\x00\x02\x00\x00'
+ data += b'\x10\x0d\x00\x01\x00'
+ data += b'\x10\x08\x00\x02\x00\x00'
+ data += b'\x10\x44\x00\x01\x00'
+ data += b'\x10\x21\x00\x00'
+ data += b'\x10\x23\x00\x00'
+ data += b'\x10\x24\x00\x00'
+ data += b'\x10\x42\x00\x00'
+ data += b'\x10\x54\x00\x08' + 8 * b'\x00'
+ data += b'\x10\x11\x00\x00'
+ data += b'\x10\x3c\x00\x01\x00'
+ data += b'\x10\x02\x00\x02\x00\x00'
+ data += b'\x10\x12\x00\x02\x00\x00'
+ data += b'\x10\x09\x00\x02\x00\x00'
+ data += b'\x10\x2d\x00\x04\x00\x00\x00\x00'
+ m1 = data
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: WSC_ACK")
+ data = b'\x0202:00:00:00:00:00' + b'\x10\x22\x00\x01\x0d'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1")
+ send_wlanevent(url, uuid, m1)
+
+ logger.info("EAP message: WSC_NACK")
+ data = b'\x0202:00:00:00:00:00' + b'\x10\x22\x00\x01\x0e'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 - Too long attribute values")
+ data = b'\x0202:00:00:00:00:00'
+ data += b'\x10\x11\x00\x21' + 33 * b'\x00'
+ data += b'\x10\x45\x00\x21' + 33 * b'\x00'
+ data += b'\x10\x42\x00\x21' + 33 * b'\x00'
+ data += b'\x10\x24\x00\x21' + 33 * b'\x00'
+ data += b'\x10\x23\x00\x21' + 33 * b'\x00'
+ data += b'\x10\x21\x00\x41' + 65 * b'\x00'
+ data += b'\x10\x49\x00\x09\x00\x37\x2a\x05\x02\x00\x00\x05\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing UUID-E")
+ data = b'\x0202:00:00:00:00:00'
+ data += b'\x10\x22\x00\x01\x04'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing MAC Address")
+ data += b'\x10\x47\x00\x10' + 16 * b'\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Enrollee Nonce")
+ data += b'\x10\x20\x00\x06\x02\x00\x00\x00\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Public Key")
+ data += b'\x10\x1a\x00\x10' + 16 * b'\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Authentication Type flags")
+ data += b'\x10\x32\x00\xc0' + 192 * b'\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Encryption Type Flags")
+ data += b'\x10\x04\x00\x02\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Connection Type flags")
+ data += b'\x10\x10\x00\x02\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Config Methods")
+ data += b'\x10\x0d\x00\x01\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Wi-Fi Protected Setup State")
+ data += b'\x10\x08\x00\x02\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Manufacturer")
+ data += b'\x10\x44\x00\x01\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Model Name")
+ data += b'\x10\x21\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Model Number")
+ data += b'\x10\x23\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Serial Number")
+ data += b'\x10\x24\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Primary Device Type")
+ data += b'\x10\x42\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Device Name")
+ data += b'\x10\x54\x00\x08' + 8 * b'\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing RF Bands")
+ data += b'\x10\x11\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Association State")
+ data += b'\x10\x3c\x00\x01\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Device Password ID")
+ data += b'\x10\x02\x00\x02\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing Configuration Error")
+ data += b'\x10\x12\x00\x02\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("EAP message: M1 missing OS Version")
+ data += b'\x10\x09\x00\x02\x00\x00'
+ send_wlanevent(url, uuid, data)
+
+ logger.info("Check max concurrent requests")
+ addr = (url.hostname, url.port)
+ socks = {}
+ for i in range(20):
+ socks[i] = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+ socket.IPPROTO_TCP)
+ socks[i].settimeout(10)
+ socks[i].connect(addr)
+ for i in range(20):
+ socks[i].send(b"GET / HTTP/1.1\r\n\r\n")
+ count = 0
+ for i in range(20):
+ try:
+ res = socks[i].recv(100).decode()
+ if "HTTP/1" in res:
+ count += 1
+ else:
+ logger.info("recv[%d]: len=%d" % (i, len(res)))
+ except:
+ pass
+ socks[i].close()
+ logger.info("%d concurrent HTTP GET operations returned response" % count)
+ if count < 8:
+ raise Exception("Too few concurrent HTTP connections accepted")
+
+ logger.info("OOM in HTTP server")
+ for func in ["http_request_init", "httpread_create",
+ "eloop_register_timeout;httpread_create",
+ "eloop_sock_table_add_sock;?eloop_register_sock;httpread_create",
+ "httpread_hdr_analyze"]:
+ with alloc_fail(dev[0], 1, func):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+ socket.IPPROTO_TCP)
+ sock.connect(addr)
+ sock.send(b"GET / HTTP/1.1\r\n\r\n")
+ try:
+ sock.recv(100)
+ except:
+ pass
+ sock.close()
+
+ logger.info("Invalid HTTP header")
+ for req in [" GET / HTTP/1.1\r\n\r\n",
+ "HTTP/1.1 200 OK\r\n\r\n",
+ "HTTP/\r\n\r\n",
+ "GET %%a%aa% HTTP/1.1\r\n\r\n",
+ "GET / HTTP/1.1\r\n FOO\r\n\r\n",
+ "NOTIFY / HTTP/1.1\r\n" + 4097*'a' + '\r\n\r\n',
+ "NOTIFY / HTTP/1.1\r\n\r\n" + 8193*'a',
+ "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n foo\r\n",
+ "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n1\r\nfoo\r\n",
+ "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n0\r\n",
+ "POST / HTTP/1.1\r\nTransfer-Encoding: CHUNKED\r\n\r\n0\r\naa\ra\r\n\ra"]:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+ socket.IPPROTO_TCP)
+ sock.settimeout(0.1)
+ sock.connect(addr)
+ sock.send(req.encode())
+ try:
+ sock.recv(100)
+ except:
+ pass
+ sock.close()
+
+ with alloc_fail(dev[0], 2, "httpread_read_handler"):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+ socket.IPPROTO_TCP)
+ sock.connect(addr)
+ sock.send(b"NOTIFY / HTTP/1.1\r\n\r\n" + 4500 * b'a')
+ try:
+ sock.recv(100)
+ except:
+ pass
+ sock.close()
+
+ conn = HTTPConnection(url.netloc)
+ payload = '<foo'
+ headers = {"Content-type": 'text/xml; charset="utf-8"',
+ "Server": "Unspecified, UPnP/1.0, Unspecified",
+ "HOST": url.netloc,
+ "NT": "upnp:event",
+ "SID": "uuid:" + uuid,
+ "SEQ": "0",
+ "Content-Length": str(len(payload))}
+ conn.request("NOTIFY", url.path, payload, headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ conn = HTTPConnection(url.netloc)
+ payload = '<WLANEvent foo></WLANEvent>'
+ headers = {"Content-type": 'text/xml; charset="utf-8"',
+ "Server": "Unspecified, UPnP/1.0, Unspecified",
+ "HOST": url.netloc,
+ "NT": "upnp:event",
+ "SID": "uuid:" + uuid,
+ "SEQ": "0",
+ "Content-Length": str(len(payload))}
+ conn.request("NOTIFY", url.path, payload, headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ with alloc_fail(dev[0], 1, "xml_get_first_item"):
+ send_wlanevent(url, uuid, b'')
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc_ext_data;xml_get_base64_item"):
+ send_wlanevent(url, uuid, b'foo')
+
+ for func in ["wps_init",
+ "wps_process_manufacturer",
+ "wps_process_model_name",
+ "wps_process_model_number",
+ "wps_process_serial_number",
+ "wps_process_dev_name"]:
+ with alloc_fail(dev[0], 1, func):
+ send_wlanevent(url, uuid, m1)
+
+ with alloc_fail(dev[0], 1, "wps_er_http_resp_ok"):
+ send_wlanevent(url, uuid, m1, no_response=True)
+
+ with alloc_fail(dev[0], 1, "wps_er_http_resp_not_found"):
+ url2 = urlparse(wps_event_url.replace('/event/', '/notfound/'))
+ send_wlanevent(url2, uuid, m1, no_response=True)
+
+ logger.info("EAP message: M1")
+ data = b'\x0202:11:22:00:00:00'
+ data += b'\x10\x22\x00\x01\x04'
+ data += b'\x10\x47\x00\x10' + 16 * b'\x00'
+ data += b'\x10\x20\x00\x06\x02\x00\x00\x00\x00\x00'
+ data += b'\x10\x1a\x00\x10' + 16 * b'\x00'
+ data += b'\x10\x32\x00\xc0' + 192 * b'\x00'
+ data += b'\x10\x04\x00\x02\x00\x00'
+ data += b'\x10\x10\x00\x02\x00\x00'
+ data += b'\x10\x0d\x00\x01\x00'
+ data += b'\x10\x08\x00\x02\x00\x00'
+ data += b'\x10\x44\x00\x01\x00'
+ data += b'\x10\x21\x00\x00'
+ data += b'\x10\x23\x00\x00'
+ data += b'\x10\x24\x00\x00'
+ data += b'\x10\x42\x00\x00'
+ data += b'\x10\x54\x00\x08' + 8 * b'\x00'
+ data += b'\x10\x11\x00\x00'
+ data += b'\x10\x3c\x00\x01\x00'
+ data += b'\x10\x02\x00\x02\x00\x00'
+ data += b'\x10\x12\x00\x02\x00\x00'
+ data += b'\x10\x09\x00\x02\x00\x00'
+ data += b'\x10\x2d\x00\x04\x00\x00\x00\x00'
+ dev[0].dump_monitor()
+ with alloc_fail(dev[0], 1, "wps_er_add_sta_data"):
+ send_wlanevent(url, uuid, data)
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected enrollee add event")
+ send_wlanevent(url, uuid, data)
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=2)
+ if ev is None:
+ raise Exception("Enrollee add event not seen")
+
+ with alloc_fail(dev[0], 1,
+ "base64_gen_encode;?base64_encode;wps_er_soap_hdr"):
+ send_wlanevent(url, uuid, data)
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;wps_er_soap_hdr"):
+ send_wlanevent(url, uuid, data)
+
+ with alloc_fail(dev[0], 1, "http_client_url_parse;wps_er_sta_send_msg"):
+ send_wlanevent(url, uuid, data)
+
+ with alloc_fail(dev[0], 1, "http_client_addr;wps_er_sta_send_msg"):
+ send_wlanevent(url, uuid, data)
+
+def test_ap_wps_er_http_proto_no_event_sub_url(dev, apdev):
+ """WPS ER HTTP protocol testing - no eventSubURL"""
+ class WPSAPHTTPServer_no_event_sub_url(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ self.wfile.write(gen_upnp_info(eventSubURL=None))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_event_sub_url,
+ no_event_url=True)
+
+def test_ap_wps_er_http_proto_event_sub_url_dns(dev, apdev):
+ """WPS ER HTTP protocol testing - DNS name in eventSubURL"""
+ class WPSAPHTTPServer_event_sub_url_dns(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ self.wfile.write(gen_upnp_info(eventSubURL='http://example.com/wps_event'))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_event_sub_url_dns,
+ no_event_url=True)
+
+def test_ap_wps_er_http_proto_subscribe_oom(dev, apdev):
+ """WPS ER HTTP protocol testing - subscribe OOM"""
+ try:
+ _test_ap_wps_er_http_proto_subscribe_oom(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_http_proto_subscribe_oom(dev, apdev):
+ tests = [(1, "http_client_url_parse"),
+ (1, "wpabuf_alloc;wps_er_subscribe"),
+ (1, "http_client_addr"),
+ (1, "eloop_sock_table_add_sock;?eloop_register_sock;http_client_addr"),
+ (1, "eloop_register_timeout;http_client_addr")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ server, sock = wps_er_start(dev[0], WPSAPHTTPServer)
+ server.handle_request()
+ server.handle_request()
+ wps_er_stop(dev[0], sock, server, on_alloc_fail=True)
+
+def test_ap_wps_er_http_proto_no_sid(dev, apdev):
+ """WPS ER HTTP protocol testing - no SID"""
+ class WPSAPHTTPServer_no_sid(WPSAPHTTPServer):
+ def handle_wps_event(self):
+ self.wfile.write(gen_wps_event(sid=None))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_sid)
+
+def test_ap_wps_er_http_proto_invalid_sid_no_uuid(dev, apdev):
+ """WPS ER HTTP protocol testing - invalid SID - no UUID"""
+ class WPSAPHTTPServer_invalid_sid_no_uuid(WPSAPHTTPServer):
+ def handle_wps_event(self):
+ self.wfile.write(gen_wps_event(sid='FOO'))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_sid_no_uuid)
+
+def test_ap_wps_er_http_proto_invalid_sid_uuid(dev, apdev):
+ """WPS ER HTTP protocol testing - invalid SID UUID"""
+ class WPSAPHTTPServer_invalid_sid_uuid(WPSAPHTTPServer):
+ def handle_wps_event(self):
+ self.wfile.write(gen_wps_event(sid='uuid:FOO'))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_sid_uuid)
+
+def test_ap_wps_er_http_proto_subscribe_failing(dev, apdev):
+ """WPS ER HTTP protocol testing - SUBSCRIBE failing"""
+ class WPSAPHTTPServer_fail_subscribe(WPSAPHTTPServer):
+ def handle_wps_event(self):
+ payload = ""
+ hdr = 'HTTP/1.1 404 Not Found\r\n' + \
+ 'Content-Type: text/xml; charset="utf-8"\r\n' + \
+ 'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+ 'Connection: close\r\n' + \
+ 'Content-Length: ' + str(len(payload)) + '\r\n' + \
+ 'Timeout: Second-1801\r\n' + \
+ 'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+ self.wfile.write((hdr + payload).encode())
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_fail_subscribe)
+
+def test_ap_wps_er_http_proto_subscribe_invalid_response(dev, apdev):
+ """WPS ER HTTP protocol testing - SUBSCRIBE and invalid response"""
+ class WPSAPHTTPServer_subscribe_invalid_response(WPSAPHTTPServer):
+ def handle_wps_event(self):
+ payload = ""
+ hdr = 'HTTP/1.1 FOO\r\n' + \
+ 'Content-Type: text/xml; charset="utf-8"\r\n' + \
+ 'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+ 'Connection: close\r\n' + \
+ 'Content-Length: ' + str(len(payload)) + '\r\n' + \
+ 'Timeout: Second-1801\r\n' + \
+ 'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+ self.wfile.write((hdr + payload).encode())
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_subscribe_invalid_response)
+
+def test_ap_wps_er_http_proto_subscribe_invalid_response(dev, apdev):
+ """WPS ER HTTP protocol testing - SUBSCRIBE and invalid response"""
+ class WPSAPHTTPServer_invalid_m1(WPSAPHTTPServer):
+ def handle_wps_control(self):
+ payload = '''<?xml version="1.0"?>
+<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
+<s:Body>
+<u:GetDeviceInfoResponse xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">
+<NewDeviceInfo>Rk9P</NewDeviceInfo>
+</u:GetDeviceInfoResponse>
+</s:Body>
+</s:Envelope>
+'''
+ self.wfile.write(gen_wps_control(payload_override=payload))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_m1, no_event_url=True)
+
+def test_ap_wps_er_http_proto_upnp_info_no_device(dev, apdev):
+ """WPS ER HTTP protocol testing - No device in UPnP info"""
+ class WPSAPHTTPServer_no_device(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ payload = '''<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+<specVersion>
+<major>1</major>
+<minor>0</minor>
+</specVersion>
+</root>
+'''
+ hdr = 'HTTP/1.1 200 OK\r\n' + \
+ 'Content-Type: text/xml; charset="utf-8"\r\n' + \
+ 'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+ 'Connection: close\r\n' + \
+ 'Content-Length: ' + str(len(payload)) + '\r\n' + \
+ 'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+ self.wfile.write((hdr + payload).encode())
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_device, no_event_url=True)
+
+def test_ap_wps_er_http_proto_upnp_info_no_device_type(dev, apdev):
+ """WPS ER HTTP protocol testing - No deviceType in UPnP info"""
+ class WPSAPHTTPServer_no_device(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ payload = '''<?xml version="1.0"?>
+<root xmlns="urn:schemas-upnp-org:device-1-0">
+<specVersion>
+<major>1</major>
+<minor>0</minor>
+</specVersion>
+<device>
+</device>
+</root>
+'''
+ hdr = 'HTTP/1.1 200 OK\r\n' + \
+ 'Content-Type: text/xml; charset="utf-8"\r\n' + \
+ 'Server: Unspecified, UPnP/1.0, Unspecified\r\n' + \
+ 'Connection: close\r\n' + \
+ 'Content-Length: ' + str(len(payload)) + '\r\n' + \
+ 'Date: Sat, 15 Aug 2015 18:55:08 GMT\r\n\r\n'
+ self.wfile.write((hdr + payload).encode())
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_device, no_event_url=True)
+
+def test_ap_wps_er_http_proto_upnp_info_invalid_udn_uuid(dev, apdev):
+ """WPS ER HTTP protocol testing - Invalid UDN UUID"""
+ class WPSAPHTTPServer_invalid_udn_uuid(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ self.wfile.write(gen_upnp_info(udn='uuid:foo'))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_invalid_udn_uuid)
+
+def test_ap_wps_er_http_proto_no_control_url(dev, apdev):
+ """WPS ER HTTP protocol testing - no controlURL"""
+ class WPSAPHTTPServer_no_control_url(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ self.wfile.write(gen_upnp_info(controlURL=None))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_no_control_url,
+ no_event_url=True)
+
+def test_ap_wps_er_http_proto_control_url_dns(dev, apdev):
+ """WPS ER HTTP protocol testing - DNS name in controlURL"""
+ class WPSAPHTTPServer_control_url_dns(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ self.wfile.write(gen_upnp_info(controlURL='http://example.com/wps_control'))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_control_url_dns,
+ no_event_url=True)
+
+def test_ap_wps_http_timeout(dev, apdev):
+ """WPS AP/ER and HTTP timeout"""
+ try:
+ _test_ap_wps_http_timeout(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_http_timeout(dev, apdev):
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ add_ssdp_ap(apdev[0], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+ url = urlparse(location)
+ addr = (url.hostname, url.port)
+ logger.debug("Open HTTP connection to hostapd, but do not complete request")
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+ socket.IPPROTO_TCP)
+ sock.connect(addr)
+ sock.send(b"G")
+
+ class DummyServer(StreamRequestHandler):
+ def handle(self):
+ logger.debug("DummyServer - start 31 sec wait")
+ time.sleep(31)
+ logger.debug("DummyServer - wait done")
+
+ logger.debug("Start WPS ER")
+ server, sock2 = wps_er_start(dev[0], DummyServer, max_age=40,
+ wait_m_search=True)
+
+ logger.debug("Start server to accept, but not complete, HTTP connection from WPS ER")
+ # This will wait for 31 seconds..
+ server.handle_request()
+
+ logger.debug("Complete HTTP connection with hostapd (that should have already closed the connection)")
+ try:
+ sock.send("ET / HTTP/1.1\r\n\r\n")
+ res = sock.recv(100)
+ sock.close()
+ except:
+ pass
+
+def test_ap_wps_er_url_parse(dev, apdev):
+ """WPS ER and URL parsing special cases"""
+ try:
+ _test_ap_wps_er_url_parse(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_url_parse(dev, apdev):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.settimeout(1)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.bind(("239.255.255.250", 1900))
+ dev[0].request("WPS_ER_START ifname=lo")
+ (msg, addr) = sock.recvfrom(1000)
+ msg = msg.decode()
+ logger.debug("Received SSDP message from %s: %s" % (str(addr), msg))
+ if "M-SEARCH" not in msg:
+ raise Exception("Not an M-SEARCH")
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1\r\ncache-control:max-age=1\r\n\r\n", addr)
+ ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=2)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://127.0.0.1/:foo\r\ncache-control:max-age=1\r\n\r\n", addr)
+ ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=2)
+ sock.sendto(b"HTTP/1.1 200 OK\r\nST: urn:schemas-wifialliance-org:device:WFADevice:1\r\nlocation:http://255.255.255.255:0/foo.xml\r\ncache-control:max-age=1\r\n\r\n", addr)
+ ev = dev[0].wait_event(["WPS-ER-AP-REMOVE"], timeout=2)
+
+ sock.close()
+
+def test_ap_wps_er_link_update(dev, apdev):
+ """WPS ER and link update special cases"""
+ class WPSAPHTTPServer_link_update(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ self.wfile.write(gen_upnp_info(controlURL='/wps_control'))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_link_update)
+
+ class WPSAPHTTPServer_link_update2(WPSAPHTTPServer):
+ def handle_others(self, data):
+ if "GET / " in data:
+ self.wfile.write(gen_upnp_info(controlURL='/wps_control'))
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_link_update2,
+ location_url='http://127.0.0.1:12345')
+
+def test_ap_wps_er_http_client(dev, apdev):
+ """WPS ER and HTTP client special cases"""
+ with alloc_fail(dev[0], 1, "http_link_update"):
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer)
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;http_client_url"):
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer, no_event_url=True)
+
+ with alloc_fail(dev[0], 1, "httpread_create;http_client_tx_ready"):
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer, no_event_url=True)
+
+ class WPSAPHTTPServer_req_as_resp(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ self.wfile.write(b"GET / HTTP/1.1\r\n\r\n")
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_req_as_resp,
+ no_event_url=True)
+
+def test_ap_wps_er_http_client_timeout(dev, apdev):
+ """WPS ER and HTTP client timeout"""
+ class WPSAPHTTPServer_timeout(WPSAPHTTPServer):
+ def handle_upnp_info(self):
+ time.sleep(31)
+ self.wfile.write(b"GET / HTTP/1.1\r\n\r\n")
+ run_wps_er_proto_test(dev[0], WPSAPHTTPServer_timeout,
+ no_event_url=True, max_age=60)
+
+def test_ap_wps_init_oom(dev, apdev):
+ """wps_init OOM cases"""
+ ssid = "test-wps"
+ appin = "12345670"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "ap_pin": appin}
+ hapd = hostapd.add_ap(apdev[0], params)
+ pin = dev[0].wps_read_pin()
+
+ with alloc_fail(hapd, 1, "wps_init"):
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP failure reported")
+ dev[0].request("WPS_CANCEL")
+
+ with alloc_fail(dev[0], 2, "wps_init"):
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP failure reported")
+ dev[0].request("WPS_CANCEL")
+
+ with alloc_fail(dev[0], 2, "wps_init"):
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PBC %s" % (apdev[0]['bssid']))
+ ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP failure reported")
+ dev[0].request("WPS_CANCEL")
+
+ dev[0].dump_monitor()
+ new_ssid = "wps-new-ssid"
+ new_passphrase = "1234567890"
+ with alloc_fail(dev[0], 3, "wps_init"):
+ dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
+ new_passphrase, no_wait=True)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP failure reported")
+
+ dev[0].flush_scan_cache()
+
+@remote_compatible
+def test_ap_wps_invalid_assoc_req_elem(dev, apdev):
+ """WPS and invalid IE in Association Request frame"""
+ ssid = "test-wps"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ pin = "12345670"
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ try:
+ dev[0].request("VENDOR_ELEM_ADD 13 dd050050f20410")
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ for i in range(5):
+ ev = hapd.wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=10)
+ if ev and "vendor=14122" in ev:
+ break
+ if ev is None or "vendor=14122" not in ev:
+ raise Exception("EAP-WSC not started")
+ dev[0].request("WPS_CANCEL")
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def test_ap_wps_pbc_pin_mismatch(dev, apdev):
+ """WPS PBC/PIN mismatch"""
+ ssid = "test-wps"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("SET wps_version_number 0x10")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ hapd.request("WPS_PBC")
+ pin = dev[0].wps_read_pin()
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+ dev[0].request("WPS_CANCEL")
+
+ hapd.request("WPS_CANCEL")
+ dev[0].flush_scan_cache()
+
+@remote_compatible
+def test_ap_wps_ie_invalid(dev, apdev):
+ """WPS PIN attempt with AP that has invalid WSC IE"""
+ ssid = "test-wps"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "vendor_elements": "dd050050f20410"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ params = {'ssid': "another", "vendor_elements": "dd050050f20410"}
+ hostapd.add_ap(apdev[1], params)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ pin = dev[0].wps_read_pin()
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+ dev[0].request("WPS_CANCEL")
+
+@remote_compatible
+def test_ap_wps_scan_prio_order(dev, apdev):
+ """WPS scan priority ordering"""
+ ssid = "test-wps"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ params = {'ssid': "another", "vendor_elements": "dd050050f20410"}
+ hostapd.add_ap(apdev[1], params)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ pin = dev[0].wps_read_pin()
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+ dev[0].request("WPS_CANCEL")
+
+def test_ap_wps_probe_req_ie_oom(dev, apdev):
+ """WPS ProbeReq IE OOM"""
+ ssid = "test-wps"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ with alloc_fail(dev[0], 1, "wps_build_probe_req_ie"):
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Association not seen")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "wps_ie_encapsulate"):
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Association not seen")
+ dev[0].request("WPS_CANCEL")
+ hapd.disable()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ time.sleep(0.2)
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_assoc_req_ie_oom(dev, apdev):
+ """WPS AssocReq IE OOM"""
+ ssid = "test-wps"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ with alloc_fail(dev[0], 1, "wps_build_assoc_req_ie"):
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Association not seen")
+ dev[0].request("WPS_CANCEL")
+
+def test_ap_wps_assoc_resp_ie_oom(dev, apdev):
+ """WPS AssocResp IE OOM"""
+ ssid = "test-wps"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ with alloc_fail(hapd, 1, "wps_build_assoc_resp_ie"):
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Association not seen")
+ dev[0].request("WPS_CANCEL")
+
+@remote_compatible
+def test_ap_wps_bss_info_errors(dev, apdev):
+ """WPS BSS info errors"""
+ params = {"ssid": "1",
+ "vendor_elements": "dd0e0050f20410440001ff101100010a"}
+ hostapd.add_ap(apdev[0], params)
+ params = {'ssid': "2", "vendor_elements": "dd050050f20410"}
+ hostapd.add_ap(apdev[1], params)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ logger.info("BSS: " + str(bss))
+ if "wps_state" in bss:
+ raise Exception("Unexpected wps_state in BSS info")
+ if 'wps_device_name' not in bss:
+ raise Exception("No wps_device_name in BSS info")
+ if bss['wps_device_name'] != '_':
+ raise Exception("Unexpected wps_device_name value")
+ bss = dev[0].get_bss(apdev[1]['bssid'])
+ logger.info("BSS: " + str(bss))
+
+ with alloc_fail(dev[0], 1, "=wps_attr_text"):
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ logger.info("BSS(OOM): " + str(bss))
+
+def wps_run_pbc_fail_ap(apdev, dev, hapd):
+ hapd.request("WPS_PBC")
+ dev.scan_for_bss(apdev['bssid'], freq="2412")
+ dev.request("WPS_PBC " + apdev['bssid'])
+ ev = dev.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP failure reported")
+ dev.request("WPS_CANCEL")
+ dev.wait_disconnected()
+ for i in range(5):
+ try:
+ dev.flush_scan_cache()
+ break
+ except Exception as e:
+ if str(e).startswith("Failed to trigger scan"):
+ # Try again
+ time.sleep(1)
+ else:
+ raise
+
+def wps_run_pbc_fail(apdev, dev):
+ hapd = wps_start_ap(apdev)
+ wps_run_pbc_fail_ap(apdev, dev, hapd)
+
+@remote_compatible
+def test_ap_wps_pk_oom(dev, apdev):
+ """WPS and public key OOM"""
+ with alloc_fail(dev[0], 1, "wps_build_public_key"):
+ wps_run_pbc_fail(apdev[0], dev[0])
+
+@remote_compatible
+def test_ap_wps_pk_oom_ap(dev, apdev):
+ """WPS and public key OOM on AP"""
+ hapd = wps_start_ap(apdev[0])
+ with alloc_fail(hapd, 1, "wps_build_public_key"):
+ wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+
+@remote_compatible
+def test_ap_wps_encr_oom_ap(dev, apdev):
+ """WPS and encrypted settings decryption OOM on AP"""
+ hapd = wps_start_ap(apdev[0])
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ with alloc_fail(hapd, 1, "wps_decrypt_encr_settings"):
+ dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " " + pin)
+ ev = hapd.wait_event(["WPS-FAIL"], timeout=10)
+ if ev is None:
+ raise Exception("No WPS-FAIL reported")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+
+@remote_compatible
+def test_ap_wps_encr_no_random_ap(dev, apdev):
+ """WPS and no random data available for encryption on AP"""
+ hapd = wps_start_ap(apdev[0])
+ with fail_test(hapd, 1, "os_get_random;wps_build_encr_settings"):
+ wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+
+@remote_compatible
+def test_ap_wps_e_hash_no_random_sta(dev, apdev):
+ """WPS and no random data available for e-hash on STA"""
+ with fail_test(dev[0], 1, "os_get_random;wps_build_e_hash"):
+ wps_run_pbc_fail(apdev[0], dev[0])
+
+@remote_compatible
+def test_ap_wps_m1_no_random(dev, apdev):
+ """WPS and no random for M1 on STA"""
+ with fail_test(dev[0], 1, "os_get_random;wps_build_m1"):
+ wps_run_pbc_fail(apdev[0], dev[0])
+
+@remote_compatible
+def test_ap_wps_m1_oom(dev, apdev):
+ """WPS and OOM for M1 on STA"""
+ with alloc_fail(dev[0], 1, "wps_build_m1"):
+ wps_run_pbc_fail(apdev[0], dev[0])
+
+@remote_compatible
+def test_ap_wps_m3_oom(dev, apdev):
+ """WPS and OOM for M3 on STA"""
+ with alloc_fail(dev[0], 1, "wps_build_m3"):
+ wps_run_pbc_fail(apdev[0], dev[0])
+
+@remote_compatible
+def test_ap_wps_m5_oom(dev, apdev):
+ """WPS and OOM for M5 on STA"""
+ hapd = wps_start_ap(apdev[0])
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ for i in range(1, 3):
+ with alloc_fail(dev[0], i, "wps_build_m5"):
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP failure reported")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+@remote_compatible
+def test_ap_wps_m5_no_random(dev, apdev):
+ """WPS and no random for M5 on STA"""
+ with fail_test(dev[0], 1,
+ "os_get_random;wps_build_encr_settings;wps_build_m5"):
+ wps_run_pbc_fail(apdev[0], dev[0])
+
+@remote_compatible
+def test_ap_wps_m7_oom(dev, apdev):
+ """WPS and OOM for M7 on STA"""
+ hapd = wps_start_ap(apdev[0])
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ for i in range(1, 3):
+ with alloc_fail(dev[0], i, "wps_build_m7"):
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP failure reported")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+@remote_compatible
+def test_ap_wps_m7_no_random(dev, apdev):
+ """WPS and no random for M7 on STA"""
+ with fail_test(dev[0], 1,
+ "os_get_random;wps_build_encr_settings;wps_build_m7"):
+ wps_run_pbc_fail(apdev[0], dev[0])
+
+@remote_compatible
+def test_ap_wps_wsc_done_oom(dev, apdev):
+ """WPS and OOM for WSC_Done on STA"""
+ with alloc_fail(dev[0], 1, "wps_build_wsc_done"):
+ wps_run_pbc_fail(apdev[0], dev[0])
+
+def test_ap_wps_random_psk_fail(dev, apdev):
+ """WPS and no random for PSK on AP"""
+ ssid = "test-wps"
+ pskfile = "/tmp/ap_wps_per_enrollee_psk.psk_file"
+ appin = "12345670"
+ try:
+ os.remove(pskfile)
+ except:
+ pass
+
+ try:
+ with open(pskfile, "w") as f:
+ f.write("# WPA PSKs\n")
+
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa": "2", "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP", "ap_pin": appin,
+ "wpa_psk_file": pskfile}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ with fail_test(hapd, 1, "os_get_random;wps_build_cred_network_key"):
+ dev[0].request("WPS_REG " + apdev[0]['bssid'] + " " + appin)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP failure reported")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+
+ with fail_test(hapd, 1, "os_get_random;wps_build_cred"):
+ wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+
+ with alloc_fail(hapd, 1, "wps_build_cred"):
+ wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+
+ with alloc_fail(hapd, 2, "wps_build_cred"):
+ wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+ finally:
+ os.remove(pskfile)
+
+def wps_ext_eap_identity_req(dev, hapd, bssid):
+ logger.debug("EAP-Identity/Request")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev.request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+def wps_ext_eap_identity_resp(hapd, dev, addr):
+ ev = dev.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+def wps_ext_eap_wsc(dst, src, src_addr, msg):
+ logger.debug(msg)
+ ev = src.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ res = dst.request("EAPOL_RX " + src_addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+
+def wps_start_ext(apdev, dev, pbc=False, pin=None):
+ addr = dev.own_addr()
+ bssid = apdev['bssid']
+ ssid = "test-wps-conf"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev, params)
+
+ if pbc:
+ hapd.request("WPS_PBC")
+ else:
+ if pin is None:
+ pin = dev.wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev.scan_for_bss(bssid, freq="2412")
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev.request("SET ext_eapol_frame_io 1")
+
+ if pbc:
+ dev.request("WPS_PBC " + bssid)
+ else:
+ dev.request("WPS_PIN " + bssid + " " + pin)
+ return addr, bssid, hapd
+
+def wps_auth_corrupt(dst, src, addr):
+ ev = src.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ src.request("SET ext_eapol_frame_io 0")
+ dst.request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[-24:-16] != '10050008':
+ raise Exception("Could not find Authenticator attribute")
+ # Corrupt Authenticator value
+ msg = msg[:-1] + '%x' % ((int(msg[-1], 16) + 1) % 16)
+ res = dst.request("EAPOL_RX " + addr + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+
+def wps_fail_finish(hapd, dev, fail_str):
+ ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
+ if ev is None:
+ raise Exception("WPS-FAIL not indicated")
+ if fail_str not in ev:
+ raise Exception("Unexpected WPS-FAIL value: " + ev)
+ dev.request("WPS_CANCEL")
+ dev.wait_disconnected()
+
+def wps_auth_corrupt_from_ap(dev, hapd, bssid, fail_str):
+ wps_auth_corrupt(dev, hapd, bssid)
+ wps_fail_finish(hapd, dev, fail_str)
+
+def wps_auth_corrupt_to_ap(dev, hapd, addr, fail_str):
+ wps_auth_corrupt(hapd, dev, addr)
+ wps_fail_finish(hapd, dev, fail_str)
+
+def test_ap_wps_authenticator_mismatch_m2(dev, apdev):
+ """WPS and Authenticator attribute mismatch in M2"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=5")
+
+def test_ap_wps_authenticator_mismatch_m3(dev, apdev):
+ """WPS and Authenticator attribute mismatch in M3"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+ logger.debug("M3")
+ wps_auth_corrupt_to_ap(dev[0], hapd, addr, "msg=7")
+
+def test_ap_wps_authenticator_mismatch_m4(dev, apdev):
+ """WPS and Authenticator attribute mismatch in M4"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
+ logger.debug("M4")
+ wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=8")
+
+def test_ap_wps_authenticator_mismatch_m5(dev, apdev):
+ """WPS and Authenticator attribute mismatch in M5"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
+ logger.debug("M5")
+ wps_auth_corrupt_to_ap(dev[0], hapd, addr, "msg=9")
+
+def test_ap_wps_authenticator_mismatch_m6(dev, apdev):
+ """WPS and Authenticator attribute mismatch in M6"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M5")
+ logger.debug("M6")
+ wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=10")
+
+def test_ap_wps_authenticator_mismatch_m7(dev, apdev):
+ """WPS and Authenticator attribute mismatch in M7"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M5")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M6")
+ logger.debug("M7")
+ wps_auth_corrupt_to_ap(dev[0], hapd, addr, "msg=11")
+
+def test_ap_wps_authenticator_mismatch_m8(dev, apdev):
+ """WPS and Authenticator attribute mismatch in M8"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M2")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M3")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M4")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M5")
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "M6")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M7")
+ logger.debug("M8")
+ wps_auth_corrupt_from_ap(dev[0], hapd, bssid, "msg=12")
+
+def test_ap_wps_authenticator_missing_m2(dev, apdev):
+ """WPS and Authenticator attribute missing from M2"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[-24:-16] != '10050008':
+ raise Exception("Could not find Authenticator attribute")
+ # Remove Authenticator value
+ msg = msg[:-24]
+ mlen = "%04x" % (int(msg[4:8], 16) - 12)
+ msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_dev_passwd_id_p2p(dev, apdev):
+ """WPS and M2 with different Device Password ID (P2P)"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[722:730] != '10120002':
+ raise Exception("Could not find Device Password ID attribute")
+ # Replace Device Password ID value. This will fail Authenticator check, but
+ # allows the code path in wps_process_dev_pw_id() to be checked from debug
+ # log.
+ msg = msg[0:730] + "0005" + msg[734:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_dev_passwd_id_change_pin_to_pbc(dev, apdev):
+ """WPS and M2 with different Device Password ID (PIN to PBC)"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[722:730] != '10120002':
+ raise Exception("Could not find Device Password ID attribute")
+ # Replace Device Password ID value (PIN --> PBC). This will be rejected.
+ msg = msg[0:730] + "0004" + msg[734:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_dev_passwd_id_change_pbc_to_pin(dev, apdev):
+ """WPS and M2 with different Device Password ID (PBC to PIN)"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[722:730] != '10120002':
+ raise Exception("Could not find Device Password ID attribute")
+ # Replace Device Password ID value. This will fail Authenticator check, but
+ # allows the code path in wps_process_dev_pw_id() to be checked from debug
+ # log.
+ msg = msg[0:730] + "0000" + msg[734:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ wps_fail_finish(hapd, dev[0], "msg=5")
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_dev_passwd_id(dev, apdev):
+ """WPS and M2 without Device Password ID"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[722:730] != '10120002':
+ raise Exception("Could not find Device Password ID attribute")
+ # Remove Device Password ID value. This will fail Authenticator check, but
+ # allows the code path in wps_process_dev_pw_id() to be checked from debug
+ # log.
+ mlen = "%04x" % (int(msg[4:8], 16) - 6)
+ msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:722] + msg[734:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ wps_fail_finish(hapd, dev[0], "msg=5")
+
+def test_ap_wps_m2_missing_registrar_nonce(dev, apdev):
+ """WPS and M2 without Registrar Nonce"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[96:104] != '10390010':
+ raise Exception("Could not find Registrar Nonce attribute")
+ # Remove Registrar Nonce. This will fail Authenticator check, but
+ # allows the code path in wps_process_registrar_nonce() to be checked from
+ # the debug log.
+ mlen = "%04x" % (int(msg[4:8], 16) - 20)
+ msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:96] + msg[136:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnect event not seen")
+ dev[0].request("WPS_CANCEL")
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_enrollee_nonce(dev, apdev):
+ """WPS and M2 without Enrollee Nonce"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[56:64] != '101a0010':
+ raise Exception("Could not find enrollee Nonce attribute")
+ # Remove Enrollee Nonce. This will fail Authenticator check, but
+ # allows the code path in wps_process_enrollee_nonce() to be checked from
+ # the debug log.
+ mlen = "%04x" % (int(msg[4:8], 16) - 20)
+ msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:56] + msg[96:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnect event not seen")
+ dev[0].request("WPS_CANCEL")
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_uuid_r(dev, apdev):
+ """WPS and M2 without UUID-R"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[136:144] != '10480010':
+ raise Exception("Could not find enrollee Nonce attribute")
+ # Remove UUID-R. This will fail Authenticator check, but allows the code
+ # path in wps_process_uuid_r() to be checked from the debug log.
+ mlen = "%04x" % (int(msg[4:8], 16) - 20)
+ msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:136] + msg[176:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnect event not seen")
+ dev[0].request("WPS_CANCEL")
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_invalid(dev, apdev):
+ """WPS and M2 parsing failure"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[136:144] != '10480010':
+ raise Exception("Could not find enrollee Nonce attribute")
+ # Remove UUID-R. This will fail Authenticator check, but allows the code
+ # path in wps_process_uuid_r() to be checked from the debug log.
+ mlen = "%04x" % (int(msg[4:8], 16) - 1)
+ msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:-2]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnect event not seen")
+ dev[0].request("WPS_CANCEL")
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_missing_msg_type(dev, apdev):
+ """WPS and M2 without Message Type"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[46:54] != '10220001':
+ raise Exception("Could not find Message Type attribute")
+ # Remove Message Type. This will fail Authenticator check, but allows the
+ # code path in wps_process_wsc_msg() to be checked from the debug log.
+ mlen = "%04x" % (int(msg[4:8], 16) - 5)
+ msg = msg[0:4] + mlen + msg[8:12] + mlen + msg[16:46] + msg[56:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnect event not seen")
+ dev[0].request("WPS_CANCEL")
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_msg_type(dev, apdev):
+ """WPS and M2 but unknown Message Type"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[46:54] != '10220001':
+ raise Exception("Could not find Message Type attribute")
+ # Replace Message Type value. This will be rejected.
+ msg = msg[0:54] + "00" + msg[56:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECT"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnect event not seen")
+ dev[0].request("WPS_CANCEL")
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_opcode(dev, apdev):
+ """WPS and M2 but unknown opcode"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ # Replace opcode. This will be discarded in EAP-WSC processing.
+ msg = msg[0:32] + "00" + msg[34:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_opcode2(dev, apdev):
+ """WPS and M2 but unknown opcode (WSC_Start)"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ # Replace opcode. This will be discarded in EAP-WSC processing.
+ msg = msg[0:32] + "01" + msg[34:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_m2_unknown_opcode3(dev, apdev):
+ """WPS and M2 but unknown opcode (WSC_Done)"""
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev[0], addr, "M1")
+ logger.debug("M2")
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ # Replace opcode. This will be discarded in WPS Enrollee processing.
+ msg = msg[0:32] + "05" + msg[34:]
+ res = dev[0].request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def wps_m2_but_other(dev, apdev, title, msgtype):
+ addr, bssid, hapd = wps_start_ext(apdev, dev)
+ wps_ext_eap_identity_req(dev, hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev, addr)
+ wps_ext_eap_wsc(dev, hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev, addr, "M1")
+ logger.debug(title)
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev.request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[46:54] != '10220001':
+ raise Exception("Could not find Message Type attribute")
+ # Replace Message Type value. This will be rejected.
+ msg = msg[0:54] + msgtype + msg[56:]
+ res = dev.request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ ev = dev.wait_event(["WPS-FAIL"], timeout=5)
+ if ev is None:
+ raise Exception("WPS-FAIL event not seen")
+ dev.request("WPS_CANCEL")
+ dev.wait_disconnected()
+
+def wps_m4_but_other(dev, apdev, title, msgtype):
+ addr, bssid, hapd = wps_start_ext(apdev, dev)
+ wps_ext_eap_identity_req(dev, hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev, addr)
+ wps_ext_eap_wsc(dev, hapd, bssid, "EAP-WSC/Start")
+ wps_ext_eap_wsc(hapd, dev, addr, "M1")
+ wps_ext_eap_wsc(dev, hapd, bssid, "M2")
+ wps_ext_eap_wsc(hapd, dev, addr, "M3")
+ logger.debug(title)
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev.request("SET ext_eapol_frame_io 0")
+ msg = ev.split(' ')[2]
+ if msg[46:54] != '10220001':
+ raise Exception("Could not find Message Type attribute")
+ # Replace Message Type value. This will be rejected.
+ msg = msg[0:54] + msgtype + msg[56:]
+ res = dev.request("EAPOL_RX " + bssid + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
+ if ev is None:
+ raise Exception("WPS-FAIL event not seen")
+ dev.request("WPS_CANCEL")
+ dev.wait_disconnected()
+
+def test_ap_wps_m2_msg_type_m4(dev, apdev):
+ """WPS and M2 but Message Type M4"""
+ wps_m2_but_other(dev[0], apdev[0], "M2/M4", "08")
+
+def test_ap_wps_m2_msg_type_m6(dev, apdev):
+ """WPS and M2 but Message Type M6"""
+ wps_m2_but_other(dev[0], apdev[0], "M2/M6", "0a")
+
+def test_ap_wps_m2_msg_type_m8(dev, apdev):
+ """WPS and M2 but Message Type M8"""
+ wps_m2_but_other(dev[0], apdev[0], "M2/M8", "0c")
+
+def test_ap_wps_m4_msg_type_m2(dev, apdev):
+ """WPS and M4 but Message Type M2"""
+ wps_m4_but_other(dev[0], apdev[0], "M4/M2", "05")
+
+def test_ap_wps_m4_msg_type_m2d(dev, apdev):
+ """WPS and M4 but Message Type M2D"""
+ wps_m4_but_other(dev[0], apdev[0], "M4/M2D", "06")
+
+@remote_compatible
+def test_ap_wps_config_methods(dev, apdev):
+ """WPS configuration method parsing"""
+ ssid = "test-wps-conf"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "config_methods": "ethernet display ext_nfc_token int_nfc_token physical_display physical_push_button"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "config_methods": "display push_button"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+def test_ap_wps_set_selected_registrar_proto(dev, apdev):
+ """WPS UPnP SetSelectedRegistrar protocol testing"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hapd = add_ssdp_ap(apdev[0], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+ urls = upnp_get_urls(location)
+ eventurl = urlparse(urls['event_sub_url'])
+ ctrlurl = urlparse(urls['control_url'])
+ url = urlparse(location)
+ conn = HTTPConnection(url.netloc)
+
+ class WPSERHTTPServer(StreamRequestHandler):
+ def handle(self):
+ data = self.rfile.readline().strip()
+ logger.debug(data)
+ self.wfile.write(gen_wps_event())
+
+ server = MyTCPServer(("127.0.0.1", 12345), WPSERHTTPServer)
+ server.timeout = 1
+
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+ sid = resp.getheader("sid")
+ logger.debug("Subscription SID " + sid)
+ server.handle_request()
+
+ tests = [(500, "10"),
+ (200, "104a000110" + "1041000101" + "101200020000" +
+ "105300023148" +
+ "1049002c00372a0001200124111111111111222222222222333333333333444444444444555555555555666666666666" +
+ "10480010362db47ba53a519188fb5458b986b2e4"),
+ (200, "104a000110" + "1041000100" + "101200020000" +
+ "105300020000"),
+ (200, "104a000110" + "1041000100"),
+ (200, "104a000110")]
+ for status, test in tests:
+ tlvs = binascii.unhexlify(test)
+ newmsg = base64.b64encode(tlvs).decode()
+ msg = '<?xml version="1.0"?>\n'
+ msg += '<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">'
+ msg += '<s:Body>'
+ msg += '<u:SetSelectedRegistrar xmlns:u="urn:schemas-wifialliance-org:service:WFAWLANConfig:1">'
+ msg += '<NewMessage>'
+ msg += newmsg
+ msg += "</NewMessage></u:SetSelectedRegistrar></s:Body></s:Envelope>"
+ headers = {"Content-type": 'text/xml; charset="utf-8"'}
+ headers["SOAPAction"] = '"urn:schemas-wifialliance-org:service:WFAWLANConfig:1#%s"' % "SetSelectedRegistrar"
+ conn.request("POST", ctrlurl.path, msg, headers)
+ resp = conn.getresponse()
+ if resp.status != status:
+ raise Exception("Unexpected HTTP response: %d (expected %d)" % (resp.status, status))
+
+def test_ap_wps_adv_oom(dev, apdev):
+ """WPS AP and advertisement OOM"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hapd = add_ssdp_ap(apdev[0], ap_uuid)
+
+ with alloc_fail(hapd, 1, "=msearchreply_state_machine_start"):
+ ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1",
+ no_recv=True)
+ time.sleep(0.2)
+
+ with alloc_fail(hapd, 1, "eloop_register_timeout;msearchreply_state_machine_start"):
+ ssdp_send_msearch("urn:schemas-wifialliance-org:service:WFAWLANConfig:1",
+ no_recv=True)
+ time.sleep(0.2)
+
+ with alloc_fail(hapd, 1,
+ "next_advertisement;advertisement_state_machine_stop"):
+ hapd.disable()
+
+ with alloc_fail(hapd, 1, "ssdp_listener_start"):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE succeeded during OOM")
+
+def test_wps_config_methods(dev):
+ """WPS config method update"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ if "OK" not in wpas.request("SET config_methods display label"):
+ raise Exception("Failed to set config_methods")
+ if wpas.request("GET config_methods").strip() != "display label":
+ raise Exception("config_methods were not updated")
+ if "OK" not in wpas.request("SET config_methods "):
+ raise Exception("Failed to clear config_methods")
+ if wpas.request("GET config_methods").strip() != "":
+ raise Exception("config_methods were not cleared")
+
+WPS_VENDOR_ID_WFA = 14122
+WPS_VENDOR_TYPE = 1
+
+# EAP-WSC Op-Code values
+WSC_Start = 0x01
+WSC_ACK = 0x02
+WSC_NACK = 0x03
+WSC_MSG = 0x04
+WSC_Done = 0x05
+WSC_FRAG_ACK = 0x06
+
+ATTR_AP_CHANNEL = 0x1001
+ATTR_ASSOC_STATE = 0x1002
+ATTR_AUTH_TYPE = 0x1003
+ATTR_AUTH_TYPE_FLAGS = 0x1004
+ATTR_AUTHENTICATOR = 0x1005
+ATTR_CONFIG_METHODS = 0x1008
+ATTR_CONFIG_ERROR = 0x1009
+ATTR_CONFIRM_URL4 = 0x100a
+ATTR_CONFIRM_URL6 = 0x100b
+ATTR_CONN_TYPE = 0x100c
+ATTR_CONN_TYPE_FLAGS = 0x100d
+ATTR_CRED = 0x100e
+ATTR_ENCR_TYPE = 0x100f
+ATTR_ENCR_TYPE_FLAGS = 0x1010
+ATTR_DEV_NAME = 0x1011
+ATTR_DEV_PASSWORD_ID = 0x1012
+ATTR_E_HASH1 = 0x1014
+ATTR_E_HASH2 = 0x1015
+ATTR_E_SNONCE1 = 0x1016
+ATTR_E_SNONCE2 = 0x1017
+ATTR_ENCR_SETTINGS = 0x1018
+ATTR_ENROLLEE_NONCE = 0x101a
+ATTR_FEATURE_ID = 0x101b
+ATTR_IDENTITY = 0x101c
+ATTR_IDENTITY_PROOF = 0x101d
+ATTR_KEY_WRAP_AUTH = 0x101e
+ATTR_KEY_ID = 0x101f
+ATTR_MAC_ADDR = 0x1020
+ATTR_MANUFACTURER = 0x1021
+ATTR_MSG_TYPE = 0x1022
+ATTR_MODEL_NAME = 0x1023
+ATTR_MODEL_NUMBER = 0x1024
+ATTR_NETWORK_INDEX = 0x1026
+ATTR_NETWORK_KEY = 0x1027
+ATTR_NETWORK_KEY_INDEX = 0x1028
+ATTR_NEW_DEVICE_NAME = 0x1029
+ATTR_NEW_PASSWORD = 0x102a
+ATTR_OOB_DEVICE_PASSWORD = 0x102c
+ATTR_OS_VERSION = 0x102d
+ATTR_POWER_LEVEL = 0x102f
+ATTR_PSK_CURRENT = 0x1030
+ATTR_PSK_MAX = 0x1031
+ATTR_PUBLIC_KEY = 0x1032
+ATTR_RADIO_ENABLE = 0x1033
+ATTR_REBOOT = 0x1034
+ATTR_REGISTRAR_CURRENT = 0x1035
+ATTR_REGISTRAR_ESTABLISHED = 0x1036
+ATTR_REGISTRAR_LIST = 0x1037
+ATTR_REGISTRAR_MAX = 0x1038
+ATTR_REGISTRAR_NONCE = 0x1039
+ATTR_REQUEST_TYPE = 0x103a
+ATTR_RESPONSE_TYPE = 0x103b
+ATTR_RF_BANDS = 0x103c
+ATTR_R_HASH1 = 0x103d
+ATTR_R_HASH2 = 0x103e
+ATTR_R_SNONCE1 = 0x103f
+ATTR_R_SNONCE2 = 0x1040
+ATTR_SELECTED_REGISTRAR = 0x1041
+ATTR_SERIAL_NUMBER = 0x1042
+ATTR_WPS_STATE = 0x1044
+ATTR_SSID = 0x1045
+ATTR_TOTAL_NETWORKS = 0x1046
+ATTR_UUID_E = 0x1047
+ATTR_UUID_R = 0x1048
+ATTR_VENDOR_EXT = 0x1049
+ATTR_VERSION = 0x104a
+ATTR_X509_CERT_REQ = 0x104b
+ATTR_X509_CERT = 0x104c
+ATTR_EAP_IDENTITY = 0x104d
+ATTR_MSG_COUNTER = 0x104e
+ATTR_PUBKEY_HASH = 0x104f
+ATTR_REKEY_KEY = 0x1050
+ATTR_KEY_LIFETIME = 0x1051
+ATTR_PERMITTED_CFG_METHODS = 0x1052
+ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053
+ATTR_PRIMARY_DEV_TYPE = 0x1054
+ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055
+ATTR_PORTABLE_DEV = 0x1056
+ATTR_AP_SETUP_LOCKED = 0x1057
+ATTR_APPLICATION_EXT = 0x1058
+ATTR_EAP_TYPE = 0x1059
+ATTR_IV = 0x1060
+ATTR_KEY_PROVIDED_AUTO = 0x1061
+ATTR_802_1X_ENABLED = 0x1062
+ATTR_APPSESSIONKEY = 0x1063
+ATTR_WEPTRANSMITKEY = 0x1064
+ATTR_REQUESTED_DEV_TYPE = 0x106a
+
+# Message Type
+WPS_Beacon = 0x01
+WPS_ProbeRequest = 0x02
+WPS_ProbeResponse = 0x03
+WPS_M1 = 0x04
+WPS_M2 = 0x05
+WPS_M2D = 0x06
+WPS_M3 = 0x07
+WPS_M4 = 0x08
+WPS_M5 = 0x09
+WPS_M6 = 0x0a
+WPS_M7 = 0x0b
+WPS_M8 = 0x0c
+WPS_WSC_ACK = 0x0d
+WPS_WSC_NACK = 0x0e
+WPS_WSC_DONE = 0x0f
+
+def get_wsc_msg(dev):
+ ev = dev.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX")
+ data = binascii.unhexlify(ev.split(' ')[2])
+ msg = {}
+
+ # Parse EAPOL header
+ if len(data) < 4:
+ raise Exception("No room for EAPOL header")
+ version, type, length = struct.unpack('>BBH', data[0:4])
+ msg['eapol_version'] = version
+ msg['eapol_type'] = type
+ msg['eapol_length'] = length
+ data = data[4:]
+ if length != len(data):
+ raise Exception("EAPOL header length mismatch (%d != %d)" % (length, len(data)))
+ if type != 0:
+ raise Exception("Unexpected EAPOL header type: %d" % type)
+
+ # Parse EAP header
+ if len(data) < 4:
+ raise Exception("No room for EAP header")
+ code, identifier, length = struct.unpack('>BBH', data[0:4])
+ msg['eap_code'] = code
+ msg['eap_identifier'] = identifier
+ msg['eap_length'] = length
+ data = data[4:]
+ if msg['eapol_length'] != msg['eap_length']:
+ raise Exception("EAP header length mismatch (%d != %d)" % (msg['eapol_length'], length))
+
+ # Parse EAP expanded header
+ if len(data) < 1:
+ raise Exception("No EAP type included")
+ msg['eap_type'], = struct.unpack('B', data[0:1])
+ data = data[1:]
+
+ if msg['eap_type'] == 254:
+ if len(data) < 3 + 4:
+ raise Exception("Truncated EAP expanded header")
+ msg['eap_vendor_id'], msg['eap_vendor_type'] = struct.unpack('>LL', b'\x00' + data[0:7])
+ data = data[7:]
+ else:
+ raise Exception("Unexpected EAP type")
+
+ if msg['eap_vendor_id'] != WPS_VENDOR_ID_WFA:
+ raise Exception("Unexpected Vendor-Id")
+ if msg['eap_vendor_type'] != WPS_VENDOR_TYPE:
+ raise Exception("Unexpected Vendor-Type")
+
+ # Parse EAP-WSC header
+ if len(data) < 2:
+ raise Exception("Truncated EAP-WSC header")
+ msg['wsc_opcode'], msg['wsc_flags'] = struct.unpack('BB', data[0:2])
+ data = data[2:]
+
+ # Parse WSC attributes
+ msg['raw_attrs'] = data
+ attrs = {}
+ while len(data) > 0:
+ if len(data) < 4:
+ raise Exception("Truncated attribute header")
+ attr, length = struct.unpack('>HH', data[0:4])
+ data = data[4:]
+ if length > len(data):
+ raise Exception("Truncated attribute 0x%04x" % attr)
+ attrs[attr] = data[0:length]
+ data = data[length:]
+ msg['wsc_attrs'] = attrs
+
+ if ATTR_MSG_TYPE in attrs:
+ msg['wsc_msg_type'], = struct.unpack('B', attrs[ATTR_MSG_TYPE])
+
+ return msg
+
+def recv_wsc_msg(dev, opcode, msg_type):
+ msg = get_wsc_msg(dev)
+ if msg['wsc_opcode'] != opcode or msg['wsc_msg_type'] != msg_type:
+ raise Exception("Unexpected Op-Code/MsgType")
+ return msg, msg['wsc_attrs'], msg['raw_attrs']
+
+def build_wsc_attr(attr, payload):
+ _payload = payload if type(payload) == bytes else payload.encode()
+ return struct.pack('>HH', attr, len(_payload)) + _payload
+
+def build_attr_msg_type(msg_type):
+ return build_wsc_attr(ATTR_MSG_TYPE, struct.pack('B', msg_type))
+
+def build_eap_wsc(eap_code, eap_id, payload, opcode=WSC_MSG):
+ length = 4 + 8 + 2 + len(payload)
+ # EAPOL header
+ msg = struct.pack('>BBH', 2, 0, length)
+ # EAP header
+ msg += struct.pack('>BBH', eap_code, eap_id, length)
+ # EAP expanded header for EAP-WSC
+ msg += struct.pack('B', 254)
+ msg += struct.pack('>L', WPS_VENDOR_ID_WFA)[1:4]
+ msg += struct.pack('>L', WPS_VENDOR_TYPE)
+ # EAP-WSC header
+ msg += struct.pack('BB', opcode, 0)
+ # WSC attributes
+ msg += payload
+ return msg
+
+def build_eap_success(eap_id):
+ length = 4
+ # EAPOL header
+ msg = struct.pack('>BBH', 2, 0, length)
+ # EAP header
+ msg += struct.pack('>BBH', 3, eap_id, length)
+ return msg
+
+def build_eap_failure(eap_id):
+ length = 4
+ # EAPOL header
+ msg = struct.pack('>BBH', 2, 0, length)
+ # EAP header
+ msg += struct.pack('>BBH', 4, eap_id, length)
+ return msg
+
+def send_wsc_msg(dev, src, msg):
+ res = dev.request("EAPOL_RX " + src + " " + binascii.hexlify(msg).decode())
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+
+group_5_prime = 0xFFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF
+group_5_generator = 2
+
+def wsc_kdf(key, label, bits):
+ result = b''
+ i = 1
+ while len(result) * 8 < bits:
+ data = struct.pack('>L', i) + label.encode() + struct.pack('>L', bits)
+ m = hmac.new(key, data, hashlib.sha256)
+ result += m.digest()
+ i += 1
+ return result[0:bits // 8]
+
+def wsc_keys(kdk):
+ keys = wsc_kdf(kdk, "Wi-Fi Easy and Secure Key Derivation", 640)
+ authkey = keys[0:32]
+ keywrapkey = keys[32:48]
+ emsk = keys[48:80]
+ return authkey, keywrapkey, emsk
+
+def wsc_dev_pw_half_psk(authkey, dev_pw):
+ m = hmac.new(authkey, dev_pw.encode(), hashlib.sha256)
+ return m.digest()[0:16]
+
+def wsc_dev_pw_psk(authkey, dev_pw):
+ dev_pw_1 = dev_pw[0:len(dev_pw) // 2]
+ dev_pw_2 = dev_pw[len(dev_pw) // 2:]
+ psk1 = wsc_dev_pw_half_psk(authkey, dev_pw_1)
+ psk2 = wsc_dev_pw_half_psk(authkey, dev_pw_2)
+ return psk1, psk2
+
+def build_attr_authenticator(authkey, prev_msg, curr_msg):
+ m = hmac.new(authkey, prev_msg + curr_msg, hashlib.sha256)
+ auth = m.digest()[0:8]
+ return build_wsc_attr(ATTR_AUTHENTICATOR, auth)
+
+def build_attr_encr_settings(authkey, keywrapkey, data):
+ m = hmac.new(authkey, data, hashlib.sha256)
+ kwa = m.digest()[0:8]
+ data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
+ iv = 16*b'\x99'
+ aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+ pad_len = 16 - len(data) % 16
+ ps = pad_len * struct.pack('B', pad_len)
+ data += ps
+ wrapped = aes.encrypt(data)
+ return build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+
+def decrypt_attr_encr_settings(authkey, keywrapkey, data):
+ if len(data) < 32 or len(data) % 16 != 0:
+ raise Exception("Unexpected Encrypted Settings length: %d" % len(data))
+ iv = data[0:16]
+ encr = data[16:]
+ aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+ decrypted = aes.decrypt(encr)
+ pad_len, = struct.unpack('B', decrypted[-1:])
+ if pad_len > len(decrypted):
+ raise Exception("Invalid padding in Encrypted Settings")
+ for i in range(-pad_len, -1):
+ if decrypted[i] != decrypted[-1]:
+ raise Exception("Invalid PS value in Encrypted Settings")
+
+ decrypted = decrypted[0:len(decrypted) - pad_len]
+ if len(decrypted) < 12:
+ raise Exception("Truncated Encrypted Settings plaintext")
+ kwa = decrypted[-12:]
+ attr, length = struct.unpack(">HH", kwa[0:4])
+ if attr != ATTR_KEY_WRAP_AUTH or length != 8:
+ raise Exception("Invalid KWA header")
+ kwa = kwa[4:]
+ decrypted = decrypted[0:len(decrypted) - 12]
+
+ m = hmac.new(authkey, decrypted, hashlib.sha256)
+ calc_kwa = m.digest()[0:8]
+ if kwa != calc_kwa:
+ raise Exception("KWA mismatch")
+
+ return decrypted
+
+def zeropad_str(val, pad_len):
+ while len(val) < pad_len * 2:
+ val = '0' + val
+ return val
+
+def wsc_dh_init():
+ # For now, use a hardcoded private key. In theory, this is supposed to be
+ # randomly selected.
+ own_private = 0x123456789
+ own_public = pow(group_5_generator, own_private, group_5_prime)
+ pk = binascii.unhexlify(zeropad_str(format(own_public, '02x'), 192))
+ return own_private, pk
+
+def wsc_dh_kdf(peer_pk, own_private, mac_addr, e_nonce, r_nonce):
+ peer_public = int(binascii.hexlify(peer_pk), 16)
+ if peer_public < 2 or peer_public >= group_5_prime:
+ raise Exception("Invalid peer public key")
+ if pow(peer_public, (group_5_prime - 1) // 2, group_5_prime) != 1:
+ raise Exception("Unexpected Legendre symbol for peer public key")
+
+ shared_secret = pow(peer_public, own_private, group_5_prime)
+ ss = zeropad_str(format(shared_secret, "02x"), 192)
+ logger.debug("DH shared secret: " + ss)
+
+ dhkey = hashlib.sha256(binascii.unhexlify(ss)).digest()
+ logger.debug("DHKey: " + binascii.hexlify(dhkey).decode())
+
+ m = hmac.new(dhkey, e_nonce + mac_addr + r_nonce, hashlib.sha256)
+ kdk = m.digest()
+ logger.debug("KDK: " + binascii.hexlify(kdk).decode())
+ authkey, keywrapkey, emsk = wsc_keys(kdk)
+ logger.debug("AuthKey: " + binascii.hexlify(authkey).decode())
+ logger.debug("KeyWrapKey: " + binascii.hexlify(keywrapkey).decode())
+ logger.debug("EMSK: " + binascii.hexlify(emsk).decode())
+ return authkey, keywrapkey
+
+def wsc_dev_pw_hash(authkey, dev_pw, e_pk, r_pk):
+ psk1, psk2 = wsc_dev_pw_psk(authkey, dev_pw)
+ logger.debug("PSK1: " + binascii.hexlify(psk1).decode())
+ logger.debug("PSK2: " + binascii.hexlify(psk2).decode())
+
+ # Note: Secret values are supposed to be random, but hardcoded values are
+ # fine for testing.
+ s1 = 16*b'\x77'
+ m = hmac.new(authkey, s1 + psk1 + e_pk + r_pk, hashlib.sha256)
+ hash1 = m.digest()
+ logger.debug("Hash1: " + binascii.hexlify(hash1).decode())
+
+ s2 = 16*b'\x88'
+ m = hmac.new(authkey, s2 + psk2 + e_pk + r_pk, hashlib.sha256)
+ hash2 = m.digest()
+ logger.debug("Hash2: " + binascii.hexlify(hash2).decode())
+ return s1, s2, hash1, hash2
+
+def build_m1(eap_id, uuid_e, mac_addr, e_nonce, e_pk,
+ manufacturer='', model_name='', config_methods='\x00\x00'):
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M1)
+ attrs += build_wsc_attr(ATTR_UUID_E, uuid_e)
+ attrs += build_wsc_attr(ATTR_MAC_ADDR, mac_addr)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_PUBLIC_KEY, e_pk)
+ attrs += build_wsc_attr(ATTR_AUTH_TYPE_FLAGS, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_ENCR_TYPE_FLAGS, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_CONN_TYPE_FLAGS, '\x00')
+ attrs += build_wsc_attr(ATTR_CONFIG_METHODS, config_methods)
+ attrs += build_wsc_attr(ATTR_WPS_STATE, '\x00')
+ attrs += build_wsc_attr(ATTR_MANUFACTURER, manufacturer)
+ attrs += build_wsc_attr(ATTR_MODEL_NAME, model_name)
+ attrs += build_wsc_attr(ATTR_MODEL_NUMBER, '')
+ attrs += build_wsc_attr(ATTR_SERIAL_NUMBER, '')
+ attrs += build_wsc_attr(ATTR_PRIMARY_DEV_TYPE, 8*'\x00')
+ attrs += build_wsc_attr(ATTR_DEV_NAME, '')
+ attrs += build_wsc_attr(ATTR_RF_BANDS, '\x00')
+ attrs += build_wsc_attr(ATTR_ASSOC_STATE, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_DEV_PASSWORD_ID, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_CONFIG_ERROR, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_OS_VERSION, '\x00\x00\x00\x00')
+ m1 = build_eap_wsc(2, eap_id, attrs)
+ return m1, attrs
+
+def build_m2(authkey, m1, eap_id, e_nonce, r_nonce, uuid_r, r_pk,
+ dev_pw_id='\x00\x00', eap_code=1):
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M2)
+ if e_nonce:
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ if r_nonce:
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_UUID_R, uuid_r)
+ if r_pk:
+ attrs += build_wsc_attr(ATTR_PUBLIC_KEY, r_pk)
+ attrs += build_wsc_attr(ATTR_AUTH_TYPE_FLAGS, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_ENCR_TYPE_FLAGS, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_CONN_TYPE_FLAGS, '\x00')
+ attrs += build_wsc_attr(ATTR_CONFIG_METHODS, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_MANUFACTURER, '')
+ attrs += build_wsc_attr(ATTR_MODEL_NAME, '')
+ attrs += build_wsc_attr(ATTR_MODEL_NUMBER, '')
+ attrs += build_wsc_attr(ATTR_SERIAL_NUMBER, '')
+ attrs += build_wsc_attr(ATTR_PRIMARY_DEV_TYPE, 8*'\x00')
+ attrs += build_wsc_attr(ATTR_DEV_NAME, '')
+ attrs += build_wsc_attr(ATTR_RF_BANDS, '\x00')
+ attrs += build_wsc_attr(ATTR_ASSOC_STATE, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_CONFIG_ERROR, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_DEV_PASSWORD_ID, dev_pw_id)
+ attrs += build_wsc_attr(ATTR_OS_VERSION, '\x00\x00\x00\x00')
+ attrs += build_attr_authenticator(authkey, m1, attrs)
+ m2 = build_eap_wsc(eap_code, eap_id, attrs)
+ return m2, attrs
+
+def build_m2d(m1, eap_id, e_nonce, r_nonce, uuid_r, dev_pw_id=None, eap_code=1):
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M2D)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_UUID_R, uuid_r)
+ attrs += build_wsc_attr(ATTR_AUTH_TYPE_FLAGS, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_ENCR_TYPE_FLAGS, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_CONN_TYPE_FLAGS, '\x00')
+ attrs += build_wsc_attr(ATTR_CONFIG_METHODS, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_MANUFACTURER, '')
+ attrs += build_wsc_attr(ATTR_MODEL_NAME, '')
+ #attrs += build_wsc_attr(ATTR_MODEL_NUMBER, '')
+ attrs += build_wsc_attr(ATTR_SERIAL_NUMBER, '')
+ attrs += build_wsc_attr(ATTR_PRIMARY_DEV_TYPE, 8*'\x00')
+ attrs += build_wsc_attr(ATTR_DEV_NAME, '')
+ attrs += build_wsc_attr(ATTR_RF_BANDS, '\x00')
+ attrs += build_wsc_attr(ATTR_ASSOC_STATE, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_CONFIG_ERROR, '\x00\x00')
+ attrs += build_wsc_attr(ATTR_OS_VERSION, '\x00\x00\x00\x00')
+ if dev_pw_id:
+ attrs += build_wsc_attr(ATTR_DEV_PASSWORD_ID, dev_pw_id)
+ m2d = build_eap_wsc(eap_code, eap_id, attrs)
+ return m2d, attrs
+
+def build_ack(eap_id, e_nonce, r_nonce, msg_type=WPS_WSC_ACK, eap_code=1):
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ if msg_type is not None:
+ attrs += build_attr_msg_type(msg_type)
+ if e_nonce:
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ if r_nonce:
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ msg = build_eap_wsc(eap_code, eap_id, attrs, opcode=WSC_ACK)
+ return msg, attrs
+
+def build_nack(eap_id, e_nonce, r_nonce, config_error='\x00\x00',
+ msg_type=WPS_WSC_NACK, eap_code=1):
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ if msg_type is not None:
+ attrs += build_attr_msg_type(msg_type)
+ if e_nonce:
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ if r_nonce:
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ if config_error:
+ attrs += build_wsc_attr(ATTR_CONFIG_ERROR, config_error)
+ msg = build_eap_wsc(eap_code, eap_id, attrs, opcode=WSC_NACK)
+ return msg, attrs
+
+def test_wps_ext(dev, apdev):
+ """WPS against external implementation"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+ wsc_start_id = msg['eap_identifier']
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+
+ authkey, keywrapkey = wsc_dh_kdf(m2_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, e_nonce,
+ m2_attrs[ATTR_REGISTRAR_NONCE])
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk,
+ m2_attrs[ATTR_PUBLIC_KEY])
+
+ logger.debug("Send M3 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
+ m2_attrs[ATTR_REGISTRAR_NONCE])
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ logger.debug("Receive M4 from AP")
+ msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+ logger.debug("Send M5 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M5)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
+ m2_attrs[ATTR_REGISTRAR_NONCE])
+ data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+ raw_m5_attrs = attrs
+ m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m5)
+
+ logger.debug("Receive M6 from AP")
+ msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
+
+ logger.debug("Send M7 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M7)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
+ m2_attrs[ATTR_REGISTRAR_NONCE])
+ data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
+ m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ raw_m7_attrs = attrs
+ send_wsc_msg(hapd, addr, m7)
+
+ logger.debug("Receive M8 from AP")
+ msg, m8_attrs, raw_m8_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M8)
+ m8_cred = decrypt_attr_encr_settings(authkey, keywrapkey,
+ m8_attrs[ATTR_ENCR_SETTINGS])
+ logger.debug("M8 Credential: " + binascii.hexlify(m8_cred).decode())
+
+ logger.debug("Prepare WSC_Done")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_WSC_DONE)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE,
+ m2_attrs[ATTR_REGISTRAR_NONCE])
+ wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+ # Do not send WSC_Done yet to allow exchangw with STA complete before the
+ # AP disconnects.
+
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+
+ eap_id = wsc_start_id
+ logger.debug("Send WSC/Start to STA")
+ wsc_start = build_eap_wsc(1, eap_id, b'', opcode=WSC_Start)
+ send_wsc_msg(dev[0], bssid, wsc_start)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M1 from STA")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+
+ authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
+ m1_attrs[ATTR_PUBLIC_KEY],
+ e_pk)
+
+ logger.debug("Send M2 to STA")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+ m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, uuid_r, e_pk)
+ send_wsc_msg(dev[0], bssid, m2)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M3 from STA")
+ msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+ raw_m4_attrs = attrs
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 from STA")
+ msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
+
+ logger.debug("Send M6 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M6)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
+ m1_attrs[ATTR_ENROLLEE_NONCE])
+ data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m5_attrs, attrs)
+ raw_m6_attrs = attrs
+ m6 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m6)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M7 from STA")
+ msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M7)
+
+ logger.debug("Send M8 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M8)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
+ m1_attrs[ATTR_ENROLLEE_NONCE])
+ attrs += build_attr_encr_settings(authkey, keywrapkey, m8_cred)
+ attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
+ raw_m8_attrs = attrs
+ m8 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m8)
+ eap_id = (eap_id + 1) % 256
+
+ ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=5)
+ if ev is None:
+ raise Exception("wpa_supplicant did not report credential")
+
+ logger.debug("Receive WSC_Done from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_Done or msg['wsc_msg_type'] != WPS_WSC_DONE:
+ raise Exception("Unexpected Op-Code/MsgType for WSC_Done")
+
+ logger.debug("Send WSC_Done to AP")
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ send_wsc_msg(hapd, addr, wsc_done)
+
+ ev = hapd.wait_event(["WPS-REG-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("hostapd did not report WPS success")
+
+ dev[0].wait_connected()
+
+def wps_start_kwa(dev, apdev):
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Receive M1 from STA")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+ eap_id = (msg['eap_identifier'] + 1) % 256
+
+ authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
+ m1_attrs[ATTR_PUBLIC_KEY],
+ e_pk)
+
+ logger.debug("Send M2 to STA")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+ m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, uuid_r, e_pk)
+ send_wsc_msg(dev[0], bssid, m2)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M3 from STA")
+ msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+
+ return r_s1, keywrapkey, authkey, raw_m3_attrs, eap_id, bssid, attrs
+
+def wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id):
+ attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_kwa_proto_no_kwa(dev, apdev):
+ """WPS and KWA error: No KWA attribute"""
+ r_s1, keywrapkey, authkey, raw_m3_attrs, eap_id, bssid, attrs = wps_start_kwa(dev, apdev)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ # Encrypted Settings without KWA
+ iv = 16*b'\x99'
+ aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+ pad_len = 16 - len(data) % 16
+ ps = pad_len * struct.pack('B', pad_len)
+ data += ps
+ wrapped = aes.encrypt(data)
+ attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+ wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id)
+
+def test_wps_ext_kwa_proto_data_after_kwa(dev, apdev):
+ """WPS and KWA error: Data after KWA"""
+ r_s1, keywrapkey, authkey, raw_m3_attrs, eap_id, bssid, attrs = wps_start_kwa(dev, apdev)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ # Encrypted Settings and data after KWA
+ m = hmac.new(authkey, data, hashlib.sha256)
+ kwa = m.digest()[0:8]
+ data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
+ data += build_wsc_attr(ATTR_VENDOR_EXT, "1234567890")
+ iv = 16*b'\x99'
+ aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+ pad_len = 16 - len(data) % 16
+ ps = pad_len * struct.pack('B', pad_len)
+ data += ps
+ wrapped = aes.encrypt(data)
+ attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+ wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id)
+
+def test_wps_ext_kwa_proto_kwa_mismatch(dev, apdev):
+ """WPS and KWA error: KWA mismatch"""
+ r_s1, keywrapkey, authkey, raw_m3_attrs, eap_id, bssid, attrs = wps_start_kwa(dev, apdev)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ # Encrypted Settings and KWA with incorrect value
+ data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, 8*'\x00')
+ iv = 16*b'\x99'
+ aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+ pad_len = 16 - len(data) % 16
+ ps = pad_len * struct.pack('B', pad_len)
+ data += ps
+ wrapped = aes.encrypt(data)
+ attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+ wps_stop_kwa(dev, bssid, attrs, authkey, raw_m3_attrs, eap_id)
+
+def wps_run_cred_proto(dev, apdev, m8_cred, connect=False, no_connect=False):
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Receive M1 from STA")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+ eap_id = (msg['eap_identifier'] + 1) % 256
+
+ authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
+ m1_attrs[ATTR_PUBLIC_KEY],
+ e_pk)
+
+ logger.debug("Send M2 to STA")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+ m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, uuid_r, e_pk)
+ send_wsc_msg(dev[0], bssid, m2)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M3 from STA")
+ msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+ raw_m4_attrs = attrs
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 from STA")
+ msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
+
+ logger.debug("Send M6 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M6)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
+ m1_attrs[ATTR_ENROLLEE_NONCE])
+ data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m5_attrs, attrs)
+ raw_m6_attrs = attrs
+ m6 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m6)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M7 from STA")
+ msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M7)
+
+ logger.debug("Send M8 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M8)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE,
+ m1_attrs[ATTR_ENROLLEE_NONCE])
+ attrs += build_attr_encr_settings(authkey, keywrapkey, m8_cred)
+ attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
+ raw_m8_attrs = attrs
+ m8 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m8)
+ eap_id = (eap_id + 1) % 256
+
+ if no_connect:
+ logger.debug("Receive WSC_Done from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_Done or msg['wsc_msg_type'] != WPS_WSC_DONE:
+ raise Exception("Unexpected Op-Code/MsgType for WSC_Done")
+
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ elif connect:
+ logger.debug("Receive WSC_Done from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_Done or msg['wsc_msg_type'] != WPS_WSC_DONE:
+ raise Exception("Unexpected Op-Code/MsgType for WSC_Done")
+
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+
+ dev[0].wait_connected()
+ else:
+ # Verify STA NACK's the credential
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def build_cred(nw_idx='\x01', ssid='test-wps-conf', auth_type='\x00\x20',
+ encr_type='\x00\x08', nw_key="12345678",
+ mac_addr='\x00\x00\x00\x00\x00\x00'):
+ attrs = b''
+ if nw_idx is not None:
+ attrs += build_wsc_attr(ATTR_NETWORK_INDEX, nw_idx)
+ if ssid is not None:
+ attrs += build_wsc_attr(ATTR_SSID, ssid)
+ if auth_type is not None:
+ attrs += build_wsc_attr(ATTR_AUTH_TYPE, auth_type)
+ if encr_type is not None:
+ attrs += build_wsc_attr(ATTR_ENCR_TYPE, encr_type)
+ if nw_key is not None:
+ attrs += build_wsc_attr(ATTR_NETWORK_KEY, nw_key)
+ if mac_addr is not None:
+ attrs += build_wsc_attr(ATTR_MAC_ADDR, mac_addr)
+ return build_wsc_attr(ATTR_CRED, attrs)
+
+def test_wps_ext_cred_proto_success(dev, apdev):
+ """WPS and Credential: success"""
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ m8_cred = build_cred(mac_addr=mac_addr)
+ wps_run_cred_proto(dev, apdev, m8_cred, connect=True)
+
+def test_wps_ext_cred_proto_mac_addr_mismatch(dev, apdev):
+ """WPS and Credential: MAC Address mismatch"""
+ m8_cred = build_cred()
+ wps_run_cred_proto(dev, apdev, m8_cred, connect=True)
+
+def test_wps_ext_cred_proto_zero_padding(dev, apdev):
+ """WPS and Credential: zeropadded attributes"""
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ m8_cred = build_cred(mac_addr=mac_addr, ssid='test-wps-conf\x00',
+ nw_key="12345678\x00")
+ wps_run_cred_proto(dev, apdev, m8_cred, connect=True)
+
+def test_wps_ext_cred_proto_ssid_missing(dev, apdev):
+ """WPS and Credential: SSID missing"""
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ m8_cred = build_cred(mac_addr=mac_addr, ssid=None)
+ wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_ssid_zero_len(dev, apdev):
+ """WPS and Credential: Zero-length SSID"""
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ m8_cred = build_cred(mac_addr=mac_addr, ssid="")
+ wps_run_cred_proto(dev, apdev, m8_cred, no_connect=True)
+
+def test_wps_ext_cred_proto_auth_type_missing(dev, apdev):
+ """WPS and Credential: Auth Type missing"""
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ m8_cred = build_cred(mac_addr=mac_addr, auth_type=None)
+ wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_encr_type_missing(dev, apdev):
+ """WPS and Credential: Encr Type missing"""
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ m8_cred = build_cred(mac_addr=mac_addr, encr_type=None)
+ wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_network_key_missing(dev, apdev):
+ """WPS and Credential: Network Key missing"""
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ m8_cred = build_cred(mac_addr=mac_addr, nw_key=None)
+ wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_network_key_missing_open(dev, apdev):
+ """WPS and Credential: Network Key missing (open)"""
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ m8_cred = build_cred(mac_addr=mac_addr, auth_type='\x00\x01',
+ encr_type='\x00\x01', nw_key=None, ssid="foo")
+ wps_run_cred_proto(dev, apdev, m8_cred, no_connect=True)
+
+def test_wps_ext_cred_proto_mac_addr_missing(dev, apdev):
+ """WPS and Credential: MAC Address missing"""
+ m8_cred = build_cred(mac_addr=None)
+ wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_invalid_encr_type(dev, apdev):
+ """WPS and Credential: Invalid Encr Type"""
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ m8_cred = build_cred(mac_addr=mac_addr, encr_type='\x00\x00')
+ wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_cred_proto_missing_cred(dev, apdev):
+ """WPS and Credential: Missing Credential"""
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ m8_cred = b''
+ wps_run_cred_proto(dev, apdev, m8_cred)
+
+def test_wps_ext_proto_m2_no_public_key(dev, apdev):
+ """WPS and no Public Key in M2"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Receive M1 from STA")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+ eap_id = (msg['eap_identifier'] + 1) % 256
+
+ authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
+ m1_attrs[ATTR_PUBLIC_KEY],
+ e_pk)
+
+ logger.debug("Send M2 to STA")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+ m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, uuid_r, None)
+ send_wsc_msg(dev[0], bssid, m2)
+ eap_id = (eap_id + 1) % 256
+
+ # Verify STA NACK's the credential
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m2_invalid_public_key(dev, apdev):
+ """WPS and invalid Public Key in M2"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Receive M1 from STA")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+ eap_id = (msg['eap_identifier'] + 1) % 256
+
+ authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
+ m1_attrs[ATTR_PUBLIC_KEY],
+ e_pk)
+
+ logger.debug("Send M2 to STA")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+ m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, uuid_r, 192*b'\xff')
+ send_wsc_msg(dev[0], bssid, m2)
+ eap_id = (eap_id + 1) % 256
+
+ # Verify STA NACK's the credential
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m2_public_key_oom(dev, apdev):
+ """WPS and Public Key OOM in M2"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Receive M1 from STA")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+ eap_id = (msg['eap_identifier'] + 1) % 256
+
+ authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
+ m1_attrs[ATTR_PUBLIC_KEY],
+ e_pk)
+
+ logger.debug("Send M2 to STA")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+ m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, uuid_r, e_pk)
+ with alloc_fail(dev[0], 1, "wpabuf_alloc_copy;wps_process_pubkey"):
+ send_wsc_msg(dev[0], bssid, m2)
+ eap_id = (eap_id + 1) % 256
+
+ # Verify STA NACK's the credential
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_nack_m3(dev, apdev):
+ """WPS and NACK M3"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Receive M1 from STA")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+ eap_id = (msg['eap_identifier'] + 1) % 256
+
+ authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
+ m1_attrs[ATTR_PUBLIC_KEY],
+ e_pk)
+
+ logger.debug("Send M2 to STA")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+ m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, uuid_r, e_pk)
+ send_wsc_msg(dev[0], bssid, m2)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M3 from STA")
+ msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_nack(eap_id, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, config_error='\x01\x23')
+ send_wsc_msg(dev[0], bssid, msg)
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=5)
+ if ev is None:
+ raise Exception("Failure not reported")
+ if "msg=7 config_error=291" not in ev:
+ raise Exception("Unexpected failure reason: " + ev)
+
+def test_wps_ext_proto_nack_m5(dev, apdev):
+ """WPS and NACK M5"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Receive M1 from STA")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+ eap_id = (msg['eap_identifier'] + 1) % 256
+
+ authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
+ m1_attrs[ATTR_PUBLIC_KEY],
+ e_pk)
+
+ logger.debug("Send M2 to STA")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+ m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, uuid_r, e_pk)
+ send_wsc_msg(dev[0], bssid, m2)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M3 from STA")
+ msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+ raw_m4_attrs = attrs
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 from STA")
+ msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
+
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_nack(eap_id, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, config_error='\x01\x24')
+ send_wsc_msg(dev[0], bssid, msg)
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=5)
+ if ev is None:
+ raise Exception("Failure not reported")
+ if "msg=9 config_error=292" not in ev:
+ raise Exception("Unexpected failure reason: " + ev)
+
+def wps_nack_m3(dev, apdev):
+ pin = "00000000"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pbc=True)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Receive M1 from STA")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+ eap_id = (msg['eap_identifier'] + 1) % 256
+
+ authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
+ m1_attrs[ATTR_PUBLIC_KEY],
+ e_pk)
+
+ logger.debug("Send M2 to STA")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+ m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, uuid_r, e_pk, dev_pw_id='\x00\x04')
+ send_wsc_msg(dev[0], bssid, m2)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M3 from STA")
+ msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+ return eap_id, m1_attrs[ATTR_ENROLLEE_NONCE], r_nonce, bssid
+
+def test_wps_ext_proto_nack_m3_no_config_error(dev, apdev):
+ """WPS and NACK M3 missing Config Error"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_nack(eap_id, e_nonce, r_nonce, config_error=None)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_no_e_nonce(dev, apdev):
+ """WPS and NACK M3 missing E-Nonce"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_nack(eap_id, None, r_nonce)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_e_nonce_mismatch(dev, apdev):
+ """WPS and NACK M3 E-Nonce mismatch"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_nack(eap_id, 16*'\x00', r_nonce)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_no_r_nonce(dev, apdev):
+ """WPS and NACK M3 missing R-Nonce"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_nack(eap_id, e_nonce, None)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_r_nonce_mismatch(dev, apdev):
+ """WPS and NACK M3 R-Nonce mismatch"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_nack(eap_id, e_nonce, 16*'\x00')
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_no_msg_type(dev, apdev):
+ """WPS and NACK M3 no Message Type"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_nack(eap_id, e_nonce, r_nonce, msg_type=None)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_invalid_msg_type(dev, apdev):
+ """WPS and NACK M3 invalid Message Type"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_nack(eap_id, e_nonce, r_nonce, msg_type=123)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_nack_m3_invalid_attr(dev, apdev):
+ """WPS and NACK M3 invalid attribute"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ attrs = b'\x10\x10\x00'
+ msg = build_eap_wsc(1, eap_id, attrs, opcode=WSC_NACK)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_no_e_nonce(dev, apdev):
+ """WPS and ACK M3 missing E-Nonce"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_ack(eap_id, None, r_nonce)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_e_nonce_mismatch(dev, apdev):
+ """WPS and ACK M3 E-Nonce mismatch"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_ack(eap_id, 16*'\x00', r_nonce)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_no_r_nonce(dev, apdev):
+ """WPS and ACK M3 missing R-Nonce"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_ack(eap_id, e_nonce, None)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_r_nonce_mismatch(dev, apdev):
+ """WPS and ACK M3 R-Nonce mismatch"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_ack(eap_id, e_nonce, 16*'\x00')
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_no_msg_type(dev, apdev):
+ """WPS and ACK M3 no Message Type"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_ack(eap_id, e_nonce, r_nonce, msg_type=None)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_invalid_msg_type(dev, apdev):
+ """WPS and ACK M3 invalid Message Type"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send NACK to STA")
+ msg, attrs = build_ack(eap_id, e_nonce, r_nonce, msg_type=123)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3_invalid_attr(dev, apdev):
+ """WPS and ACK M3 invalid attribute"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send ACK to STA")
+ attrs = b'\x10\x10\x00'
+ msg = build_eap_wsc(1, eap_id, attrs, opcode=WSC_ACK)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def test_wps_ext_proto_ack_m3(dev, apdev):
+ """WPS and ACK M3"""
+ eap_id, e_nonce, r_nonce, bssid = wps_nack_m3(dev, apdev)
+ logger.debug("Send ACK to STA")
+ msg, attrs = build_ack(eap_id, e_nonce, r_nonce)
+ send_wsc_msg(dev[0], bssid, msg)
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ dev[0].flush_scan_cache()
+
+def wps_to_m3_helper(dev, apdev):
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+ wps_ext_eap_wsc(dev[0], hapd, bssid, "EAP-WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Receive M1 from STA")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M1)
+ eap_id = (msg['eap_identifier'] + 1) % 256
+
+ authkey, keywrapkey = wsc_dh_kdf(m1_attrs[ATTR_PUBLIC_KEY], own_private,
+ mac_addr, m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, pin,
+ m1_attrs[ATTR_PUBLIC_KEY],
+ e_pk)
+
+ logger.debug("Send M2 to STA")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, eap_id,
+ m1_attrs[ATTR_ENROLLEE_NONCE],
+ r_nonce, uuid_r, e_pk)
+ send_wsc_msg(dev[0], bssid, m2)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M3 from STA")
+ msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M3)
+ return eap_id, m1_attrs, r_nonce, bssid, r_hash1, r_hash2, r_s1, r_s2, raw_m3_attrs, authkey, keywrapkey
+
+def wps_to_m3(dev, apdev):
+ eap_id, m1_attrs, r_nonce, bssid, r_hash1, r_hash2, r_s1, r_s2, raw_m3_attrs, authkey, keywrapkey = wps_to_m3_helper(dev, apdev)
+ return eap_id, m1_attrs[ATTR_ENROLLEE_NONCE], r_nonce, bssid, r_hash1, r_hash2, r_s1, raw_m3_attrs, authkey, keywrapkey
+
+def wps_to_m5(dev, apdev):
+ eap_id, m1_attrs, r_nonce, bssid, r_hash1, r_hash2, r_s1, r_s2, raw_m3_attrs, authkey, keywrapkey = wps_to_m3_helper(dev, apdev)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, m1_attrs[ATTR_ENROLLEE_NONCE])
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+ raw_m4_attrs = attrs
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 from STA")
+ msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M5)
+
+ return eap_id, m1_attrs[ATTR_ENROLLEE_NONCE], r_nonce, bssid, r_hash1, r_hash2, r_s2, raw_m5_attrs, authkey, keywrapkey
+
+def test_wps_ext_proto_m4_missing_r_hash1(dev, apdev):
+ """WPS and no R-Hash1 in M4"""
+ eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ #attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, m3, attrs)
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 (NACK) from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m4_missing_r_hash2(dev, apdev):
+ """WPS and no R-Hash2 in M4"""
+ eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ #attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, m3, attrs)
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 (NACK) from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m4_missing_r_snonce1(dev, apdev):
+ """WPS and no R-SNonce1 in M4"""
+ eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ #data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ data = b''
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, m3, attrs)
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 (NACK) from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m4_invalid_pad_string(dev, apdev):
+ """WPS and invalid pad string in M4"""
+ eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+
+ m = hmac.new(authkey, data, hashlib.sha256)
+ kwa = m.digest()[0:8]
+ data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
+ iv = 16*b'\x99'
+ aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+ pad_len = 16 - len(data) % 16
+ ps = (pad_len - 1) * struct.pack('B', pad_len) + struct.pack('B', pad_len - 1)
+ data += ps
+ wrapped = aes.encrypt(data)
+ attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+
+ attrs += build_attr_authenticator(authkey, m3, attrs)
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 (NACK) from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m4_invalid_pad_value(dev, apdev):
+ """WPS and invalid pad value in M4"""
+ eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+
+ m = hmac.new(authkey, data, hashlib.sha256)
+ kwa = m.digest()[0:8]
+ data += build_wsc_attr(ATTR_KEY_WRAP_AUTH, kwa)
+ iv = 16*b'\x99'
+ aes = AES.new(keywrapkey, AES.MODE_CBC, iv)
+ pad_len = 16 - len(data) % 16
+ ps = (pad_len - 1) * struct.pack('B', pad_len) + struct.pack('B', 255)
+ data += ps
+ wrapped = aes.encrypt(data)
+ attrs += build_wsc_attr(ATTR_ENCR_SETTINGS, iv + wrapped)
+
+ attrs += build_attr_authenticator(authkey, m3, attrs)
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 (NACK) from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m4_no_encr_settings(dev, apdev):
+ """WPS and no Encr Settings in M4"""
+ eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s1, m3, authkey, keywrapkey = wps_to_m3(dev, apdev)
+
+ logger.debug("Send M4 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ attrs += build_attr_authenticator(authkey, m3, attrs)
+ m4 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m4)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M5 (NACK) from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m6_missing_r_snonce2(dev, apdev):
+ """WPS and no R-SNonce2 in M6"""
+ eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s2, m5, authkey, keywrapkey = wps_to_m5(dev, apdev)
+
+ logger.debug("Send M6 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M6)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ #data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+ data = b''
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, m5, attrs)
+ m6 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m6)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M7 (NACK) from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m6_no_encr_settings(dev, apdev):
+ """WPS and no Encr Settings in M6"""
+ eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s2, m5, authkey, keywrapkey = wps_to_m5(dev, apdev)
+
+ logger.debug("Send M6 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M6)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+ #attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, m5, attrs)
+ m6 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m6)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M7 (NACK) from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def test_wps_ext_proto_m8_no_encr_settings(dev, apdev):
+ """WPS and no Encr Settings in M6"""
+ eap_id, e_nonce, r_nonce, bssid, r_hash1, r_hash2, r_s2, m5, authkey, keywrapkey = wps_to_m5(dev, apdev)
+
+ logger.debug("Send M6 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M6)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, m5, attrs)
+ raw_m6_attrs = attrs
+ m6 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m6)
+ eap_id = (eap_id + 1) % 256
+
+ logger.debug("Receive M7 from STA")
+ msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(dev[0], WSC_MSG, WPS_M7)
+
+ logger.debug("Send M8 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M8)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ #attrs += build_attr_encr_settings(authkey, keywrapkey, m8_cred)
+ attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
+ raw_m8_attrs = attrs
+ m8 = build_eap_wsc(1, eap_id, attrs)
+ send_wsc_msg(dev[0], bssid, m8)
+
+ logger.debug("Receive WSC_Done (NACK) from STA")
+ msg = get_wsc_msg(dev[0])
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_Nack")
+
+ dev[0].request("WPS_CANCEL")
+ send_wsc_msg(dev[0], bssid, build_eap_failure(eap_id))
+ dev[0].wait_disconnected()
+
+def wps_start_ext_reg(apdev, dev):
+ addr = dev.own_addr()
+ bssid = apdev['bssid']
+ ssid = "test-wps-conf"
+ appin = "12345670"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "ap_pin": appin}
+ hapd = hostapd.add_ap(apdev, params)
+
+ dev.scan_for_bss(bssid, freq="2412")
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev.request("SET ext_eapol_frame_io 1")
+
+ dev.request("WPS_REG " + bssid + " " + appin)
+
+ return addr, bssid, hapd
+
+def wps_run_ap_settings_proto(dev, apdev, ap_settings, success):
+ addr, bssid, hapd = wps_start_ext_reg(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive M1 from AP")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M1)
+ mac_addr = m1_attrs[ATTR_MAC_ADDR]
+ e_nonce = m1_attrs[ATTR_ENROLLEE_NONCE]
+ e_pk = m1_attrs[ATTR_PUBLIC_KEY]
+
+ appin = '12345670'
+ uuid_r = 16*b'\x33'
+ r_nonce = 16*b'\x44'
+ own_private, r_pk = wsc_dh_init()
+ authkey, keywrapkey = wsc_dh_kdf(e_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ r_s1, r_s2, r_hash1, r_hash2 = wsc_dev_pw_hash(authkey, appin, e_pk, r_pk)
+
+ logger.debug("Send M2 to AP")
+ m2, raw_m2_attrs = build_m2(authkey, raw_m1_attrs, msg['eap_identifier'],
+ e_nonce, r_nonce, uuid_r, r_pk, eap_code=2)
+ send_wsc_msg(hapd, addr, m2)
+
+ logger.debug("Receive M3 from AP")
+ msg, m3_attrs, raw_m3_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M3)
+
+ logger.debug("Send M4 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M4)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_R_HASH1, r_hash1)
+ attrs += build_wsc_attr(ATTR_R_HASH2, r_hash2)
+ data = build_wsc_attr(ATTR_R_SNONCE1, r_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m3_attrs, attrs)
+ raw_m4_attrs = attrs
+ m4 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m4)
+
+ logger.debug("Receive M5 from AP")
+ msg, m5_attrs, raw_m5_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M5)
+
+ logger.debug("Send M6 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M6)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ data = build_wsc_attr(ATTR_R_SNONCE2, r_s2)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m5_attrs, attrs)
+ raw_m6_attrs = attrs
+ m6 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m6)
+
+ logger.debug("Receive M7 from AP")
+ msg, m7_attrs, raw_m7_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M7)
+
+ logger.debug("Send M8 to STA")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M8)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ if ap_settings:
+ attrs += build_attr_encr_settings(authkey, keywrapkey, ap_settings)
+ attrs += build_attr_authenticator(authkey, raw_m7_attrs, attrs)
+ raw_m8_attrs = attrs
+ m8 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m8)
+
+ if success:
+ ev = hapd.wait_event(["WPS-NEW-AP-SETTINGS"], timeout=5)
+ if ev is None:
+ raise Exception("New AP settings not reported")
+ logger.debug("Receive WSC_Done from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Done:
+ raise Exception("Unexpected message - expected WSC_Done")
+
+ logger.debug("Send WSC_ACK to AP")
+ ack, attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce,
+ eap_code=2)
+ send_wsc_msg(hapd, addr, ack)
+ dev[0].wait_disconnected()
+ else:
+ ev = hapd.wait_event(["WPS-FAIL"], timeout=5)
+ if ev is None:
+ raise Exception("WPS failure not reported")
+ logger.debug("Receive WSC_NACK from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_NACK")
+
+ logger.debug("Send WSC_NACK to AP")
+ nack, attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
+ eap_code=2)
+ send_wsc_msg(hapd, addr, nack)
+ dev[0].wait_disconnected()
+
+def test_wps_ext_ap_settings_success(dev, apdev):
+ """WPS and AP Settings: success"""
+ ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
+ ap_settings += build_wsc_attr(ATTR_SSID, "test")
+ ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
+ ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x01')
+ ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
+ ap_settings += build_wsc_attr(ATTR_MAC_ADDR, binascii.unhexlify(apdev[0]['bssid'].replace(':', '')))
+ wps_run_ap_settings_proto(dev, apdev, ap_settings, True)
+
+@remote_compatible
+def test_wps_ext_ap_settings_missing(dev, apdev):
+ """WPS and AP Settings: missing"""
+ wps_run_ap_settings_proto(dev, apdev, None, False)
+
+@remote_compatible
+def test_wps_ext_ap_settings_mac_addr_mismatch(dev, apdev):
+ """WPS and AP Settings: MAC Address mismatch"""
+ ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
+ ap_settings += build_wsc_attr(ATTR_SSID, "test")
+ ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
+ ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x01')
+ ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
+ ap_settings += build_wsc_attr(ATTR_MAC_ADDR, '\x00\x00\x00\x00\x00\x00')
+ wps_run_ap_settings_proto(dev, apdev, ap_settings, True)
+
+@remote_compatible
+def test_wps_ext_ap_settings_mac_addr_missing(dev, apdev):
+ """WPS and AP Settings: missing MAC Address"""
+ ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
+ ap_settings += build_wsc_attr(ATTR_SSID, "test")
+ ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
+ ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x01')
+ ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
+ wps_run_ap_settings_proto(dev, apdev, ap_settings, False)
+
+@remote_compatible
+def test_wps_ext_ap_settings_reject_encr_type(dev, apdev):
+ """WPS and AP Settings: reject Encr Type"""
+ ap_settings = build_wsc_attr(ATTR_NETWORK_INDEX, '\x01')
+ ap_settings += build_wsc_attr(ATTR_SSID, "test")
+ ap_settings += build_wsc_attr(ATTR_AUTH_TYPE, '\x00\x01')
+ ap_settings += build_wsc_attr(ATTR_ENCR_TYPE, '\x00\x00')
+ ap_settings += build_wsc_attr(ATTR_NETWORK_KEY, '')
+ ap_settings += build_wsc_attr(ATTR_MAC_ADDR, binascii.unhexlify(apdev[0]['bssid'].replace(':', '')))
+ wps_run_ap_settings_proto(dev, apdev, ap_settings, False)
+
+@remote_compatible
+def test_wps_ext_ap_settings_m2d(dev, apdev):
+ """WPS and AP Settings: M2D"""
+ addr, bssid, hapd = wps_start_ext_reg(apdev[0], dev[0])
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive M1 from AP")
+ msg, m1_attrs, raw_m1_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M1)
+ e_nonce = m1_attrs[ATTR_ENROLLEE_NONCE]
+
+ r_nonce = 16*'\x44'
+ uuid_r = 16*'\x33'
+
+ logger.debug("Send M2D to AP")
+ m2d, raw_m2d_attrs = build_m2d(raw_m1_attrs, msg['eap_identifier'],
+ e_nonce, r_nonce, uuid_r,
+ dev_pw_id='\x00\x00', eap_code=2)
+ send_wsc_msg(hapd, addr, m2d)
+
+ ev = hapd.wait_event(["WPS-M2D"], timeout=5)
+ if ev is None:
+ raise Exception("M2D not reported")
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def wps_wait_ap_nack(hapd, dev, e_nonce, r_nonce):
+ logger.debug("Receive WSC_NACK from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_NACK:
+ raise Exception("Unexpected message - expected WSC_NACK")
+
+ logger.debug("Send WSC_NACK to AP")
+ nack, attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
+ eap_code=2)
+ send_wsc_msg(hapd, dev.own_addr(), nack)
+ dev.wait_disconnected()
+
+@remote_compatible
+def test_wps_ext_m3_missing_e_hash1(dev, apdev):
+ """WPS proto: M3 missing E-Hash1"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ #attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+@remote_compatible
+def test_wps_ext_m3_missing_e_hash2(dev, apdev):
+ """WPS proto: M3 missing E-Hash2"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ #attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+@remote_compatible
+def test_wps_ext_m5_missing_e_snonce1(dev, apdev):
+ """WPS proto: M5 missing E-SNonce1"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ logger.debug("Receive M4 from AP")
+ msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+ logger.debug("Send M5 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M5)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ #data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+ data = b''
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+ raw_m5_attrs = attrs
+ m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m5)
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+@remote_compatible
+def test_wps_ext_m5_e_snonce1_mismatch(dev, apdev):
+ """WPS proto: M5 E-SNonce1 mismatch"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ logger.debug("Receive M4 from AP")
+ msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+ logger.debug("Send M5 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M5)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ data = build_wsc_attr(ATTR_E_SNONCE1, 16*'\x00')
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+ raw_m5_attrs = attrs
+ m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m5)
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+def test_wps_ext_m7_missing_e_snonce2(dev, apdev):
+ """WPS proto: M7 missing E-SNonce2"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ logger.debug("Receive M4 from AP")
+ msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+ logger.debug("Send M5 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M5)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+ raw_m5_attrs = attrs
+ m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m5)
+
+ logger.debug("Receive M6 from AP")
+ msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
+
+ logger.debug("Send M7 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M7)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ #data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
+ data = b''
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
+ m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ raw_m7_attrs = attrs
+ send_wsc_msg(hapd, addr, m7)
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+@remote_compatible
+def test_wps_ext_m7_e_snonce2_mismatch(dev, apdev):
+ """WPS proto: M7 E-SNonce2 mismatch"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ logger.debug("Receive M4 from AP")
+ msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+ logger.debug("Send M5 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M5)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+ raw_m5_attrs = attrs
+ m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m5)
+
+ logger.debug("Receive M6 from AP")
+ msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
+
+ logger.debug("Send M7 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M7)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ data = build_wsc_attr(ATTR_E_SNONCE2, 16*'\x00')
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
+ m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ raw_m7_attrs = attrs
+ send_wsc_msg(hapd, addr, m7)
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+@remote_compatible
+def test_wps_ext_m1_pubkey_oom(dev, apdev):
+ """WPS proto: M1 PubKey OOM"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*'\x11'
+ e_nonce = 16*'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ with alloc_fail(hapd, 1, "wpabuf_alloc_copy;wps_process_pubkey"):
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+ wps_wait_eap_failure(hapd, dev[0])
+
+def wps_wait_eap_failure(hapd, dev):
+ ev = hapd.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("EAP-Failure not reported")
+ dev.wait_disconnected()
+
+@remote_compatible
+def test_wps_ext_m3_m1(dev, apdev):
+ """WPS proto: M3 replaced with M1"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3(M1) to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M1)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m5_m3(dev, apdev):
+ """WPS proto: M5 replaced with M3"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ logger.debug("Receive M4 from AP")
+ msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+ logger.debug("Send M5(M3) to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+ raw_m5_attrs = attrs
+ m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m5)
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+@remote_compatible
+def test_wps_ext_m3_m2(dev, apdev):
+ """WPS proto: M3 replaced with M2"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3(M2) to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M2)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m3_m5(dev, apdev):
+ """WPS proto: M3 replaced with M5"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3(M5) to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M5)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+@remote_compatible
+def test_wps_ext_m3_m7(dev, apdev):
+ """WPS proto: M3 replaced with M7"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3(M7) to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M7)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+@remote_compatible
+def test_wps_ext_m3_done(dev, apdev):
+ """WPS proto: M3 replaced with WSC_Done"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3(WSC_Done) to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_WSC_DONE)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+ send_wsc_msg(hapd, addr, m3)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m2_nack_invalid(dev, apdev):
+ """WPS proto: M2 followed by invalid NACK"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send WSC_NACK to AP")
+ attrs = b'\x10\x00\x00'
+ nack = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_NACK)
+ send_wsc_msg(hapd, addr, nack)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m2_nack_no_msg_type(dev, apdev):
+ """WPS proto: M2 followed by NACK without Msg Type"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send WSC_NACK to AP")
+ nack, attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
+ msg_type=None, eap_code=2)
+ send_wsc_msg(hapd, addr, nack)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m2_nack_invalid_msg_type(dev, apdev):
+ """WPS proto: M2 followed by NACK with invalid Msg Type"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send WSC_NACK to AP")
+ nack, attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
+ msg_type=WPS_WSC_ACK, eap_code=2)
+ send_wsc_msg(hapd, addr, nack)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m2_nack_e_nonce_mismatch(dev, apdev):
+ """WPS proto: M2 followed by NACK with e-nonce mismatch"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send WSC_NACK to AP")
+ nack, attrs = build_nack(msg['eap_identifier'], 16*b'\x00', r_nonce,
+ eap_code=2)
+ send_wsc_msg(hapd, addr, nack)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m2_nack_no_config_error(dev, apdev):
+ """WPS proto: M2 followed by NACK without Config Error"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send WSC_NACK to AP")
+ nack, attrs = build_nack(msg['eap_identifier'], e_nonce, r_nonce,
+ config_error=None, eap_code=2)
+ send_wsc_msg(hapd, addr, nack)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m2_ack_invalid(dev, apdev):
+ """WPS proto: M2 followed by invalid ACK"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send WSC_ACK to AP")
+ attrs = b'\x10\x00\x00'
+ ack = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_ACK)
+ send_wsc_msg(hapd, addr, ack)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m2_ack(dev, apdev):
+ """WPS proto: M2 followed by ACK"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send WSC_ACK to AP")
+ ack, attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce, eap_code=2)
+ send_wsc_msg(hapd, addr, ack)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m2_ack_no_msg_type(dev, apdev):
+ """WPS proto: M2 followed by ACK missing Msg Type"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send WSC_ACK to AP")
+ ack, attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce,
+ msg_type=None, eap_code=2)
+ send_wsc_msg(hapd, addr, ack)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m2_ack_invalid_msg_type(dev, apdev):
+ """WPS proto: M2 followed by ACK with invalid Msg Type"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send WSC_ACK to AP")
+ ack, attrs = build_ack(msg['eap_identifier'], e_nonce, r_nonce,
+ msg_type=WPS_WSC_NACK, eap_code=2)
+ send_wsc_msg(hapd, addr, ack)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m2_ack_e_nonce_mismatch(dev, apdev):
+ """WPS proto: M2 followed by ACK with e-nonce mismatch"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send WSC_ACK to AP")
+ ack, attrs = build_ack(msg['eap_identifier'], 16*b'\x00', r_nonce,
+ eap_code=2)
+ send_wsc_msg(hapd, addr, ack)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m1_invalid(dev, apdev):
+ """WPS proto: M1 failing parsing"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ logger.debug("Send M1 to AP")
+ attrs = b'\x10\x00\x00'
+ m1 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m1)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_m1_missing_msg_type(dev, apdev):
+ """WPS proto: M1 missing Msg Type"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ logger.debug("Send M1 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ m1 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m1)
+
+ wps_wait_ap_nack(hapd, dev[0], 16*b'\x00', 16*b'\x00')
+
+def wps_ext_wsc_done(dev, apdev):
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ logger.debug("Receive M4 from AP")
+ msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+ logger.debug("Send M5 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M5)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+ raw_m5_attrs = attrs
+ m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m5)
+
+ logger.debug("Receive M6 from AP")
+ msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
+
+ logger.debug("Send M7 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M7)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
+ m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ raw_m7_attrs = attrs
+ send_wsc_msg(hapd, addr, m7)
+
+ logger.debug("Receive M8 from AP")
+ msg, m8_attrs, raw_m8_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M8)
+ return hapd, msg, e_nonce, r_nonce
+
+@remote_compatible
+def test_wps_ext_wsc_done_invalid(dev, apdev):
+ """WPS proto: invalid WSC_Done"""
+ hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
+
+ logger.debug("Send WSC_Done to AP")
+ attrs = b'\x10\x00\x00'
+ wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+ send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_wsc_done_no_msg_type(dev, apdev):
+ """WPS proto: invalid WSC_Done"""
+ hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
+
+ logger.debug("Send WSC_Done to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ #attrs += build_attr_msg_type(WPS_WSC_DONE)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+ send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_wsc_done_wrong_msg_type(dev, apdev):
+ """WPS proto: WSC_Done with wrong Msg Type"""
+ hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
+
+ logger.debug("Send WSC_Done to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_WSC_ACK)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+ send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_wsc_done_no_e_nonce(dev, apdev):
+ """WPS proto: WSC_Done without e_nonce"""
+ hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
+
+ logger.debug("Send WSC_Done to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_WSC_DONE)
+ #attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+ send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+def test_wps_ext_wsc_done_no_r_nonce(dev, apdev):
+ """WPS proto: WSC_Done without r_nonce"""
+ hapd, msg, e_nonce, r_nonce = wps_ext_wsc_done(dev, apdev)
+
+ logger.debug("Send WSC_Done to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_WSC_DONE)
+ attrs += build_wsc_attr(ATTR_ENROLLEE_NONCE, e_nonce)
+ #attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ wsc_done = build_eap_wsc(2, msg['eap_identifier'], attrs, opcode=WSC_Done)
+ send_wsc_msg(hapd, dev[0].own_addr(), wsc_done)
+
+ wps_wait_eap_failure(hapd, dev[0])
+
+@remote_compatible
+def test_wps_ext_m7_no_encr_settings(dev, apdev):
+ """WPS proto: M7 without Encr Settings"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk)
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+ r_nonce = m2_attrs[ATTR_REGISTRAR_NONCE]
+ r_pk = m2_attrs[ATTR_PUBLIC_KEY]
+
+ authkey, keywrapkey = wsc_dh_kdf(r_pk, own_private, mac_addr, e_nonce,
+ r_nonce)
+ e_s1, e_s2, e_hash1, e_hash2 = wsc_dev_pw_hash(authkey, pin, e_pk, r_pk)
+
+ logger.debug("Send M3 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M3)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ attrs += build_wsc_attr(ATTR_E_HASH1, e_hash1)
+ attrs += build_wsc_attr(ATTR_E_HASH2, e_hash2)
+ attrs += build_attr_authenticator(authkey, raw_m2_attrs, attrs)
+ raw_m3_attrs = attrs
+ m3 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m3)
+
+ logger.debug("Receive M4 from AP")
+ msg, m4_attrs, raw_m4_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M4)
+
+ logger.debug("Send M5 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M5)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ data = build_wsc_attr(ATTR_E_SNONCE1, e_s1)
+ attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m4_attrs, attrs)
+ raw_m5_attrs = attrs
+ m5 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ send_wsc_msg(hapd, addr, m5)
+
+ logger.debug("Receive M6 from AP")
+ msg, m6_attrs, raw_m6_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M6)
+
+ logger.debug("Send M7 to AP")
+ attrs = build_wsc_attr(ATTR_VERSION, '\x10')
+ attrs += build_attr_msg_type(WPS_M7)
+ attrs += build_wsc_attr(ATTR_REGISTRAR_NONCE, r_nonce)
+ #data = build_wsc_attr(ATTR_E_SNONCE2, e_s2)
+ #attrs += build_attr_encr_settings(authkey, keywrapkey, data)
+ attrs += build_attr_authenticator(authkey, raw_m6_attrs, attrs)
+ m7 = build_eap_wsc(2, msg['eap_identifier'], attrs)
+ raw_m7_attrs = attrs
+ send_wsc_msg(hapd, addr, m7)
+
+ wps_wait_ap_nack(hapd, dev[0], e_nonce, r_nonce)
+
+@remote_compatible
+def test_wps_ext_m1_workaround(dev, apdev):
+ """WPS proto: M1 Manufacturer/Model workaround"""
+ pin = "12345670"
+ addr, bssid, hapd = wps_start_ext(apdev[0], dev[0], pin=pin)
+ wps_ext_eap_identity_req(dev[0], hapd, bssid)
+ wps_ext_eap_identity_resp(hapd, dev[0], addr)
+
+ logger.debug("Receive WSC/Start from AP")
+ msg = get_wsc_msg(hapd)
+ if msg['wsc_opcode'] != WSC_Start:
+ raise Exception("Unexpected Op-Code for WSC/Start")
+
+ mac_addr = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ uuid_e = 16*b'\x11'
+ e_nonce = 16*b'\x22'
+ own_private, e_pk = wsc_dh_init()
+
+ logger.debug("Send M1 to AP")
+ m1, raw_m1_attrs = build_m1(msg['eap_identifier'], uuid_e, mac_addr,
+ e_nonce, e_pk, manufacturer='Apple TEST',
+ model_name='AirPort', config_methods=b'\xff\xff')
+ send_wsc_msg(hapd, addr, m1)
+
+ logger.debug("Receive M2 from AP")
+ msg, m2_attrs, raw_m2_attrs = recv_wsc_msg(hapd, WSC_MSG, WPS_M2)
+
+@remote_compatible
+def test_ap_wps_disable_enable(dev, apdev):
+ """WPS and DISABLE/ENABLE AP"""
+ hapd = wps_start_ap(apdev[0])
+ hapd.disable()
+ hapd.enable()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+
+def test_ap_wps_upnp_web_oom(dev, apdev, params):
+ """hostapd WPS UPnP web OOM"""
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ hapd = add_ssdp_ap(apdev[0], ap_uuid)
+
+ location = ssdp_get_location(ap_uuid)
+ url = urlparse(location)
+ urls = upnp_get_urls(location)
+ eventurl = urlparse(urls['event_sub_url'])
+ ctrlurl = urlparse(urls['control_url'])
+
+ conn = HTTPConnection(url.netloc)
+ with alloc_fail(hapd, 1, "web_connection_parse_get"):
+ conn.request("GET", "/wps_device.xml")
+ try:
+ resp = conn.getresponse()
+ except:
+ pass
+
+ conn = HTTPConnection(url.netloc)
+ conn.request("GET", "/unknown")
+ resp = conn.getresponse()
+ if resp.status != 404:
+ raise Exception("Unexpected HTTP result for unknown URL: %d" + resp.status)
+
+ with alloc_fail(hapd, 1, "web_connection_parse_get"):
+ conn.request("GET", "/unknown")
+ try:
+ resp = conn.getresponse()
+ print(resp.status)
+ except:
+ pass
+
+ conn = HTTPConnection(url.netloc)
+ conn.request("GET", "/wps_device.xml")
+ resp = conn.getresponse()
+ if resp.status != 200:
+ raise Exception("GET /wps_device.xml failed")
+
+ conn = HTTPConnection(url.netloc)
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
+ if resp.status != 200:
+ raise Exception("GetDeviceInfo failed")
+
+ with alloc_fail(hapd, 1, "web_process_get_device_info"):
+ conn = HTTPConnection(url.netloc)
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
+ if resp.status != 500:
+ raise Exception("Internal error not reported from GetDeviceInfo OOM")
+
+ with alloc_fail(hapd, 1, "wps_build_m1;web_process_get_device_info"):
+ conn = HTTPConnection(url.netloc)
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
+ if resp.status != 500:
+ raise Exception("Internal error not reported from GetDeviceInfo OOM")
+
+ with alloc_fail(hapd, 1, "wpabuf_alloc;web_connection_send_reply"):
+ conn = HTTPConnection(url.netloc)
+ try:
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
+ except:
+ pass
+
+ conn = HTTPConnection(url.netloc)
+ resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
+ if resp.status != 200:
+ raise Exception("GetDeviceInfo failed")
+
+ # No NewWLANEventType in PutWLANResponse NewMessage
+ conn = HTTPConnection(url.netloc)
+ resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse", newmsg="foo")
+ if resp.status != 600:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ # No NewWLANEventMAC in PutWLANResponse NewMessage
+ conn = HTTPConnection(url.netloc)
+ resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse",
+ newmsg="foo", neweventtype="1")
+ if resp.status != 600:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ # Invalid NewWLANEventMAC in PutWLANResponse NewMessage
+ conn = HTTPConnection(url.netloc)
+ resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse",
+ newmsg="foo", neweventtype="1",
+ neweventmac="foo")
+ if resp.status != 600:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ # Workaround for NewWLANEventMAC in PutWLANResponse NewMessage
+ # Ignored unexpected PutWLANResponse WLANEventType 1
+ conn = HTTPConnection(url.netloc)
+ resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse",
+ newmsg="foo", neweventtype="1",
+ neweventmac="00.11.22.33.44.55")
+ if resp.status != 500:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ # PutWLANResponse NewMessage with invalid EAP message
+ conn = HTTPConnection(url.netloc)
+ resp = upnp_soap_action(conn, ctrlurl.path, "PutWLANResponse",
+ newmsg="foo", neweventtype="2",
+ neweventmac="00:11:22:33:44:55")
+ if resp.status != 200:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ with alloc_fail(hapd, 1, "web_connection_parse_subscribe"):
+ conn = HTTPConnection(url.netloc)
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ try:
+ resp = conn.getresponse()
+ except:
+ pass
+
+ with alloc_fail(hapd, 1, "dup_binstr;web_connection_parse_subscribe"):
+ conn = HTTPConnection(url.netloc)
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ resp = conn.getresponse()
+ if resp.status != 500:
+ raise Exception("Unexpected HTTP response: %d" % resp.status)
+
+ with alloc_fail(hapd, 1, "wpabuf_alloc;web_connection_parse_unsubscribe"):
+ conn = HTTPConnection(url.netloc)
+ headers = {"callback": '<http://127.0.0.1:12345/event>',
+ "NT": "upnp:event",
+ "timeout": "Second-1234"}
+ conn.request("UNSUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+ try:
+ resp = conn.getresponse()
+ except:
+ pass
+
+ with alloc_fail(hapd, 1, "web_connection_unimplemented"):
+ conn = HTTPConnection(url.netloc)
+ conn.request("HEAD", "/wps_device.xml")
+ try:
+ resp = conn.getresponse()
+ except:
+ pass
+
+def test_ap_wps_frag_ack_oom(dev, apdev):
+ """WPS and fragment ack OOM"""
+ dev[0].request("SET wps_fragment_size 50")
+ hapd = wps_start_ap(apdev[0])
+ with alloc_fail(hapd, 1, "eap_wsc_build_frag_ack"):
+ wps_run_pbc_fail_ap(apdev[0], dev[0], hapd)
+
+def wait_scan_stopped(dev):
+ dev.request("ABORT_SCAN")
+ for i in range(50):
+ res = dev.get_driver_status_field("scan_state")
+ if "SCAN_STARTED" not in res and "SCAN_REQUESTED" not in res:
+ break
+ logger.debug("Waiting for scan to complete")
+ time.sleep(0.1)
+
+@remote_compatible
+def test_ap_wps_eap_wsc_errors(dev, apdev):
+ """WPS and EAP-WSC error cases"""
+ ssid = "test-wps-conf-pin"
+ appin = "12345670"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "fragment_size": "300", "ap_pin": appin}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+
+ dev[0].wps_reg(bssid, appin + " new_ssid=a", "new ssid", "WPA2PSK", "CCMP",
+ "new passphrase", no_wait=True)
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=10)
+ if ev is None:
+ raise Exception("WPS-FAIL not reported")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ wait_scan_stopped(dev[0])
+ dev[0].dump_monitor()
+
+ dev[0].wps_reg(bssid, appin, "new ssid", "FOO", "CCMP",
+ "new passphrase", no_wait=True)
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=10)
+ if ev is None:
+ raise Exception("WPS-FAIL not reported")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ wait_scan_stopped(dev[0])
+ dev[0].dump_monitor()
+
+ dev[0].wps_reg(bssid, appin, "new ssid", "WPA2PSK", "FOO",
+ "new passphrase", no_wait=True)
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=10)
+ if ev is None:
+ raise Exception("WPS-FAIL not reported")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ wait_scan_stopped(dev[0])
+ dev[0].dump_monitor()
+
+ dev[0].wps_reg(bssid, appin + "new_key=a", "new ssid", "WPA2PSK", "CCMP",
+ "new passphrase", no_wait=True)
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=10)
+ if ev is None:
+ raise Exception("WPS-FAIL not reported")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ wait_scan_stopped(dev[0])
+ dev[0].dump_monitor()
+
+ tests = ["eap_wsc_init",
+ "eap_msg_alloc;eap_wsc_build_msg",
+ "wpabuf_alloc;eap_wsc_process_fragment"]
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].request("WPS_PIN %s %s" % (bssid, pin))
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ wait_scan_stopped(dev[0])
+ dev[0].dump_monitor()
+
+ tests = [(1, "wps_decrypt_encr_settings"),
+ (2, "hmac_sha256;wps_derive_psk")]
+ for count, func in tests:
+ hapd.request("WPS_PIN any " + pin)
+ with fail_test(dev[0], count, func):
+ dev[0].request("WPS_PIN %s %s" % (bssid, pin))
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ wait_scan_stopped(dev[0])
+ dev[0].dump_monitor()
+
+ with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_sm_build_expanded_nak"):
+ dev[0].wps_reg(bssid, appin + " new_ssid=a", "new ssid", "WPA2PSK",
+ "CCMP", "new passphrase", no_wait=True)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("WPS_CANCEL")
+ dev[0].wait_disconnected()
+ wait_scan_stopped(dev[0])
+ dev[0].dump_monitor()
+
+def test_ap_wps_eap_wsc(dev, apdev):
+ """WPS and EAP-WSC in network profile"""
+ params = int_eap_server_params()
+ params["wps_state"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ logger.info("Unexpected identity")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="WSC", identity="WFA-SimpleConfig-Enrollee-unexpected",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("No EAP-Failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ logger.info("No phase1 parameter")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("No EAP-Failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ logger.info("No PIN/PBC in phase1")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
+ phase1="foo", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("No EAP-Failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ logger.info("Invalid pkhash in phase1")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
+ phase1="foo pkhash=q pbc=1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("No EAP-Failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ logger.info("Zero fragment_size")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
+ fragment_size="0", phase1="pin=12345670", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ ev = dev[0].wait_event(["WPS-M2D"], timeout=5)
+ if ev is None:
+ raise Exception("No M2D seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("No EAP-Failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ logger.info("Missing new_auth")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
+ phase1="pin=12345670 new_ssid=aa", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("No EAP-Failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ logger.info("Missing new_encr")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
+ phase1="pin=12345670 new_auth=WPA2PSK new_ssid=aa", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("No EAP-Failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ logger.info("Missing new_key")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="WSC", identity="WFA-SimpleConfig-Enrollee-1-0",
+ phase1="pin=12345670 new_auth=WPA2PSK new_ssid=aa new_encr=CCMP",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("No EAP-Failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_ap_wps_and_bss_limit(dev, apdev):
+ """WPS and wpa_supplicant BSS entry limit"""
+ try:
+ _test_ap_wps_and_bss_limit(dev, apdev)
+ finally:
+ dev[0].request("SET bss_max_count 200")
+ pass
+
+def _test_ap_wps_and_bss_limit(dev, apdev):
+ params = {"ssid": "test-wps", "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "test-wps-2", "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "1234567890", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ id = dev[1].add_network()
+ dev[1].set_network(id, "mode", "2")
+ dev[1].set_network_quoted(id, "ssid", "wpas-ap-no-wps")
+ dev[1].set_network_quoted(id, "psk", "12345678")
+ dev[1].set_network(id, "frequency", "2462")
+ dev[1].set_network(id, "scan_freq", "2462")
+ dev[1].set_network(id, "wps_disabled", "1")
+ dev[1].select_network(id)
+
+ id = dev[2].add_network()
+ dev[2].set_network(id, "mode", "2")
+ dev[2].set_network_quoted(id, "ssid", "wpas-ap")
+ dev[2].set_network_quoted(id, "psk", "12345678")
+ dev[2].set_network(id, "frequency", "2437")
+ dev[2].set_network(id, "scan_freq", "2437")
+ dev[2].select_network(id)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ id = wpas.add_network()
+ wpas.set_network(id, "mode", "2")
+ wpas.set_network_quoted(id, "ssid", "wpas-ap")
+ wpas.set_network_quoted(id, "psk", "12345678")
+ wpas.set_network(id, "frequency", "2437")
+ wpas.set_network(id, "scan_freq", "2437")
+ wpas.select_network(id)
+
+ dev[1].wait_connected()
+ dev[2].wait_connected()
+ wpas.wait_connected()
+ wpas.request("WPS_PIN any 12345670")
+
+ hapd.request("WPS_PBC")
+ hapd2.request("WPS_PBC")
+
+ dev[0].request("SET bss_max_count 1")
+
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", "testing")
+
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", "testing")
+ dev[0].set_network(id, "key_mgmt", "WPS")
+
+ dev[0].request("WPS_PBC")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ dev[0].request("WPS_CANCEL")
+
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", "testing")
+ dev[0].set_network(id, "key_mgmt", "WPS")
+
+ dev[0].scan(freq="2412")
+
+def test_ap_wps_pbc_2ap(dev, apdev):
+ """WPS PBC with two APs advertising same SSID"""
+ params = {"ssid": "wps", "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wps_independent": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ params = {"ssid": "wps", "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "123456789", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wps_independent": "1"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ hapd.request("WPS_PBC")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.dump_monitor()
+ wpas.flush_scan_cache()
+
+ wpas.scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ wpas.scan_for_bss(apdev[1]['bssid'], freq="2412")
+ wpas.request("WPS_PBC")
+ wpas.wait_connected()
+ wpas.request("DISCONNECT")
+ hapd.request("DISABLE")
+ hapd2.request("DISABLE")
+ wpas.flush_scan_cache()
+
+def test_ap_wps_er_enrollee_to_conf_ap(dev, apdev):
+ """WPS ER enrolling a new device to a configured AP"""
+ try:
+ _test_ap_wps_er_enrollee_to_conf_ap(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_enrollee_to_conf_ap(dev, apdev):
+ ssid = "wps-er-enrollee-to-conf-ap"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ id = dev[0].connect(ssid, psk="12345678", scan_freq="2412")
+ dev[0].dump_monitor()
+
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+
+ pin = dev[2].wps_read_pin()
+ addr2 = dev[2].own_addr()
+ dev[0].dump_monitor()
+ dev[2].scan_for_bss(bssid, freq=2412)
+ dev[2].dump_monitor()
+ dev[2].request("WPS_PIN %s %s" % (bssid, pin))
+
+ for i in range(3):
+ ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=10)
+ if ev is None:
+ raise Exception("Enrollee not seen")
+ if addr2 in ev:
+ break
+ if addr2 not in ev:
+ raise Exception("Unexpected Enrollee MAC address")
+ dev[0].dump_monitor()
+
+ dev[0].request("WPS_ER_SET_CONFIG " + ap_uuid + " " + str(id))
+ dev[0].request("WPS_ER_PIN " + addr2 + " " + pin + " " + addr2)
+ dev[2].wait_connected(timeout=30)
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS ER did not report success")
+
+def test_ap_wps_er_enrollee_to_conf_ap2(dev, apdev):
+ """WPS ER enrolling a new device to a configured AP (2)"""
+ try:
+ _test_ap_wps_er_enrollee_to_conf_ap2(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_ap_wps_er_enrollee_to_conf_ap2(dev, apdev):
+ ssid = "wps-er-enrollee-to-conf-ap"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ id = dev[0].connect(ssid, psk="12345678", scan_freq="2412")
+ dev[0].dump_monitor()
+
+ dev[0].request("WPS_ER_START ifname=lo")
+ ev = dev[0].wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+
+ dev[0].request("WPS_ER_LEARN " + ap_uuid + " " + ap_pin)
+ ev = dev[0].wait_event(["WPS-ER-AP-SETTINGS"], timeout=15)
+ if ev is None:
+ raise Exception("AP learn timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not in settings")
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-FAIL after AP learn timed out")
+ time.sleep(0.1)
+
+ pin = dev[1].wps_read_pin()
+ addr1 = dev[1].own_addr()
+ dev[0].dump_monitor()
+ dev[0].request("WPS_ER_PIN any " + pin)
+ time.sleep(0.1)
+ dev[1].scan_for_bss(bssid, freq=2412)
+ dev[1].request("WPS_PIN any %s" % pin)
+ ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=30)
+ if ev is None:
+ raise Exception("Enrollee did not report success")
+ dev[1].wait_connected(timeout=15)
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS ER did not report success")
+
+def test_ap_wps_ignore_broadcast_ssid(dev, apdev):
+ """WPS AP trying to ignore broadcast SSID"""
+ ssid = "test-wps"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1",
+ "ignore_broadcast_ssid": "1"})
+ if "FAIL" not in hapd.request("WPS_PBC"):
+ raise Exception("WPS unexpectedly enabled")
+
+def test_ap_wps_wep(dev, apdev):
+ """WPS AP trying to enable WEP"""
+ check_wep_capa(dev[0])
+ ssid = "test-wps"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1",
+ "ieee80211n": "0", "wep_key0": '"hello"'})
+ if "FAIL" not in hapd.request("WPS_PBC"):
+ raise Exception("WPS unexpectedly enabled")
+
+def test_ap_wps_tkip(dev, apdev):
+ """WPS AP trying to enable TKIP"""
+ ssid = "test-wps"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1",
+ "ieee80211n": "0", "wpa": '1',
+ "wpa_key_mgmt": "WPA-PSK",
+ "wpa_passphrase": "12345678"})
+ if "FAIL" not in hapd.request("WPS_PBC"):
+ raise Exception("WPS unexpectedly enabled")
+
+def test_ap_wps_conf_dummy_cred(dev, apdev):
+ """WPS PIN provisioning with configured AP using dummy cred"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ hapd.request("WPS_PIN any 12345670")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ try:
+ hapd.set("wps_testing_dummy_cred", "1")
+ dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ for i in range(1, 3):
+ ev = dev[0].wait_event(["WPS-CRED-RECEIVED"], timeout=15)
+ if ev is None:
+ raise Exception("WPS credential %d not received" % i)
+ dev[0].wait_connected(timeout=30)
+ finally:
+ hapd.set("wps_testing_dummy_cred", "0")
+
+def test_ap_wps_rf_bands(dev, apdev):
+ """WPS and wps_rf_bands configuration"""
+ ssid = "test-wps-conf"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wps_rf_bands": "ag"}
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PBC " + bssid)
+ dev[0].wait_connected(timeout=30)
+ bss = dev[0].get_bss(bssid)
+ logger.info("BSS: " + str(bss))
+ if "103c000103" not in bss['ie']:
+ raise Exception("RF Bands attribute with expected values not found")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.set("wps_rf_bands", "ad")
+ hapd.set("wps_rf_bands", "a")
+ hapd.set("wps_rf_bands", "g")
+ hapd.set("wps_rf_bands", "b")
+ hapd.set("wps_rf_bands", "ga")
+ hapd.disable()
+ dev[0].dump_monitor()
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_pbc_in_m1(dev, apdev):
+ """WPS and pbc_in_m1"""
+ ssid = "test-wps-conf"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "config_methods": "virtual_push_button virtual_display",
+ "pbc_in_m1": "1"}
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ hapd.request("WPS_PBC")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("WPS_PBC " + bssid)
+ dev[0].wait_connected(timeout=30)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.disable()
+ dev[0].dump_monitor()
+ dev[0].flush_scan_cache()
+
+def test_ap_wps_pbc_mac_addr_change(dev, apdev, params):
+ """WPS M1 with MAC address change"""
+ skip_without_tkip(dev[0])
+ ssid = "test-wps-mac-addr-change"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1"})
+ hapd.request("WPS_PBC")
+ if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+ dev[0].flush_scan_cache()
+
+ test_addr = '02:11:22:33:44:55'
+ addr = dev[0].get_status_field("address")
+ if addr == test_addr:
+ raise Exception("Unexpected initial MAC address")
+
+ try:
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'down'])
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'address',
+ test_addr])
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'up'])
+ addr1 = dev[0].get_status_field("address")
+ if addr1 != test_addr:
+ raise Exception("Failed to change MAC address")
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[0].request("WPS_PBC " + apdev[0]['bssid'])
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or \
+ status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wps.message_type == 0x04",
+ display=["wps.mac_address"])
+ res = out.splitlines()
+
+ if len(res) < 1:
+ raise Exception("No M1 message with MAC address found")
+ if res[0] != addr1:
+ raise Exception("Wrong M1 MAC address")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.disable()
+ dev[0].dump_monitor()
+ dev[0].flush_scan_cache()
+ finally:
+ # Restore MAC address
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'down'])
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'address',
+ addr])
+ subprocess.call(['ip', 'link', 'set', 'dev', dev[0].ifname, 'up'])
+
+def test_ap_wps_pin_start_failure(dev, apdev):
+ """WPS_PIN start failure"""
+ with alloc_fail(dev[0], 1, "wpas_wps_start_dev_pw"):
+ if "FAIL" not in dev[0].request("WPS_PIN any 12345670"):
+ raise Exception("WPS_PIN not rejected during OOM")
+ with alloc_fail(dev[0], 1, "wpas_wps_start_dev_pw"):
+ if "FAIL" not in dev[0].request("WPS_PIN any"):
+ raise Exception("WPS_PIN not rejected during OOM")
+
+def test_ap_wps_ap_pin_failure(dev, apdev):
+ """WPS_AP_PIN failure"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-wps")
+ dev[0].set_network_quoted(id, "psk", "1234567890")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ dev[0].wait_connected()
+
+ with fail_test(dev[0], 1,
+ "os_get_random;wpa_supplicant_ctrl_iface_wps_ap_pin"):
+ if "FAIL" not in dev[0].request("WPS_AP_PIN random"):
+ raise Exception("WPS_AP_PIN random accepted")
+ with alloc_fail(dev[0], 1, "wpas_wps_ap_pin_set"):
+ if "FAIL" not in dev[0].request("WPS_AP_PIN set 12345670"):
+ raise Exception("WPS_AP_PIN set accepted")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_ap_wps_random_uuid(dev, apdev, params):
+ """WPS and random UUID on Enrollee"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+
+ config = os.path.join(params['logdir'], 'ap_wps_random_uuid.conf')
+ with open(config, "w") as f:
+ f.write("auto_uuid=1\n")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+
+ uuid = []
+ for i in range(3):
+ wpas.interface_add("wlan5", config=config)
+
+ wpas.scan_for_bss(apdev[0]['bssid'], freq="2412")
+ wpas.dump_monitor()
+ wpas.request("WPS_PBC " + apdev[0]['bssid'])
+
+ ev = hapd.wait_event(["WPS-ENROLLEE-SEEN"], timeout=10)
+ if ev is None:
+ raise Exception("Enrollee not seen")
+ uuid.append(ev.split(' ')[2])
+ wpas.request("WPS_CANCEL")
+ wpas.dump_monitor()
+
+ wpas.interface_remove("wlan5")
+
+ hapd.dump_monitor()
+
+ logger.info("Seen UUIDs: " + str(uuid))
+ if uuid[0] == uuid[1] or uuid[0] == uuid[2] or uuid[1] == uuid[2]:
+ raise Exception("Same UUID used multiple times")
+
+def test_ap_wps_conf_pin_gcmp_128(dev, apdev):
+ """WPS PIN provisioning with configured AP using GCMP-128"""
+ run_ap_wps_conf_pin_cipher(dev, apdev, "GCMP")
+
+def test_ap_wps_conf_pin_gcmp_256(dev, apdev):
+ """WPS PIN provisioning with configured AP using GCMP-256"""
+ run_ap_wps_conf_pin_cipher(dev, apdev, "GCMP-256")
+
+def test_ap_wps_conf_pin_ccmp_256(dev, apdev):
+ """WPS PIN provisioning with configured AP using CCMP-256"""
+ run_ap_wps_conf_pin_cipher(dev, apdev, "CCMP-256")
+
+def run_ap_wps_conf_pin_cipher(dev, apdev, cipher):
+ if cipher not in dev[0].get_capability("pairwise"):
+ raise HwsimSkip("Cipher %s not supported" % cipher)
+ ssid = "test-wps-conf-pin"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": cipher})
+ logger.info("WPS provisioning step")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_connected(timeout=15)
+
+def test_ap_wps_and_sae(dev, apdev):
+ """Initial AP configuration with first WPS Enrollee and adding SAE"""
+ skip_without_tkip(dev[0])
+ skip_without_tkip(dev[1])
+ try:
+ run_ap_wps_and_sae(dev, apdev)
+ finally:
+ dev[0].set("wps_cred_add_sae", "0")
+
+def run_ap_wps_and_sae(dev, apdev):
+ check_sae_capab(dev[0])
+ ssid = "test-wps-sae"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1",
+ "wps_cred_add_sae": "1"})
+ logger.info("WPS provisioning step")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+
+ dev[0].set("wps_cred_add_sae", "1")
+ dev[0].request("SET sae_groups ")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " " + pin)
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['key_mgmt'] != "SAE":
+ raise Exception("SAE not used")
+ if 'pmf' not in status or status['pmf'] != "1":
+ raise Exception("PMF not enabled")
+
+ pin = dev[1].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[1].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[1].request("WPS_PIN " + apdev[0]['bssid'] + " " + pin)
+ dev[1].wait_connected(timeout=30)
+ status = dev[1].get_status()
+ if status['key_mgmt'] != "WPA2-PSK":
+ raise Exception("WPA2-PSK not used")
+ if 'pmf' in status:
+ raise Exception("PMF enabled")
+
+def test_ap_wps_conf_and_sae(dev, apdev):
+ """WPS PBC provisioning with configured AP using PSK+SAE"""
+ try:
+ run_ap_wps_conf_and_sae(dev, apdev)
+ finally:
+ dev[0].set("wps_cred_add_sae", "0")
+
+def run_ap_wps_conf_and_sae(dev, apdev):
+ check_sae_capab(dev[0])
+ ssid = "test-wps-conf-sae"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "ieee80211w": "1", "sae_require_mfp": "1",
+ "wpa_key_mgmt": "WPA-PSK SAE",
+ "rsn_pairwise": "CCMP"})
+
+ dev[0].set("wps_cred_add_sae", "1")
+ dev[0].request("SET sae_groups ")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].request("WPS_PIN " + apdev[0]['bssid'] + " " + pin)
+ dev[0].wait_connected(timeout=30)
+ status = dev[0].get_status()
+ if status['key_mgmt'] != "SAE":
+ raise Exception("SAE not used")
+ if 'pmf' not in status or status['pmf'] != "1":
+ raise Exception("PMF not enabled")
+
+ dev[1].connect(ssid, psk="12345678", scan_freq="2412", proto="WPA2",
+ key_mgmt="WPA-PSK", ieee80211w="0")
+
+def test_ap_wps_reg_config_and_sae(dev, apdev):
+ """WPS registrar configuring an AP using AP PIN and using PSK+SAE"""
+ try:
+ run_ap_wps_reg_config_and_sae(dev, apdev)
+ finally:
+ dev[0].set("wps_cred_add_sae", "0")
+
+def run_ap_wps_reg_config_and_sae(dev, apdev):
+ check_sae_capab(dev[0])
+ ssid = "test-wps-init-ap-pin-sae"
+ appin = "12345670"
+ hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "ap_pin": appin, "wps_cred_add_sae": "1"})
+ logger.info("WPS configuration step")
+ dev[0].flush_scan_cache()
+ dev[0].set("wps_cred_add_sae", "1")
+ dev[0].request("SET sae_groups ")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
+ dev[0].dump_monitor()
+ new_ssid = "wps-new-ssid"
+ new_passphrase = "1234567890"
+ dev[0].wps_reg(apdev[0]['bssid'], appin, new_ssid, "WPA2PSK", "CCMP",
+ new_passphrase)
+ status = dev[0].get_status()
+ if status['key_mgmt'] != "SAE":
+ raise Exception("SAE not used")
+ if 'pmf' not in status or status['pmf'] != "1":
+ raise Exception("PMF not enabled")
+
+ dev[1].connect(new_ssid, psk=new_passphrase, scan_freq="2412", proto="WPA2",
+ key_mgmt="WPA-PSK", ieee80211w="0")
+
+def test_ap_wps_appl_ext(dev, apdev):
+ """WPS Application Extension attribute"""
+ ssid = "test-wps-conf"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wps_application_ext": 16*"11" + 5*"ee",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ pin = dev[0].wps_read_pin()
+ hapd.request("WPS_PIN any " + pin)
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PIN %s %s" % (apdev[0]['bssid'], pin))
+ dev[0].wait_connected(timeout=30)
+
+@long_duration_test
+def test_ap_wps_pbc_ap_timeout(dev, apdev):
+ """WPS PBC timeout on AP"""
+ run_ap_wps_ap_timeout(dev, apdev, "WPS_PBC")
+
+@long_duration_test
+def test_ap_wps_pin_ap_timeout(dev, apdev):
+ """WPS PIN timeout on AP"""
+ run_ap_wps_ap_timeout(dev, apdev, "WPS_PIN any 12345670 10")
+
+def run_ap_wps_ap_timeout(dev, apdev, cmd):
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ bssid = hapd.own_addr()
+ hapd.request(cmd)
+ time.sleep(1)
+ dev[0].scan_for_bss(bssid, freq="2412")
+ bss = dev[0].get_bss(bssid)
+ logger.info("BSS during active Registrar: " + str(bss))
+ if not bss['ie'].endswith("0106ffffffffffff"):
+ raise Exception("Authorized MAC not included")
+ ev = hapd.wait_event(["WPS-TIMEOUT"], timeout=130)
+ if ev is None and "PBC" in cmd:
+ raise Exception("WPS-TIMEOUT not reported")
+ if "PBC" in cmd and \
+ "PBC Status: Timed-out" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+
+ time.sleep(5)
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ bss = dev[0].get_bss(bssid)
+ logger.info("BSS after timeout: " + str(bss))
+ if bss['ie'].endswith("0106ffffffffffff"):
+ raise Exception("Authorized MAC not removed")
+
+def test_ap_wps_er_unsubscribe_errors(dev, apdev):
+ """WPS ER and UNSUBSCRIBE errors"""
+ start_wps_ap(apdev[0])
+ tests = [(1, "http_client_url_parse;wps_er_ap_unsubscribe"),
+ (1, "wpabuf_alloc;wps_er_ap_unsubscribe"),
+ (1, "http_client_addr;wps_er_ap_unsubscribe")]
+ try:
+ for count, func in tests:
+ start_wps_er(dev[0])
+ with alloc_fail(dev[0], count, func):
+ dev[0].request("WPS_ER_STOP")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def start_wps_ap(apdev):
+ ssid = "wps-er-ap-config"
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hostapd.add_ap(apdev, params)
+
+def start_wps_er(dev):
+ ssid = "wps-er-ap-config"
+ dev.connect(ssid, psk="12345678", scan_freq="2412")
+ dev.request("WPS_ER_START ifname=lo")
+ ev = dev.wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+
+def test_ap_wps_registrar_init_errors(dev, apdev):
+ """WPS Registrar init errors"""
+ hapd = wps_start_ap(apdev[0], extra_cred="wps-mixed-cred")
+ hapd.disable()
+ tests = [(1, "wps_registrar_init"),
+ (1, "wpabuf_alloc_copy;wps_registrar_init"),
+ (1, "wps_set_ie;wps_registrar_init")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE succeeded unexpectedly")
diff --git a/contrib/wpa/tests/hwsim/test_authsrv.py b/contrib/wpa/tests/hwsim/test_authsrv.py
new file mode 100644
index 000000000000..e0665bcb26b2
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_authsrv.py
@@ -0,0 +1,262 @@
+# hostapd authentication server tests
+# Copyright (c) 2017, Jouni Malinen
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hostapd
+from utils import alloc_fail, fail_test, wait_fail_trigger
+
+def authsrv_params():
+ params = {"ssid": "as", "beacon_int": "2000",
+ "radius_server_clients": "auth_serv/radius_clients.conf",
+ "radius_server_auth_port": '18128',
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "eap_sim_db": "unix:/tmp/hlr_auc_gw.sock",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key",
+ "eap_message": "hello"}
+ return params
+
+def test_authsrv_oom(dev, apdev):
+ """Authentication server OOM"""
+ params = authsrv_params()
+ authsrv = hostapd.add_ap(apdev[1], params)
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18128"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(hapd.own_addr(), 2412)
+ with alloc_fail(authsrv, 1, "hostapd_radius_get_eap_user"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ with alloc_fail(authsrv, 1, "srv_log"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ with alloc_fail(authsrv, 1, "radius_server_new_session"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ for count in range(1, 3):
+ with alloc_fail(authsrv, count, "=radius_server_get_new_session"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ with alloc_fail(authsrv, 1, "eap_server_sm_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = ["radius_server_encapsulate_eap",
+ "radius_server_receive_auth"]
+ for t in tests:
+ with alloc_fail(authsrv, 1, t):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(authsrv, "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ tests = ["radius_msg_add_attr;radius_server_encapsulate_eap",
+ "radius_msg_add_eap;radius_server_encapsulate_eap",
+ "radius_msg_finish_srv;radius_server_encapsulate_eap"]
+ for t in tests:
+ with fail_test(authsrv, 1, t):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(authsrv, "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ with alloc_fail(authsrv, 1, "radius_server_get_new_session"):
+ with fail_test(authsrv, 1, "radius_msg_add_eap;radius_server_reject"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(authsrv, "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ with alloc_fail(authsrv, 1, "radius_server_get_new_session"):
+ with fail_test(authsrv, 1,
+ "radius_msg_finish_srv;radius_server_reject"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ wait_fail_trigger(authsrv, "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ authsrv.disable()
+ with alloc_fail(authsrv, 1, "radius_server_init;hostapd_setup_radius_srv"):
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded during OOM")
+ with alloc_fail(authsrv, 2, "radius_server_init;hostapd_setup_radius_srv"):
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded during OOM")
+
+ for count in range(1, 4):
+ with alloc_fail(authsrv, count,
+ "radius_server_read_clients;radius_server_init;hostapd_setup_radius_srv"):
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded during OOM")
+
+ with alloc_fail(authsrv, 1, "eloop_sock_table_add_sock;radius_server_init;hostapd_setup_radius_srv"):
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded during OOM")
+
+ with alloc_fail(authsrv, 1, "tls_init;authsrv_init"):
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded during OOM")
+
+ for count in range(1, 3):
+ with alloc_fail(authsrv, count, "eap_sim_db_init;authsrv_init"):
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded during OOM")
+
+def test_authsrv_errors_1(dev, apdev):
+ """Authentication server errors (1)"""
+ params = authsrv_params()
+ params["eap_user_file"] = "sqlite:auth_serv/does-not-exist/does-not-exist"
+ authsrv = hostapd.add_ap(apdev[1], params, no_enable=True)
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded with invalid SQLite EAP user file")
+
+def test_authsrv_errors_2(dev, apdev):
+ """Authentication server errors (2)"""
+ params = authsrv_params()
+ params["radius_server_clients"] = "auth_serv/does-not-exist"
+ authsrv = hostapd.add_ap(apdev[1], params, no_enable=True)
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded with invalid RADIUS client file")
+
+def test_authsrv_errors_3(dev, apdev):
+ """Authentication server errors (3)"""
+ params = authsrv_params()
+ params["eap_sim_db"] = "unix:/tmp/hlr_auc_gw.sock db=auth_serv/does-not-exist/does-not-exist"
+ authsrv = hostapd.add_ap(apdev[1], params, no_enable=True)
+ if "FAIL" not in authsrv.request("ENABLE"):
+ raise Exception("ENABLE succeeded with invalid RADIUS client file")
+
+def test_authsrv_testing_options(dev, apdev):
+ """Authentication server and testing options"""
+ params = authsrv_params()
+ authsrv = hostapd.add_ap(apdev[1], params)
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18128"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(hapd.own_addr(), 2412)
+ # The first two would be fine to run with any server build; the rest are
+ # actually supposed to fail, but they don't fail when using a server build
+ # that does not support the TLS protocol tests.
+ tests = ["foo@test-unknown",
+ "foo@test-tls-unknown",
+ "foo@test-tls-1",
+ "foo@test-tls-2",
+ "foo@test-tls-3",
+ "foo@test-tls-4",
+ "foo@test-tls-5",
+ "foo@test-tls-6",
+ "foo@test-tls-7",
+ "foo@test-tls-8"]
+ for t in tests:
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity=t,
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_authsrv_unknown_user(dev, apdev):
+ """Authentication server and unknown user"""
+ params = authsrv_params()
+ params["eap_user_file"] = "auth_serv/eap_user_vlan.conf"
+ authsrv = hostapd.add_ap(apdev[1], params)
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18128"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_authsrv_unknown_client(dev, apdev):
+ """Authentication server and unknown user"""
+ params = authsrv_params()
+ params["radius_server_clients"] = "auth_serv/radius_clients_none.conf"
+ authsrv = hostapd.add_ap(apdev[1], params)
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18128"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # RADIUS SRV: Unknown client 127.0.0.1 - packet ignored
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="user",
+ anonymous_identity="ttls", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP not started")
+ dev[0].request("REMOVE_NETWORK all")
diff --git a/contrib/wpa/tests/hwsim/test_autoscan.py b/contrib/wpa/tests/hwsim/test_autoscan.py
new file mode 100644
index 000000000000..544cd0099d0f
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_autoscan.py
@@ -0,0 +1,81 @@
+# autoscan tests
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import logging
+logger = logging.getLogger()
+import os
+
+import hostapd
+
+def test_autoscan_periodic(dev, apdev):
+ """autoscan_periodic"""
+ hostapd.add_ap(apdev[0], {"ssid": "autoscan"})
+
+ try:
+ if "OK" not in dev[0].request("AUTOSCAN periodic:1"):
+ raise Exception("Failed to set autoscan")
+ id = dev[0].connect("not-used", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ times = {}
+ for i in range(0, 3):
+ logger.info("Waiting for scan to start")
+ start = os.times()[4]
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("did not start a scan")
+ stop = os.times()[4]
+ times[i] = stop - start
+ logger.info("Waiting for scan to complete")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("did not complete a scan")
+ if times[0] > 1 or times[1] < 0.5 or times[1] > 1.5 or times[2] < 0.5 or times[2] > 1.5:
+ raise Exception("Unexpected scan timing: " + str(times))
+
+ # scan some more channels to allow some more time for reseting AUTOSCAN
+ # while a scan is in progress
+ dev[0].set_network(id, "scan_freq", "2412 2437 2462 5180 5200 5220 5240")
+ dev[0].dump_monitor()
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("did not start a scan")
+ if "OK" not in dev[0].request("AUTOSCAN periodic:2"):
+ raise Exception("Failed to (re)set autoscan")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("did not complete a scan")
+ finally:
+ dev[0].request("AUTOSCAN ")
+
+@remote_compatible
+def test_autoscan_exponential(dev, apdev):
+ """autoscan_exponential"""
+ hostapd.add_ap(apdev[0], {"ssid": "autoscan"})
+
+ try:
+ if "OK" not in dev[0].request("AUTOSCAN exponential:2:10"):
+ raise Exception("Failed to set autoscan")
+ dev[0].connect("not-used", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ times = {}
+ for i in range(0, 3):
+ logger.info("Waiting for scan to start")
+ start = os.times()[4]
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("did not start a scan")
+ stop = os.times()[4]
+ times[i] = stop - start
+ logger.info("Waiting for scan to complete")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("did not complete a scan")
+ if times[0] > 1 or times[1] < 1 or times[1] > 3 or times[2] < 3 or times[2] > 5:
+ raise Exception("Unexpected scan timing: " + str(times))
+ finally:
+ dev[0].request("AUTOSCAN ")
diff --git a/contrib/wpa/tests/hwsim/test_bgscan.py b/contrib/wpa/tests/hwsim/test_bgscan.py
new file mode 100644
index 000000000000..e3c1790ba420
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_bgscan.py
@@ -0,0 +1,315 @@
+# bgscan tests
+# Copyright (c) 2014-2017, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import logging
+logger = logging.getLogger()
+import os
+
+import hostapd
+from utils import alloc_fail, fail_test
+
+def test_bgscan_simple(dev, apdev):
+ """bgscan_simple"""
+ hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+ hostapd.add_ap(apdev[1], {"ssid": "bgscan"})
+
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1:-20:2")
+ dev[1].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1:-45:2")
+
+ dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1:-45")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[2].wait_disconnected()
+
+ dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:0:0")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[2].wait_disconnected()
+
+ dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[2].wait_disconnected()
+
+ dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[2].wait_disconnected()
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SIGNAL-CHANGE"], timeout=10)
+ if ev is None:
+ raise Exception("dev0 did not indicate signal change event")
+ if "above=0" not in ev:
+ raise Exception("Unexpected signal change event contents from dev0: " + ev)
+
+ ev = dev[1].wait_event(["CTRL-EVENT-SIGNAL-CHANGE"], timeout=10)
+ if ev is None:
+ raise Exception("dev1 did not indicate signal change event")
+ if "above=1" not in ev:
+ raise Exception("Unexpected signal change event contents from dev1: " + ev)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=3)
+ if ev is None:
+ raise Exception("dev0 did not start a scan")
+
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=3)
+ if ev is None:
+ raise Exception("dev1 did not start a scan")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ if ev is None:
+ raise Exception("dev0 did not complete a scan")
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ if ev is None:
+ raise Exception("dev1 did not complete a scan")
+
+def test_bgscan_simple_beacon_loss(dev, apdev):
+ """bgscan_simple and beacon loss"""
+ params = hostapd.wpa2_params(ssid="bgscan", passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].set("disable_sa_query", "1")
+ dev[0].connect("bgscan", ieee80211w="2", key_mgmt="WPA-PSK-SHA256",
+ psk="12345678", scan_freq="2412",
+ bgscan="simple:100:-20:200")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ if "OK" not in hapd.request("STOP_AP"):
+ raise Exception("Failed to stop AP")
+ hapd.disable()
+ hapd.set("ssid", "foo")
+ hapd.set("beacon_int", "10000")
+ hapd.enable()
+ ev = dev[0].wait_event(["CTRL-EVENT-BEACON-LOSS"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon loss not reported")
+
+def test_bgscan_simple_scan_failure(dev, apdev):
+ """bgscan_simple and scan failure"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1:-20:2")
+ with alloc_fail(dev[0], 1,
+ "wpa_supplicant_trigger_scan;bgscan_simple_timeout"):
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=10)
+ if ev is None:
+ raise Exception("No scan failure reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("Scanning not continued after failure")
+
+def test_bgscan_simple_scanning(dev, apdev):
+ """bgscan_simple and scanning behavior"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1:-20:2")
+ # Go through seven bgscan_simple_timeout calls for code coverage. This falls
+ # back from short to long scan interval and then reduces short_scan_count
+ # back to zero.
+ for i in range(7):
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("Scanning not continued")
+
+def test_bgscan_simple_same_scan_int(dev, apdev):
+ """bgscan_simple and same short/long scan interval"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1:-20:1")
+ for i in range(2):
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("Scanning not continued")
+
+def test_bgscan_simple_oom(dev, apdev):
+ """bgscan_simple OOM"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+
+ with alloc_fail(dev[0], 1, "bgscan_simple_init"):
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1:-20:2")
+
+def test_bgscan_simple_driver_conf_failure(dev, apdev):
+ """bgscan_simple driver configuration failure"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+
+ with fail_test(dev[0], 1, "bgscan_simple_init"):
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1:-20:2")
+
+def test_bgscan_learn(dev, apdev):
+ """bgscan_learn"""
+ hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+ hostapd.add_ap(apdev[1], {"ssid": "bgscan"})
+
+ try:
+ os.remove("/tmp/test_bgscan_learn.bgscan")
+ except:
+ pass
+
+ try:
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="learn:1:-20:2")
+ id = dev[1].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="learn:1:-45:2:/tmp/test_bgscan_learn.bgscan")
+
+ dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="learn:1:-45")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[2].wait_disconnected()
+
+ dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="learn:0:0")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[2].wait_disconnected()
+
+ dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="learn")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[2].wait_disconnected()
+
+ dev[2].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="learn:1")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[2].wait_disconnected()
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SIGNAL-CHANGE"], timeout=10)
+ if ev is None:
+ raise Exception("dev0 did not indicate signal change event")
+ if "above=0" not in ev:
+ raise Exception("Unexpected signal change event contents from dev0: " + ev)
+
+ ev = dev[1].wait_event(["CTRL-EVENT-SIGNAL-CHANGE"], timeout=10)
+ if ev is None:
+ raise Exception("dev1 did not indicate signal change event")
+ if "above=1" not in ev:
+ raise Exception("Unexpected signal change event contents from dev1: " + ev)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=3)
+ if ev is None:
+ raise Exception("dev0 did not start a scan")
+
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=3)
+ if ev is None:
+ raise Exception("dev1 did not start a scan")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ if ev is None:
+ raise Exception("dev0 did not complete a scan")
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ if ev is None:
+ raise Exception("dev1 did not complete a scan")
+
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ dev[0].request("REMOVE_NETWORK all")
+
+ with open("/tmp/test_bgscan_learn.bgscan", "r") as f:
+ lines = f.read().splitlines()
+ if lines[0] != "wpa_supplicant-bgscan-learn":
+ raise Exception("Unexpected bgscan header line")
+ if 'BSS 02:00:00:00:03:00 2412' not in lines:
+ raise Exception("Missing BSS1")
+ if 'BSS 02:00:00:00:04:00 2412' not in lines:
+ raise Exception("Missing BSS2")
+ if 'NEIGHBOR 02:00:00:00:03:00 02:00:00:00:04:00' not in lines:
+ raise Exception("Missing BSS1->BSS2 neighbor entry")
+ if 'NEIGHBOR 02:00:00:00:04:00 02:00:00:00:03:00' not in lines:
+ raise Exception("Missing BSS2->BSS1 neighbor entry")
+
+ dev[1].set_network(id, "scan_freq", "")
+ dev[1].connect_network(id)
+
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("dev1 did not start a scan")
+
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("dev1 did not complete a scan")
+
+ dev[1].request("REMOVE_NETWORK all")
+ finally:
+ try:
+ os.remove("/tmp/test_bgscan_learn.bgscan")
+ except:
+ pass
+
+def test_bgscan_learn_beacon_loss(dev, apdev):
+ """bgscan_simple and beacon loss"""
+ params = hostapd.wpa2_params(ssid="bgscan", passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].set("disable_sa_query", "1")
+ dev[0].connect("bgscan", ieee80211w="2", key_mgmt="WPA-PSK-SHA256",
+ psk="12345678", scan_freq="2412", bgscan="learn:100:-20:200")
+ hapd.set("ext_mgmt_frame_handling", "1")
+ if "OK" not in hapd.request("STOP_AP"):
+ raise Exception("Failed to stop AP")
+ hapd.disable()
+ hapd.set("ssid", "foo")
+ hapd.set("beacon_int", "10000")
+ hapd.enable()
+ ev = dev[0].wait_event(["CTRL-EVENT-BEACON-LOSS"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon loss not reported")
+
+def test_bgscan_learn_scan_failure(dev, apdev):
+ """bgscan_learn and scan failure"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="learn:1:-20:2")
+ with alloc_fail(dev[0], 1,
+ "wpa_supplicant_trigger_scan;bgscan_learn_timeout"):
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=10)
+ if ev is None:
+ raise Exception("No scan failure reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("Scanning not continued after failure")
+
+def test_bgscan_learn_oom(dev, apdev):
+ """bgscan_learn OOM"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+
+ with alloc_fail(dev[0], 1, "bgscan_learn_init"):
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="learn:1:-20:2")
+
+def test_bgscan_learn_driver_conf_failure(dev, apdev):
+ """bgscan_learn driver configuration failure"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+
+ with fail_test(dev[0], 1, "bgscan_learn_init"):
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="learn:1:-20:2")
+
+def test_bgscan_unknown_module(dev, apdev):
+ """bgscan init failing due to unknown module"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+ dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="unknown:-20:2")
+
+def test_bgscan_reconfig(dev, apdev):
+ """bgscan parameter update"""
+ hostapd.add_ap(apdev[0], {"ssid": "bgscan"})
+ hostapd.add_ap(apdev[1], {"ssid": "bgscan"})
+
+ id = dev[0].connect("bgscan", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1:-20:2")
+ dev[0].set_network_quoted(id, "bgscan", "simple:1:-45:2")
+ dev[0].set_network_quoted(id, "bgscan", "learn:1:-20:2")
+ dev[0].set_network_quoted(id, "bgscan", "")
diff --git a/contrib/wpa/tests/hwsim/test_cert_check.py b/contrib/wpa/tests/hwsim/test_cert_check.py
new file mode 100644
index 000000000000..191a1d1aa1ce
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_cert_check.py
@@ -0,0 +1,312 @@
+# Test cases for X.509 certificate checking
+# Copyright (c) 2019, The Linux Foundation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+try:
+ import OpenSSL
+ openssl_imported = True
+except ImportError:
+ openssl_imported = False
+
+from utils import HwsimSkip
+import hostapd
+from test_ap_eap import check_domain_suffix_match, check_altsubject_match_support, check_domain_match
+
+def check_cert_check_support():
+ if not openssl_imported:
+ raise HwsimSkip("OpenSSL python method not available")
+
+def start_hapd(apdev, server_cert="auth_serv/server.pem"):
+ params = {"ssid": "cert-check", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP", "ieee8021x": "1",
+ "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": server_cert,
+ "private_key": "auth_serv/server.key",
+ "dh_file": "auth_serv/dh.conf"}
+ hapd = hostapd.add_ap(apdev, params)
+ return hapd
+
+def load_certs():
+ with open("auth_serv/ca.pem", "rb") as f:
+ res = f.read()
+ cacert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
+ res)
+
+ with open("auth_serv/ca-key.pem", "rb") as f:
+ res = f.read()
+ cakey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, res)
+
+ with open("auth_serv/server.pem", "rb") as f:
+ res = f.read()
+ servercert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, res)
+
+ return cacert, cakey, servercert
+
+def start_cert(servercert, cacert, cn='server.w1.fi', v3=True):
+ cert = OpenSSL.crypto.X509()
+ cert.set_serial_number(12345)
+ cert.gmtime_adj_notBefore(-10)
+ cert.gmtime_adj_notAfter(1000)
+ cert.set_pubkey(servercert.get_pubkey())
+ dn = cert.get_subject()
+ dn.CN = cn
+ cert.set_subject(dn)
+ if v3:
+ cert.set_version(2)
+ cert.add_extensions([
+ OpenSSL.crypto.X509Extension(b"basicConstraints", True,
+ b"CA:FALSE"),
+ OpenSSL.crypto.X509Extension(b"subjectKeyIdentifier", False,
+ b"hash", subject=cert),
+ OpenSSL.crypto.X509Extension(b"authorityKeyIdentifier", False,
+ b"keyid:always", issuer=cacert),
+ ])
+ return cert
+
+def sign_cert(cert, cert_file, cakey, cacert):
+ cert.set_issuer(cacert.get_subject())
+ cert.sign(cakey, "sha256")
+ with open(cert_file, 'wb') as f:
+ f.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
+ cert))
+
+def check_connect(dev, fail=False, wait_error=None, **kwargs):
+ dev.connect("cert-check", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap user", anonymous_identity="ttls",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ scan_freq="2412", wait_connect=False, **kwargs)
+ ev = dev.wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("EAP not started")
+ if fail:
+ if wait_error:
+ ev = dev.wait_event([wait_error], timeout=5)
+ if ev is None:
+ raise Exception("Specific error not reported")
+ ev = dev.wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ else:
+ dev.wait_connected()
+ dev.request("REMOVE_NETWORK all")
+ dev.request("ABORT_SCAN")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_cert_check_basic(dev, apdev, params):
+ """Basic test with generated X.509 server certificate"""
+ check_cert_check_support()
+ cert_file = os.path.join(params['logdir'], "cert_check_basic.pem")
+ cacert, cakey, servercert = load_certs()
+
+ cert = start_cert(servercert, cacert, v3=False)
+ sign_cert(cert, cert_file, cakey, cacert)
+ hapd = start_hapd(apdev[0], server_cert=cert_file)
+ check_connect(dev[0])
+
+def test_cert_check_v3(dev, apdev, params):
+ """Basic test with generated X.509v3 server certificate"""
+ check_cert_check_support()
+ cert_file = os.path.join(params['logdir'], "cert_check_v3.pem")
+ cacert, cakey, servercert = load_certs()
+
+ cert = start_cert(servercert, cacert)
+ sign_cert(cert, cert_file, cakey, cacert)
+ hapd = start_hapd(apdev[0], server_cert=cert_file)
+ check_connect(dev[0])
+
+def test_cert_check_dnsname(dev, apdev, params):
+ """Certificate check with multiple dNSName values"""
+ check_cert_check_support()
+ check_domain_suffix_match(dev[0])
+ check_domain_match(dev[0])
+ cert_file = os.path.join(params['logdir'], "cert_check_dnsname.pem")
+ cacert, cakey, servercert = load_certs()
+
+ cert = start_cert(servercert, cacert, cn="server")
+ dns = ["DNS:one.example.com", "DNS:two.example.com",
+ "DNS:three.example.com"]
+ cert.add_extensions([OpenSSL.crypto.X509Extension(b"subjectAltName", False,
+ ",".join(dns).encode())])
+ sign_cert(cert, cert_file, cakey, cacert)
+ hapd = start_hapd(apdev[0], server_cert=cert_file)
+ check_connect(dev[0])
+
+ tests = ["two.example.com",
+ "one.example.com",
+ "tWo.Example.com",
+ "three.example.com",
+ "no.match.example.com;two.example.com;no.match.example.org",
+ "no.match.example.com;example.com;no.match.example.org",
+ "no.match.example.com;no.match.example.org;example.com",
+ "example.com",
+ "com"]
+ for match in tests:
+ check_connect(dev[0], domain_suffix_match=match)
+
+ tests = ["four.example.com",
+ "foo.one.example.com",
+ "no.match.example.org;no.match.example.com",
+ "xample.com"]
+ for match in tests:
+ check_connect(dev[0], fail=True,
+ wait_error="CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ domain_suffix_match=match)
+
+ tests = ["one.example.com",
+ "two.example.com",
+ "three.example.com",
+ "no.match.example.com;two.example.com;no.match.example.org",
+ "tWo.Example.Com"]
+ for match in tests:
+ check_connect(dev[0], domain_match=match)
+
+ tests = ["four.example.com",
+ "foo.one.example.com",
+ "example.com",
+ "xample.com",
+ "no.match.example.org;no.match.example.com",
+ "ne.example.com"]
+ for match in tests:
+ check_connect(dev[0], fail=True,
+ wait_error="CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ domain_match=match)
+
+def test_cert_check_dnsname_wildcard(dev, apdev, params):
+ """Certificate check with multiple dNSName wildcard values"""
+ check_cert_check_support()
+ check_domain_suffix_match(dev[0])
+ check_domain_match(dev[0])
+ cert_file = os.path.join(params['logdir'], "cert_check_dnsname.pem")
+ cacert, cakey, servercert = load_certs()
+
+ cert = start_cert(servercert, cacert, cn="server")
+ dns = ["DNS:*.one.example.com", "DNS:two.example.com",
+ "DNS:*.three.example.com"]
+ cert.add_extensions([OpenSSL.crypto.X509Extension(b"subjectAltName", False,
+ ",".join(dns).encode())])
+ sign_cert(cert, cert_file, cakey, cacert)
+ hapd = start_hapd(apdev[0], server_cert=cert_file)
+ check_connect(dev[0])
+
+ tests = ["two.example.com",
+ "one.example.com",
+ "tWo.Example.com",
+ "three.example.com",
+ "no.match.example.com;two.example.com;no.match.example.org",
+ "no.match.example.com;example.com;no.match.example.org",
+ "no.match.example.com;no.match.example.org;example.com",
+ "example.com",
+ "com"]
+ for match in tests:
+ check_connect(dev[0], domain_suffix_match=match)
+
+ tests = ["four.example.com",
+ "foo.one.example.com",
+ "no.match.example.org;no.match.example.com",
+ "xample.com"]
+ for match in tests:
+ check_connect(dev[0], fail=True,
+ wait_error="CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ domain_suffix_match=match)
+
+ tests = ["*.one.example.com",
+ "two.example.com",
+ "*.three.example.com",
+ "no.match.example.com;two.example.com;no.match.example.org",
+ "tWo.Example.Com"]
+ for match in tests:
+ check_connect(dev[0], domain_match=match)
+
+ tests = ["four.example.com",
+ "foo.one.example.com",
+ "example.com",
+ "xample.com",
+ "no.match.example.org;no.match.example.com",
+ "one.example.com"]
+ for match in tests:
+ check_connect(dev[0], fail=True,
+ wait_error="CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ domain_match=match)
+
+def test_cert_check_dnsname_alt(dev, apdev, params):
+ """Certificate check with multiple dNSName values using altsubject_match"""
+ check_cert_check_support()
+ check_altsubject_match_support(dev[0])
+ cert_file = os.path.join(params['logdir'], "cert_check_dnsname_alt.pem")
+ cacert, cakey, servercert = load_certs()
+
+ cert = start_cert(servercert, cacert, cn="server")
+ dns = ["DNS:*.one.example.com", "DNS:two.example.com",
+ "DNS:*.three.example.com"]
+ cert.add_extensions([OpenSSL.crypto.X509Extension(b"subjectAltName", False,
+ ",".join(dns).encode())])
+ sign_cert(cert, cert_file, cakey, cacert)
+ hapd = start_hapd(apdev[0], server_cert=cert_file)
+
+ tests = ["DNS:*.one.example.com",
+ "DNS:two.example.com",
+ "DNS:*.three.example.com",
+ "DNS:*.three.example.com;DNS:two.example.com;DNS:*.one.example.com",
+ "DNS:foo.example.org;DNS:two.example.com;DNS:bar.example.org"]
+ for alt in tests:
+ check_connect(dev[0], altsubject_match=alt)
+
+ tests = ["DNS:one.example.com",
+ "DNS:four.example.com;DNS:five.example.com"]
+ for alt in tests:
+ check_connect(dev[0], fail=True,
+ wait_error="CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ altsubject_match=alt)
+
+def test_cert_check_dnsname_cn(dev, apdev, params):
+ """Certificate check with dNSName in CN"""
+ check_cert_check_support()
+ check_domain_suffix_match(dev[0])
+ check_domain_match(dev[0])
+ cert_file = os.path.join(params['logdir'], "cert_check_dnsname_cn.pem")
+ cacert, cakey, servercert = load_certs()
+
+ cert = start_cert(servercert, cacert, cn="server.example.com")
+ sign_cert(cert, cert_file, cakey, cacert)
+ hapd = start_hapd(apdev[0], server_cert=cert_file)
+ check_connect(dev[0])
+
+ tests = ["server.example.com",
+ "example.com",
+ "eXample.Com",
+ "no.match.example.com;example.com;no.match.example.org",
+ "no.match.example.com;server.example.com;no.match.example.org",
+ "com"]
+ for match in tests:
+ check_connect(dev[0], domain_suffix_match=match)
+
+ tests = ["aaa.example.com",
+ "foo.server.example.com",
+ "no.match.example.org;no.match.example.com",
+ "xample.com"]
+ for match in tests:
+ check_connect(dev[0], fail=True,
+ wait_error="CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ domain_suffix_match=match)
+
+ tests = ["server.example.com",
+ "no.match.example.com;server.example.com;no.match.example.org",
+ "sErver.Example.Com"]
+ for match in tests:
+ check_connect(dev[0], domain_match=match)
+
+ tests = ["aaa.example.com",
+ "foo.server.example.com",
+ "example.com",
+ "no.match.example.org;no.match.example.com",
+ "xample.com"]
+ for match in tests:
+ check_connect(dev[0], fail=True,
+ wait_error="CTRL-EVENT-EAP-TLS-CERT-ERROR",
+ domain_match=match)
diff --git a/contrib/wpa/tests/hwsim/test_cfg80211.py b/contrib/wpa/tests/hwsim/test_cfg80211.py
new file mode 100644
index 000000000000..3ee7a909b8ba
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_cfg80211.py
@@ -0,0 +1,150 @@
+# cfg80211 test cases
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import binascii
+import os
+import time
+
+import hostapd
+import hwsim_utils
+from tshark import run_tshark
+from nl80211 import *
+from wpasupplicant import WpaSupplicant
+from utils import *
+
+def nl80211_command(dev, cmd, attr):
+ res = dev.request("VENDOR ffffffff {} {}".format(nl80211_cmd[cmd],
+ binascii.hexlify(attr).decode()))
+ if "FAIL" in res:
+ raise Exception("nl80211 command failed")
+ return binascii.unhexlify(res)
+
+@remote_compatible
+def test_cfg80211_disassociate(dev, apdev):
+ """cfg80211 disassociation command"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+
+ ifindex = int(dev[0].get_driver_status_field("ifindex"))
+ attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+ attrs += build_nl80211_attr_u16('REASON_CODE', 1)
+ attrs += build_nl80211_attr_mac('MAC', apdev[0]['bssid'])
+ nl80211_command(dev[0], 'DISASSOCIATE', attrs)
+
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No disconnection event received from hostapd")
+
+def nl80211_frame(dev, ifindex, frame, freq=None, duration=None, offchannel_tx_ok=False):
+ attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+ if freq is not None:
+ attrs += build_nl80211_attr_u32('WIPHY_FREQ', freq)
+ if duration is not None:
+ attrs += build_nl80211_attr_u32('DURATION', duration)
+ if offchannel_tx_ok:
+ attrs += build_nl80211_attr_flag('OFFCHANNEL_TX_OK')
+ attrs += build_nl80211_attr('FRAME', frame)
+ return parse_nl80211_attrs(nl80211_command(dev, 'FRAME', attrs))
+
+def nl80211_frame_wait_cancel(dev, ifindex, cookie):
+ attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+ attrs += build_nl80211_attr('COOKIE', cookie)
+ return nl80211_command(dev, 'FRAME_WAIT_CANCEL', attrs)
+
+def nl80211_remain_on_channel(dev, ifindex, freq, duration):
+ attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+ attrs += build_nl80211_attr_u32('WIPHY_FREQ', freq)
+ attrs += build_nl80211_attr_u32('DURATION', duration)
+ return nl80211_command(dev, 'REMAIN_ON_CHANNEL', attrs)
+
+def test_cfg80211_tx_frame(dev, apdev, params):
+ """cfg80211 offchannel TX frame command"""
+
+ dev[0].p2p_start_go(freq='2412')
+ go = WpaSupplicant(dev[0].group_ifname)
+ frame = binascii.unhexlify("d0000000020000000100" + go.own_addr().replace(':', '') + "02000000010000000409506f9a090001dd5e506f9a0902020025080401001f0502006414060500585804510b0906000200000000000b1000585804510b0102030405060708090a0b0d1d000200000000000108000000000000000000101100084465766963652041110500585804510bdd190050f204104a0001101012000200011049000600372a000120")
+ ifindex = int(go.get_driver_status_field("ifindex"))
+ res = nl80211_frame(go, ifindex, frame, freq=2422, duration=500,
+ offchannel_tx_ok=True)
+ time.sleep(0.1)
+
+ # note: Uncommenting this seems to remove the incorrect channel issue
+ #nl80211_frame_wait_cancel(dev[0], ifindex, res[nl80211_attr['COOKIE']])
+
+ # note: this Action frame ends up getting sent incorrectly on 2422 MHz
+ nl80211_frame(go, ifindex, frame, freq=2412)
+ time.sleep(1.5)
+ # note: also the Deauthenticate frame sent by the GO going down ends up
+ # being transmitted incorrectly on 2422 MHz.
+
+ del go
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan.fc.type_subtype == 13", ["radiotap.channel.freq"])
+ if out is not None:
+ freq = out.splitlines()
+ if len(freq) != 2:
+ raise Exception("Unexpected number of Action frames (%d)" % len(freq))
+ if freq[0] != "2422":
+ raise Exception("First Action frame on unexpected channel: %s MHz" % freq[0])
+ if freq[1] != "2412":
+ raise Exception("Second Action frame on unexpected channel: %s MHz" % freq[1])
+
+@remote_compatible
+def test_cfg80211_wep_key_idx_change(dev, apdev):
+ """WEP Shared Key authentication and key index change without deauth"""
+ check_wep_capa(dev[0])
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wep-shared-key",
+ "wep_key0": '"hello12345678"',
+ "wep_key1": '"other12345678"',
+ "auth_algs": "2"})
+ id = dev[0].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+ wep_key0='"hello12345678"',
+ wep_key1='"other12345678"',
+ wep_tx_keyidx="0",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[0].set_network(id, "wep_tx_keyidx", "1")
+
+ # clear cfg80211 auth state to allow new auth without deauth frame
+ ifindex = int(dev[0].get_driver_status_field("ifindex"))
+ attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+ attrs += build_nl80211_attr_u16('REASON_CODE', 1)
+ attrs += build_nl80211_attr_mac('MAC', apdev[0]['bssid'])
+ attrs += build_nl80211_attr_flag('LOCAL_STATE_CHANGE')
+ nl80211_command(dev[0], 'DEAUTHENTICATE', attrs)
+ dev[0].wait_disconnected(timeout=5, error="Local-deauth timed out")
+
+ # the previous command results in deauth event followed by auto-reconnect
+ dev[0].wait_connected(timeout=10, error="Reassociation timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_cfg80211_hostapd_ext_sta_remove(dev, apdev):
+ """cfg80211 DEL_STATION issued externally to hostapd"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ id = dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+ ifindex = int(hapd.get_driver_status_field("ifindex"))
+ attrs = build_nl80211_attr_u32('IFINDEX', ifindex)
+ attrs += build_nl80211_attr_u16('REASON_CODE', 1)
+ attrs += build_nl80211_attr_u8('MGMT_SUBTYPE', 12)
+ attrs += build_nl80211_attr_mac('MAC', dev[0].own_addr())
+ nl80211_command(hapd, 'DEL_STATION', attrs)
+
+ # Currently, hostapd ignores the NL80211_CMD_DEL_STATION event if
+ # drv->device_ap_sme == 0 (which is the case with mac80211_hwsim), so no
+ # further action happens here. If that event were to be used to remove the
+ # STA entry from hostapd even in device_ap_sme == 0 case, this test case
+ # could be extended to cover additional operations.
diff --git a/contrib/wpa/tests/hwsim/test_connect_cmd.py b/contrib/wpa/tests/hwsim/test_connect_cmd.py
new file mode 100644
index 000000000000..3c0985137d41
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_connect_cmd.py
@@ -0,0 +1,235 @@
+# cfg80211 connect command (SME in the driver/firmware)
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+from p2p_utils import *
+from utils import *
+
+def test_connect_cmd_open(dev, apdev):
+ """Open connection using cfg80211 connect command"""
+ params = {"ssid": "sta-connect",
+ "manage_p2p": "1",
+ "allow_cross_connection": "1"}
+ hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412",
+ bg_scan_period="1")
+ wpas.dump_monitor()
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+def test_connect_cmd_wep(dev, apdev):
+ """WEP Open System using cfg80211 connect command"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ check_wep_capa(wpas)
+
+ params = {"ssid": "sta-connect-wep", "wep_key0": '"hello"'}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas.connect("sta-connect-wep", key_mgmt="NONE", scan_freq="2412",
+ wep_key0='"hello"')
+ wpas.dump_monitor()
+ hwsim_utils.test_connectivity(wpas, hapd)
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+def test_connect_cmd_wep_shared(dev, apdev):
+ """WEP Shared key using cfg80211 connect command"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ check_wep_capa(wpas)
+
+ params = {"ssid": "sta-connect-wep", "wep_key0": '"hello"',
+ "auth_algs": "2"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ id = wpas.connect("sta-connect-wep", key_mgmt="NONE", scan_freq="2412",
+ auth_alg="SHARED", wep_key0='"hello"')
+ wpas.dump_monitor()
+ hwsim_utils.test_connectivity(wpas, hapd)
+ wpas.request("DISCONNECT")
+ wpas.remove_network(id)
+ wpas.connect("sta-connect-wep", key_mgmt="NONE", scan_freq="2412",
+ auth_alg="OPEN SHARED", wep_key0='"hello"')
+ wpas.dump_monitor()
+ hwsim_utils.test_connectivity(wpas, hapd)
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+def test_connect_cmd_p2p_management(dev, apdev):
+ """Open connection using cfg80211 connect command and AP using P2P management"""
+ params = {"ssid": "sta-connect",
+ "manage_p2p": "1",
+ "allow_cross_connection": "0"}
+ hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412")
+ wpas.dump_monitor()
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+def test_connect_cmd_wpa2_psk(dev, apdev):
+ """WPA2-PSK connection using cfg80211 connect command"""
+ params = hostapd.wpa2_params(ssid="sta-connect", passphrase="12345678")
+ hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("sta-connect", psk="12345678", scan_freq="2412")
+ wpas.dump_monitor()
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+def test_connect_cmd_concurrent_grpform_while_connecting(dev, apdev):
+ """Concurrent P2P group formation while connecting to an AP using cfg80211 connect command"""
+ logger.info("Start connection to an infrastructure AP")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("test-open", key_mgmt="NONE", wait_connect=False)
+ wpas.dump_monitor()
+
+ logger.info("Form a P2P group while connecting to an AP")
+ wpas.request("SET p2p_no_group_iface 0")
+
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_freq=2412,
+ r_dev=wpas, r_freq=2412)
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], wpas)
+ wpas.dump_monitor()
+
+ logger.info("Confirm AP connection after P2P group removal")
+ hwsim_utils.test_connectivity(wpas, hapd)
+
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+def test_connect_cmd_reject_assoc(dev, apdev):
+ """Connection using cfg80211 connect command getting rejected"""
+ params = {"ssid": "sta-connect",
+ "require_ht": "1"}
+ hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412",
+ disable_ht="1", wait_connect=False)
+ ev = wpas.wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=15)
+ if ev is None:
+ raise Exception("Association rejection timed out")
+ if "status_code=27" not in ev:
+ raise Exception("Unexpected rejection status code")
+
+ wpas.request("DISCONNECT")
+ wpas.dump_monitor()
+
+def test_connect_cmd_disconnect_event(dev, apdev):
+ """Connection using cfg80211 connect command getting disconnected by the AP"""
+ params = {"ssid": "sta-connect"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412")
+
+ if "OK" not in hapd.request("DEAUTHENTICATE " + wpas.p2p_interface_addr()):
+ raise Exception("DEAUTHENTICATE command failed")
+ ev = wpas.wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnection event timed out")
+ # This event was actually based on deauthenticate event since we force
+ # connect command to be used with a driver that supports auth+assoc for
+ # testing purposes. Anyway, wait some time to allow the debug log to capture
+ # the following NL80211_CMD_DISCONNECT event.
+ time.sleep(0.1)
+ wpas.dump_monitor()
+
+ # Clean up to avoid causing issue for following test cases
+ wpas.request("REMOVE_NETWORK all")
+ wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=2)
+ wpas.flush_scan_cache()
+ wpas.dump_monitor()
+ wpas.interface_remove("wlan5")
+ del wpas
+
+def test_connect_cmd_roam(dev, apdev):
+ """cfg80211 connect command to trigger roam"""
+ params = {"ssid": "sta-connect"}
+ hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412")
+ wpas.dump_monitor()
+
+ hostapd.add_ap(apdev[1], params)
+ wpas.scan_for_bss(apdev[1]['bssid'], freq=2412, force_scan=True)
+ wpas.roam(apdev[1]['bssid'])
+ time.sleep(0.1)
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+def test_connect_cmd_bssid_hint(dev, apdev):
+ """cfg80211 connect command with bssid_hint"""
+ params = {"ssid": "sta-connect"}
+ hostapd.add_ap(apdev[0], params)
+ hostapd.add_ap(apdev[1], params)
+
+ # This does not really give full coverage with mac80211_hwsim since the
+ # driver does not end up claiming support for driver-based BSS selection.
+ # Anyway, some test coverage can be achieved for setting the parameter and
+ # checking that it does not prevent connection with another BSSID.
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+
+ wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412",
+ bssid_hint=apdev[0]['bssid'])
+ wpas.request("REMOVE_NETWORK all")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+ wpas.request("BSS_FLUSH 0")
+ wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412",
+ bssid_hint='22:33:44:55:66:77')
+ wpas.request("REMOVE_NETWORK all")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+ # Additional coverage using ap_scan=2 to prevent scan entry -based selection
+ # within wpa_supplicant from overriding bssid_hint.
+
+ try:
+ if "OK" not in wpas.request("AP_SCAN 2"):
+ raise Exception("Failed to set AP_SCAN 2")
+ wpas.request("BSS_FLUSH 0")
+ wpas.connect("sta-connect", key_mgmt="NONE", scan_freq="2412",
+ bssid_hint='22:33:44:55:66:77')
+ wpas.request("REMOVE_NETWORK all")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+ finally:
+ wpas.request("AP_SCAN 1")
+ wpas.flush_scan_cache()
diff --git a/contrib/wpa/tests/hwsim/test_dbus.py b/contrib/wpa/tests/hwsim/test_dbus.py
new file mode 100644
index 000000000000..1143802c6131
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_dbus.py
@@ -0,0 +1,6093 @@
+# wpa_supplicant D-Bus interface tests
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+import shutil
+import struct
+import sys
+
+try:
+ if sys.version_info[0] > 2:
+ from gi.repository import GObject as gobject
+ else:
+ import gobject
+ import dbus
+ dbus_imported = True
+except ImportError:
+ dbus_imported = False
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+from p2p_utils import *
+from test_ap_tdls import connect_2sta_open
+from test_ap_eap import check_altsubject_match_support
+from test_nfc_p2p import set_ip_addr_info
+from test_wpas_mesh import check_mesh_support, add_open_mesh_network
+
+WPAS_DBUS_SERVICE = "fi.w1.wpa_supplicant1"
+WPAS_DBUS_PATH = "/fi/w1/wpa_supplicant1"
+WPAS_DBUS_IFACE = "fi.w1.wpa_supplicant1.Interface"
+WPAS_DBUS_IFACE_WPS = WPAS_DBUS_IFACE + ".WPS"
+WPAS_DBUS_NETWORK = "fi.w1.wpa_supplicant1.Network"
+WPAS_DBUS_BSS = "fi.w1.wpa_supplicant1.BSS"
+WPAS_DBUS_IFACE_P2PDEVICE = WPAS_DBUS_IFACE + ".P2PDevice"
+WPAS_DBUS_P2P_PEER = "fi.w1.wpa_supplicant1.Peer"
+WPAS_DBUS_GROUP = "fi.w1.wpa_supplicant1.Group"
+WPAS_DBUS_PERSISTENT_GROUP = "fi.w1.wpa_supplicant1.PersistentGroup"
+WPAS_DBUS_IFACE_MESH = WPAS_DBUS_IFACE + ".Mesh"
+
+def prepare_dbus(dev):
+ if not dbus_imported:
+ logger.info("No dbus module available")
+ raise HwsimSkip("No dbus module available")
+ try:
+ from dbus.mainloop.glib import DBusGMainLoop
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+ bus = dbus.SystemBus()
+ wpas_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_PATH)
+ wpas = dbus.Interface(wpas_obj, WPAS_DBUS_SERVICE)
+ path = wpas.GetInterface(dev.ifname)
+ if_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+ return (bus, wpas_obj, path, if_obj)
+ except Exception as e:
+ raise HwsimSkip("Could not connect to D-Bus: %s" % e)
+
+class TestDbus(object):
+ def __init__(self, bus):
+ self.loop = gobject.MainLoop()
+ self.signals = []
+ self.bus = bus
+
+ def __exit__(self, type, value, traceback):
+ for s in self.signals:
+ s.remove()
+
+ def add_signal(self, handler, interface, name, byte_arrays=False):
+ s = self.bus.add_signal_receiver(handler, dbus_interface=interface,
+ signal_name=name,
+ byte_arrays=byte_arrays)
+ self.signals.append(s)
+
+ def timeout(self, *args):
+ logger.debug("timeout")
+ self.loop.quit()
+ return False
+
+class alloc_fail_dbus(object):
+ def __init__(self, dev, count, funcs, operation="Operation",
+ expected="NoMemory"):
+ self._dev = dev
+ self._count = count
+ self._funcs = funcs
+ self._operation = operation
+ self._expected = expected
+ def __enter__(self):
+ cmd = "TEST_ALLOC_FAIL %d:%s" % (self._count, self._funcs)
+ if "OK" not in self._dev.request(cmd):
+ raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+ def __exit__(self, type, value, traceback):
+ if type is None:
+ raise Exception("%s succeeded during out-of-memory" % self._operation)
+ if type == dbus.exceptions.DBusException and self._expected in str(value):
+ return True
+ if self._dev.request("GET_ALLOC_FAIL") != "0:%s" % self._funcs:
+ raise Exception("%s did not trigger allocation failure" % self._operation)
+ return False
+
+def start_ap(ap, ssid="test-wps",
+ ap_uuid="27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"):
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "ap_pin": "12345670", "uuid": ap_uuid}
+ return hostapd.add_ap(ap, params)
+
+def test_dbus_getall(dev, apdev):
+ """D-Bus GetAll"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ props = wpas_obj.GetAll(WPAS_DBUS_SERVICE,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ logger.debug("GetAll(fi.w1.wpa.supplicant1, /fi/w1/wpa_supplicant1) ==> " + str(props))
+
+ props = if_obj.GetAll(WPAS_DBUS_IFACE,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ logger.debug("GetAll(%s, %s): %s" % (WPAS_DBUS_IFACE, path, str(props)))
+
+ props = if_obj.GetAll(WPAS_DBUS_IFACE_WPS,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ logger.debug("GetAll(%s, %s): %s" % (WPAS_DBUS_IFACE_WPS, path, str(props)))
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, 'BSSs',
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 0:
+ raise Exception("Unexpected BSSs entry: " + str(res))
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, 'Networks',
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 0:
+ raise Exception("Unexpected Networks entry: " + str(res))
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(bssid, freq=2412)
+ id = dev[0].add_network()
+ dev[0].set_network(id, "disabled", "0")
+ dev[0].set_network_quoted(id, "ssid", "test")
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, 'BSSs',
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 1:
+ raise Exception("Missing BSSs entry: " + str(res))
+ bss_obj = bus.get_object(WPAS_DBUS_SERVICE, res[0])
+ props = bss_obj.GetAll(WPAS_DBUS_BSS, dbus_interface=dbus.PROPERTIES_IFACE)
+ logger.debug("GetAll(%s, %s): %s" % (WPAS_DBUS_BSS, res[0], str(props)))
+ bssid_str = ''
+ for item in props['BSSID']:
+ if len(bssid_str) > 0:
+ bssid_str += ':'
+ bssid_str += '%02x' % item
+ if bssid_str != bssid:
+ raise Exception("Unexpected BSSID in BSSs entry")
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, 'Networks',
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 1:
+ raise Exception("Missing Networks entry: " + str(res))
+ net_obj = bus.get_object(WPAS_DBUS_SERVICE, res[0])
+ props = net_obj.GetAll(WPAS_DBUS_NETWORK,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ logger.debug("GetAll(%s, %s): %s" % (WPAS_DBUS_NETWORK, res[0], str(props)))
+ ssid = props['Properties']['ssid']
+ if ssid != '"test"':
+ raise Exception("Unexpected SSID in network entry")
+
+def test_dbus_getall_oom(dev, apdev):
+ """D-Bus GetAll wpa_config_get_all() OOM"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ id = dev[0].add_network()
+ dev[0].set_network(id, "disabled", "0")
+ dev[0].set_network_quoted(id, "ssid", "test")
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, 'Networks',
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 1:
+ raise Exception("Missing Networks entry: " + str(res))
+ net_obj = bus.get_object(WPAS_DBUS_SERVICE, res[0])
+ for i in range(1, 50):
+ with alloc_fail(dev[0], i, "wpa_config_get_all"):
+ try:
+ props = net_obj.GetAll(WPAS_DBUS_NETWORK,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ except dbus.exceptions.DBusException as e:
+ pass
+
+def dbus_get(dbus, wpas_obj, prop, expect=None, byte_arrays=False):
+ val = wpas_obj.Get(WPAS_DBUS_SERVICE, prop,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=byte_arrays)
+ if expect is not None and val != expect:
+ raise Exception("Unexpected %s: %s (expected: %s)" %
+ (prop, str(val), str(expect)))
+ return val
+
+def dbus_set(dbus, wpas_obj, prop, val):
+ wpas_obj.Set(WPAS_DBUS_SERVICE, prop, val,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_properties(dev, apdev):
+ """D-Bus Get/Set fi.w1.wpa_supplicant1 properties"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ dbus_get(dbus, wpas_obj, "DebugLevel", expect="msgdump")
+ dbus_set(dbus, wpas_obj, "DebugLevel", "debug")
+ dbus_get(dbus, wpas_obj, "DebugLevel", expect="debug")
+ for (val, err) in [(3, "Error.Failed: wrong property type"),
+ ("foo", "Error.Failed: wrong debug level value")]:
+ try:
+ dbus_set(dbus, wpas_obj, "DebugLevel", val)
+ raise Exception("Invalid DebugLevel value accepted: " + str(val))
+ except dbus.exceptions.DBusException as e:
+ if err not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+ dbus_set(dbus, wpas_obj, "DebugLevel", "msgdump")
+ dbus_get(dbus, wpas_obj, "DebugLevel", expect="msgdump")
+
+ dbus_get(dbus, wpas_obj, "DebugTimestamp", expect=True)
+ dbus_set(dbus, wpas_obj, "DebugTimestamp", False)
+ dbus_get(dbus, wpas_obj, "DebugTimestamp", expect=False)
+ try:
+ dbus_set(dbus, wpas_obj, "DebugTimestamp", "foo")
+ raise Exception("Invalid DebugTimestamp value accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: wrong property type" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+ dbus_set(dbus, wpas_obj, "DebugTimestamp", True)
+ dbus_get(dbus, wpas_obj, "DebugTimestamp", expect=True)
+
+ dbus_get(dbus, wpas_obj, "DebugShowKeys", expect=True)
+ dbus_set(dbus, wpas_obj, "DebugShowKeys", False)
+ dbus_get(dbus, wpas_obj, "DebugShowKeys", expect=False)
+ try:
+ dbus_set(dbus, wpas_obj, "DebugShowKeys", "foo")
+ raise Exception("Invalid DebugShowKeys value accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: wrong property type" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+ dbus_set(dbus, wpas_obj, "DebugShowKeys", True)
+ dbus_get(dbus, wpas_obj, "DebugShowKeys", expect=True)
+
+ res = dbus_get(dbus, wpas_obj, "Interfaces")
+ if len(res) != 1:
+ raise Exception("Unexpected Interfaces value: " + str(res))
+
+ res = dbus_get(dbus, wpas_obj, "EapMethods")
+ if len(res) < 5 or "TTLS" not in res:
+ raise Exception("Unexpected EapMethods value: " + str(res))
+
+ res = dbus_get(dbus, wpas_obj, "Capabilities")
+ if len(res) < 2 or "p2p" not in res:
+ raise Exception("Unexpected Capabilities value: " + str(res))
+
+ dbus_get(dbus, wpas_obj, "WFDIEs", byte_arrays=True)
+ val = binascii.unhexlify("010006020304050608")
+ dbus_set(dbus, wpas_obj, "WFDIEs", dbus.ByteArray(val))
+ res = dbus_get(dbus, wpas_obj, "WFDIEs", byte_arrays=True)
+ if val != res:
+ raise Exception("WFDIEs value changed")
+ try:
+ dbus_set(dbus, wpas_obj, "WFDIEs", dbus.ByteArray(b'\x00'))
+ raise Exception("Invalid WFDIEs value accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+ dbus_set(dbus, wpas_obj, "WFDIEs", dbus.ByteArray(b''))
+ dbus_set(dbus, wpas_obj, "WFDIEs", dbus.ByteArray(val))
+ dbus_set(dbus, wpas_obj, "WFDIEs", dbus.ByteArray(b''))
+ res = dbus_get(dbus, wpas_obj, "WFDIEs", byte_arrays=True)
+ if len(res) != 0:
+ raise Exception("WFDIEs not cleared properly")
+
+ res = dbus_get(dbus, wpas_obj, "EapMethods")
+ try:
+ dbus_set(dbus, wpas_obj, "EapMethods", res)
+ raise Exception("Invalid Set accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs: Property is read-only" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ try:
+ wpas_obj.SetFoo(WPAS_DBUS_SERVICE, "DebugShowKeys", True,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Unknown method accepted")
+ except dbus.exceptions.DBusException as e:
+ if "UnknownMethod" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ try:
+ wpas_obj.Get("foo", "DebugShowKeys",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Get accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs: No such property" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ test_obj = bus.get_object(WPAS_DBUS_SERVICE, WPAS_DBUS_PATH,
+ introspect=False)
+ try:
+ test_obj.Get(123, "DebugShowKeys",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Get accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs: Invalid arguments" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+ try:
+ test_obj.Get(WPAS_DBUS_SERVICE, 123,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Get accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs: Invalid arguments" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ try:
+ wpas_obj.Set(WPAS_DBUS_SERVICE, "WFDIEs",
+ dbus.ByteArray(b'', variant_level=2),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs: invalid message format" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+def test_dbus_set_global_properties(dev, apdev):
+ """D-Bus Get/Set fi.w1.wpa_supplicant1 interface global properties"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ dev[0].set("model_name", "")
+ props = [('Okc', '0', '1'), ('ModelName', '', 'blahblahblah')]
+
+ for p in props:
+ res = if_obj.Get(WPAS_DBUS_IFACE, p[0],
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != p[1]:
+ raise Exception("Unexpected " + p[0] + " value: " + str(res))
+
+ if_obj.Set(WPAS_DBUS_IFACE, p[0], p[2],
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, p[0],
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != p[2]:
+ raise Exception("Unexpected " + p[0] + " value after set: " + str(res))
+ dev[0].set("model_name", "")
+
+def test_dbus_invalid_method(dev, apdev):
+ """D-Bus invalid method"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+ try:
+ wps.Foo()
+ raise Exception("Unknown method accepted")
+ except dbus.exceptions.DBusException as e:
+ if "UnknownMethod" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ test_obj = bus.get_object(WPAS_DBUS_SERVICE, path, introspect=False)
+ test_wps = dbus.Interface(test_obj, WPAS_DBUS_IFACE_WPS)
+ try:
+ test_wps.Start(123)
+ raise Exception("WPS.Start with incorrect signature accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs: Invalid arg" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+def test_dbus_get_set_wps(dev, apdev):
+ """D-Bus Get/Set for WPS properties"""
+ try:
+ _test_dbus_get_set_wps(dev, apdev)
+ finally:
+ dev[0].request("SET wps_cred_processing 0")
+ dev[0].request("SET config_methods display keypad virtual_display nfc_interface p2ps")
+ dev[0].set("device_name", "Device A")
+ dev[0].set("manufacturer", "")
+ dev[0].set("model_name", "")
+ dev[0].set("model_number", "")
+ dev[0].set("serial_number", "")
+ dev[0].set("device_type", "0-00000000-0")
+
+def _test_dbus_get_set_wps(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ if_obj.Get(WPAS_DBUS_IFACE_WPS, "ConfigMethods",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ val = "display keypad virtual_display nfc_interface"
+ dev[0].request("SET config_methods " + val)
+
+ config = if_obj.Get(WPAS_DBUS_IFACE_WPS, "ConfigMethods",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if config != val:
+ raise Exception("Unexpected Get(ConfigMethods) result: " + config)
+
+ val2 = "push_button display"
+ if_obj.Set(WPAS_DBUS_IFACE_WPS, "ConfigMethods", val2,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ config = if_obj.Get(WPAS_DBUS_IFACE_WPS, "ConfigMethods",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if config != val2:
+ raise Exception("Unexpected Get(ConfigMethods) result after Set: " + config)
+
+ dev[0].request("SET config_methods " + val)
+
+ for i in range(3):
+ dev[0].request("SET wps_cred_processing " + str(i))
+ val = if_obj.Get(WPAS_DBUS_IFACE_WPS, "ProcessCredentials",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ expected_val = False if i == 1 else True
+ if val != expected_val:
+ raise Exception("Unexpected Get(ProcessCredentials) result({}): {}".format(i, val))
+
+ tests = [("device_name", "DeviceName"),
+ ("manufacturer", "Manufacturer"),
+ ("model_name", "ModelName"),
+ ("model_number", "ModelNumber"),
+ ("serial_number", "SerialNumber")]
+
+ for f1, f2 in tests:
+ val2 = "test-value-test"
+ dev[0].set(f1, val2)
+ val = if_obj.Get(WPAS_DBUS_IFACE_WPS, f2,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if val != val2:
+ raise Exception("Get(%s) returned unexpected value" % f2)
+ val2 = "TEST-value"
+ if_obj.Set(WPAS_DBUS_IFACE_WPS, f2, val2,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ val = if_obj.Get(WPAS_DBUS_IFACE_WPS, f2,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if val != val2:
+ raise Exception("Get(%s) returned unexpected value after Set" % f2)
+
+ dev[0].set("device_type", "5-0050F204-1")
+ val = if_obj.Get(WPAS_DBUS_IFACE_WPS, "DeviceType",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if val[0] != 0x00 or val[1] != 0x05 != val[2] != 0x00 or val[3] != 0x50 or val[4] != 0xf2 or val[5] != 0x04 or val[6] != 0x00 or val[7] != 0x01:
+ raise Exception("DeviceType mismatch")
+ if_obj.Set(WPAS_DBUS_IFACE_WPS, "DeviceType", val,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ val = if_obj.Get(WPAS_DBUS_IFACE_WPS, "DeviceType",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if val[0] != 0x00 or val[1] != 0x05 != val[2] != 0x00 or val[3] != 0x50 or val[4] != 0xf2 or val[5] != 0x04 or val[6] != 0x00 or val[7] != 0x01:
+ raise Exception("DeviceType mismatch after Set")
+
+ val2 = b'\x01\x02\x03\x04\x05\x06\x07\x08'
+ if_obj.Set(WPAS_DBUS_IFACE_WPS, "DeviceType", dbus.ByteArray(val2),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ val = if_obj.Get(WPAS_DBUS_IFACE_WPS, "DeviceType",
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ if val != val2:
+ raise Exception("DeviceType mismatch after Set (2)")
+
+ class TestDbusGetSet(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.signal_received = False
+ self.signal_received_deprecated = False
+ self.sets_done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_sets)
+ gobject.timeout_add(1000, self.timeout)
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE_WPS,
+ "PropertiesChanged")
+ self.add_signal(self.propertiesChanged2, dbus.PROPERTIES_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def propertiesChanged(self, properties):
+ logger.debug("PropertiesChanged: " + str(properties))
+ if "ProcessCredentials" in properties:
+ self.signal_received_deprecated = True
+ if self.sets_done and self.signal_received:
+ self.loop.quit()
+
+ def propertiesChanged2(self, interface_name, changed_properties,
+ invalidated_properties):
+ logger.debug("propertiesChanged2: interface_name=%s changed_properties=%s invalidated_properties=%s" % (interface_name, str(changed_properties), str(invalidated_properties)))
+ if interface_name != WPAS_DBUS_IFACE_WPS:
+ return
+ if "ProcessCredentials" in changed_properties:
+ self.signal_received = True
+ if self.sets_done and self.signal_received_deprecated:
+ self.loop.quit()
+
+ def run_sets(self, *args):
+ logger.debug("run_sets")
+ if_obj.Set(WPAS_DBUS_IFACE_WPS, "ProcessCredentials",
+ dbus.Boolean(1),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if if_obj.Get(WPAS_DBUS_IFACE_WPS, "ProcessCredentials",
+ dbus_interface=dbus.PROPERTIES_IFACE) != True:
+ raise Exception("Unexpected Get(ProcessCredentials) result after Set")
+ if_obj.Set(WPAS_DBUS_IFACE_WPS, "ProcessCredentials",
+ dbus.Boolean(0),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if if_obj.Get(WPAS_DBUS_IFACE_WPS, "ProcessCredentials",
+ dbus_interface=dbus.PROPERTIES_IFACE) != False:
+ raise Exception("Unexpected Get(ProcessCredentials) result after Set")
+
+ self.dbus_sets_done = True
+ return False
+
+ def success(self):
+ return self.signal_received and self.signal_received_deprecated
+
+ with TestDbusGetSet(bus) as t:
+ if not t.success():
+ raise Exception("No signal received for ProcessCredentials change")
+
+def test_dbus_wps_invalid(dev, apdev):
+ """D-Bus invaldi WPS operation"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+ failures = [{'Role': 'foo', 'Type': 'pbc'},
+ {'Role': 123, 'Type': 'pbc'},
+ {'Type': 'pbc'},
+ {'Role': 'enrollee'},
+ {'Role': 'registrar'},
+ {'Role': 'enrollee', 'Type': 123},
+ {'Role': 'enrollee', 'Type': 'foo'},
+ {'Role': 'enrollee', 'Type': 'pbc',
+ 'Bssid': '02:33:44:55:66:77'},
+ {'Role': 'enrollee', 'Type': 'pin', 'Pin': 123},
+ {'Role': 'enrollee', 'Type': 'pbc',
+ 'Bssid': dbus.ByteArray(b'12345')},
+ {'Role': 'enrollee', 'Type': 'pbc',
+ 'P2PDeviceAddress': 12345},
+ {'Role': 'enrollee', 'Type': 'pbc',
+ 'P2PDeviceAddress': dbus.ByteArray(b'12345')},
+ {'Role': 'enrollee', 'Type': 'pbc', 'Foo': 'bar'}]
+ for args in failures:
+ try:
+ wps.Start(args)
+ raise Exception("Invalid WPS.Start() arguments accepted: " + str(args))
+ except dbus.exceptions.DBusException as e:
+ if not str(e).startswith("fi.w1.wpa_supplicant1.InvalidArgs"):
+ raise Exception("Unexpected error message: " + str(e))
+
+def test_dbus_wps_oom(dev, apdev):
+ """D-Bus WPS operation (OOM)"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+ with alloc_fail_dbus(dev[0], 1, "=wpas_dbus_getter_state", "Get"):
+ if_obj.Get(WPAS_DBUS_IFACE, "State",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(bssid, freq=2412)
+
+ time.sleep(0.05)
+ for i in range(1, 3):
+ with alloc_fail_dbus(dev[0], i, "=wpas_dbus_getter_bsss", "Get"):
+ if_obj.Get(WPAS_DBUS_IFACE, "BSSs",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, 'BSSs',
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ bss_obj = bus.get_object(WPAS_DBUS_SERVICE, res[0])
+ with alloc_fail_dbus(dev[0], 1, "=wpas_dbus_getter_bss_rates", "Get"):
+ bss_obj.Get(WPAS_DBUS_BSS, "Rates",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ with alloc_fail(dev[0], 1,
+ "wpa_bss_get_bit_rates;wpas_dbus_getter_bss_rates"):
+ try:
+ bss_obj.Get(WPAS_DBUS_BSS, "Rates",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ except dbus.exceptions.DBusException as e:
+ pass
+
+ id = dev[0].add_network()
+ dev[0].set_network(id, "disabled", "0")
+ dev[0].set_network_quoted(id, "ssid", "test")
+
+ for i in range(1, 3):
+ with alloc_fail_dbus(dev[0], i, "=wpas_dbus_getter_networks", "Get"):
+ if_obj.Get(WPAS_DBUS_IFACE, "Networks",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ with alloc_fail_dbus(dev[0], 1, "wpas_dbus_getter_interfaces", "Get"):
+ dbus_get(dbus, wpas_obj, "Interfaces")
+
+ for i in range(1, 6):
+ with alloc_fail_dbus(dev[0], i, "=eap_get_names_as_string_array;wpas_dbus_getter_eap_methods", "Get"):
+ dbus_get(dbus, wpas_obj, "EapMethods")
+
+ with alloc_fail_dbus(dev[0], 1, "wpas_dbus_setter_config_methods", "Set",
+ expected="Error.Failed: Failed to set property"):
+ val2 = "push_button display"
+ if_obj.Set(WPAS_DBUS_IFACE_WPS, "ConfigMethods", val2,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ with alloc_fail_dbus(dev[0], 1, "=wpa_config_add_network;wpas_dbus_handler_wps_start",
+ "WPS.Start",
+ expected="UnknownError: WPS start failed"):
+ wps.Start({'Role': 'enrollee', 'Type': 'pin', 'Pin': '12345670'})
+
+def test_dbus_wps_pbc(dev, apdev):
+ """D-Bus WPS/PBC operation and signals"""
+ try:
+ _test_dbus_wps_pbc(dev, apdev)
+ finally:
+ dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_wps_pbc(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+ hapd = start_ap(apdev[0])
+ hapd.request("WPS_PBC")
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("SET wps_cred_processing 2")
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, 'BSSs',
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 1:
+ raise Exception("Missing BSSs entry: " + str(res))
+ bss_obj = bus.get_object(WPAS_DBUS_SERVICE, res[0])
+ props = bss_obj.GetAll(WPAS_DBUS_BSS, dbus_interface=dbus.PROPERTIES_IFACE)
+ logger.debug("GetAll(%s, %s): %s" % (WPAS_DBUS_BSS, res[0], str(props)))
+ if 'WPS' not in props:
+ raise Exception("No WPS information in the BSS entry")
+ if 'Type' not in props['WPS']:
+ raise Exception("No Type field in the WPS dictionary")
+ if props['WPS']['Type'] != 'pbc':
+ raise Exception("Unexpected WPS Type: " + props['WPS']['Type'])
+
+ class TestDbusWps(TestDbus):
+ def __init__(self, bus, wps):
+ TestDbus.__init__(self, bus)
+ self.success_seen = False
+ self.credentials_received = False
+ self.wps = wps
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.start_pbc)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+ self.add_signal(self.credentials, WPAS_DBUS_IFACE_WPS,
+ "Credentials")
+ self.loop.run()
+ return self
+
+ def wpsEvent(self, name, args):
+ logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+ if name == "success":
+ self.success_seen = True
+ if self.credentials_received:
+ self.loop.quit()
+
+ def credentials(self, args):
+ logger.debug("credentials: " + str(args))
+ self.credentials_received = True
+ if self.success_seen:
+ self.loop.quit()
+
+ def start_pbc(self, *args):
+ logger.debug("start_pbc")
+ self.wps.Start({'Role': 'enrollee', 'Type': 'pbc'})
+ return False
+
+ def success(self):
+ return self.success_seen and self.credentials_received
+
+ with TestDbusWps(bus, wps) as t:
+ if not t.success():
+ raise Exception("Failure in D-Bus operations")
+
+ dev[0].wait_connected(timeout=10)
+ dev[0].request("DISCONNECT")
+ hapd.disable()
+ dev[0].flush_scan_cache()
+
+def test_dbus_wps_pbc_overlap(dev, apdev):
+ """D-Bus WPS/PBC operation and signal for PBC overlap"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+ hapd = start_ap(apdev[0])
+ hapd2 = start_ap(apdev[1], ssid="test-wps2",
+ ap_uuid="27ea801a-9e5c-4e73-bd82-f89cbcd10d7f")
+ hapd.request("WPS_PBC")
+ hapd2.request("WPS_PBC")
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(bssid, freq="2412")
+ bssid2 = apdev[1]['bssid']
+ dev[0].scan_for_bss(bssid2, freq="2412")
+
+ class TestDbusWps(TestDbus):
+ def __init__(self, bus, wps):
+ TestDbus.__init__(self, bus)
+ self.overlap_seen = False
+ self.wps = wps
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.start_pbc)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+ self.loop.run()
+ return self
+
+ def wpsEvent(self, name, args):
+ logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+ if name == "pbc-overlap":
+ self.overlap_seen = True
+ self.loop.quit()
+
+ def start_pbc(self, *args):
+ logger.debug("start_pbc")
+ self.wps.Start({'Role': 'enrollee', 'Type': 'pbc'})
+ return False
+
+ def success(self):
+ return self.overlap_seen
+
+ with TestDbusWps(bus, wps) as t:
+ if not t.success():
+ raise Exception("Failure in D-Bus operations")
+
+ dev[0].request("WPS_CANCEL")
+ dev[0].request("DISCONNECT")
+ hapd.disable()
+ dev[0].flush_scan_cache()
+
+def test_dbus_wps_pin(dev, apdev):
+ """D-Bus WPS/PIN operation and signals"""
+ try:
+ _test_dbus_wps_pin(dev, apdev)
+ finally:
+ dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_wps_pin(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+ hapd = start_ap(apdev[0])
+ hapd.request("WPS_PIN any 12345670")
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("SET wps_cred_processing 2")
+
+ class TestDbusWps(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.success_seen = False
+ self.credentials_received = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.start_pin)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+ self.add_signal(self.credentials, WPAS_DBUS_IFACE_WPS,
+ "Credentials")
+ self.loop.run()
+ return self
+
+ def wpsEvent(self, name, args):
+ logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+ if name == "success":
+ self.success_seen = True
+ if self.credentials_received:
+ self.loop.quit()
+
+ def credentials(self, args):
+ logger.debug("credentials: " + str(args))
+ self.credentials_received = True
+ if self.success_seen:
+ self.loop.quit()
+
+ def start_pin(self, *args):
+ logger.debug("start_pin")
+ bssid_ay = dbus.ByteArray(binascii.unhexlify(bssid.replace(':', '').encode()))
+ wps.Start({'Role': 'enrollee', 'Type': 'pin', 'Pin': '12345670',
+ 'Bssid': bssid_ay})
+ return False
+
+ def success(self):
+ return self.success_seen and self.credentials_received
+
+ with TestDbusWps(bus) as t:
+ if not t.success():
+ raise Exception("Failure in D-Bus operations")
+
+ dev[0].wait_connected(timeout=10)
+
+def test_dbus_wps_pin2(dev, apdev):
+ """D-Bus WPS/PIN operation and signals (PIN from wpa_supplicant)"""
+ try:
+ _test_dbus_wps_pin2(dev, apdev)
+ finally:
+ dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_wps_pin2(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("SET wps_cred_processing 2")
+
+ class TestDbusWps(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.success_seen = False
+ self.failed = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.start_pin)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+ self.add_signal(self.credentials, WPAS_DBUS_IFACE_WPS,
+ "Credentials")
+ self.loop.run()
+ return self
+
+ def wpsEvent(self, name, args):
+ logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+ if name == "success":
+ self.success_seen = True
+ if self.credentials_received:
+ self.loop.quit()
+
+ def credentials(self, args):
+ logger.debug("credentials: " + str(args))
+ self.credentials_received = True
+ if self.success_seen:
+ self.loop.quit()
+
+ def start_pin(self, *args):
+ logger.debug("start_pin")
+ bssid_ay = dbus.ByteArray(binascii.unhexlify(bssid.replace(':', '').encode()))
+ res = wps.Start({'Role': 'enrollee', 'Type': 'pin',
+ 'Bssid': bssid_ay})
+ pin = res['Pin']
+ h = hostapd.Hostapd(apdev[0]['ifname'])
+ h.request("WPS_PIN any " + pin)
+ return False
+
+ def success(self):
+ return self.success_seen and self.credentials_received
+
+ with TestDbusWps(bus) as t:
+ if not t.success():
+ raise Exception("Failure in D-Bus operations")
+
+ dev[0].wait_connected(timeout=10)
+
+def test_dbus_wps_pin_m2d(dev, apdev):
+ """D-Bus WPS/PIN operation and signals with M2D"""
+ try:
+ _test_dbus_wps_pin_m2d(dev, apdev)
+ finally:
+ dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_wps_pin_m2d(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("SET wps_cred_processing 2")
+
+ class TestDbusWps(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.success_seen = False
+ self.credentials_received = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.start_pin)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+ self.add_signal(self.credentials, WPAS_DBUS_IFACE_WPS,
+ "Credentials")
+ self.loop.run()
+ return self
+
+ def wpsEvent(self, name, args):
+ logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+ if name == "success":
+ self.success_seen = True
+ if self.credentials_received:
+ self.loop.quit()
+ elif name == "m2d":
+ h = hostapd.Hostapd(apdev[0]['ifname'])
+ h.request("WPS_PIN any 12345670")
+
+ def credentials(self, args):
+ logger.debug("credentials: " + str(args))
+ self.credentials_received = True
+ if self.success_seen:
+ self.loop.quit()
+
+ def start_pin(self, *args):
+ logger.debug("start_pin")
+ bssid_ay = dbus.ByteArray(binascii.unhexlify(bssid.replace(':', '').encode()))
+ wps.Start({'Role': 'enrollee', 'Type': 'pin', 'Pin': '12345670',
+ 'Bssid': bssid_ay})
+ return False
+
+ def success(self):
+ return self.success_seen and self.credentials_received
+
+ with TestDbusWps(bus) as t:
+ if not t.success():
+ raise Exception("Failure in D-Bus operations")
+
+ dev[0].wait_connected(timeout=10)
+
+def test_dbus_wps_reg(dev, apdev):
+ """D-Bus WPS/Registrar operation and signals"""
+ try:
+ _test_dbus_wps_reg(dev, apdev)
+ finally:
+ dev[0].request("SET wps_cred_processing 0")
+
+def _test_dbus_wps_reg(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+ hapd = start_ap(apdev[0])
+ hapd.request("WPS_PIN any 12345670")
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].request("SET wps_cred_processing 2")
+
+ class TestDbusWps(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.credentials_received = False
+
+ def __enter__(self):
+ gobject.timeout_add(100, self.start_reg)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.wpsEvent, WPAS_DBUS_IFACE_WPS, "Event")
+ self.add_signal(self.credentials, WPAS_DBUS_IFACE_WPS,
+ "Credentials")
+ self.loop.run()
+ return self
+
+ def wpsEvent(self, name, args):
+ logger.debug("wpsEvent: %s args='%s'" % (name, str(args)))
+
+ def credentials(self, args):
+ logger.debug("credentials: " + str(args))
+ self.credentials_received = True
+ self.loop.quit()
+
+ def start_reg(self, *args):
+ logger.debug("start_reg")
+ bssid_ay = dbus.ByteArray(binascii.unhexlify(bssid.replace(':', '').encode()))
+ wps.Start({'Role': 'registrar', 'Type': 'pin',
+ 'Pin': '12345670', 'Bssid': bssid_ay})
+ return False
+
+ def success(self):
+ return self.credentials_received
+
+ with TestDbusWps(bus) as t:
+ if not t.success():
+ raise Exception("Failure in D-Bus operations")
+
+ dev[0].wait_connected(timeout=10)
+
+def test_dbus_wps_cancel(dev, apdev):
+ """D-Bus WPS Cancel operation"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wps = dbus.Interface(if_obj, WPAS_DBUS_IFACE_WPS)
+
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ wps.Cancel()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ bssid_ay = dbus.ByteArray(binascii.unhexlify(bssid.replace(':', '').encode()))
+ wps.Start({'Role': 'enrollee', 'Type': 'pin', 'Pin': '12345670',
+ 'Bssid': bssid_ay})
+ wps.Cancel()
+ dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 1)
+
+def test_dbus_scan_invalid(dev, apdev):
+ """D-Bus invalid scan method"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ tests = [({}, "InvalidArgs"),
+ ({'Type': 123}, "InvalidArgs"),
+ ({'Type': 'foo'}, "InvalidArgs"),
+ ({'Type': 'active', 'Foo': 'bar'}, "InvalidArgs"),
+ ({'Type': 'active', 'SSIDs': 'foo'}, "InvalidArgs"),
+ ({'Type': 'active', 'SSIDs': ['foo']}, "InvalidArgs"),
+ ({'Type': 'active',
+ 'SSIDs': [dbus.ByteArray(b"1"), dbus.ByteArray(b"2"),
+ dbus.ByteArray(b"3"), dbus.ByteArray(b"4"),
+ dbus.ByteArray(b"5"), dbus.ByteArray(b"6"),
+ dbus.ByteArray(b"7"), dbus.ByteArray(b"8"),
+ dbus.ByteArray(b"9"), dbus.ByteArray(b"10"),
+ dbus.ByteArray(b"11"), dbus.ByteArray(b"12"),
+ dbus.ByteArray(b"13"), dbus.ByteArray(b"14"),
+ dbus.ByteArray(b"15"), dbus.ByteArray(b"16"),
+ dbus.ByteArray(b"17")]},
+ "InvalidArgs"),
+ ({'Type': 'active',
+ 'SSIDs': [dbus.ByteArray(b"1234567890abcdef1234567890abcdef1")]},
+ "InvalidArgs"),
+ ({'Type': 'active', 'IEs': 'foo'}, "InvalidArgs"),
+ ({'Type': 'active', 'IEs': ['foo']}, "InvalidArgs"),
+ ({'Type': 'active', 'Channels': 2412}, "InvalidArgs"),
+ ({'Type': 'active', 'Channels': [2412]}, "InvalidArgs"),
+ ({'Type': 'active',
+ 'Channels': [(dbus.Int32(2412), dbus.UInt32(20))]},
+ "InvalidArgs"),
+ ({'Type': 'active',
+ 'Channels': [(dbus.UInt32(2412), dbus.Int32(20))]},
+ "InvalidArgs"),
+ ({'Type': 'active', 'AllowRoam': "yes"}, "InvalidArgs"),
+ ({'Type': 'passive', 'IEs': [dbus.ByteArray(b"\xdd\x00")]},
+ "InvalidArgs"),
+ ({'Type': 'passive', 'SSIDs': [dbus.ByteArray(b"foo")]},
+ "InvalidArgs")]
+ for (t, err) in tests:
+ try:
+ iface.Scan(t)
+ raise Exception("Invalid Scan() arguments accepted: " + str(t))
+ except dbus.exceptions.DBusException as e:
+ if err not in str(e):
+ raise Exception("Unexpected error message for invalid Scan(%s): %s" % (str(t), str(e)))
+
+def test_dbus_scan_oom(dev, apdev):
+ """D-Bus scan method and OOM"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ with alloc_fail_dbus(dev[0], 1,
+ "wpa_scan_clone_params;wpas_dbus_handler_scan",
+ "Scan", expected="ScanError: Scan request rejected"):
+ iface.Scan({'Type': 'passive',
+ 'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+
+ with alloc_fail_dbus(dev[0], 1,
+ "=wpas_dbus_get_scan_channels;wpas_dbus_handler_scan",
+ "Scan"):
+ iface.Scan({'Type': 'passive',
+ 'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+
+ with alloc_fail_dbus(dev[0], 1,
+ "=wpas_dbus_get_scan_ies;wpas_dbus_handler_scan",
+ "Scan"):
+ iface.Scan({'Type': 'active',
+ 'IEs': [dbus.ByteArray(b"\xdd\x00")],
+ 'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+
+ with alloc_fail_dbus(dev[0], 1,
+ "=wpas_dbus_get_scan_ssids;wpas_dbus_handler_scan",
+ "Scan"):
+ iface.Scan({'Type': 'active',
+ 'SSIDs': [dbus.ByteArray(b"open"),
+ dbus.ByteArray()],
+ 'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+
+def test_dbus_scan(dev, apdev):
+ """D-Bus scan and related signals"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+
+ class TestDbusScan(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.scan_completed = 0
+ self.bss_added = False
+ self.fail_reason = None
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_scan)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.scanDone, WPAS_DBUS_IFACE, "ScanDone")
+ self.add_signal(self.bssAdded, WPAS_DBUS_IFACE, "BSSAdded")
+ self.add_signal(self.bssRemoved, WPAS_DBUS_IFACE, "BSSRemoved")
+ self.loop.run()
+ return self
+
+ def scanDone(self, success):
+ logger.debug("scanDone: success=%s" % success)
+ self.scan_completed += 1
+ if self.scan_completed == 1:
+ iface.Scan({'Type': 'passive',
+ 'AllowRoam': True,
+ 'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+ elif self.scan_completed == 2:
+ iface.Scan({'Type': 'passive',
+ 'AllowRoam': False})
+ elif self.bss_added and self.scan_completed == 3:
+ self.loop.quit()
+
+ def bssAdded(self, bss, properties):
+ logger.debug("bssAdded: %s" % bss)
+ logger.debug(str(properties))
+ if 'WPS' in properties:
+ if 'Type' in properties['WPS']:
+ self.fail_reason = "Unexpected WPS dictionary entry in non-WPS BSS"
+ self.loop.quit()
+ self.bss_added = True
+ if self.scan_completed == 3:
+ self.loop.quit()
+
+ def bssRemoved(self, bss):
+ logger.debug("bssRemoved: %s" % bss)
+
+ def run_scan(self, *args):
+ logger.debug("run_scan")
+ iface.Scan({'Type': 'active',
+ 'SSIDs': [dbus.ByteArray(b"open"),
+ dbus.ByteArray()],
+ 'IEs': [dbus.ByteArray(b"\xdd\x00"),
+ dbus.ByteArray()],
+ 'AllowRoam': False,
+ 'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+ return False
+
+ def success(self):
+ return self.scan_completed == 3 and self.bss_added
+
+ with TestDbusScan(bus) as t:
+ if t.fail_reason:
+ raise Exception(t.fail_reason)
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, "BSSs",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) < 1:
+ raise Exception("Scan result not in BSSs property")
+ iface.FlushBSS(0)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "BSSs",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 0:
+ raise Exception("FlushBSS() did not remove scan results from BSSs property")
+ iface.FlushBSS(1)
+
+def test_dbus_scan_rand(dev, apdev):
+ """D-Bus MACAddressRandomizationMask property Get/Set"""
+ try:
+ run_dbus_scan_rand(dev, apdev)
+ finally:
+ dev[0].request("MAC_RAND_SCAN all enable=0")
+
+def run_dbus_scan_rand(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, "MACAddressRandomizationMask",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 0:
+ logger.info(str(res))
+ raise Exception("Unexpected initial MACAddressRandomizationMask value")
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "MACAddressRandomizationMask", "foo",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs: invalid message format" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "MACAddressRandomizationMask",
+ {"foo": "bar"},
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set accepted")
+ except dbus.exceptions.DBusException as e:
+ if "wpas_dbus_setter_mac_address_randomization_mask: mask was not a byte array" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "MACAddressRandomizationMask",
+ {"foo": dbus.ByteArray(b'123456')},
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set accepted")
+ except dbus.exceptions.DBusException as e:
+ if 'wpas_dbus_setter_mac_address_randomization_mask: bad scan type "foo"' not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "MACAddressRandomizationMask",
+ {"scan": dbus.ByteArray(b'12345')},
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set accepted")
+ except dbus.exceptions.DBusException as e:
+ if 'wpas_dbus_setter_mac_address_randomization_mask: malformed MAC mask given' not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ if_obj.Set(WPAS_DBUS_IFACE, "MACAddressRandomizationMask",
+ {"scan": dbus.ByteArray(b'123456')},
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "MACAddressRandomizationMask",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 1:
+ logger.info(str(res))
+ raise Exception("Unexpected MACAddressRandomizationMask value")
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "MACAddressRandomizationMask",
+ {"scan": dbus.ByteArray(b'123456'),
+ "sched_scan": dbus.ByteArray(b'987654')},
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ except dbus.exceptions.DBusException as e:
+ # sched_scan is unlikely to be supported
+ pass
+
+ if_obj.Set(WPAS_DBUS_IFACE, "MACAddressRandomizationMask",
+ dbus.Dictionary({}, signature='sv'),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "MACAddressRandomizationMask",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 0:
+ logger.info(str(res))
+ raise Exception("Unexpected MACAddressRandomizationMask value")
+
+def test_dbus_scan_busy(dev, apdev):
+ """D-Bus scan trigger rejection when busy with previous scan"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ if "OK" not in dev[0].request("SCAN freq=2412-2462"):
+ raise Exception("Failed to start scan")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], 15)
+ if ev is None:
+ raise Exception("Scan start timed out")
+
+ try:
+ iface.Scan({'Type': 'active', 'AllowRoam': False})
+ raise Exception("Scan() accepted when busy")
+ except dbus.exceptions.DBusException as e:
+ if "ScanError: Scan request reject" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+ if ev is None:
+ raise Exception("Scan timed out")
+
+def test_dbus_scan_abort(dev, apdev):
+ """D-Bus scan trigger and abort"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ iface.Scan({'Type': 'active', 'AllowRoam': False})
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], 15)
+ if ev is None:
+ raise Exception("Scan start timed out")
+
+ iface.AbortScan()
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+ if ev is None:
+ raise Exception("Scan abort result timed out")
+ dev[0].dump_monitor()
+ iface.Scan({'Type': 'active', 'AllowRoam': False})
+ iface.AbortScan()
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+ if ev is None:
+ raise Exception("Scan timed out")
+
+def test_dbus_connect(dev, apdev):
+ """D-Bus AddNetwork and connect"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ class TestDbusConnect(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.network_added = False
+ self.network_selected = False
+ self.network_removed = False
+ self.state = 0
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_connect)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.networkAdded, WPAS_DBUS_IFACE, "NetworkAdded")
+ self.add_signal(self.networkRemoved, WPAS_DBUS_IFACE,
+ "NetworkRemoved")
+ self.add_signal(self.networkSelected, WPAS_DBUS_IFACE,
+ "NetworkSelected")
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def networkAdded(self, network, properties):
+ logger.debug("networkAdded: %s" % str(network))
+ logger.debug(str(properties))
+ self.network_added = True
+
+ def networkRemoved(self, network):
+ logger.debug("networkRemoved: %s" % str(network))
+ self.network_removed = True
+
+ def networkSelected(self, network):
+ logger.debug("networkSelected: %s" % str(network))
+ self.network_selected = True
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+ if 'State' in properties and properties['State'] == "completed":
+ if self.state == 0:
+ self.state = 1
+ iface.Disconnect()
+ elif self.state == 2:
+ self.state = 3
+ iface.Disconnect()
+ elif self.state == 4:
+ self.state = 5
+ iface.Reattach()
+ elif self.state == 5:
+ self.state = 6
+ iface.Disconnect()
+ elif self.state == 7:
+ self.state = 8
+ res = iface.SignalPoll()
+ logger.debug("SignalPoll: " + str(res))
+ if 'frequency' not in res or res['frequency'] != 2412:
+ self.state = -1
+ logger.info("Unexpected SignalPoll result")
+ iface.RemoveNetwork(self.netw)
+ if 'State' in properties and properties['State'] == "disconnected":
+ if self.state == 1:
+ self.state = 2
+ iface.SelectNetwork(self.netw)
+ elif self.state == 3:
+ self.state = 4
+ iface.Reassociate()
+ elif self.state == 6:
+ self.state = 7
+ iface.Reconnect()
+ elif self.state == 8:
+ self.state = 9
+ self.loop.quit()
+
+ def run_connect(self, *args):
+ logger.debug("run_connect")
+ args = dbus.Dictionary({'ssid': ssid,
+ 'key_mgmt': 'WPA-PSK',
+ 'psk': passphrase,
+ 'scan_freq': 2412},
+ signature='sv')
+ self.netw = iface.AddNetwork(args)
+ iface.SelectNetwork(self.netw)
+ return False
+
+ def success(self):
+ if not self.network_added or \
+ not self.network_removed or \
+ not self.network_selected:
+ return False
+ return self.state == 9
+
+ with TestDbusConnect(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_remove_connected(dev, apdev):
+ """D-Bus RemoveAllNetworks while connected"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ ssid = "test-open"
+ hapd = hostapd.add_ap(apdev[0], {"ssid": ssid})
+
+ class TestDbusConnect(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.network_added = False
+ self.network_selected = False
+ self.network_removed = False
+ self.state = 0
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_connect)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.networkAdded, WPAS_DBUS_IFACE, "NetworkAdded")
+ self.add_signal(self.networkRemoved, WPAS_DBUS_IFACE,
+ "NetworkRemoved")
+ self.add_signal(self.networkSelected, WPAS_DBUS_IFACE,
+ "NetworkSelected")
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def networkAdded(self, network, properties):
+ logger.debug("networkAdded: %s" % str(network))
+ logger.debug(str(properties))
+ self.network_added = True
+
+ def networkRemoved(self, network):
+ logger.debug("networkRemoved: %s" % str(network))
+ self.network_removed = True
+
+ def networkSelected(self, network):
+ logger.debug("networkSelected: %s" % str(network))
+ self.network_selected = True
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+ if 'State' in properties and properties['State'] == "completed":
+ if self.state == 0:
+ self.state = 1
+ iface.Disconnect()
+ elif self.state == 2:
+ self.state = 3
+ iface.Disconnect()
+ elif self.state == 4:
+ self.state = 5
+ iface.Reattach()
+ elif self.state == 5:
+ self.state = 6
+ iface.Disconnect()
+ elif self.state == 7:
+ self.state = 8
+ res = iface.SignalPoll()
+ logger.debug("SignalPoll: " + str(res))
+ if 'frequency' not in res or res['frequency'] != 2412:
+ self.state = -1
+ logger.info("Unexpected SignalPoll result")
+ iface.RemoveAllNetworks()
+ if 'State' in properties and properties['State'] == "disconnected":
+ if self.state == 1:
+ self.state = 2
+ iface.SelectNetwork(self.netw)
+ elif self.state == 3:
+ self.state = 4
+ iface.Reassociate()
+ elif self.state == 6:
+ self.state = 7
+ iface.Reconnect()
+ elif self.state == 8:
+ self.state = 9
+ self.loop.quit()
+
+ def run_connect(self, *args):
+ logger.debug("run_connect")
+ args = dbus.Dictionary({'ssid': ssid,
+ 'key_mgmt': 'NONE',
+ 'scan_freq': 2412},
+ signature='sv')
+ self.netw = iface.AddNetwork(args)
+ iface.SelectNetwork(self.netw)
+ return False
+
+ def success(self):
+ if not self.network_added or \
+ not self.network_removed or \
+ not self.network_selected:
+ return False
+ return self.state == 9
+
+ with TestDbusConnect(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_connect_psk_mem(dev, apdev):
+ """D-Bus AddNetwork and connect with memory-only PSK"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ class TestDbusConnect(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.connected = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_connect)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.add_signal(self.networkRequest, WPAS_DBUS_IFACE,
+ "NetworkRequest")
+ self.loop.run()
+ return self
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+ if 'State' in properties and properties['State'] == "completed":
+ self.connected = True
+ self.loop.quit()
+
+ def networkRequest(self, path, field, txt):
+ logger.debug("networkRequest: %s %s %s" % (path, field, txt))
+ if field == "PSK_PASSPHRASE":
+ iface.NetworkReply(path, field, '"' + passphrase + '"')
+
+ def run_connect(self, *args):
+ logger.debug("run_connect")
+ args = dbus.Dictionary({'ssid': ssid,
+ 'key_mgmt': 'WPA-PSK',
+ 'mem_only_psk': 1,
+ 'scan_freq': 2412},
+ signature='sv')
+ self.netw = iface.AddNetwork(args)
+ iface.SelectNetwork(self.netw)
+ return False
+
+ def success(self):
+ return self.connected
+
+ with TestDbusConnect(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_connect_oom(dev, apdev):
+ """D-Bus AddNetwork and connect when out-of-memory"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ if "OK" not in dev[0].request("TEST_ALLOC_FAIL 0:"):
+ raise HwsimSkip("TEST_ALLOC_FAIL not supported in the build")
+
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ class TestDbusConnect(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.network_added = False
+ self.network_selected = False
+ self.network_removed = False
+ self.state = 0
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_connect)
+ gobject.timeout_add(1500, self.timeout)
+ self.add_signal(self.networkAdded, WPAS_DBUS_IFACE, "NetworkAdded")
+ self.add_signal(self.networkRemoved, WPAS_DBUS_IFACE,
+ "NetworkRemoved")
+ self.add_signal(self.networkSelected, WPAS_DBUS_IFACE,
+ "NetworkSelected")
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def networkAdded(self, network, properties):
+ logger.debug("networkAdded: %s" % str(network))
+ logger.debug(str(properties))
+ self.network_added = True
+
+ def networkRemoved(self, network):
+ logger.debug("networkRemoved: %s" % str(network))
+ self.network_removed = True
+
+ def networkSelected(self, network):
+ logger.debug("networkSelected: %s" % str(network))
+ self.network_selected = True
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+ if 'State' in properties and properties['State'] == "completed":
+ if self.state == 0:
+ self.state = 1
+ iface.Disconnect()
+ elif self.state == 2:
+ self.state = 3
+ iface.Disconnect()
+ elif self.state == 4:
+ self.state = 5
+ iface.Reattach()
+ elif self.state == 5:
+ self.state = 6
+ res = iface.SignalPoll()
+ logger.debug("SignalPoll: " + str(res))
+ if 'frequency' not in res or res['frequency'] != 2412:
+ self.state = -1
+ logger.info("Unexpected SignalPoll result")
+ iface.RemoveNetwork(self.netw)
+ if 'State' in properties and properties['State'] == "disconnected":
+ if self.state == 1:
+ self.state = 2
+ iface.SelectNetwork(self.netw)
+ elif self.state == 3:
+ self.state = 4
+ iface.Reassociate()
+ elif self.state == 6:
+ self.state = 7
+ self.loop.quit()
+
+ def run_connect(self, *args):
+ logger.debug("run_connect")
+ args = dbus.Dictionary({'ssid': ssid,
+ 'key_mgmt': 'WPA-PSK',
+ 'psk': passphrase,
+ 'scan_freq': 2412},
+ signature='sv')
+ try:
+ self.netw = iface.AddNetwork(args)
+ except Exception as e:
+ logger.info("Exception on AddNetwork: " + str(e))
+ self.loop.quit()
+ return False
+ try:
+ iface.SelectNetwork(self.netw)
+ except Exception as e:
+ logger.info("Exception on SelectNetwork: " + str(e))
+ self.loop.quit()
+
+ return False
+
+ def success(self):
+ if not self.network_added or \
+ not self.network_removed or \
+ not self.network_selected:
+ return False
+ return self.state == 7
+
+ count = 0
+ for i in range(1, 1000):
+ for j in range(3):
+ dev[j].dump_monitor()
+ dev[0].request("TEST_ALLOC_FAIL %d:main" % i)
+ try:
+ with TestDbusConnect(bus) as t:
+ if not t.success():
+ logger.info("Iteration %d - Expected signals not seen" % i)
+ else:
+ logger.info("Iteration %d - success" % i)
+
+ state = dev[0].request('GET_ALLOC_FAIL')
+ logger.info("GET_ALLOC_FAIL: " + state)
+ dev[0].dump_monitor()
+ dev[0].request("TEST_ALLOC_FAIL 0:")
+ if i < 3:
+ raise Exception("Connection succeeded during out-of-memory")
+ if not state.startswith('0:'):
+ count += 1
+ if count == 5:
+ break
+ except:
+ pass
+
+ # Force regulatory update to re-fetch hw capabilities for the following
+ # test cases.
+ try:
+ dev[0].dump_monitor()
+ subprocess.call(['iw', 'reg', 'set', 'US'])
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+ finally:
+ dev[0].dump_monitor()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+
+def test_dbus_while_not_connected(dev, apdev):
+ """D-Bus invalid operations while not connected"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ try:
+ iface.Disconnect()
+ raise Exception("Disconnect() accepted when not connected")
+ except dbus.exceptions.DBusException as e:
+ if "NotConnected" not in str(e):
+ raise Exception("Unexpected error message for invalid Disconnect: " + str(e))
+
+ try:
+ iface.Reattach()
+ raise Exception("Reattach() accepted when not connected")
+ except dbus.exceptions.DBusException as e:
+ if "NotConnected" not in str(e):
+ raise Exception("Unexpected error message for invalid Reattach: " + str(e))
+
+def test_dbus_connect_eap(dev, apdev):
+ """D-Bus AddNetwork and connect to EAP network"""
+ check_altsubject_match_support(dev[0])
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ ssid = "ieee8021x-open"
+ params = hostapd.radius_params()
+ params["ssid"] = ssid
+ params["ieee8021x"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ class TestDbusConnect(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.certification_received = False
+ self.eap_status = False
+ self.state = 0
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_connect)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.add_signal(self.certification, WPAS_DBUS_IFACE,
+ "Certification", byte_arrays=True)
+ self.add_signal(self.networkRequest, WPAS_DBUS_IFACE,
+ "NetworkRequest")
+ self.add_signal(self.eap, WPAS_DBUS_IFACE, "EAP")
+ self.loop.run()
+ return self
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+ if 'State' in properties and properties['State'] == "completed":
+ if self.state == 0:
+ self.state = 1
+ iface.EAPLogoff()
+ logger.info("Set dNSName constraint")
+ net_obj = bus.get_object(WPAS_DBUS_SERVICE, self.netw)
+ args = dbus.Dictionary({'altsubject_match':
+ self.server_dnsname},
+ signature='sv')
+ net_obj.Set(WPAS_DBUS_NETWORK, "Properties", args,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ elif self.state == 2:
+ self.state = 3
+ iface.Disconnect()
+ logger.info("Set non-matching dNSName constraint")
+ net_obj = bus.get_object(WPAS_DBUS_SERVICE, self.netw)
+ args = dbus.Dictionary({'altsubject_match':
+ self.server_dnsname + "FOO"},
+ signature='sv')
+ net_obj.Set(WPAS_DBUS_NETWORK, "Properties", args,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if 'State' in properties and properties['State'] == "disconnected":
+ if self.state == 1:
+ self.state = 2
+ iface.EAPLogon()
+ iface.SelectNetwork(self.netw)
+ if self.state == 3:
+ self.state = 4
+ iface.SelectNetwork(self.netw)
+
+ def certification(self, args):
+ logger.debug("certification: %s" % str(args))
+ self.certification_received = True
+ if args['depth'] == 0:
+ # The test server certificate is supposed to have dNSName
+ if len(args['altsubject']) < 1:
+ raise Exception("Missing dNSName")
+ dnsname = args['altsubject'][0]
+ if not dnsname.startswith("DNS:"):
+ raise Exception("Expected dNSName not found: " + dnsname)
+ logger.info("altsubject: " + dnsname)
+ self.server_dnsname = dnsname
+
+ def eap(self, status, parameter):
+ logger.debug("EAP: status=%s parameter=%s" % (status, parameter))
+ if status == 'completion' and parameter == 'success':
+ self.eap_status = True
+ if self.state == 4 and status == 'remote certificate verification' and parameter == 'AltSubject mismatch':
+ self.state = 5
+ self.loop.quit()
+
+ def networkRequest(self, path, field, txt):
+ logger.debug("networkRequest: %s %s %s" % (path, field, txt))
+ if field == "PASSWORD":
+ iface.NetworkReply(path, field, "password")
+
+ def run_connect(self, *args):
+ logger.debug("run_connect")
+ args = dbus.Dictionary({'ssid': ssid,
+ 'key_mgmt': 'IEEE8021X',
+ 'eapol_flags': 0,
+ 'eap': 'TTLS',
+ 'anonymous_identity': 'ttls',
+ 'identity': 'pap user',
+ 'ca_cert': 'auth_serv/ca.pem',
+ 'phase2': 'auth=PAP',
+ 'scan_freq': 2412},
+ signature='sv')
+ self.netw = iface.AddNetwork(args)
+ iface.SelectNetwork(self.netw)
+ return False
+
+ def success(self):
+ if not self.eap_status or not self.certification_received:
+ return False
+ return self.state == 5
+
+ with TestDbusConnect(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_network(dev, apdev):
+ """D-Bus AddNetwork/RemoveNetwork parameters and error cases"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ args = dbus.Dictionary({'ssid': "foo",
+ 'key_mgmt': 'WPA-PSK',
+ 'psk': "12345678",
+ 'identity': dbus.ByteArray([1, 2]),
+ 'priority': dbus.Int32(0),
+ 'scan_freq': dbus.UInt32(2412)},
+ signature='sv')
+ netw = iface.AddNetwork(args)
+ id = int(dev[0].list_networks()[0]['id'])
+ val = dev[0].get_network(id, "scan_freq")
+ if val != "2412":
+ raise Exception("Invalid scan_freq value: " + str(val))
+ iface.RemoveNetwork(netw)
+
+ args = dbus.Dictionary({'ssid': "foo",
+ 'key_mgmt': 'NONE',
+ 'scan_freq': "2412 2432",
+ 'freq_list': "2412 2417 2432"},
+ signature='sv')
+ netw = iface.AddNetwork(args)
+ id = int(dev[0].list_networks()[0]['id'])
+ val = dev[0].get_network(id, "scan_freq")
+ if val != "2412 2432":
+ raise Exception("Invalid scan_freq value (2): " + str(val))
+ val = dev[0].get_network(id, "freq_list")
+ if val != "2412 2417 2432":
+ raise Exception("Invalid freq_list value: " + str(val))
+ iface.RemoveNetwork(netw)
+ try:
+ iface.RemoveNetwork(netw)
+ raise Exception("Invalid RemoveNetwork() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "NetworkUnknown" not in str(e):
+ raise Exception("Unexpected error message for invalid RemoveNetwork: " + str(e))
+ try:
+ iface.SelectNetwork(netw)
+ raise Exception("Invalid SelectNetwork() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "NetworkUnknown" not in str(e):
+ raise Exception("Unexpected error message for invalid RemoveNetwork: " + str(e))
+
+ args = dbus.Dictionary({'ssid': "foo1", 'key_mgmt': 'NONE',
+ 'identity': "testuser", 'scan_freq': '2412'},
+ signature='sv')
+ netw1 = iface.AddNetwork(args)
+ args = dbus.Dictionary({'ssid': "foo2", 'key_mgmt': 'NONE'},
+ signature='sv')
+ netw2 = iface.AddNetwork(args)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "Networks",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 2:
+ raise Exception("Unexpected number of networks")
+
+ net_obj = bus.get_object(WPAS_DBUS_SERVICE, netw1)
+ res = net_obj.Get(WPAS_DBUS_NETWORK, "Enabled",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != False:
+ raise Exception("Added network was unexpectedly enabled by default")
+ net_obj.Set(WPAS_DBUS_NETWORK, "Enabled", dbus.Boolean(True),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = net_obj.Get(WPAS_DBUS_NETWORK, "Enabled",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != True:
+ raise Exception("Set(Enabled,True) did not seem to change property value")
+ net_obj.Set(WPAS_DBUS_NETWORK, "Enabled", dbus.Boolean(False),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = net_obj.Get(WPAS_DBUS_NETWORK, "Enabled",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != False:
+ raise Exception("Set(Enabled,False) did not seem to change property value")
+ try:
+ net_obj.Set(WPAS_DBUS_NETWORK, "Enabled", dbus.UInt32(1),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(Enabled,1) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: wrong property type" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(Enabled,1): " + str(e))
+
+ args = dbus.Dictionary({'ssid': "foo1new"}, signature='sv')
+ net_obj.Set(WPAS_DBUS_NETWORK, "Properties", args,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = net_obj.Get(WPAS_DBUS_NETWORK, "Properties",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res['ssid'] != '"foo1new"':
+ raise Exception("Set(Properties) failed to update ssid")
+ if res['identity'] != '"testuser"':
+ raise Exception("Set(Properties) unexpectedly changed unrelated parameter")
+
+ iface.RemoveAllNetworks()
+ res = if_obj.Get(WPAS_DBUS_IFACE, "Networks",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != 0:
+ raise Exception("Unexpected number of networks")
+ iface.RemoveAllNetworks()
+
+ tests = [dbus.Dictionary({'psk': "1234567"}, signature='sv'),
+ dbus.Dictionary({'identity': dbus.ByteArray()},
+ signature='sv'),
+ dbus.Dictionary({'identity': dbus.Byte(1)}, signature='sv')]
+ for args in tests:
+ try:
+ iface.AddNetwork(args)
+ raise Exception("Invalid AddNetwork args accepted: " + str(args))
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid AddNetwork: " + str(e))
+
+def test_dbus_network_oom(dev, apdev):
+ """D-Bus AddNetwork/RemoveNetwork parameters and OOM error cases"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ args = dbus.Dictionary({'ssid': "foo1", 'key_mgmt': 'NONE',
+ 'identity': "testuser", 'scan_freq': '2412'},
+ signature='sv')
+ netw1 = iface.AddNetwork(args)
+ net_obj = bus.get_object(WPAS_DBUS_SERVICE, netw1)
+
+ with alloc_fail_dbus(dev[0], 1,
+ "wpa_config_get_all;wpas_dbus_getter_network_properties",
+ "Get"):
+ net_obj.Get(WPAS_DBUS_NETWORK, "Properties",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ iface.RemoveAllNetworks()
+
+ with alloc_fail_dbus(dev[0], 1,
+ "wpas_dbus_new_decompose_object_path;wpas_dbus_handler_remove_network",
+ "RemoveNetwork", "InvalidArgs"):
+ iface.RemoveNetwork(dbus.ObjectPath("/fi/w1/wpa_supplicant1/Interfaces/1234/Networks/1234"))
+
+ with alloc_fail(dev[0], 1, "wpa_dbus_register_object_per_iface;wpas_dbus_register_network"):
+ args = dbus.Dictionary({'ssid': "foo2", 'key_mgmt': 'NONE'},
+ signature='sv')
+ try:
+ netw = iface.AddNetwork(args)
+ # Currently, AddNetwork() succeeds even if os_strdup() for path
+ # fails, so remove the network if that occurs.
+ iface.RemoveNetwork(netw)
+ except dbus.exceptions.DBusException as e:
+ pass
+
+ for i in range(1, 3):
+ with alloc_fail(dev[0], i, "=wpas_dbus_register_network"):
+ try:
+ netw = iface.AddNetwork(args)
+ # Currently, AddNetwork() succeeds even if network registration
+ # fails, so remove the network if that occurs.
+ iface.RemoveNetwork(netw)
+ except dbus.exceptions.DBusException as e:
+ pass
+
+ with alloc_fail_dbus(dev[0], 1,
+ "=wpa_config_add_network;wpas_dbus_handler_add_network",
+ "AddNetwork",
+ "UnknownError: wpa_supplicant could not add a network"):
+ args = dbus.Dictionary({'ssid': "foo2", 'key_mgmt': 'NONE'},
+ signature='sv')
+ netw = iface.AddNetwork(args)
+
+ tests = [(1,
+ 'wpa_dbus_dict_get_entry;set_network_properties;wpas_dbus_handler_add_network',
+ dbus.Dictionary({'ssid': dbus.ByteArray(b' ')},
+ signature='sv')),
+ (1, '=set_network_properties;wpas_dbus_handler_add_network',
+ dbus.Dictionary({'ssid': 'foo'}, signature='sv')),
+ (1, '=set_network_properties;wpas_dbus_handler_add_network',
+ dbus.Dictionary({'eap': 'foo'}, signature='sv')),
+ (1, '=set_network_properties;wpas_dbus_handler_add_network',
+ dbus.Dictionary({'priority': dbus.UInt32(1)},
+ signature='sv')),
+ (1, '=set_network_properties;wpas_dbus_handler_add_network',
+ dbus.Dictionary({'priority': dbus.Int32(1)},
+ signature='sv')),
+ (1, '=set_network_properties;wpas_dbus_handler_add_network',
+ dbus.Dictionary({'ssid': dbus.ByteArray(b' ')},
+ signature='sv'))]
+ for (count, funcs, args) in tests:
+ with alloc_fail_dbus(dev[0], count, funcs, "AddNetwork", "InvalidArgs"):
+ netw = iface.AddNetwork(args)
+
+ if len(if_obj.Get(WPAS_DBUS_IFACE, 'Networks',
+ dbus_interface=dbus.PROPERTIES_IFACE)) > 0:
+ raise Exception("Unexpected network block added")
+ if len(dev[0].list_networks()) > 0:
+ raise Exception("Unexpected network block visible")
+
+def test_dbus_interface(dev, apdev):
+ """D-Bus CreateInterface/GetInterface/RemoveInterface parameters and error cases"""
+ try:
+ _test_dbus_interface(dev, apdev)
+ finally:
+ # Need to force P2P channel list update since the 'lo' interface
+ # with driver=none ends up configuring default dualband channels.
+ dev[0].request("SET country US")
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+ if ev is None:
+ ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"],
+ timeout=1)
+ dev[0].request("SET country 00")
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+ if ev is None:
+ ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"],
+ timeout=1)
+ subprocess.call(['iw', 'reg', 'set', '00'])
+
+def _test_dbus_interface(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wpas = dbus.Interface(wpas_obj, WPAS_DBUS_SERVICE)
+
+ params = dbus.Dictionary({'Ifname': 'lo', 'Driver': 'none'},
+ signature='sv')
+ path = wpas.CreateInterface(params)
+ logger.debug("New interface path: " + str(path))
+ path2 = wpas.GetInterface("lo")
+ if path != path2:
+ raise Exception("Interface object mismatch")
+
+ params = dbus.Dictionary({'Ifname': 'lo',
+ 'Driver': 'none',
+ 'ConfigFile': 'foo',
+ 'BridgeIfname': 'foo',},
+ signature='sv')
+ try:
+ wpas.CreateInterface(params)
+ raise Exception("Invalid CreateInterface() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InterfaceExists" not in str(e):
+ raise Exception("Unexpected error message for invalid CreateInterface: " + str(e))
+
+ wpas.RemoveInterface(path)
+ try:
+ wpas.RemoveInterface(path)
+ raise Exception("Invalid RemoveInterface() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InterfaceUnknown" not in str(e):
+ raise Exception("Unexpected error message for invalid RemoveInterface: " + str(e))
+
+ params = dbus.Dictionary({'Ifname': 'lo', 'Driver': 'none',
+ 'Foo': 123},
+ signature='sv')
+ try:
+ wpas.CreateInterface(params)
+ raise Exception("Invalid CreateInterface() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid CreateInterface: " + str(e))
+
+ params = dbus.Dictionary({'Driver': 'none'}, signature='sv')
+ try:
+ wpas.CreateInterface(params)
+ raise Exception("Invalid CreateInterface() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid CreateInterface: " + str(e))
+
+ try:
+ wpas.GetInterface("lo")
+ raise Exception("Invalid GetInterface() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InterfaceUnknown" not in str(e):
+ raise Exception("Unexpected error message for invalid RemoveInterface: " + str(e))
+
+def test_dbus_interface_oom(dev, apdev):
+ """D-Bus CreateInterface/GetInterface/RemoveInterface OOM error cases"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wpas = dbus.Interface(wpas_obj, WPAS_DBUS_SERVICE)
+
+ with alloc_fail_dbus(dev[0], 1, "wpa_dbus_dict_get_entry;wpas_dbus_handler_create_interface", "CreateInterface", "InvalidArgs"):
+ params = dbus.Dictionary({'Ifname': 'lo', 'Driver': 'none'},
+ signature='sv')
+ wpas.CreateInterface(params)
+
+ for i in range(1, 1000):
+ dev[0].request("TEST_ALLOC_FAIL %d:wpa_supplicant_add_iface;wpas_dbus_handler_create_interface" % i)
+ params = dbus.Dictionary({'Ifname': 'lo', 'Driver': 'none'},
+ signature='sv')
+ try:
+ npath = wpas.CreateInterface(params)
+ wpas.RemoveInterface(npath)
+ logger.info("CreateInterface succeeds after %d allocation failures" % i)
+ state = dev[0].request('GET_ALLOC_FAIL')
+ logger.info("GET_ALLOC_FAIL: " + state)
+ dev[0].dump_monitor()
+ dev[0].request("TEST_ALLOC_FAIL 0:")
+ if i < 5:
+ raise Exception("CreateInterface succeeded during out-of-memory")
+ if not state.startswith('0:'):
+ break
+ except dbus.exceptions.DBusException as e:
+ pass
+
+ for arg in ['Driver', 'Ifname', 'ConfigFile', 'BridgeIfname']:
+ with alloc_fail_dbus(dev[0], 1, "=wpas_dbus_handler_create_interface",
+ "CreateInterface"):
+ params = dbus.Dictionary({arg: 'foo'}, signature='sv')
+ wpas.CreateInterface(params)
+
+def test_dbus_blob(dev, apdev):
+ """D-Bus AddNetwork/RemoveNetwork parameters and error cases"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ blob = dbus.ByteArray(b"\x01\x02\x03")
+ iface.AddBlob('blob1', blob)
+ try:
+ iface.AddBlob('blob1', dbus.ByteArray(b"\x01\x02\x04"))
+ raise Exception("Invalid AddBlob() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "BlobExists" not in str(e):
+ raise Exception("Unexpected error message for invalid AddBlob: " + str(e))
+ res = iface.GetBlob('blob1')
+ if len(res) != len(blob):
+ raise Exception("Unexpected blob data length")
+ for i in range(len(res)):
+ if res[i] != dbus.Byte(blob[i]):
+ raise Exception("Unexpected blob data")
+ res = if_obj.Get(WPAS_DBUS_IFACE, "Blobs",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if 'blob1' not in res:
+ raise Exception("Added blob missing from Blobs property")
+ iface.RemoveBlob('blob1')
+ try:
+ iface.RemoveBlob('blob1')
+ raise Exception("Invalid RemoveBlob() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "BlobUnknown" not in str(e):
+ raise Exception("Unexpected error message for invalid RemoveBlob: " + str(e))
+ try:
+ iface.GetBlob('blob1')
+ raise Exception("Invalid GetBlob() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "BlobUnknown" not in str(e):
+ raise Exception("Unexpected error message for invalid GetBlob: " + str(e))
+
+ class TestDbusBlob(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.blob_added = False
+ self.blob_removed = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_blob)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.blobAdded, WPAS_DBUS_IFACE, "BlobAdded")
+ self.add_signal(self.blobRemoved, WPAS_DBUS_IFACE, "BlobRemoved")
+ self.loop.run()
+ return self
+
+ def blobAdded(self, blobName):
+ logger.debug("blobAdded: %s" % blobName)
+ if blobName == 'blob2':
+ self.blob_added = True
+
+ def blobRemoved(self, blobName):
+ logger.debug("blobRemoved: %s" % blobName)
+ if blobName == 'blob2':
+ self.blob_removed = True
+ self.loop.quit()
+
+ def run_blob(self, *args):
+ logger.debug("run_blob")
+ iface.AddBlob('blob2', dbus.ByteArray(b"\x01\x02\x04"))
+ iface.RemoveBlob('blob2')
+ return False
+
+ def success(self):
+ return self.blob_added and self.blob_removed
+
+ with TestDbusBlob(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_blob_oom(dev, apdev):
+ """D-Bus AddNetwork/RemoveNetwork OOM error cases"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ for i in range(1, 4):
+ with alloc_fail_dbus(dev[0], i, "wpas_dbus_handler_add_blob",
+ "AddBlob"):
+ iface.AddBlob('blob_no_mem', dbus.ByteArray(b"\x01\x02\x03\x04"))
+
+def test_dbus_autoscan(dev, apdev):
+ """D-Bus Autoscan()"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ iface.AutoScan("foo")
+ iface.AutoScan("periodic:1")
+ iface.AutoScan("")
+ dev[0].request("AUTOSCAN ")
+
+def test_dbus_autoscan_oom(dev, apdev):
+ """D-Bus Autoscan() OOM"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ with alloc_fail_dbus(dev[0], 1, "wpas_dbus_handler_autoscan", "AutoScan"):
+ iface.AutoScan("foo")
+ dev[0].request("AUTOSCAN ")
+
+def test_dbus_tdls_invalid(dev, apdev):
+ """D-Bus invalid TDLS operations"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ connect_2sta_open(dev, hapd)
+ addr1 = dev[1].p2p_interface_addr()
+
+ try:
+ iface.TDLSDiscover("foo")
+ raise Exception("Invalid TDLSDiscover() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid TDLSDiscover: " + str(e))
+
+ try:
+ iface.TDLSStatus("foo")
+ raise Exception("Invalid TDLSStatus() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid TDLSStatus: " + str(e))
+
+ res = iface.TDLSStatus(addr1)
+ if res != "peer does not exist":
+ raise Exception("Unexpected TDLSStatus response")
+
+ try:
+ iface.TDLSSetup("foo")
+ raise Exception("Invalid TDLSSetup() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid TDLSSetup: " + str(e))
+
+ try:
+ iface.TDLSTeardown("foo")
+ raise Exception("Invalid TDLSTeardown() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid TDLSTeardown: " + str(e))
+
+ try:
+ iface.TDLSTeardown("00:11:22:33:44:55")
+ raise Exception("TDLSTeardown accepted for unknown peer")
+ except dbus.exceptions.DBusException as e:
+ if "UnknownError: error performing TDLS teardown" not in str(e):
+ raise Exception("Unexpected error message: " + str(e))
+
+ try:
+ iface.TDLSChannelSwitch({})
+ raise Exception("Invalid TDLSChannelSwitch() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid TDLSChannelSwitch: " + str(e))
+
+ try:
+ iface.TDLSCancelChannelSwitch("foo")
+ raise Exception("Invalid TDLSCancelChannelSwitch() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid TDLSCancelChannelSwitch: " + str(e))
+
+def test_dbus_tdls_oom(dev, apdev):
+ """D-Bus TDLS operations during OOM"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ with alloc_fail_dbus(dev[0], 1, "wpa_tdls_add_peer", "TDLSSetup",
+ "UnknownError: error performing TDLS setup"):
+ iface.TDLSSetup("00:11:22:33:44:55")
+
+def test_dbus_tdls(dev, apdev):
+ """D-Bus TDLS"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ connect_2sta_open(dev, hapd)
+
+ addr1 = dev[1].p2p_interface_addr()
+
+ class TestDbusTdls(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.tdls_setup = False
+ self.tdls_teardown = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_tdls)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+
+ def run_tdls(self, *args):
+ logger.debug("run_tdls")
+ iface.TDLSDiscover(addr1)
+ gobject.timeout_add(100, self.run_tdls2)
+ return False
+
+ def run_tdls2(self, *args):
+ logger.debug("run_tdls2")
+ iface.TDLSSetup(addr1)
+ gobject.timeout_add(500, self.run_tdls3)
+ return False
+
+ def run_tdls3(self, *args):
+ logger.debug("run_tdls3")
+ res = iface.TDLSStatus(addr1)
+ if res == "connected":
+ self.tdls_setup = True
+ else:
+ logger.info("Unexpected TDLSStatus: " + res)
+ iface.TDLSTeardown(addr1)
+ gobject.timeout_add(200, self.run_tdls4)
+ return False
+
+ def run_tdls4(self, *args):
+ logger.debug("run_tdls4")
+ res = iface.TDLSStatus(addr1)
+ if res == "peer does not exist":
+ self.tdls_teardown = True
+ else:
+ logger.info("Unexpected TDLSStatus: " + res)
+ self.loop.quit()
+ return False
+
+ def success(self):
+ return self.tdls_setup and self.tdls_teardown
+
+ with TestDbusTdls(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_tdls_channel_switch(dev, apdev):
+ """D-Bus TDLS channel switch configuration"""
+ flags = int(dev[0].get_driver_status_field('capa.flags'), 16)
+ if flags & 0x800000000 == 0:
+ raise HwsimSkip("Driver does not support TDLS channel switching")
+
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ connect_2sta_open(dev, hapd)
+
+ addr1 = dev[1].p2p_interface_addr()
+
+ class TestDbusTdls(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.tdls_setup = False
+ self.tdls_done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_tdls)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+
+ def run_tdls(self, *args):
+ logger.debug("run_tdls")
+ iface.TDLSDiscover(addr1)
+ gobject.timeout_add(100, self.run_tdls2)
+ return False
+
+ def run_tdls2(self, *args):
+ logger.debug("run_tdls2")
+ iface.TDLSSetup(addr1)
+ gobject.timeout_add(500, self.run_tdls3)
+ return False
+
+ def run_tdls3(self, *args):
+ logger.debug("run_tdls3")
+ res = iface.TDLSStatus(addr1)
+ if res == "connected":
+ self.tdls_setup = True
+ else:
+ logger.info("Unexpected TDLSStatus: " + res)
+
+ # Unknown dict entry
+ args = dbus.Dictionary({'Foobar': dbus.Byte(1)},
+ signature='sv')
+ try:
+ iface.TDLSChannelSwitch(args)
+ except Exception as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected exception")
+
+ # Missing OperClass
+ args = dbus.Dictionary({}, signature='sv')
+ try:
+ iface.TDLSChannelSwitch(args)
+ except Exception as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected exception")
+
+ # Missing Frequency
+ args = dbus.Dictionary({'OperClass': dbus.Byte(1)},
+ signature='sv')
+ try:
+ iface.TDLSChannelSwitch(args)
+ except Exception as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected exception")
+
+ # Missing PeerAddress
+ args = dbus.Dictionary({'OperClass': dbus.Byte(1),
+ 'Frequency': dbus.UInt32(2417)},
+ signature='sv')
+ try:
+ iface.TDLSChannelSwitch(args)
+ except Exception as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected exception")
+
+ # Valid parameters
+ args = dbus.Dictionary({'OperClass': dbus.Byte(1),
+ 'Frequency': dbus.UInt32(2417),
+ 'PeerAddress': addr1,
+ 'SecChannelOffset': dbus.UInt32(0),
+ 'CenterFrequency1': dbus.UInt32(0),
+ 'CenterFrequency2': dbus.UInt32(0),
+ 'Bandwidth': dbus.UInt32(20),
+ 'HT': dbus.Boolean(False),
+ 'VHT': dbus.Boolean(False)},
+ signature='sv')
+ iface.TDLSChannelSwitch(args)
+
+ gobject.timeout_add(200, self.run_tdls4)
+ return False
+
+ def run_tdls4(self, *args):
+ logger.debug("run_tdls4")
+ iface.TDLSCancelChannelSwitch(addr1)
+ self.tdls_done = True
+ self.loop.quit()
+ return False
+
+ def success(self):
+ return self.tdls_setup and self.tdls_done
+
+ with TestDbusTdls(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_pkcs11(dev, apdev):
+ """D-Bus SetPKCS11EngineAndModulePath()"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ try:
+ iface.SetPKCS11EngineAndModulePath("foo", "bar")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: Reinit of the EAPOL" not in str(e):
+ raise Exception("Unexpected error message for invalid SetPKCS11EngineAndModulePath: " + str(e))
+
+ try:
+ iface.SetPKCS11EngineAndModulePath("foo", "")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: Reinit of the EAPOL" not in str(e):
+ raise Exception("Unexpected error message for invalid SetPKCS11EngineAndModulePath: " + str(e))
+
+ iface.SetPKCS11EngineAndModulePath("", "bar")
+ res = if_obj.Get(WPAS_DBUS_IFACE, "PKCS11EnginePath",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != "":
+ raise Exception("Unexpected PKCS11EnginePath value: " + res)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "PKCS11ModulePath",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != "bar":
+ raise Exception("Unexpected PKCS11ModulePath value: " + res)
+
+ iface.SetPKCS11EngineAndModulePath("", "")
+ res = if_obj.Get(WPAS_DBUS_IFACE, "PKCS11EnginePath",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != "":
+ raise Exception("Unexpected PKCS11EnginePath value: " + res)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "PKCS11ModulePath",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != "":
+ raise Exception("Unexpected PKCS11ModulePath value: " + res)
+
+def test_dbus_apscan(dev, apdev):
+ """D-Bus Get/Set ApScan"""
+ try:
+ _test_dbus_apscan(dev, apdev)
+ finally:
+ dev[0].request("AP_SCAN 1")
+
+def _test_dbus_apscan(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, "ApScan",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != 1:
+ raise Exception("Unexpected initial ApScan value: %d" % res)
+
+ for i in range(3):
+ if_obj.Set(WPAS_DBUS_IFACE, "ApScan", dbus.UInt32(i),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "ApScan",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != i:
+ raise Exception("Unexpected ApScan value %d (expected %d)" % (res, i))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "ApScan", dbus.Int16(-1),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(ApScan,-1) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: wrong property type" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(ApScan,-1): " + str(e))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "ApScan", dbus.UInt32(123),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(ApScan,123) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: ap_scan must be 0, 1, or 2" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(ApScan,123): " + str(e))
+
+ if_obj.Set(WPAS_DBUS_IFACE, "ApScan", dbus.UInt32(1),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_pmf(dev, apdev):
+ """D-Bus Get/Set Pmf"""
+ try:
+ _test_dbus_pmf(dev, apdev)
+ finally:
+ dev[0].request("SET pmf 0")
+
+def _test_dbus_pmf(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ dev[0].set("pmf", "0")
+ res = if_obj.Get(WPAS_DBUS_IFACE, "Pmf",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != "0":
+ raise Exception("Unexpected initial Pmf value: %s" % res)
+
+ for i in range(3):
+ if_obj.Set(WPAS_DBUS_IFACE, "Pmf", str(i),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "Pmf",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != str(i):
+ raise Exception("Unexpected Pmf value %s (expected %d)" % (res, i))
+
+ if_obj.Set(WPAS_DBUS_IFACE, "Pmf", "1",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_fastreauth(dev, apdev):
+ """D-Bus Get/Set FastReauth"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ res = if_obj.Get(WPAS_DBUS_IFACE, "FastReauth",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != True:
+ raise Exception("Unexpected initial FastReauth value: " + str(res))
+
+ for i in [False, True]:
+ if_obj.Set(WPAS_DBUS_IFACE, "FastReauth", dbus.Boolean(i),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "FastReauth",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != i:
+ raise Exception("Unexpected FastReauth value %d (expected %d)" % (res, i))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "FastReauth", dbus.Int16(-1),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(FastReauth,-1) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: wrong property type" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(ApScan,-1): " + str(e))
+
+ if_obj.Set(WPAS_DBUS_IFACE, "FastReauth", dbus.Boolean(True),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_bss_expire(dev, apdev):
+ """D-Bus Get/Set BSSExpireAge and BSSExpireCount"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireAge", dbus.UInt32(179),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "BSSExpireAge",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != 179:
+ raise Exception("Unexpected BSSExpireAge value %d (expected %d)" % (res, i))
+
+ if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireCount", dbus.UInt32(3),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "BSSExpireCount",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != 3:
+ raise Exception("Unexpected BSSExpireCount value %d (expected %d)" % (res, i))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireAge", dbus.Int16(-1),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(BSSExpireAge,-1) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: wrong property type" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(BSSExpireAge,-1): " + str(e))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireAge", dbus.UInt32(9),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(BSSExpireAge,9) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: BSSExpireAge must be >= 10" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(BSSExpireAge,9): " + str(e))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireCount", dbus.Int16(-1),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(BSSExpireCount,-1) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: wrong property type" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(BSSExpireCount,-1): " + str(e))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireCount", dbus.UInt32(0),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(BSSExpireCount,0) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: BSSExpireCount must be > 0" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(BSSExpireCount,0): " + str(e))
+
+ if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireAge", dbus.UInt32(180),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if_obj.Set(WPAS_DBUS_IFACE, "BSSExpireCount", dbus.UInt32(2),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_country(dev, apdev):
+ """D-Bus Get/Set Country"""
+ try:
+ _test_dbus_country(dev, apdev)
+ finally:
+ dev[0].request("SET country 00")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+
+def _test_dbus_country(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ # work around issues with possible pending regdom event from the end of
+ # the previous test case
+ time.sleep(0.2)
+ dev[0].dump_monitor()
+
+ if_obj.Set(WPAS_DBUS_IFACE, "Country", "FI",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "Country",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != "FI":
+ raise Exception("Unexpected Country value %s (expected FI)" % res)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"])
+ if ev is None:
+ # For now, work around separate P2P Device interface event delivery
+ ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+ if ev is None:
+ raise Exception("regdom change event not seen")
+ if "init=USER type=COUNTRY alpha2=FI" not in ev:
+ raise Exception("Unexpected event contents: " + ev)
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "Country", dbus.Int16(-1),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(Country,-1) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: wrong property type" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(Country,-1): " + str(e))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "Country", "F",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(Country,F) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: invalid country code" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(Country,F): " + str(e))
+
+ if_obj.Set(WPAS_DBUS_IFACE, "Country", "00",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"])
+ if ev is None:
+ # For now, work around separate P2P Device interface event delivery
+ ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+ if ev is None:
+ raise Exception("regdom change event not seen")
+ # init=CORE was previously used due to invalid db.txt data for 00. For
+ # now, allow both it and the new init=USER after fixed db.txt.
+ if "init=CORE type=WORLD" not in ev and "init=USER type=WORLD" not in ev:
+ raise Exception("Unexpected event contents: " + ev)
+
+def test_dbus_scan_interval(dev, apdev):
+ """D-Bus Get/Set ScanInterval"""
+ try:
+ _test_dbus_scan_interval(dev, apdev)
+ finally:
+ dev[0].request("SCAN_INTERVAL 5")
+
+def _test_dbus_scan_interval(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ if_obj.Set(WPAS_DBUS_IFACE, "ScanInterval", dbus.Int32(3),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = if_obj.Get(WPAS_DBUS_IFACE, "ScanInterval",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if res != 3:
+ raise Exception("Unexpected ScanInterval value %d (expected %d)" % (res, i))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "ScanInterval", dbus.UInt16(100),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(ScanInterval,100) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: wrong property type" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(ScanInterval,100): " + str(e))
+
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE, "ScanInterval", dbus.Int32(-1),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(ScanInterval,-1) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: scan_interval must be >= 0" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(ScanInterval,-1): " + str(e))
+
+ if_obj.Set(WPAS_DBUS_IFACE, "ScanInterval", dbus.Int32(5),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+def test_dbus_probe_req_reporting(dev, apdev):
+ """D-Bus Probe Request reporting"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ dev[1].p2p_find(social=True)
+
+ class TestDbusProbe(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.reported = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.probeRequest, WPAS_DBUS_IFACE, "ProbeRequest",
+ byte_arrays=True)
+ self.loop.run()
+ return self
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ self.iface = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE)
+ self.iface.SubscribeProbeReq()
+ self.group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ def probeRequest(self, args):
+ logger.debug("probeRequest: args=%s" % str(args))
+ self.reported = True
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ params = dbus.Dictionary({'frequency': 2412})
+ p2p.GroupAdd(params)
+ return False
+
+ def success(self):
+ return self.reported
+
+ with TestDbusProbe(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+ t.iface.UnsubscribeProbeReq()
+ try:
+ t.iface.UnsubscribeProbeReq()
+ raise Exception("Invalid UnsubscribeProbeReq() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "NoSubscription" not in str(e):
+ raise Exception("Unexpected error message for invalid UnsubscribeProbeReq(): " + str(e))
+ t.group_p2p.Disconnect()
+
+ with TestDbusProbe(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+ # On purpose, leave ProbeReq subscription in place to test automatic
+ # cleanup.
+
+ dev[1].p2p_stop_find()
+
+def test_dbus_probe_req_reporting_oom(dev, apdev):
+ """D-Bus Probe Request reporting (OOM)"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ # Need to make sure this process has not already subscribed to avoid false
+ # failures due to the operation succeeding due to os_strdup() not even
+ # getting called.
+ try:
+ iface.UnsubscribeProbeReq()
+ was_subscribed = True
+ except dbus.exceptions.DBusException as e:
+ was_subscribed = False
+ pass
+
+ with alloc_fail_dbus(dev[0], 1, "wpas_dbus_handler_subscribe_preq",
+ "SubscribeProbeReq"):
+ iface.SubscribeProbeReq()
+
+ if was_subscribed:
+ # On purpose, leave ProbeReq subscription in place to test automatic
+ # cleanup.
+ iface.SubscribeProbeReq()
+
+def test_dbus_p2p_invalid(dev, apdev):
+ """D-Bus invalid P2P operations"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ try:
+ p2p.RejectPeer(path + "/Peers/00112233445566")
+ raise Exception("Invalid RejectPeer accepted")
+ except dbus.exceptions.DBusException as e:
+ if "UnknownError: Failed to call wpas_p2p_reject" not in str(e):
+ raise Exception("Unexpected error message for invalid RejectPeer(): " + str(e))
+
+ try:
+ p2p.RejectPeer("/foo")
+ raise Exception("Invalid RejectPeer accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid RejectPeer(): " + str(e))
+
+ tests = [{},
+ {'peer': 'foo'},
+ {'foo': "bar"},
+ {'iface': "abc"},
+ {'iface': 123}]
+ for t in tests:
+ try:
+ p2p.RemoveClient(t)
+ raise Exception("Invalid RemoveClient accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid RemoveClient(): " + str(e))
+
+ tests = [{'DiscoveryType': 'foo'},
+ {'RequestedDeviceTypes': 'foo'},
+ {'RequestedDeviceTypes': ['foo']},
+ {'RequestedDeviceTypes': ['1', '2', '3', '4', '5', '6', '7', '8',
+ '9', '10', '11', '12', '13', '14', '15',
+ '16', '17']},
+ {'RequestedDeviceTypes': dbus.Array([], signature="s")},
+ {'RequestedDeviceTypes': dbus.Array([['foo']], signature="as")},
+ {'RequestedDeviceTypes': dbus.Array([], signature="i")},
+ {'RequestedDeviceTypes': [dbus.ByteArray(b'12345678'),
+ dbus.ByteArray(b'1234567')]},
+ {'Foo': dbus.Int16(1)},
+ {'Foo': dbus.UInt16(1)},
+ {'Foo': dbus.Int64(1)},
+ {'Foo': dbus.UInt64(1)},
+ {'Foo': dbus.Double(1.23)},
+ {'Foo': dbus.Signature('s')},
+ {'Foo': 'bar'}]
+ for t in tests:
+ try:
+ p2p.Find(dbus.Dictionary(t))
+ raise Exception("Invalid Find accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid Find(): " + str(e))
+
+ for p in ["/foo",
+ "/fi/w1/wpa_supplicant1/Interfaces/1234",
+ "/fi/w1/wpa_supplicant1/Interfaces/1234/Networks/1234"]:
+ try:
+ p2p.RemovePersistentGroup(dbus.ObjectPath(p))
+ raise Exception("Invalid RemovePersistentGroup accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid RemovePersistentGroup: " + str(e))
+
+ try:
+ dev[0].request("P2P_SET disabled 1")
+ p2p.Listen(5)
+ raise Exception("Invalid Listen accepted")
+ except dbus.exceptions.DBusException as e:
+ if "UnknownError: Could not start P2P listen" not in str(e):
+ raise Exception("Unexpected error message for invalid Listen: " + str(e))
+ finally:
+ dev[0].request("P2P_SET disabled 0")
+
+ test_obj = bus.get_object(WPAS_DBUS_SERVICE, path, introspect=False)
+ test_p2p = dbus.Interface(test_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ try:
+ test_p2p.Listen("foo")
+ raise Exception("Invalid Listen accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid Listen: " + str(e))
+
+ try:
+ dev[0].request("P2P_SET disabled 1")
+ p2p.ExtendedListen(dbus.Dictionary({}))
+ raise Exception("Invalid ExtendedListen accepted")
+ except dbus.exceptions.DBusException as e:
+ if "UnknownError: failed to initiate a p2p_ext_listen" not in str(e):
+ raise Exception("Unexpected error message for invalid ExtendedListen: " + str(e))
+ finally:
+ dev[0].request("P2P_SET disabled 0")
+
+ try:
+ dev[0].request("P2P_SET disabled 1")
+ args = {'duration1': 30000, 'interval1': 102400,
+ 'duration2': 20000, 'interval2': 102400}
+ p2p.PresenceRequest(args)
+ raise Exception("Invalid PresenceRequest accepted")
+ except dbus.exceptions.DBusException as e:
+ if "UnknownError: Failed to invoke presence request" not in str(e):
+ raise Exception("Unexpected error message for invalid PresenceRequest: " + str(e))
+ finally:
+ dev[0].request("P2P_SET disabled 0")
+
+ try:
+ params = dbus.Dictionary({'frequency': dbus.Int32(-1)})
+ p2p.GroupAdd(params)
+ raise Exception("Invalid GroupAdd accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid GroupAdd: " + str(e))
+
+ try:
+ params = dbus.Dictionary({'persistent_group_object':
+ dbus.ObjectPath(path),
+ 'frequency': 2412})
+ p2p.GroupAdd(params)
+ raise Exception("Invalid GroupAdd accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid GroupAdd: " + str(e))
+
+ try:
+ p2p.Disconnect()
+ raise Exception("Invalid Disconnect accepted")
+ except dbus.exceptions.DBusException as e:
+ if "UnknownError: failed to disconnect" not in str(e):
+ raise Exception("Unexpected error message for invalid Disconnect: " + str(e))
+
+ try:
+ dev[0].request("P2P_SET disabled 1")
+ p2p.Flush()
+ raise Exception("Invalid Flush accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: P2P is not available for this interface" not in str(e):
+ raise Exception("Unexpected error message for invalid Flush: " + str(e))
+ finally:
+ dev[0].request("P2P_SET disabled 0")
+
+ try:
+ dev[0].request("P2P_SET disabled 1")
+ args = {'peer': path,
+ 'join': True,
+ 'wps_method': 'pbc',
+ 'frequency': 2412}
+ pin = p2p.Connect(args)
+ raise Exception("Invalid Connect accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: P2P is not available for this interface" not in str(e):
+ raise Exception("Unexpected error message for invalid Connect: " + str(e))
+ finally:
+ dev[0].request("P2P_SET disabled 0")
+
+ tests = [{'frequency': dbus.Int32(-1)},
+ {'wps_method': 'pbc'},
+ {'wps_method': 'foo'}]
+ for args in tests:
+ try:
+ pin = p2p.Connect(args)
+ raise Exception("Invalid Connect accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid Connect: " + str(e))
+
+ try:
+ dev[0].request("P2P_SET disabled 1")
+ args = {'peer': path}
+ pin = p2p.Invite(args)
+ raise Exception("Invalid Invite accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: P2P is not available for this interface" not in str(e):
+ raise Exception("Unexpected error message for invalid Invite: " + str(e))
+ finally:
+ dev[0].request("P2P_SET disabled 0")
+
+ try:
+ args = {'foo': 'bar'}
+ pin = p2p.Invite(args)
+ raise Exception("Invalid Invite accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid Connect: " + str(e))
+
+ tests = [(path, 'display', "InvalidArgs"),
+ (dbus.ObjectPath(path + "/Peers/00112233445566"),
+ 'display',
+ "UnknownError: Failed to send provision discovery request"),
+ (dbus.ObjectPath(path + "/Peers/00112233445566"),
+ 'keypad',
+ "UnknownError: Failed to send provision discovery request"),
+ (dbus.ObjectPath(path + "/Peers/00112233445566"),
+ 'pbc',
+ "UnknownError: Failed to send provision discovery request"),
+ (dbus.ObjectPath(path + "/Peers/00112233445566"),
+ 'pushbutton',
+ "UnknownError: Failed to send provision discovery request"),
+ (dbus.ObjectPath(path + "/Peers/00112233445566"),
+ 'foo', "InvalidArgs")]
+ for (p, method, err) in tests:
+ try:
+ p2p.ProvisionDiscoveryRequest(p, method)
+ raise Exception("Invalid ProvisionDiscoveryRequest accepted")
+ except dbus.exceptions.DBusException as e:
+ if err not in str(e):
+ raise Exception("Unexpected error message for invalid ProvisionDiscoveryRequest: " + str(e))
+
+ try:
+ dev[0].request("P2P_SET disabled 1")
+ if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Peers",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Get(Peers) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: P2P is not available for this interface" not in str(e):
+ raise Exception("Unexpected error message for invalid Get(Peers): " + str(e))
+ finally:
+ dev[0].request("P2P_SET disabled 0")
+
+def test_dbus_p2p_oom(dev, apdev):
+ """D-Bus P2P operations and OOM"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ with alloc_fail_dbus(dev[0], 1, "_wpa_dbus_dict_entry_get_string_array",
+ "Find", "InvalidArgs"):
+ p2p.Find(dbus.Dictionary({'Foo': ['bar']}))
+
+ with alloc_fail_dbus(dev[0], 2, "_wpa_dbus_dict_entry_get_string_array",
+ "Find", "InvalidArgs"):
+ p2p.Find(dbus.Dictionary({'Foo': ['bar']}))
+
+ with alloc_fail_dbus(dev[0], 10, "_wpa_dbus_dict_entry_get_string_array",
+ "Find", "InvalidArgs"):
+ p2p.Find(dbus.Dictionary({'Foo': ['1', '2', '3', '4', '5', '6', '7',
+ '8', '9']}))
+
+ with alloc_fail_dbus(dev[0], 1, ":=_wpa_dbus_dict_entry_get_binarray",
+ "Find", "InvalidArgs"):
+ p2p.Find(dbus.Dictionary({'Foo': [dbus.ByteArray(b'123')]}))
+
+ with alloc_fail_dbus(dev[0], 1, "_wpa_dbus_dict_entry_get_byte_array;_wpa_dbus_dict_entry_get_binarray",
+ "Find", "InvalidArgs"):
+ p2p.Find(dbus.Dictionary({'Foo': [dbus.ByteArray(b'123')]}))
+
+ with alloc_fail_dbus(dev[0], 2, "=_wpa_dbus_dict_entry_get_binarray",
+ "Find", "InvalidArgs"):
+ p2p.Find(dbus.Dictionary({'Foo': [dbus.ByteArray(b'123'),
+ dbus.ByteArray(b'123'),
+ dbus.ByteArray(b'123'),
+ dbus.ByteArray(b'123'),
+ dbus.ByteArray(b'123'),
+ dbus.ByteArray(b'123'),
+ dbus.ByteArray(b'123'),
+ dbus.ByteArray(b'123'),
+ dbus.ByteArray(b'123'),
+ dbus.ByteArray(b'123'),
+ dbus.ByteArray(b'123')]}))
+
+ with alloc_fail_dbus(dev[0], 1, "wpabuf_alloc_ext_data;_wpa_dbus_dict_entry_get_binarray",
+ "Find", "InvalidArgs"):
+ p2p.Find(dbus.Dictionary({'Foo': [dbus.ByteArray(b'123')]}))
+
+ with alloc_fail_dbus(dev[0], 1, "_wpa_dbus_dict_fill_value_from_variant;wpas_dbus_handler_p2p_find",
+ "Find", "InvalidArgs"):
+ p2p.Find(dbus.Dictionary({'Foo': path}))
+
+ with alloc_fail_dbus(dev[0], 1, "_wpa_dbus_dict_entry_get_byte_array",
+ "AddService", "InvalidArgs"):
+ args = {'service_type': 'bonjour',
+ 'response': dbus.ByteArray(500*b'b')}
+ p2p.AddService(args)
+
+ with alloc_fail_dbus(dev[0], 2, "_wpa_dbus_dict_entry_get_byte_array",
+ "AddService", "InvalidArgs"):
+ p2p.AddService(args)
+
+def test_dbus_p2p_discovery(dev, apdev):
+ """D-Bus P2P discovery"""
+ try:
+ run_dbus_p2p_discovery(dev, apdev)
+ finally:
+ dev[1].request("VENDOR_ELEM_REMOVE 1 *")
+
+def run_dbus_p2p_discovery(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ addr0 = dev[0].p2p_dev_addr()
+
+ dev[1].request("SET sec_device_type 1-0050F204-2")
+ dev[1].request("VENDOR_ELEM_ADD 1 dd0c0050f2041049000411223344")
+ dev[1].request("VENDOR_ELEM_ADD 1 dd06001122335566")
+ dev[1].p2p_listen()
+ addr1 = dev[1].p2p_dev_addr()
+ a1 = binascii.unhexlify(addr1.replace(':', ''))
+
+ wfd_devinfo = "00001c440028"
+ dev[2].request("SET wifi_display 1")
+ dev[2].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo)
+ wfd = binascii.unhexlify('000006' + wfd_devinfo)
+ dev[2].p2p_listen()
+ addr2 = dev[2].p2p_dev_addr()
+ a2 = binascii.unhexlify(addr2.replace(':', ''))
+
+ res = if_obj.GetAll(WPAS_DBUS_IFACE_P2PDEVICE,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if 'Peers' not in res:
+ raise Exception("GetAll result missing Peers")
+ if len(res['Peers']) != 0:
+ raise Exception("Unexpected peer(s) in the list")
+
+ args = {'DiscoveryType': 'social',
+ 'RequestedDeviceTypes': [dbus.ByteArray(b'12345678')],
+ 'Timeout': dbus.Int32(1)}
+ p2p.Find(dbus.Dictionary(args))
+ p2p.StopFind()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.found = False
+ self.found2 = False
+ self.found_prop = False
+ self.lost = False
+ self.find_stopped = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.deviceFoundProperties,
+ WPAS_DBUS_IFACE_P2PDEVICE, "DeviceFoundProperties")
+ self.add_signal(self.deviceLost, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceLost")
+ self.add_signal(self.provisionDiscoveryResponseEnterPin,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "ProvisionDiscoveryResponseEnterPin")
+ self.add_signal(self.findStopped, WPAS_DBUS_IFACE_P2PDEVICE,
+ "FindStopped")
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ res = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Peers",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) < 1:
+ raise Exception("Unexpected number of peers")
+ if path not in res:
+ raise Exception("Mismatch in peer object path")
+ peer_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+ res = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug("peer properties: " + str(res))
+
+ if res['DeviceAddress'] == a1:
+ if 'SecondaryDeviceTypes' not in res:
+ raise Exception("Missing SecondaryDeviceTypes")
+ sec = res['SecondaryDeviceTypes']
+ if len(sec) < 1:
+ raise Exception("Secondary device type missing")
+ if b"\x00\x01\x00\x50\xF2\x04\x00\x02" not in sec:
+ raise Exception("Secondary device type mismatch")
+
+ if 'VendorExtension' not in res:
+ raise Exception("Missing VendorExtension")
+ vendor = res['VendorExtension']
+ if len(vendor) < 1:
+ raise Exception("Vendor extension missing")
+ if b"\x11\x22\x33\x44" not in vendor:
+ raise Exception("Secondary device type mismatch")
+
+ if 'VSIE' not in res:
+ raise Exception("Missing VSIE")
+ vendor = res['VSIE']
+ if len(vendor) < 1:
+ raise Exception("VSIE missing")
+ if vendor != b"\xdd\x06\x00\x11\x22\x33\x55\x66":
+ raise Exception("VSIE mismatch")
+
+ self.found = True
+ elif res['DeviceAddress'] == a2:
+ if 'IEs' not in res:
+ raise Exception("IEs missing")
+ if res['IEs'] != wfd:
+ raise Exception("IEs mismatch")
+ self.found2 = True
+ else:
+ raise Exception("Unexpected peer device address")
+
+ if self.found and self.found2:
+ p2p.StopFind()
+ p2p.RejectPeer(path)
+ p2p.ProvisionDiscoveryRequest(path, 'display')
+
+ def deviceLost(self, path):
+ logger.debug("deviceLost: path=%s" % path)
+ if not self.found or not self.found2:
+ # This may happen if a previous test case ended up scheduling
+ # deviceLost event and that event did not get delivered before
+ # starting the next test execution.
+ logger.debug("Ignore deviceLost before the deviceFound events")
+ return
+ self.lost = True
+ try:
+ p2p.RejectPeer(path)
+ raise Exception("Invalid RejectPeer accepted")
+ except dbus.exceptions.DBusException as e:
+ if "UnknownError: Failed to call wpas_p2p_reject" not in str(e):
+ raise Exception("Unexpected error message for invalid RejectPeer(): " + str(e))
+ self.loop.quit()
+
+ def deviceFoundProperties(self, path, properties):
+ logger.debug("deviceFoundProperties: path=%s" % path)
+ logger.debug("peer properties: " + str(properties))
+ if properties['DeviceAddress'] == a1:
+ self.found_prop = True
+
+ def provisionDiscoveryResponseEnterPin(self, peer_object):
+ logger.debug("provisionDiscoveryResponseEnterPin - peer=%s" % peer_object)
+ p2p.Flush()
+
+ def findStopped(self):
+ logger.debug("findStopped")
+ self.find_stopped = True
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Find(dbus.Dictionary({'DiscoveryType': 'social',
+ 'Timeout': dbus.Int32(10)}))
+ return False
+
+ def success(self):
+ return self.found and self.lost and self.found2 and self.find_stopped
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+ dev[1].request("VENDOR_ELEM_REMOVE 1 *")
+ dev[1].p2p_stop_find()
+
+ p2p.Listen(1)
+ dev[2].p2p_stop_find()
+ dev[2].request("P2P_FLUSH")
+ if not dev[2].discover_peer(addr0):
+ raise Exception("Peer not found")
+ p2p.StopFind()
+ dev[2].p2p_stop_find()
+
+ try:
+ p2p.ExtendedListen(dbus.Dictionary({'foo': 100}))
+ raise Exception("Invalid ExtendedListen accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid ExtendedListen(): " + str(e))
+
+ p2p.ExtendedListen(dbus.Dictionary({'period': 100, 'interval': 1000}))
+ p2p.ExtendedListen(dbus.Dictionary({}))
+ dev[0].global_request("P2P_EXT_LISTEN")
+
+def test_dbus_p2p_discovery_freq(dev, apdev):
+ """D-Bus P2P discovery on a specific non-social channel"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ addr1 = dev[1].p2p_dev_addr()
+ autogo(dev[1], freq=2422)
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.found = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(5000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ self.found = True
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Find(dbus.Dictionary({'freq': 2422}))
+ return False
+
+ def success(self):
+ return self.found
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+ dev[1].remove_group()
+ p2p.StopFind()
+
+def test_dbus_p2p_service_discovery(dev, apdev):
+ """D-Bus P2P service discovery"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ bonjour_query = dbus.ByteArray(binascii.unhexlify('0b5f6166706f766572746370c00c000c01'))
+ bonjour_response = dbus.ByteArray(binascii.unhexlify('074578616d706c65c027'))
+
+ args = {'service_type': 'bonjour',
+ 'query': bonjour_query,
+ 'response': bonjour_response}
+ p2p.AddService(args)
+ p2p.FlushService()
+ p2p.AddService(args)
+
+ try:
+ p2p.DeleteService(args)
+ raise Exception("Invalid DeleteService() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid DeleteService(): " + str(e))
+
+ args = {'service_type': 'bonjour',
+ 'query': bonjour_query}
+ p2p.DeleteService(args)
+ try:
+ p2p.DeleteService(args)
+ raise Exception("Invalid DeleteService() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid DeleteService(): " + str(e))
+
+ args = {'service_type': 'upnp',
+ 'version': 0x10,
+ 'service': 'uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice'}
+ p2p.AddService(args)
+ p2p.DeleteService(args)
+ try:
+ p2p.DeleteService(args)
+ raise Exception("Invalid DeleteService() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid DeleteService(): " + str(e))
+
+ tests = [{'service_type': 'foo'},
+ {'service_type': 'foo', 'query': bonjour_query},
+ {'service_type': 'upnp'},
+ {'service_type': 'upnp', 'version': 0x10},
+ {'service_type': 'upnp',
+ 'service': 'uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice'},
+ {'version': 0x10,
+ 'service': 'uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice'},
+ {'service_type': 'upnp', 'foo': 'bar'},
+ {'service_type': 'bonjour'},
+ {'service_type': 'bonjour', 'query': 'foo'},
+ {'service_type': 'bonjour', 'foo': 'bar'}]
+ for args in tests:
+ try:
+ p2p.DeleteService(args)
+ raise Exception("Invalid DeleteService() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid DeleteService(): " + str(e))
+
+ tests = [{'service_type': 'foo'},
+ {'service_type': 'upnp'},
+ {'service_type': 'upnp', 'version': 0x10},
+ {'service_type': 'upnp',
+ 'service': 'uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice'},
+ {'version': 0x10,
+ 'service': 'uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice'},
+ {'service_type': 'upnp', 'foo': 'bar'},
+ {'service_type': 'bonjour'},
+ {'service_type': 'bonjour', 'query': 'foo'},
+ {'service_type': 'bonjour', 'response': 'foo'},
+ {'service_type': 'bonjour', 'query': bonjour_query},
+ {'service_type': 'bonjour', 'response': bonjour_response},
+ {'service_type': 'bonjour', 'query': dbus.ByteArray(500*b'a')},
+ {'service_type': 'bonjour', 'foo': 'bar'}]
+ for args in tests:
+ try:
+ p2p.AddService(args)
+ raise Exception("Invalid AddService() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid AddService(): " + str(e))
+
+ args = {'tlv': dbus.ByteArray(b"\x02\x00\x00\x01")}
+ ref = p2p.ServiceDiscoveryRequest(args)
+ p2p.ServiceDiscoveryCancelRequest(ref)
+ try:
+ p2p.ServiceDiscoveryCancelRequest(ref)
+ raise Exception("Invalid ServiceDiscoveryCancelRequest() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid AddService(): " + str(e))
+ try:
+ p2p.ServiceDiscoveryCancelRequest(dbus.UInt64(0))
+ raise Exception("Invalid ServiceDiscoveryCancelRequest() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid AddService(): " + str(e))
+
+ args = {'service_type': 'upnp',
+ 'version': 0x10,
+ 'service': 'ssdp:foo'}
+ ref = p2p.ServiceDiscoveryRequest(args)
+ p2p.ServiceDiscoveryCancelRequest(ref)
+
+ tests = [{'service_type': 'foo'},
+ {'foo': 'bar'},
+ {'tlv': 'foo'},
+ {},
+ {'version': 0},
+ {'service_type': 'upnp',
+ 'service': 'ssdp:foo'},
+ {'service_type': 'upnp',
+ 'version': 0x10},
+ {'service_type': 'upnp',
+ 'version': 0x10,
+ 'service': 'ssdp:foo',
+ 'peer_object': dbus.ObjectPath(path + "/Peers")},
+ {'service_type': 'upnp',
+ 'version': 0x10,
+ 'service': 'ssdp:foo',
+ 'peer_object': path + "/Peers"},
+ {'service_type': 'upnp',
+ 'version': 0x10,
+ 'service': 'ssdp:foo',
+ 'peer_object': dbus.ObjectPath(path + "/Peers/00112233445566")}]
+ for args in tests:
+ try:
+ p2p.ServiceDiscoveryRequest(args)
+ raise Exception("Invalid ServiceDiscoveryRequest accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid ServiceDiscoveryRequest(): " + str(e))
+
+ args = {'foo': 'bar'}
+ try:
+ p2p.ServiceDiscoveryResponse(dbus.Dictionary(args, signature='sv'))
+ raise Exception("Invalid ServiceDiscoveryResponse accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid ServiceDiscoveryResponse(): " + str(e))
+
+def test_dbus_p2p_service_discovery_query(dev, apdev):
+ """D-Bus P2P service discovery query"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ addr0 = dev[0].p2p_dev_addr()
+ dev[1].request("P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027")
+ dev[1].p2p_listen()
+ addr1 = dev[1].p2p_dev_addr()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.serviceDiscoveryResponse,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "ServiceDiscoveryResponse", byte_arrays=True)
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ args = {'peer_object': path,
+ 'tlv': dbus.ByteArray(b"\x02\x00\x00\x01")}
+ p2p.ServiceDiscoveryRequest(args)
+
+ def serviceDiscoveryResponse(self, sd_request):
+ logger.debug("serviceDiscoveryResponse: sd_request=%s" % str(sd_request))
+ self.done = True
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Find(dbus.Dictionary({'DiscoveryType': 'social',
+ 'Timeout': dbus.Int32(10)}))
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+ dev[1].p2p_stop_find()
+
+def test_dbus_p2p_service_discovery_external(dev, apdev):
+ """D-Bus P2P service discovery with external response"""
+ try:
+ _test_dbus_p2p_service_discovery_external(dev, apdev)
+ finally:
+ dev[0].request("P2P_SERV_DISC_EXTERNAL 0")
+
+def _test_dbus_p2p_service_discovery_external(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ resp = "0300000101"
+
+ dev[1].request("P2P_FLUSH")
+ dev[1].request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+ dev[1].p2p_find(social=True)
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.sd = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.serviceDiscoveryRequest,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "ServiceDiscoveryRequest")
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+
+ def serviceDiscoveryRequest(self, sd_request):
+ logger.debug("serviceDiscoveryRequest: sd_request=%s" % str(sd_request))
+ self.sd = True
+ args = {'peer_object': sd_request['peer_object'],
+ 'frequency': sd_request['frequency'],
+ 'dialog_token': sd_request['dialog_token'],
+ 'tlvs': dbus.ByteArray(binascii.unhexlify(resp))}
+ p2p.ServiceDiscoveryResponse(dbus.Dictionary(args, signature='sv'))
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.ServiceDiscoveryExternal(1)
+ p2p.ServiceUpdate()
+ p2p.Listen(15)
+ return False
+
+ def success(self):
+ return self.sd
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+ ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=5)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr0 not in ev:
+ raise Exception("Unexpected address in SD Response: " + ev)
+ if ev.split(' ')[4] != resp:
+ raise Exception("Unexpected response data SD Response: " + ev)
+ dev[1].p2p_stop_find()
+
+ p2p.StopFind()
+ p2p.ServiceDiscoveryExternal(0)
+
+def test_dbus_p2p_autogo(dev, apdev):
+ """D-Bus P2P autonomous GO"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ addr0 = dev[0].p2p_dev_addr()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.first = True
+ self.waiting_end = False
+ self.exceptions = False
+ self.deauthorized = False
+ self.done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.persistentGroupAdded,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "PersistentGroupAdded")
+ self.add_signal(self.persistentGroupRemoved,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "PersistentGroupRemoved")
+ self.add_signal(self.provisionDiscoveryRequestDisplayPin,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "ProvisionDiscoveryRequestDisplayPin")
+ self.add_signal(self.staAuthorized, WPAS_DBUS_IFACE,
+ "StaAuthorized")
+ self.add_signal(self.staDeauthorized, WPAS_DBUS_IFACE,
+ "StaDeauthorized")
+ self.loop.run()
+ return self
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ self.group = properties['group_object']
+ self.g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ role = self.g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Role",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if role != "GO":
+ self.exceptions = True
+ raise Exception("Unexpected role reported: " + role)
+ group = self.g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Group",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if group != properties['group_object']:
+ self.exceptions = True
+ raise Exception("Unexpected Group reported: " + str(group))
+ go = self.g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "PeerGO",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if go != '/':
+ self.exceptions = True
+ raise Exception("Unexpected PeerGO value: " + str(go))
+ if self.first:
+ self.first = False
+ logger.info("Remove persistent group instance")
+ group_p2p = dbus.Interface(self.g_if_obj,
+ WPAS_DBUS_IFACE_P2PDEVICE)
+ group_p2p.Disconnect()
+ else:
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 join")
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ if self.waiting_end:
+ logger.info("Remove persistent group")
+ p2p.RemovePersistentGroup(self.persistent)
+ else:
+ logger.info("Re-start persistent group")
+ params = dbus.Dictionary({'persistent_group_object':
+ self.persistent,
+ 'frequency': 2412})
+ p2p.GroupAdd(params)
+
+ def persistentGroupAdded(self, path, properties):
+ logger.debug("persistentGroupAdded: %s %s" % (path, str(properties)))
+ self.persistent = path
+
+ def persistentGroupRemoved(self, path):
+ logger.debug("persistentGroupRemoved: %s" % path)
+ self.done = True
+ self.loop.quit()
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ peer_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+ self.peer = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug('peer properties: ' + str(self.peer))
+
+ def provisionDiscoveryRequestDisplayPin(self, peer_object, pin):
+ logger.debug("provisionDiscoveryRequestDisplayPin - peer=%s pin=%s" % (peer_object, pin))
+ self.peer_path = peer_object
+ peer = binascii.unhexlify(peer_object.split('/')[-1])
+ addr = ':'.join(["%02x" % i for i in struct.unpack('6B', peer)])
+
+ params = {'Role': 'registrar',
+ 'P2PDeviceAddress': self.peer['DeviceAddress'],
+ 'Bssid': self.peer['DeviceAddress'],
+ 'Type': 'pin'}
+ wps = dbus.Interface(self.g_if_obj, WPAS_DBUS_IFACE_WPS)
+ try:
+ wps.Start(params)
+ self.exceptions = True
+ raise Exception("Invalid WPS.Start() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ self.exceptions = True
+ raise Exception("Unexpected error message: " + str(e))
+ params = {'Role': 'registrar',
+ 'P2PDeviceAddress': self.peer['DeviceAddress'],
+ 'Type': 'pin',
+ 'Pin': '12345670'}
+ logger.info("Authorize peer to connect to the group")
+ wps.Start(params)
+
+ def staAuthorized(self, name):
+ logger.debug("staAuthorized: " + name)
+ peer_obj = bus.get_object(WPAS_DBUS_SERVICE, self.peer_path)
+ res = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug("Peer properties: " + str(res))
+ if 'Groups' not in res or len(res['Groups']) != 1:
+ self.exceptions = True
+ raise Exception("Unexpected number of peer Groups entries")
+ if res['Groups'][0] != self.group:
+ self.exceptions = True
+ raise Exception("Unexpected peer Groups[0] value")
+
+ g_obj = bus.get_object(WPAS_DBUS_SERVICE, self.group)
+ res = g_obj.GetAll(WPAS_DBUS_GROUP,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug("Group properties: " + str(res))
+ if 'Members' not in res or len(res['Members']) != 1:
+ self.exceptions = True
+ raise Exception("Unexpected number of group members")
+
+ ext = dbus.ByteArray(b"\x11\x22\x33\x44")
+ # Earlier implementation of this interface was a bit strange. The
+ # property is defined to have aay signature and that is what the
+ # getter returned. However, the setter expected there to be a
+ # dictionary with 'WPSVendorExtensions' as the key surrounding these
+ # values.. The current implementations maintains support for that
+ # for backwards compability reasons. Verify that encoding first.
+ vals = dbus.Dictionary({'WPSVendorExtensions': [ext]},
+ signature='sv')
+ g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', vals,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res = g_obj.Get(WPAS_DBUS_GROUP, 'WPSVendorExtensions',
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ if len(res) != 1:
+ self.exceptions = True
+ raise Exception("Unexpected number of vendor extensions")
+ if res[0] != ext:
+ self.exceptions = True
+ raise Exception("Vendor extension value changed")
+
+ # And now verify that the more appropriate encoding is accepted as
+ # well.
+ res.append(dbus.ByteArray(b'\xaa\xbb\xcc\xdd\xee\xff'))
+ g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', res,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res2 = g_obj.Get(WPAS_DBUS_GROUP, 'WPSVendorExtensions',
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ if len(res) != 2:
+ self.exceptions = True
+ raise Exception("Unexpected number of vendor extensions")
+ if res[0] != res2[0] or res[1] != res2[1]:
+ self.exceptions = True
+ raise Exception("Vendor extension value changed")
+
+ for i in range(10):
+ res.append(dbus.ByteArray(b'\xaa\xbb'))
+ try:
+ g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', res,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ self.exceptions = True
+ raise Exception("Invalid Set(WPSVendorExtensions) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed" not in str(e):
+ self.exceptions = True
+ raise Exception("Unexpected error message for invalid Set(WPSVendorExtensions): " + str(e))
+
+ vals = dbus.Dictionary({'Foo': [ext]}, signature='sv')
+ try:
+ g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', vals,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ self.exceptions = True
+ raise Exception("Invalid Set(WPSVendorExtensions) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ self.exceptions = True
+ raise Exception("Unexpected error message for invalid Set(WPSVendorExtensions): " + str(e))
+
+ vals = ["foo"]
+ try:
+ g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', vals,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ self.exceptions = True
+ raise Exception("Invalid Set(WPSVendorExtensions) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed" not in str(e):
+ self.exceptions = True
+ raise Exception("Unexpected error message for invalid Set(WPSVendorExtensions): " + str(e))
+
+ vals = [["foo"]]
+ try:
+ g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', vals,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ self.exceptions = True
+ raise Exception("Invalid Set(WPSVendorExtensions) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed" not in str(e):
+ self.exceptions = True
+ raise Exception("Unexpected error message for invalid Set(WPSVendorExtensions): " + str(e))
+
+ p2p.RemoveClient({'peer': self.peer_path})
+
+ self.waiting_end = True
+ group_p2p = dbus.Interface(self.g_if_obj,
+ WPAS_DBUS_IFACE_P2PDEVICE)
+ group_p2p.Disconnect()
+
+ def staDeauthorized(self, name):
+ logger.debug("staDeauthorized: " + name)
+ self.deauthorized = True
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ params = dbus.Dictionary({'persistent': True,
+ 'frequency': 2412})
+ logger.info("Add a persistent group")
+ p2p.GroupAdd(params)
+ return False
+
+ def success(self):
+ return self.done and self.deauthorized and not self.exceptions
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+ dev[1].wait_go_ending_session()
+
+def test_dbus_p2p_autogo_pbc(dev, apdev):
+ """D-Bus P2P autonomous GO and PBC"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ addr0 = dev[0].p2p_dev_addr()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.first = True
+ self.waiting_end = False
+ self.done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.provisionDiscoveryPBCRequest,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "ProvisionDiscoveryPBCRequest")
+ self.add_signal(self.staAuthorized, WPAS_DBUS_IFACE,
+ "StaAuthorized")
+ self.loop.run()
+ return self
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ self.group = properties['group_object']
+ self.g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.global_request("P2P_CONNECT " + addr0 + " pbc join")
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ self.done = True
+ self.loop.quit()
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ peer_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+ self.peer = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug('peer properties: ' + str(self.peer))
+
+ def provisionDiscoveryPBCRequest(self, peer_object):
+ logger.debug("provisionDiscoveryPBCRequest - peer=%s" % peer_object)
+ self.peer_path = peer_object
+ peer = binascii.unhexlify(peer_object.split('/')[-1])
+ addr = ':'.join(["%02x" % i for i in struct.unpack('6B', peer)])
+ params = {'Role': 'registrar',
+ 'P2PDeviceAddress': self.peer['DeviceAddress'],
+ 'Type': 'pbc'}
+ logger.info("Authorize peer to connect to the group")
+ wps = dbus.Interface(self.g_if_obj, WPAS_DBUS_IFACE_WPS)
+ wps.Start(params)
+
+ def staAuthorized(self, name):
+ logger.debug("staAuthorized: " + name)
+ group_p2p = dbus.Interface(self.g_if_obj,
+ WPAS_DBUS_IFACE_P2PDEVICE)
+ group_p2p.Disconnect()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ params = dbus.Dictionary({'frequency': 2412})
+ p2p.GroupAdd(params)
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+ dev[1].wait_go_ending_session()
+ dev[1].flush_scan_cache()
+
+def test_dbus_p2p_autogo_legacy(dev, apdev):
+ """D-Bus P2P autonomous GO and legacy STA"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ addr0 = dev[0].p2p_dev_addr()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.staAuthorized, WPAS_DBUS_IFACE,
+ "StaAuthorized")
+ self.loop.run()
+ return self
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ g_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['group_object'])
+ res = g_obj.GetAll(WPAS_DBUS_GROUP,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ bssid = ':'.join(["%02x" % i for i in struct.unpack('6B', res['BSSID'])])
+
+ pin = '12345670'
+ params = {'Role': 'enrollee',
+ 'Type': 'pin',
+ 'Pin': pin}
+ g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ wps = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_WPS)
+ wps.Start(params)
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.scan_for_bss(bssid, freq=2412)
+ dev1.request("WPS_PIN " + bssid + " " + pin)
+ self.group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ self.done = True
+ self.loop.quit()
+
+ def staAuthorized(self, name):
+ logger.debug("staAuthorized: " + name)
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.request("DISCONNECT")
+ self.group_p2p.Disconnect()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ params = dbus.Dictionary({'frequency': 2412})
+ p2p.GroupAdd(params)
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_join(dev, apdev):
+ """D-Bus P2P join an autonomous GO"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ dev[1].p2p_start_go(freq=2412)
+ dev1_group_ifname = dev[1].group_ifname
+ dev[2].p2p_listen()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+ self.peer = None
+ self.go = None
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.invitationResult, WPAS_DBUS_IFACE_P2PDEVICE,
+ "InvitationResult")
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ peer_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+ res = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug('peer properties: ' + str(res))
+ if addr2.replace(':', '') in path:
+ self.peer = path
+ elif addr1.replace(':', '') in path:
+ self.go = path
+ if self.peer and self.go:
+ logger.info("Join the group")
+ p2p.StopFind()
+ args = {'peer': self.go,
+ 'join': True,
+ 'wps_method': 'pin',
+ 'frequency': 2412}
+ pin = p2p.Connect(args)
+
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.group_ifname = dev1_group_ifname
+ dev1.group_request("WPS_PIN any " + pin)
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ role = g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Role",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if role != "client":
+ raise Exception("Unexpected role reported: " + role)
+ group = g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "Group",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if group != properties['group_object']:
+ raise Exception("Unexpected Group reported: " + str(group))
+ go = g_if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "PeerGO",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if go != self.go:
+ raise Exception("Unexpected PeerGO value: " + str(go))
+
+ g_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['group_object'])
+ res = g_obj.GetAll(WPAS_DBUS_GROUP,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug("Group properties: " + str(res))
+
+ ext = dbus.ByteArray(b"\x11\x22\x33\x44")
+ try:
+ # Set(WPSVendorExtensions) not allowed for P2P Client
+ g_obj.Set(WPAS_DBUS_GROUP, 'WPSVendorExtensions', res,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(WPSVendorExtensions) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: Failed to set property" not in str(e):
+ raise Exception("Unexpected error message for invalid Set(WPSVendorExtensions): " + str(e))
+
+ group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ args = {'duration1': 30000, 'interval1': 102400,
+ 'duration2': 20000, 'interval2': 102400}
+ group_p2p.PresenceRequest(args)
+
+ args = {'peer': self.peer}
+ group_p2p.Invite(args)
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ self.done = True
+ self.loop.quit()
+
+ def invitationResult(self, result):
+ logger.debug("invitationResult: " + str(result))
+ if result['status'] != 1:
+ raise Exception("Unexpected invitation result: " + str(result))
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.group_ifname = dev1_group_ifname
+ dev1.remove_group()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+ dev[2].p2p_stop_find()
+
+def test_dbus_p2p_invitation_received(dev, apdev):
+ """D-Bus P2P and InvitationReceived"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ form(dev[0], dev[1])
+ addr0 = dev[0].p2p_dev_addr()
+ dev[0].p2p_listen()
+ dev[0].global_request("SET persistent_reconnect 0")
+
+ if not dev[1].discover_peer(addr0, social=True):
+ raise Exception("Peer " + addr0 + " not found")
+ peer = dev[1].get_peer(addr0)
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.invitationReceived, WPAS_DBUS_IFACE_P2PDEVICE,
+ "InvitationReceived")
+ self.loop.run()
+ return self
+
+ def invitationReceived(self, result):
+ logger.debug("invitationReceived: " + str(result))
+ self.done = True
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ cmd = "P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr0
+ dev1.global_request(cmd)
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+
+def test_dbus_p2p_config(dev, apdev):
+ """D-Bus Get/Set P2PDeviceConfig"""
+ try:
+ _test_dbus_p2p_config(dev, apdev)
+ finally:
+ dev[0].request("P2P_SET ssid_postfix ")
+
+def _test_dbus_p2p_config(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ res = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ if_obj.Set(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig", res,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res2 = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+
+ if len(res) != len(res2):
+ raise Exception("Different number of parameters")
+ for k in res:
+ if res[k] != res2[k]:
+ raise Exception("Parameter %s value changes" % k)
+
+ changes = {'SsidPostfix': 'foo',
+ 'VendorExtension': [dbus.ByteArray(b'\x11\x22\x33\x44')],
+ 'SecondaryDeviceTypes': [dbus.ByteArray(b'\x11\x22\x33\x44\x55\x66\x77\x88')]}
+ if_obj.Set(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+ dbus.Dictionary(changes, signature='sv'),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ res2 = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug("P2PDeviceConfig: " + str(res2))
+ if 'VendorExtension' not in res2 or len(res2['VendorExtension']) != 1:
+ raise Exception("VendorExtension does not match")
+ if 'SecondaryDeviceTypes' not in res2 or len(res2['SecondaryDeviceTypes']) != 1:
+ raise Exception("SecondaryDeviceType does not match")
+
+ changes = {'SsidPostfix': '',
+ 'VendorExtension': dbus.Array([], signature="ay"),
+ 'SecondaryDeviceTypes': dbus.Array([], signature="ay")}
+ if_obj.Set(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+ dbus.Dictionary(changes, signature='sv'),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+
+ res3 = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug("P2PDeviceConfig: " + str(res3))
+ if 'VendorExtension' in res3:
+ raise Exception("VendorExtension not removed")
+ if 'SecondaryDeviceTypes' in res3:
+ raise Exception("SecondaryDeviceType not removed")
+
+ try:
+ dev[0].request("P2P_SET disabled 1")
+ if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ raise Exception("Invalid Get(P2PDeviceConfig) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: P2P is not available for this interface" not in str(e):
+ raise Exception("Unexpected error message for invalid Invite: " + str(e))
+ finally:
+ dev[0].request("P2P_SET disabled 0")
+
+ try:
+ dev[0].request("P2P_SET disabled 1")
+ changes = {'SsidPostfix': 'foo'}
+ if_obj.Set(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+ dbus.Dictionary(changes, signature='sv'),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(P2PDeviceConfig) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "Error.Failed: P2P is not available for this interface" not in str(e):
+ raise Exception("Unexpected error message for invalid Invite: " + str(e))
+ finally:
+ dev[0].request("P2P_SET disabled 0")
+
+ tests = [{'DeviceName': 123},
+ {'SsidPostfix': 123},
+ {'Foo': 'Bar'}]
+ for changes in tests:
+ try:
+ if_obj.Set(WPAS_DBUS_IFACE_P2PDEVICE, "P2PDeviceConfig",
+ dbus.Dictionary(changes, signature='sv'),
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ raise Exception("Invalid Set(P2PDeviceConfig) accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid Invite: " + str(e))
+
+def test_dbus_p2p_persistent(dev, apdev):
+ """D-Bus P2P persistent group"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.persistentGroupAdded,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "PersistentGroupAdded")
+ self.loop.run()
+ return self
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ group_p2p.Disconnect()
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ self.loop.quit()
+
+ def persistentGroupAdded(self, path, properties):
+ logger.debug("persistentGroupAdded: %s %s" % (path, str(properties)))
+ self.persistent = path
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ params = dbus.Dictionary({'persistent': True,
+ 'frequency': 2412})
+ logger.info("Add a persistent group")
+ p2p.GroupAdd(params)
+ return False
+
+ def success(self):
+ return True
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+ persistent = t.persistent
+
+ p_obj = bus.get_object(WPAS_DBUS_SERVICE, persistent)
+ res = p_obj.Get(WPAS_DBUS_PERSISTENT_GROUP, "Properties",
+ dbus_interface=dbus.PROPERTIES_IFACE, byte_arrays=True)
+ logger.info("Persistent group Properties: " + str(res))
+ vals = dbus.Dictionary({'ssid': 'DIRECT-foo'}, signature='sv')
+ p_obj.Set(WPAS_DBUS_PERSISTENT_GROUP, "Properties", vals,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ res2 = p_obj.Get(WPAS_DBUS_PERSISTENT_GROUP, "Properties",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(res) != len(res2):
+ raise Exception("Different number of parameters")
+ for k in res:
+ if k != 'ssid' and res[k] != res2[k]:
+ raise Exception("Parameter %s value changes" % k)
+ if res2['ssid'] != '"DIRECT-foo"':
+ raise Exception("Unexpected ssid")
+
+ args = dbus.Dictionary({'ssid': 'DIRECT-testing',
+ 'psk': '1234567890'}, signature='sv')
+ group = p2p.AddPersistentGroup(args)
+
+ groups = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "PersistentGroups",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(groups) != 2:
+ raise Exception("Unexpected number of persistent groups: " + str(groups))
+
+ p2p.RemoveAllPersistentGroups()
+
+ groups = if_obj.Get(WPAS_DBUS_IFACE_P2PDEVICE, "PersistentGroups",
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if len(groups) != 0:
+ raise Exception("Unexpected number of persistent groups: " + str(groups))
+
+ try:
+ p2p.RemovePersistentGroup(persistent)
+ raise Exception("Invalid RemovePersistentGroup accepted")
+ except dbus.exceptions.DBusException as e:
+ if "NetworkUnknown: There is no such persistent group" not in str(e):
+ raise Exception("Unexpected error message for invalid RemovePersistentGroup: " + str(e))
+
+def test_dbus_p2p_reinvoke_persistent(dev, apdev):
+ """D-Bus P2P reinvoke persistent group"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ addr0 = dev[0].p2p_dev_addr()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.first = True
+ self.waiting_end = False
+ self.done = False
+ self.invited = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.persistentGroupAdded,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "PersistentGroupAdded")
+ self.add_signal(self.provisionDiscoveryRequestDisplayPin,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "ProvisionDiscoveryRequestDisplayPin")
+ self.add_signal(self.staAuthorized, WPAS_DBUS_IFACE,
+ "StaAuthorized")
+ self.loop.run()
+ return self
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ self.g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ if not self.invited:
+ g_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['group_object'])
+ res = g_obj.GetAll(WPAS_DBUS_GROUP,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ bssid = ':'.join(["%02x" % i for i in struct.unpack('6B', res['BSSID'])])
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.scan_for_bss(bssid, freq=2412)
+ dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 join")
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ if self.invited:
+ self.done = True
+ self.loop.quit()
+ else:
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.global_request("SET persistent_reconnect 1")
+ dev1.p2p_listen()
+
+ args = {'persistent_group_object': dbus.ObjectPath(path),
+ 'peer': self.peer_path}
+ try:
+ pin = p2p.Invite(args)
+ raise Exception("Invalid Invite accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid Invite: " + str(e))
+
+ args = {'persistent_group_object': self.persistent,
+ 'peer': self.peer_path}
+ pin = p2p.Invite(args)
+ self.invited = True
+
+ self.sta_group_ev = dev1.wait_global_event(["P2P-GROUP-STARTED"],
+ timeout=15)
+ if self.sta_group_ev is None:
+ raise Exception("P2P-GROUP-STARTED event not seen")
+
+ def persistentGroupAdded(self, path, properties):
+ logger.debug("persistentGroupAdded: %s %s" % (path, str(properties)))
+ self.persistent = path
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ peer_obj = bus.get_object(WPAS_DBUS_SERVICE, path)
+ self.peer = peer_obj.GetAll(WPAS_DBUS_P2P_PEER,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+
+ def provisionDiscoveryRequestDisplayPin(self, peer_object, pin):
+ logger.debug("provisionDiscoveryRequestDisplayPin - peer=%s pin=%s" % (peer_object, pin))
+ self.peer_path = peer_object
+ peer = binascii.unhexlify(peer_object.split('/')[-1])
+ addr = ':'.join(["%02x" % i for i in struct.unpack('6B', peer)])
+ params = {'Role': 'registrar',
+ 'P2PDeviceAddress': self.peer['DeviceAddress'],
+ 'Bssid': self.peer['DeviceAddress'],
+ 'Type': 'pin',
+ 'Pin': '12345670'}
+ logger.info("Authorize peer to connect to the group")
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ wps = dbus.Interface(self.g_if_obj, WPAS_DBUS_IFACE_WPS)
+ wps.Start(params)
+ self.sta_group_ev = dev1.wait_global_event(["P2P-GROUP-STARTED"],
+ timeout=15)
+ if self.sta_group_ev is None:
+ raise Exception("P2P-GROUP-STARTED event not seen")
+
+ def staAuthorized(self, name):
+ logger.debug("staAuthorized: " + name)
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.group_form_result(self.sta_group_ev)
+ dev1.remove_group()
+ ev = dev1.wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+ if ev is None:
+ raise Exception("Group removal timed out")
+ group_p2p = dbus.Interface(self.g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ group_p2p.Disconnect()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ params = dbus.Dictionary({'persistent': True,
+ 'frequency': 2412})
+ logger.info("Add a persistent group")
+ p2p.GroupAdd(params)
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_go_neg_rx(dev, apdev):
+ """D-Bus P2P GO Negotiation receive"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ addr0 = dev[0].p2p_dev_addr()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.goNegotiationRequest,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "GONegotiationRequest",
+ byte_arrays=True)
+ self.add_signal(self.goNegotiationSuccess,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "GONegotiationSuccess",
+ byte_arrays=True)
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+
+ def goNegotiationRequest(self, path, dev_passwd_id, go_intent=0):
+ logger.debug("goNegotiationRequest: path=%s dev_passwd_id=%d go_intent=%d" % (path, dev_passwd_id, go_intent))
+ if dev_passwd_id != 1:
+ raise Exception("Unexpected dev_passwd_id=%d" % dev_passwd_id)
+ args = {'peer': path, 'wps_method': 'display', 'pin': '12345670',
+ 'go_intent': 15, 'persistent': False, 'frequency': 5175}
+ try:
+ p2p.Connect(args)
+ raise Exception("Invalid Connect accepted")
+ except dbus.exceptions.DBusException as e:
+ if "ConnectChannelUnsupported" not in str(e):
+ raise Exception("Unexpected error message for invalid Connect: " + str(e))
+
+ args = {'peer': path, 'wps_method': 'display', 'pin': '12345670',
+ 'go_intent': 15, 'persistent': False}
+ p2p.Connect(args)
+
+ def goNegotiationSuccess(self, properties):
+ logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ group_p2p.Disconnect()
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ self.done = True
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Listen(10)
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ if not dev1.discover_peer(addr0):
+ raise Exception("Peer not found")
+ dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 enter")
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_go_neg_auth(dev, apdev):
+ """D-Bus P2P GO Negotiation authorized"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ addr0 = dev[0].p2p_dev_addr()
+ dev[1].p2p_listen()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+ self.peer_joined = False
+ self.peer_disconnected = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.goNegotiationSuccess,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "GONegotiationSuccess",
+ byte_arrays=True)
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.staDeauthorized, WPAS_DBUS_IFACE,
+ "StaDeauthorized")
+ self.add_signal(self.peerJoined, WPAS_DBUS_GROUP,
+ "PeerJoined")
+ self.add_signal(self.peerDisconnected, WPAS_DBUS_GROUP,
+ "PeerDisconnected")
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ args = {'peer': path, 'wps_method': 'keypad',
+ 'go_intent': 15, 'authorize_only': True}
+ try:
+ p2p.Connect(args)
+ raise Exception("Invalid Connect accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e):
+ raise Exception("Unexpected error message for invalid Connect: " + str(e))
+
+ args = {'peer': path, 'wps_method': 'keypad', 'pin': '12345670',
+ 'go_intent': 15, 'authorize_only': True}
+ p2p.Connect(args)
+ p2p.Listen(10)
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ if not dev1.discover_peer(addr0):
+ raise Exception("Peer not found")
+ dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 display go_intent=0")
+ ev = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ self.sta_group_ev = ev
+
+ def goNegotiationSuccess(self, properties):
+ logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ self.g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.group_form_result(self.sta_group_ev)
+ dev1.remove_group()
+
+ def staDeauthorized(self, name):
+ logger.debug("staDeuthorized: " + name)
+ group_p2p = dbus.Interface(self.g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ group_p2p.Disconnect()
+
+ def peerJoined(self, peer):
+ logger.debug("peerJoined: " + peer)
+ self.peer_joined = True
+
+ def peerDisconnected(self, peer):
+ logger.debug("peerDisconnected: " + peer)
+ self.peer_disconnected = True
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ self.done = True
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+ return False
+
+ def success(self):
+ return self.done and self.peer_joined and self.peer_disconnected
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_go_neg_init(dev, apdev):
+ """D-Bus P2P GO Negotiation initiation"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ addr0 = dev[0].p2p_dev_addr()
+ dev[1].p2p_listen()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+ self.peer_group_added = False
+ self.peer_group_removed = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.goNegotiationSuccess,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "GONegotiationSuccess",
+ byte_arrays=True)
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.propertiesChanged, dbus.PROPERTIES_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ args = {'peer': path, 'wps_method': 'keypad', 'pin': '12345670',
+ 'go_intent': 0}
+ p2p.Connect(args)
+
+ ev = dev1.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout while waiting for GO Neg Request")
+ dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 display go_intent=15")
+ ev = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ self.sta_group_ev = ev
+ dev1.close_monitor_global()
+ dev1.close_monitor_mon()
+ dev1 = None
+
+ def goNegotiationSuccess(self, properties):
+ logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ group_p2p = dbus.Interface(g_if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ group_p2p.Disconnect()
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1', monitor=False)
+ dev1.group_form_result(self.sta_group_ev)
+ dev1.remove_group()
+ dev1 = None
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ self.done = True
+
+ def propertiesChanged(self, interface_name, changed_properties,
+ invalidated_properties):
+ logger.debug("propertiesChanged: interface_name=%s changed_properties=%s invalidated_properties=%s" % (interface_name, str(changed_properties), str(invalidated_properties)))
+ if interface_name != WPAS_DBUS_P2P_PEER:
+ return
+ if "Groups" not in changed_properties:
+ return
+ if len(changed_properties["Groups"]) > 0:
+ self.peer_group_added = True
+ if len(changed_properties["Groups"]) == 0:
+ if not self.peer_group_added:
+ # This is likely a leftover event from an earlier test case,
+ # ignore it to allow this test case to go through its steps.
+ logger.info("Ignore propertiesChanged indicating group removal before group has been added")
+ return
+ self.peer_group_removed = True
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+ return False
+
+ def success(self):
+ return self.done and self.peer_group_added and self.peer_group_removed
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_group_termination_by_go(dev, apdev):
+ """D-Bus P2P group removal on GO terminating the group"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ addr0 = dev[0].p2p_dev_addr()
+ dev[1].p2p_listen()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+ self.peer_group_added = False
+ self.peer_group_removed = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.goNegotiationSuccess,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "GONegotiationSuccess",
+ byte_arrays=True)
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.propertiesChanged, dbus.PROPERTIES_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ args = {'peer': path, 'wps_method': 'keypad', 'pin': '12345670',
+ 'go_intent': 0}
+ p2p.Connect(args)
+
+ ev = dev1.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout while waiting for GO Neg Request")
+ dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 display go_intent=15")
+ ev = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ self.sta_group_ev = ev
+ dev1.close_monitor_global()
+ dev1.close_monitor_mon()
+ dev1 = None
+
+ def goNegotiationSuccess(self, properties):
+ logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1', monitor=False)
+ dev1.group_form_result(self.sta_group_ev)
+ dev1.remove_group()
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ self.done = True
+
+ def propertiesChanged(self, interface_name, changed_properties,
+ invalidated_properties):
+ logger.debug("propertiesChanged: interface_name=%s changed_properties=%s invalidated_properties=%s" % (interface_name, str(changed_properties), str(invalidated_properties)))
+ if interface_name != WPAS_DBUS_P2P_PEER:
+ return
+ if "Groups" not in changed_properties:
+ return
+ if len(changed_properties["Groups"]) > 0:
+ self.peer_group_added = True
+ if len(changed_properties["Groups"]) == 0 and self.peer_group_added:
+ self.peer_group_removed = True
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+ return False
+
+ def success(self):
+ return self.done and self.peer_group_added and self.peer_group_removed
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_group_idle_timeout(dev, apdev):
+ """D-Bus P2P group removal on idle timeout"""
+ try:
+ dev[0].global_request("SET p2p_group_idle 1")
+ _test_dbus_p2p_group_idle_timeout(dev, apdev)
+ finally:
+ dev[0].global_request("SET p2p_group_idle 0")
+
+def _test_dbus_p2p_group_idle_timeout(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ addr0 = dev[0].p2p_dev_addr()
+ dev[1].p2p_listen()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+ self.group_started = False
+ self.peer_group_added = False
+ self.peer_group_removed = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.goNegotiationSuccess,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "GONegotiationSuccess",
+ byte_arrays=True)
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.propertiesChanged, dbus.PROPERTIES_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ args = {'peer': path, 'wps_method': 'keypad', 'pin': '12345670',
+ 'go_intent': 0}
+ p2p.Connect(args)
+
+ ev = dev1.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout while waiting for GO Neg Request")
+ dev1.global_request("P2P_CONNECT " + addr0 + " 12345670 display go_intent=15")
+ ev = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ self.sta_group_ev = ev
+ dev1.close_monitor_global()
+ dev1.close_monitor_mon()
+ dev1 = None
+
+ def goNegotiationSuccess(self, properties):
+ logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ self.group_started = True
+ g_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['interface_object'])
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1', monitor=False)
+ dev1.group_form_result(self.sta_group_ev)
+ ifaddr = dev1.group_request("STA-FIRST").splitlines()[0]
+ # Force disassociation with different reason code so that the
+ # P2P Client using D-Bus does not get normal group termination event
+ # from the GO.
+ dev1.group_request("DEAUTHENTICATE " + ifaddr + " reason=0 test=0")
+ dev1.remove_group()
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+ self.done = True
+
+ def propertiesChanged(self, interface_name, changed_properties,
+ invalidated_properties):
+ logger.debug("propertiesChanged: interface_name=%s changed_properties=%s invalidated_properties=%s" % (interface_name, str(changed_properties), str(invalidated_properties)))
+ if interface_name != WPAS_DBUS_P2P_PEER:
+ return
+ if not self.group_started:
+ return
+ if "Groups" not in changed_properties:
+ return
+ if len(changed_properties["Groups"]) > 0:
+ self.peer_group_added = True
+ if len(changed_properties["Groups"]) == 0:
+ self.peer_group_removed = True
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+ return False
+
+ def success(self):
+ return self.done and self.peer_group_added and self.peer_group_removed
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_wps_failure(dev, apdev):
+ """D-Bus P2P WPS failure"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ addr0 = dev[0].p2p_dev_addr()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.wps_failed = False
+ self.formation_failure = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.goNegotiationRequest,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "GONegotiationRequest",
+ byte_arrays=True)
+ self.add_signal(self.goNegotiationSuccess,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "GONegotiationSuccess",
+ byte_arrays=True)
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.wpsFailed, WPAS_DBUS_IFACE_P2PDEVICE,
+ "WpsFailed")
+ self.add_signal(self.groupFormationFailure,
+ WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFormationFailure")
+ self.loop.run()
+ return self
+
+ def goNegotiationRequest(self, path, dev_passwd_id, go_intent=0):
+ logger.debug("goNegotiationRequest: path=%s dev_passwd_id=%d go_intent=%d" % (path, dev_passwd_id, go_intent))
+ if dev_passwd_id != 1:
+ raise Exception("Unexpected dev_passwd_id=%d" % dev_passwd_id)
+ args = {'peer': path, 'wps_method': 'display', 'pin': '12345670',
+ 'go_intent': 15}
+ p2p.Connect(args)
+
+ def goNegotiationSuccess(self, properties):
+ logger.debug("goNegotiationSuccess: properties=%s" % str(properties))
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ raise Exception("Unexpected GroupStarted")
+
+ def wpsFailed(self, name, args):
+ logger.debug("wpsFailed - name=%s args=%s" % (name, str(args)))
+ self.wps_failed = True
+ if self.formation_failure:
+ self.loop.quit()
+
+ def groupFormationFailure(self, reason):
+ logger.debug("groupFormationFailure - reason=%s" % reason)
+ self.formation_failure = True
+ if self.wps_failed:
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Listen(10)
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ if not dev1.discover_peer(addr0):
+ raise Exception("Peer not found")
+ dev1.global_request("P2P_CONNECT " + addr0 + " 87654321 enter")
+ return False
+
+ def success(self):
+ return self.wps_failed and self.formation_failure
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_two_groups(dev, apdev):
+ """D-Bus P2P with two concurrent groups"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ dev[0].request("SET p2p_no_group_iface 0")
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ dev[1].p2p_start_go(freq=2412)
+ dev1_group_ifname = dev[1].group_ifname
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+ self.peer = None
+ self.go = None
+ self.group1 = None
+ self.group2 = None
+ self.groups_removed = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.propertiesChanged, dbus.PROPERTIES_IFACE,
+ "PropertiesChanged", byte_arrays=True)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.add_signal(self.groupFinished, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupFinished")
+ self.add_signal(self.peerJoined, WPAS_DBUS_GROUP,
+ "PeerJoined")
+ self.loop.run()
+ return self
+
+ def propertiesChanged(self, interface_name, changed_properties,
+ invalidated_properties):
+ logger.debug("propertiesChanged: interface_name=%s changed_properties=%s invalidated_properties=%s" % (interface_name, str(changed_properties), str(invalidated_properties)))
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ if addr2.replace(':', '') in path:
+ self.peer = path
+ elif addr1.replace(':', '') in path:
+ self.go = path
+ if self.go and not self.group1:
+ logger.info("Join the group")
+ p2p.StopFind()
+ pin = '12345670'
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.group_ifname = dev1_group_ifname
+ dev1.group_request("WPS_PIN any " + pin)
+ args = {'peer': self.go,
+ 'join': True,
+ 'wps_method': 'pin',
+ 'pin': pin,
+ 'frequency': 2412}
+ p2p.Connect(args)
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ prop = if_obj.GetAll(WPAS_DBUS_IFACE_P2PDEVICE,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ logger.debug("p2pdevice properties: " + str(prop))
+
+ g_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ properties['group_object'])
+ res = g_obj.GetAll(WPAS_DBUS_GROUP,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug("Group properties: " + str(res))
+
+ if not self.group1:
+ self.group1 = properties['group_object']
+ self.group1iface = properties['interface_object']
+ self.g1_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ self.group1iface)
+
+ logger.info("Start autonomous GO")
+ params = dbus.Dictionary({'frequency': 2412})
+ p2p.GroupAdd(params)
+ elif not self.group2:
+ self.group2 = properties['group_object']
+ self.group2iface = properties['interface_object']
+ self.g2_if_obj = bus.get_object(WPAS_DBUS_SERVICE,
+ self.group2iface)
+ self.g2_bssid = res['BSSID']
+
+ if self.group1 and self.group2:
+ logger.info("Authorize peer to join the group")
+ a2 = binascii.unhexlify(addr2.replace(':', ''))
+ params = {'Role': 'enrollee',
+ 'P2PDeviceAddress': dbus.ByteArray(a2),
+ 'Bssid': dbus.ByteArray(a2),
+ 'Type': 'pin',
+ 'Pin': '12345670'}
+ g_wps = dbus.Interface(self.g2_if_obj, WPAS_DBUS_IFACE_WPS)
+ g_wps.Start(params)
+
+ bssid = ':'.join(["%02x" % i for i in struct.unpack('6B', self.g2_bssid)])
+ dev2 = WpaSupplicant('wlan2', '/tmp/wpas-wlan2')
+ dev2.scan_for_bss(bssid, freq=2412)
+ dev2.global_request("P2P_CONNECT " + bssid + " 12345670 join freq=2412")
+ ev = dev2.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group join timed out")
+ self.dev2_group_ev = ev
+
+ def groupFinished(self, properties):
+ logger.debug("groupFinished: " + str(properties))
+
+ if self.group1 == properties['group_object']:
+ self.group1 = None
+ elif self.group2 == properties['group_object']:
+ self.group2 = None
+
+ if not self.group1 and not self.group2:
+ self.done = True
+ self.loop.quit()
+
+ def peerJoined(self, peer):
+ logger.debug("peerJoined: " + peer)
+ if self.groups_removed:
+ return
+ self.check_results()
+
+ dev2 = WpaSupplicant('wlan2', '/tmp/wpas-wlan2')
+ dev2.group_form_result(self.dev2_group_ev)
+ dev2.remove_group()
+
+ logger.info("Disconnect group2")
+ group_p2p = dbus.Interface(self.g2_if_obj,
+ WPAS_DBUS_IFACE_P2PDEVICE)
+ group_p2p.Disconnect()
+
+ logger.info("Disconnect group1")
+ group_p2p = dbus.Interface(self.g1_if_obj,
+ WPAS_DBUS_IFACE_P2PDEVICE)
+ group_p2p.Disconnect()
+ self.groups_removed = True
+
+ def check_results(self):
+ logger.info("Check results with two concurrent groups in operation")
+
+ g1_obj = bus.get_object(WPAS_DBUS_SERVICE, self.group1)
+ res1 = g1_obj.GetAll(WPAS_DBUS_GROUP,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+
+ g2_obj = bus.get_object(WPAS_DBUS_SERVICE, self.group2)
+ res2 = g2_obj.GetAll(WPAS_DBUS_GROUP,
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+
+ logger.info("group1 = " + self.group1)
+ logger.debug("Group properties: " + str(res1))
+
+ logger.info("group2 = " + self.group2)
+ logger.debug("Group properties: " + str(res2))
+
+ prop = if_obj.GetAll(WPAS_DBUS_IFACE_P2PDEVICE,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ logger.debug("p2pdevice properties: " + str(prop))
+
+ if res1['Role'] != 'client':
+ raise Exception("Group1 role reported incorrectly: " + res1['Role'])
+ if res2['Role'] != 'GO':
+ raise Exception("Group2 role reported incorrectly: " + res2['Role'])
+ if prop['Role'] != 'device':
+ raise Exception("p2pdevice role reported incorrectly: " + prop['Role'])
+
+ if len(res2['Members']) != 1:
+ raise Exception("Unexpected Members value for group 2")
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+ dev[1].remove_group()
+
+def test_dbus_p2p_cancel(dev, apdev):
+ """D-Bus P2P Cancel"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+ try:
+ p2p.Cancel()
+ raise Exception("Unexpected p2p.Cancel() success")
+ except dbus.exceptions.DBusException as e:
+ pass
+
+ addr0 = dev[0].p2p_dev_addr()
+ dev[1].p2p_listen()
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.deviceFound, WPAS_DBUS_IFACE_P2PDEVICE,
+ "DeviceFound")
+ self.loop.run()
+ return self
+
+ def deviceFound(self, path):
+ logger.debug("deviceFound: path=%s" % path)
+ args = {'peer': path, 'wps_method': 'keypad', 'pin': '12345670',
+ 'go_intent': 0}
+ p2p.Connect(args)
+ p2p.Cancel()
+ self.done = True
+ self.loop.quit()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ p2p.Find(dbus.Dictionary({'DiscoveryType': 'social'}))
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_p2p_ip_addr(dev, apdev):
+ """D-Bus P2P and IP address parameters"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ p2p = dbus.Interface(if_obj, WPAS_DBUS_IFACE_P2PDEVICE)
+
+ vals = [("IpAddrGo", "192.168.43.1"),
+ ("IpAddrMask", "255.255.255.0"),
+ ("IpAddrStart", "192.168.43.100"),
+ ("IpAddrEnd", "192.168.43.199")]
+ for field, value in vals:
+ if_obj.Set(WPAS_DBUS_IFACE, field, value,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ val = if_obj.Get(WPAS_DBUS_IFACE, field,
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ if val != value:
+ raise Exception("Unexpected %s value: %s" % (field, val))
+
+ set_ip_addr_info(dev[1])
+
+ dev[0].global_request("SET p2p_go_intent 0")
+
+ req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+ res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+ class TestDbusP2p(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.groupStarted, WPAS_DBUS_IFACE_P2PDEVICE,
+ "GroupStarted")
+ self.loop.run()
+ return self
+
+ def groupStarted(self, properties):
+ logger.debug("groupStarted: " + str(properties))
+ self.loop.quit()
+
+ if 'IpAddrGo' not in properties:
+ logger.info("IpAddrGo missing from GroupStarted")
+ ip_addr_go = properties['IpAddrGo']
+ addr = "%d.%d.%d.%d" % (ip_addr_go[0], ip_addr_go[1], ip_addr_go[2], ip_addr_go[3])
+ if addr != "192.168.42.1":
+ logger.info("Unexpected IpAddrGo value: " + addr)
+ self.done = True
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusP2p(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_introspect(dev, apdev):
+ """D-Bus introspection"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+
+ res = if_obj.Introspect(WPAS_DBUS_IFACE,
+ dbus_interface=dbus.INTROSPECTABLE_IFACE)
+ logger.info("Initial Introspect: " + str(res))
+ if res is None or "Introspectable" not in res or "GroupStarted" not in res:
+ raise Exception("Unexpected initial Introspect response: " + str(res))
+ if "FastReauth" not in res or "PassiveScan" not in res:
+ raise Exception("Unexpected initial Introspect response: " + str(res))
+
+ with alloc_fail(dev[0], 1, "wpa_dbus_introspect"):
+ res2 = if_obj.Introspect(WPAS_DBUS_IFACE,
+ dbus_interface=dbus.INTROSPECTABLE_IFACE)
+ logger.info("Introspect: " + str(res2))
+ if res2 is not None:
+ raise Exception("Unexpected Introspect response")
+
+ with alloc_fail(dev[0], 1, "=add_interface;wpa_dbus_introspect"):
+ res2 = if_obj.Introspect(WPAS_DBUS_IFACE,
+ dbus_interface=dbus.INTROSPECTABLE_IFACE)
+ logger.info("Introspect: " + str(res2))
+ if res2 is None:
+ raise Exception("No Introspect response")
+ if len(res2) >= len(res):
+ raise Exception("Unexpected Introspect response")
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;add_interface;wpa_dbus_introspect"):
+ res2 = if_obj.Introspect(WPAS_DBUS_IFACE,
+ dbus_interface=dbus.INTROSPECTABLE_IFACE)
+ logger.info("Introspect: " + str(res2))
+ if res2 is None:
+ raise Exception("No Introspect response")
+ if len(res2) >= len(res):
+ raise Exception("Unexpected Introspect response")
+
+ with alloc_fail(dev[0], 2, "=add_interface;wpa_dbus_introspect"):
+ res2 = if_obj.Introspect(WPAS_DBUS_IFACE,
+ dbus_interface=dbus.INTROSPECTABLE_IFACE)
+ logger.info("Introspect: " + str(res2))
+ if res2 is None:
+ raise Exception("No Introspect response")
+ if len(res2) >= len(res):
+ raise Exception("Unexpected Introspect response")
+
+def run_busctl(service, obj):
+ if not shutil.which("busctl"):
+ raise HwsimSkip("No busctl available")
+ logger.info("busctl introspect %s %s" % (service, obj))
+ cmd = subprocess.Popen(['busctl', 'introspect', service, obj],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ out = cmd.communicate()
+ cmd.wait()
+ logger.info("busctl stdout:\n%s" % out[0].strip())
+ if len(out[1]) > 0:
+ logger.info("busctl stderr: %s" % out[1].decode().strip())
+ if "Duplicate property" in out[1].decode():
+ raise Exception("Duplicate property")
+
+def test_dbus_introspect_busctl(dev, apdev):
+ """D-Bus introspection with busctl"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ ifaces = dbus_get(dbus, wpas_obj, "Interfaces")
+ run_busctl(WPAS_DBUS_SERVICE, WPAS_DBUS_PATH)
+ run_busctl(WPAS_DBUS_SERVICE, WPAS_DBUS_PATH + "/Interfaces")
+ run_busctl(WPAS_DBUS_SERVICE, ifaces[0])
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(bssid, freq=2412)
+ id = dev[0].add_network()
+ dev[0].set_network(id, "disabled", "0")
+ dev[0].set_network_quoted(id, "ssid", "test")
+
+ run_busctl(WPAS_DBUS_SERVICE, ifaces[0] + "/BSSs/0")
+ run_busctl(WPAS_DBUS_SERVICE, ifaces[0] + "/Networks/0")
+
+def test_dbus_ap(dev, apdev):
+ """D-Bus AddNetwork for AP mode"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+
+ class TestDbusConnect(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.started = False
+ self.sta_added = False
+ self.sta_removed = False
+ self.authorized = False
+ self.deauthorized = False
+ self.stations = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_connect)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.networkAdded, WPAS_DBUS_IFACE, "NetworkAdded")
+ self.add_signal(self.networkSelected, WPAS_DBUS_IFACE,
+ "NetworkSelected")
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.add_signal(self.stationAdded, WPAS_DBUS_IFACE, "StationAdded")
+ self.add_signal(self.stationRemoved, WPAS_DBUS_IFACE,
+ "StationRemoved")
+ self.add_signal(self.staAuthorized, WPAS_DBUS_IFACE,
+ "StaAuthorized")
+ self.add_signal(self.staDeauthorized, WPAS_DBUS_IFACE,
+ "StaDeauthorized")
+ self.loop.run()
+ return self
+
+ def networkAdded(self, network, properties):
+ logger.debug("networkAdded: %s" % str(network))
+ logger.debug(str(properties))
+
+ def networkSelected(self, network):
+ logger.debug("networkSelected: %s" % str(network))
+ self.network_selected = True
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+ if 'State' in properties and properties['State'] == "completed":
+ self.started = True
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.connect(ssid, psk=passphrase, scan_freq="2412")
+
+ def stationAdded(self, station, properties):
+ logger.debug("stationAdded: %s" % str(station))
+ logger.debug(str(properties))
+ self.sta_added = True
+ res = if_obj.Get(WPAS_DBUS_IFACE, 'Stations',
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ logger.info("Stations: " + str(res))
+ if len(res) == 1:
+ self.stations = True
+ else:
+ raise Exception("Missing Stations entry: " + str(res))
+
+ def stationRemoved(self, station):
+ logger.debug("stationRemoved: %s" % str(station))
+ self.sta_removed = True
+ res = if_obj.Get(WPAS_DBUS_IFACE, 'Stations',
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ logger.info("Stations: " + str(res))
+ if len(res) != 0:
+ self.stations = False
+ raise Exception("Unexpected Stations entry: " + str(res))
+ self.loop.quit()
+
+ def staAuthorized(self, name):
+ logger.debug("staAuthorized: " + name)
+ self.authorized = True
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.request("DISCONNECT")
+
+ def staDeauthorized(self, name):
+ logger.debug("staDeauthorized: " + name)
+ self.deauthorized = True
+
+ def run_connect(self, *args):
+ logger.debug("run_connect")
+ args = dbus.Dictionary({'ssid': ssid,
+ 'key_mgmt': 'WPA-PSK',
+ 'psk': passphrase,
+ 'mode': 2,
+ 'frequency': 2412,
+ 'scan_freq': 2412},
+ signature='sv')
+ self.netw = iface.AddNetwork(args)
+ iface.SelectNetwork(self.netw)
+ return False
+
+ def success(self):
+ return self.started and self.sta_added and self.sta_removed and \
+ self.authorized and self.deauthorized
+
+ with TestDbusConnect(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_ap_scan(dev, apdev):
+ """D-Bus AddNetwork for AP mode and scan"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ bssid = hapd.own_addr()
+
+ class TestDbusConnect(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.started = False
+ self.scan_completed = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_connect)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.add_signal(self.scanDone, WPAS_DBUS_IFACE, "ScanDone")
+ self.loop.run()
+ return self
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+ if 'State' in properties and properties['State'] == "completed":
+ self.started = True
+ logger.info("Try to scan in AP mode")
+ iface.Scan({'Type': 'active',
+ 'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+ logger.info("Scan() returned")
+
+ def scanDone(self, success):
+ logger.debug("scanDone: success=%s" % success)
+ if self.started:
+ self.scan_completed = True
+ self.loop.quit()
+
+ def run_connect(self, *args):
+ logger.debug("run_connect")
+ args = dbus.Dictionary({'ssid': ssid,
+ 'key_mgmt': 'WPA-PSK',
+ 'psk': passphrase,
+ 'mode': 2,
+ 'frequency': 2412,
+ 'scan_freq': 2412},
+ signature='sv')
+ self.netw = iface.AddNetwork(args)
+ iface.SelectNetwork(self.netw)
+ return False
+
+ def success(self):
+ return self.started and self.scan_completed
+
+ with TestDbusConnect(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_connect_wpa_eap(dev, apdev):
+ """D-Bus AddNetwork and connection with WPA+WPA2-Enterprise AP"""
+ skip_without_tkip(dev[0])
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ ssid = "test-wpa-eap"
+ params = hostapd.wpa_eap_params(ssid=ssid)
+ params["wpa"] = "3"
+ params["rsn_pairwise"] = "CCMP"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ class TestDbusConnect(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_connect)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.add_signal(self.eap, WPAS_DBUS_IFACE, "EAP")
+ self.loop.run()
+ return self
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+ if 'State' in properties and properties['State'] == "completed":
+ self.done = True
+ self.loop.quit()
+
+ def eap(self, status, parameter):
+ logger.debug("EAP: status=%s parameter=%s" % (status, parameter))
+
+ def run_connect(self, *args):
+ logger.debug("run_connect")
+ args = dbus.Dictionary({'ssid': ssid,
+ 'key_mgmt': 'WPA-EAP',
+ 'eap': 'PEAP',
+ 'identity': 'user',
+ 'password': 'password',
+ 'ca_cert': 'auth_serv/ca.pem',
+ 'phase2': 'auth=MSCHAPV2',
+ 'scan_freq': 2412},
+ signature='sv')
+ self.netw = iface.AddNetwork(args)
+ iface.SelectNetwork(self.netw)
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusConnect(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_ap_scan_2_ap_mode_scan(dev, apdev):
+ """AP_SCAN 2 AP mode and D-Bus Scan()"""
+ try:
+ _test_dbus_ap_scan_2_ap_mode_scan(dev, apdev)
+ finally:
+ dev[0].request("AP_SCAN 1")
+
+def _test_dbus_ap_scan_2_ap_mode_scan(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ if "OK" not in dev[0].request("AP_SCAN 2"):
+ raise Exception("Failed to set AP_SCAN 2")
+
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "disabled", "0")
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("AP failed to start")
+
+ with fail_test(dev[0], 1, "wpa_driver_nl80211_scan"):
+ iface.Scan({'Type': 'active',
+ 'AllowRoam': True,
+ 'Channels': [(dbus.UInt32(2412), dbus.UInt32(20))]})
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED",
+ "AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("CTRL-EVENT-SCAN-FAILED not seen")
+ if "AP-DISABLED" in ev:
+ raise Exception("Unexpected AP-DISABLED event")
+ if "retry=1" in ev:
+ # Wait for the retry to scan happen
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED",
+ "AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("CTRL-EVENT-SCAN-FAILED not seen - retry")
+ if "AP-DISABLED" in ev:
+ raise Exception("Unexpected AP-DISABLED event - retry")
+
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_dbus_expectdisconnect(dev, apdev):
+ """D-Bus ExpectDisconnect"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ wpas = dbus.Interface(wpas_obj, WPAS_DBUS_SERVICE)
+
+ params = {"ssid": "test-open"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+
+ # This does not really verify the behavior other than by going through the
+ # code path for additional coverage.
+ wpas.ExpectDisconnect()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_dbus_save_config(dev, apdev):
+ """D-Bus SaveConfig"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+ try:
+ iface.SaveConfig()
+ raise Exception("SaveConfig() accepted unexpectedly")
+ except dbus.exceptions.DBusException as e:
+ if not str(e).startswith("fi.w1.wpa_supplicant1.UnknownError: Not allowed to update configuration"):
+ raise Exception("Unexpected error message for SaveConfig(): " + str(e))
+
+def test_dbus_vendor_elem(dev, apdev):
+ """D-Bus vendor element operations"""
+ try:
+ _test_dbus_vendor_elem(dev, apdev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 1 *")
+
+def _test_dbus_vendor_elem(dev, apdev):
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ dev[0].request("VENDOR_ELEM_REMOVE 1 *")
+
+ try:
+ ie = dbus.ByteArray(b"\x00\x00")
+ iface.VendorElemAdd(-1, ie)
+ raise Exception("Invalid VendorElemAdd() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e) or "Invalid ID" not in str(e):
+ raise Exception("Unexpected error message for invalid VendorElemAdd[1]: " + str(e))
+
+ try:
+ ie = dbus.ByteArray(b'')
+ iface.VendorElemAdd(1, ie)
+ raise Exception("Invalid VendorElemAdd() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e) or "Invalid value" not in str(e):
+ raise Exception("Unexpected error message for invalid VendorElemAdd[2]: " + str(e))
+
+ try:
+ ie = dbus.ByteArray(b"\x00\x01")
+ iface.VendorElemAdd(1, ie)
+ raise Exception("Invalid VendorElemAdd() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e) or "Parse error" not in str(e):
+ raise Exception("Unexpected error message for invalid VendorElemAdd[3]: " + str(e))
+
+ try:
+ iface.VendorElemGet(-1)
+ raise Exception("Invalid VendorElemGet() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e) or "Invalid ID" not in str(e):
+ raise Exception("Unexpected error message for invalid VendorElemGet[1]: " + str(e))
+
+ try:
+ iface.VendorElemGet(1)
+ raise Exception("Invalid VendorElemGet() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e) or "ID value does not exist" not in str(e):
+ raise Exception("Unexpected error message for invalid VendorElemGet[2]: " + str(e))
+
+ try:
+ ie = dbus.ByteArray(b"\x00\x00")
+ iface.VendorElemRem(-1, ie)
+ raise Exception("Invalid VendorElemRemove() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e) or "Invalid ID" not in str(e):
+ raise Exception("Unexpected error message for invalid VendorElemRemove[1]: " + str(e))
+
+ try:
+ ie = dbus.ByteArray(b'')
+ iface.VendorElemRem(1, ie)
+ raise Exception("Invalid VendorElemRemove() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e) or "Invalid value" not in str(e):
+ raise Exception("Unexpected error message for invalid VendorElemRemove[1]: " + str(e))
+
+ iface.VendorElemRem(1, b"*")
+
+ ie = dbus.ByteArray(b"\x00\x01\x00")
+ iface.VendorElemAdd(1, ie)
+
+ val = iface.VendorElemGet(1)
+ if len(val) != len(ie):
+ raise Exception("Unexpected VendorElemGet length")
+ for i in range(len(val)):
+ if val[i] != dbus.Byte(ie[i]):
+ raise Exception("Unexpected VendorElemGet data")
+
+ ie2 = dbus.ByteArray(b"\xe0\x00")
+ iface.VendorElemAdd(1, ie2)
+
+ ies = ie + ie2
+ val = iface.VendorElemGet(1)
+ if len(val) != len(ies):
+ raise Exception("Unexpected VendorElemGet length[2]")
+ for i in range(len(val)):
+ if val[i] != dbus.Byte(ies[i]):
+ raise Exception("Unexpected VendorElemGet data[2]")
+
+ try:
+ test_ie = dbus.ByteArray(b"\x01\x01")
+ iface.VendorElemRem(1, test_ie)
+ raise Exception("Invalid VendorElemRemove() accepted")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e) or "Parse error" not in str(e):
+ raise Exception("Unexpected error message for invalid VendorElemRemove[1]: " + str(e))
+
+ iface.VendorElemRem(1, ie)
+ val = iface.VendorElemGet(1)
+ if len(val) != len(ie2):
+ raise Exception("Unexpected VendorElemGet length[3]")
+
+ iface.VendorElemRem(1, b"*")
+ try:
+ iface.VendorElemGet(1)
+ raise Exception("Invalid VendorElemGet() accepted after removal")
+ except dbus.exceptions.DBusException as e:
+ if "InvalidArgs" not in str(e) or "ID value does not exist" not in str(e):
+ raise Exception("Unexpected error message for invalid VendorElemGet after removal: " + str(e))
+
+def test_dbus_assoc_reject(dev, apdev):
+ """D-Bus AssocStatusCode"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ ssid = "test-open"
+ params = {"ssid": ssid,
+ "max_listen_interval": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ class TestDbusConnect(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.assoc_status_seen = False
+ self.state = 0
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_connect)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+ if 'AssocStatusCode' in properties:
+ status = properties['AssocStatusCode']
+ if status != 51:
+ logger.info("Unexpected status code: " + str(status))
+ else:
+ self.assoc_status_seen = True
+ iface.Disconnect()
+ self.loop.quit()
+
+ def run_connect(self, *args):
+ args = dbus.Dictionary({'ssid': ssid,
+ 'key_mgmt': 'NONE',
+ 'scan_freq': 2412},
+ signature='sv')
+ self.netw = iface.AddNetwork(args)
+ iface.SelectNetwork(self.netw)
+ return False
+
+ def success(self):
+ return self.assoc_status_seen
+
+ with TestDbusConnect(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_mesh(dev, apdev):
+ """D-Bus mesh"""
+ check_mesh_support(dev[0])
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ mesh = dbus.Interface(if_obj, WPAS_DBUS_IFACE_MESH)
+
+ add_open_mesh_network(dev[1])
+ addr1 = dev[1].own_addr()
+
+ class TestDbusMesh(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.done = False
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_test)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.meshGroupStarted, WPAS_DBUS_IFACE_MESH,
+ "MeshGroupStarted")
+ self.add_signal(self.meshGroupRemoved, WPAS_DBUS_IFACE_MESH,
+ "MeshGroupRemoved")
+ self.add_signal(self.meshPeerConnected, WPAS_DBUS_IFACE_MESH,
+ "MeshPeerConnected")
+ self.add_signal(self.meshPeerDisconnected, WPAS_DBUS_IFACE_MESH,
+ "MeshPeerDisconnected")
+ self.loop.run()
+ return self
+
+ def meshGroupStarted(self, args):
+ logger.debug("MeshGroupStarted: " + str(args))
+
+ def meshGroupRemoved(self, args):
+ logger.debug("MeshGroupRemoved: " + str(args))
+ self.done = True
+ self.loop.quit()
+
+ def meshPeerConnected(self, args):
+ logger.debug("MeshPeerConnected: " + str(args))
+
+ res = if_obj.Get(WPAS_DBUS_IFACE_MESH, 'MeshPeers',
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug("MeshPeers: " + str(res))
+ if len(res) != 1:
+ raise Exception("Unexpected number of MeshPeer values")
+ if binascii.hexlify(res[0]).decode() != addr1.replace(':', ''):
+ raise Exception("Unexpected peer address")
+
+ res = if_obj.Get(WPAS_DBUS_IFACE_MESH, 'MeshGroup',
+ dbus_interface=dbus.PROPERTIES_IFACE,
+ byte_arrays=True)
+ logger.debug("MeshGroup: " + str(res))
+ if res != b"wpas-mesh-open":
+ raise Exception("Unexpected MeshGroup")
+ dev1 = WpaSupplicant('wlan1', '/tmp/wpas-wlan1')
+ dev1.mesh_group_remove()
+
+ def meshPeerDisconnected(self, args):
+ logger.debug("MeshPeerDisconnected: " + str(args))
+ dev0 = WpaSupplicant('wlan0', '/tmp/wpas-wlan0')
+ dev0.mesh_group_remove()
+
+ def run_test(self, *args):
+ logger.debug("run_test")
+ dev0 = WpaSupplicant('wlan0', '/tmp/wpas-wlan0')
+ add_open_mesh_network(dev0)
+ return False
+
+ def success(self):
+ return self.done
+
+ with TestDbusMesh(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
+
+def test_dbus_roam(dev, apdev):
+ """D-Bus Roam"""
+ (bus, wpas_obj, path, if_obj) = prepare_dbus(dev[0])
+ iface = dbus.Interface(if_obj, WPAS_DBUS_IFACE)
+
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(bssid, freq=2412)
+ bssid2 = apdev[1]['bssid']
+ dev[0].scan_for_bss(bssid2, freq=2412)
+
+ class TestDbusConnect(TestDbus):
+ def __init__(self, bus):
+ TestDbus.__init__(self, bus)
+ self.state = 0
+
+ def __enter__(self):
+ gobject.timeout_add(1, self.run_connect)
+ gobject.timeout_add(15000, self.timeout)
+ self.add_signal(self.propertiesChanged, WPAS_DBUS_IFACE,
+ "PropertiesChanged")
+ self.loop.run()
+ return self
+
+ def propertiesChanged(self, properties):
+ logger.debug("propertiesChanged: %s" % str(properties))
+ if 'State' in properties and properties['State'] == "completed":
+ if self.state == 0:
+ self.state = 1
+ cur = properties["CurrentBSS"]
+ bss_obj = bus.get_object(WPAS_DBUS_SERVICE, cur)
+ res = bss_obj.Get(WPAS_DBUS_BSS, 'BSSID',
+ dbus_interface=dbus.PROPERTIES_IFACE)
+ bssid_str = ''
+ for item in res:
+ if len(bssid_str) > 0:
+ bssid_str += ':'
+ bssid_str += '%02x' % item
+ dst = bssid if bssid_str == bssid2 else bssid2
+ iface.Roam(dst)
+ elif self.state == 1:
+ if "RoamComplete" in properties and \
+ properties["RoamComplete"]:
+ self.state = 2
+ self.loop.quit()
+
+ def run_connect(self, *args):
+ logger.debug("run_connect")
+ args = dbus.Dictionary({'ssid': ssid,
+ 'key_mgmt': 'WPA-PSK',
+ 'psk': passphrase,
+ 'scan_freq': 2412},
+ signature='sv')
+ self.netw = iface.AddNetwork(args)
+ iface.SelectNetwork(self.netw)
+ return False
+
+ def success(self):
+ return self.state == 2
+
+ with TestDbusConnect(bus) as t:
+ if not t.success():
+ raise Exception("Expected signals not seen")
diff --git a/contrib/wpa/tests/hwsim/test_dfs.py b/contrib/wpa/tests/hwsim/test_dfs.py
new file mode 100644
index 000000000000..c5876539ffc6
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_dfs.py
@@ -0,0 +1,767 @@
+# Test cases for DFS
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import os
+import subprocess
+import time
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import *
+
+def wait_dfs_event(hapd, event, timeout):
+ dfs_events = ["DFS-RADAR-DETECTED", "DFS-NEW-CHANNEL",
+ "DFS-CAC-START", "DFS-CAC-COMPLETED",
+ "DFS-NOP-FINISHED", "AP-ENABLED", "AP-CSA-FINISHED"]
+ ev = hapd.wait_event(dfs_events, timeout=timeout)
+ if not ev:
+ raise Exception("DFS event timed out")
+ if event and event not in ev:
+ raise Exception("Unexpected DFS event: " + ev + " (expected: %s)" % event)
+ return ev
+
+def start_dfs_ap(ap, ssid="dfs", ht=True, ht40=False,
+ ht40minus=False, vht80=False, vht20=False, chanlist=None,
+ channel=None, country="FI", rrm_beacon_report=False,
+ chan100=False):
+ ifname = ap['ifname']
+ logger.info("Starting AP " + ifname + " on DFS channel")
+ hapd = hostapd.add_ap(ap, {}, no_enable=True)
+ hapd.set("ssid", ssid)
+ hapd.set("country_code", country)
+ hapd.set("ieee80211d", "1")
+ hapd.set("ieee80211h", "1")
+ hapd.set("hw_mode", "a")
+ if chan100:
+ hapd.set("channel", "100")
+ else:
+ hapd.set("channel", "52")
+ if not ht:
+ hapd.set("ieee80211n", "0")
+ if ht40:
+ hapd.set("ht_capab", "[HT40+]")
+ elif ht40minus:
+ hapd.set("ht_capab", "[HT40-]")
+ hapd.set("channel", "56")
+ if vht80:
+ hapd.set("ieee80211ac", "1")
+ hapd.set("vht_oper_chwidth", "1")
+ if chan100:
+ hapd.set("vht_oper_centr_freq_seg0_idx", "106")
+ else:
+ hapd.set("vht_oper_centr_freq_seg0_idx", "58")
+ if vht20:
+ hapd.set("ieee80211ac", "1")
+ hapd.set("vht_oper_chwidth", "0")
+ hapd.set("vht_oper_centr_freq_seg0_idx", "0")
+ if chanlist:
+ hapd.set("chanlist", chanlist)
+ if channel:
+ hapd.set("channel", str(channel))
+ if rrm_beacon_report:
+ hapd.set("rrm_beacon_report", "1")
+ hapd.enable()
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ if "DFS-CAC-START" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+
+ state = hapd.get_status_field("state")
+ if state != "DFS":
+ raise Exception("Unexpected interface state: " + state)
+
+ return hapd
+
+def dfs_simulate_radar(hapd):
+ logger.info("Trigger a simulated radar event")
+ phyname = hapd.get_driver_status_field("phyname")
+ radar_file = '/sys/kernel/debug/ieee80211/' + phyname + '/hwsim/dfs_simulate_radar'
+ with open(radar_file, 'w') as f:
+ f.write('1')
+
+def test_dfs(dev, apdev):
+ """DFS CAC functionality on clear channel"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], country="US")
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5260" not in ev:
+ raise Exception("Unexpected DFS freq result")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "5260":
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("dfs", key_mgmt="NONE")
+ dev[0].wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("RADAR DETECTED freq=5260 ht_enabled=1 chan_width=1")
+ ev = hapd.wait_event(["DFS-RADAR-DETECTED"], timeout=10)
+ if ev is None:
+ raise Exception("DFS-RADAR-DETECTED event not reported")
+ if "freq=5260" not in ev:
+ raise Exception("Incorrect frequency in radar detected event: " + ev)
+ ev = hapd.wait_event(["DFS-NEW-CHANNEL"], timeout=70)
+ if ev is None:
+ raise Exception("DFS-NEW-CHANNEL event not reported")
+ if "freq=5260" in ev:
+ raise Exception("Channel did not change after radar was detected")
+
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=70)
+ if ev is None:
+ raise Exception("AP-CSA-FINISHED event not reported")
+ if "freq=5260" in ev:
+ raise Exception("Channel did not change after radar was detected(2)")
+ time.sleep(1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ clear_regdom(hapd, dev)
+
+@long_duration_test
+def test_dfs_etsi(dev, apdev):
+ """DFS and uniform spreading requirement for ETSI"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0])
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5260" not in ev:
+ raise Exception("Unexpected DFS freq result")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "5260":
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("dfs", key_mgmt="NONE")
+ dev[0].wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("RADAR DETECTED freq=%s ht_enabled=1 chan_width=1" % freq)
+ ev = hapd.wait_event(["DFS-RADAR-DETECTED"], timeout=5)
+ if ev is None:
+ raise Exception("DFS-RADAR-DETECTED event not reported")
+ if "freq=%s" % freq not in ev:
+ raise Exception("Incorrect frequency in radar detected event: " + ev)
+ ev = hapd.wait_event(["DFS-NEW-CHANNEL"], timeout=5)
+ if ev is None:
+ raise Exception("DFS-NEW-CHANNEL event not reported")
+ if "freq=%s" % freq in ev:
+ raise Exception("Channel did not change after radar was detected")
+
+ ev = hapd.wait_event(["AP-CSA-FINISHED", "DFS-CAC-START"], timeout=10)
+ if ev is None:
+ raise Exception("AP-CSA-FINISHED or DFS-CAC-START event not reported")
+ if "DFS-CAC-START" in ev:
+ # The selected new channel requires CAC
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=30)
+ if not ev:
+ raise Exception("STA did not reconnect on new DFS channel")
+ else:
+ # The new channel did not require CAC - try again
+ if "freq=%s" % freq in ev:
+ raise Exception("Channel did not change after radar was detected(2)")
+ time.sleep(1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_dfs_radar1(dev, apdev):
+ """DFS CAC functionality with radar detected during initial CAC"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0])
+ time.sleep(1)
+
+ dfs_simulate_radar(hapd)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+ if ev is None:
+ raise Exception("Timeout on DFS aborted event")
+ if "success=0 freq=5260" not in ev:
+ raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5260" not in ev:
+ raise Exception("Unexpected DFS radar detection freq")
+
+ ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+ if "freq=5260" in ev:
+ raise Exception("Unexpected DFS new freq")
+
+ ev = wait_dfs_event(hapd, None, 5)
+ if "AP-ENABLED" in ev:
+ logger.info("Started AP on non-DFS channel")
+ else:
+ logger.info("Trying to start AP on another DFS channel")
+ if "DFS-CAC-START" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+ if "freq=5260" in ev:
+ raise Exception("Unexpected DFS CAC freq")
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5260" in ev:
+ raise Exception("Unexpected DFS freq result - radar channel")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state")
+
+ freq = hapd.get_status_field("freq")
+ if freq == "5260":
+ raise Exception("Unexpected frequency: " + freq)
+
+ dev[0].connect("dfs", key_mgmt="NONE")
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_dfs_radar2(dev, apdev):
+ """DFS CAC functionality with radar detected after initial CAC"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], ssid="dfs2", ht40=True)
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=70)
+ if not ev:
+ raise Exception("AP2 setup timed out")
+
+ dfs_simulate_radar(hapd)
+
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5260 ht_enabled=1 chan_offset=1 chan_width=2" not in ev:
+ raise Exception("Unexpected DFS radar detection freq from AP2")
+
+ ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+ if "freq=5260" in ev:
+ raise Exception("Unexpected DFS new freq for AP2")
+
+ wait_dfs_event(hapd, None, 5)
+ finally:
+ clear_regdom(hapd, dev)
+
+@remote_compatible
+def test_dfs_radar_on_non_dfs_channel(dev, apdev):
+ """DFS radar detection test code on non-DFS channel"""
+ params = {"ssid": "radar"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ hapd.request("RADAR DETECTED freq=5260 ht_enabled=1 chan_width=1")
+ hapd.request("RADAR DETECTED freq=2412 ht_enabled=1 chan_width=1")
+
+def test_dfs_radar_chanlist(dev, apdev):
+ """DFS chanlist when radar is detected"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], chanlist="40 44")
+ time.sleep(1)
+
+ dfs_simulate_radar(hapd)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+ if ev is None:
+ raise Exception("Timeout on DFS aborted event")
+ if "success=0 freq=5260" not in ev:
+ raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5260" not in ev:
+ raise Exception("Unexpected DFS radar detection freq")
+
+ ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+ if "freq=5200 chan=40" not in ev and "freq=5220 chan=44" not in ev:
+ raise Exception("Unexpected DFS new freq: " + ev)
+
+ ev = wait_dfs_event(hapd, None, 5)
+ if "AP-ENABLED" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+ dev[0].connect("dfs", key_mgmt="NONE")
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_dfs_radar_chanlist_vht80(dev, apdev):
+ """DFS chanlist when radar is detected and VHT80 configured"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], chanlist="36", ht40=True, vht80=True)
+ time.sleep(1)
+
+ dfs_simulate_radar(hapd)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+ if ev is None:
+ raise Exception("Timeout on DFS aborted event")
+ if "success=0 freq=5260" not in ev:
+ raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5260" not in ev:
+ raise Exception("Unexpected DFS radar detection freq")
+
+ ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+ if "freq=5180 chan=36 sec_chan=1" not in ev:
+ raise Exception("Unexpected DFS new freq: " + ev)
+
+ ev = wait_dfs_event(hapd, None, 5)
+ if "AP-ENABLED" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+ dev[0].connect("dfs", key_mgmt="NONE")
+ dev[0].wait_regdom(country_ie=True)
+
+ if hapd.get_status_field('vht_oper_centr_freq_seg0_idx') != "42":
+ raise Exception("Unexpected seg0 idx")
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_dfs_radar_chanlist_vht20(dev, apdev):
+ """DFS chanlist when radar is detected and VHT40 configured"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], chanlist="36", vht20=True)
+ time.sleep(1)
+
+ dfs_simulate_radar(hapd)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+ if ev is None:
+ raise Exception("Timeout on DFS aborted event")
+ if "success=0 freq=5260" not in ev:
+ raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5260" not in ev:
+ raise Exception("Unexpected DFS radar detection freq")
+
+ ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+ if "freq=5180 chan=36 sec_chan=0" not in ev:
+ raise Exception("Unexpected DFS new freq: " + ev)
+
+ ev = wait_dfs_event(hapd, None, 5)
+ if "AP-ENABLED" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+ dev[0].connect("dfs", key_mgmt="NONE")
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_dfs_radar_no_ht(dev, apdev):
+ """DFS chanlist when radar is detected and no HT configured"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], chanlist="36", ht=False)
+ time.sleep(1)
+
+ dfs_simulate_radar(hapd)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+ if ev is None:
+ raise Exception("Timeout on DFS aborted event")
+ if "success=0 freq=5260" not in ev:
+ raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5260 ht_enabled=0" not in ev:
+ raise Exception("Unexpected DFS radar detection freq: " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+ if "freq=5180 chan=36 sec_chan=0" not in ev:
+ raise Exception("Unexpected DFS new freq: " + ev)
+
+ ev = wait_dfs_event(hapd, None, 5)
+ if "AP-ENABLED" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+ dev[0].connect("dfs", key_mgmt="NONE")
+ dev[0].wait_regdom(country_ie=True)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_dfs_radar_ht40minus(dev, apdev):
+ """DFS chanlist when radar is detected and HT40- configured"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], chanlist="36", ht40minus=True)
+ time.sleep(1)
+
+ dfs_simulate_radar(hapd)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+ if ev is None:
+ raise Exception("Timeout on DFS aborted event")
+ if "success=0 freq=5280 ht_enabled=1 chan_offset=-1" not in ev:
+ raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5280 ht_enabled=1 chan_offset=-1" not in ev:
+ raise Exception("Unexpected DFS radar detection freq: " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+ if "freq=5180 chan=36 sec_chan=1" not in ev:
+ raise Exception("Unexpected DFS new freq: " + ev)
+
+ ev = wait_dfs_event(hapd, None, 5)
+ if "AP-ENABLED" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+ dev[0].connect("dfs", key_mgmt="NONE")
+ dev[0].wait_regdom(country_ie=True)
+ dev[0].request("STA_AUTOCONNECT 0")
+ finally:
+ clear_regdom(hapd, dev)
+ dev[0].request("STA_AUTOCONNECT 1")
+
+@long_duration_test
+def test_dfs_ht40_minus(dev, apdev):
+ """DFS CAC functionality on channel 104 HT40-"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], ht40minus=True, channel=104)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5520" not in ev:
+ raise Exception("Unexpected DFS freq result")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "5520":
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("dfs", key_mgmt="NONE", scan_freq="5520")
+ dev[0].wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_dfs_cac_restart_on_enable(dev, apdev):
+ """DFS CAC interrupted and restarted"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0])
+ time.sleep(0.1)
+ subprocess.check_call(['ip', 'link', 'set', 'dev', hapd.ifname, 'down'])
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+ if ev is None:
+ raise Exception("Timeout on DFS aborted event")
+ if "success=0 freq=5260" not in ev:
+ raise Exception("Unexpected DFS aborted event contents: " + ev)
+ time.sleep(0.1)
+ subprocess.check_call(['ip', 'link', 'set', 'dev', hapd.ifname, 'up'])
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ if "DFS-CAC-START" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+ hapd.disable()
+
+ finally:
+ clear_regdom(hapd, dev)
+
+@long_duration_test
+def test_dfs_rrm(dev, apdev):
+ """DFS with RRM"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], country="US", rrm_beacon_report=True)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev or "freq=5260" not in ev:
+ raise Exception("Unexpected DFS freq result")
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ dev[0].connect("dfs", key_mgmt="NONE", scan_freq="5260")
+ dev[0].wait_regdom(country_ie=True)
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ addr = dev[0].own_addr()
+ token = hapd.request("REQ_BEACON " + addr + " " + "51000000000002ffffffffffff")
+ if "FAIL" in token:
+ raise Exception("REQ_BEACON failed")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ finally:
+ clear_regdom(hapd, dev)
+
+@long_duration_test
+def test_dfs_radar_vht80_downgrade(dev, apdev):
+ """DFS channel bandwidth downgrade from VHT80 to VHT40"""
+ try:
+ # Start with 80 MHz channel 100 (5500 MHz) to find a radar
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], chanlist="100-140",
+ ht40=True, vht80=True, chan100=True)
+ time.sleep(1)
+ dfs_simulate_radar(hapd)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+ if ev is None:
+ raise Exception("Timeout on DFS aborted event")
+ if "success=0 freq=5500" not in ev:
+ raise Exception("Unexpected DFS aborted event contents: " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5500" not in ev:
+ raise Exception("Unexpected DFS radar detection freq: " + ev)
+
+ # The only other available 80 MHz channel in the chanlist is
+ # 116 (5580 MHz), so that will be selected next.
+ ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+ if "freq=5580 chan=116 sec_chan=1" not in ev:
+ raise Exception("Unexpected DFS new freq: " + ev)
+
+ ev = wait_dfs_event(hapd, None, 5)
+ if "DFS-CAC-START" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+ if "freq=5580" not in ev:
+ raise Exception("Unexpected DFS CAC freq: " + ev)
+
+ time.sleep(1)
+ dfs_simulate_radar(hapd)
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 5)
+ if ev is None:
+ raise Exception("Timeout on DFS aborted event (2)")
+ if "success=0 freq=5580" not in ev:
+ raise Exception("Unexpected DFS aborted event (2) contents: " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5580" not in ev:
+ raise Exception("Unexpected DFS radar detection (2) freq: " + ev)
+
+ # No more 80 MHz channels are available, so have to downgrade to 40 MHz
+ # channels and the only remaining one is channel 132 (5660 MHz).
+ ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+ if "freq=5660 chan=132 sec_chan=1" not in ev:
+ raise Exception("Unexpected DFS new freq (2): " + ev)
+
+ ev = wait_dfs_event(hapd, None, 5)
+ if "DFS-CAC-START" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+ if "freq=5660" not in ev:
+ raise Exception("Unexpected DFS CAC freq (2): " + ev)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5660" not in ev:
+ raise Exception("Unexpected DFS freq result: " + ev)
+
+ ev = wait_dfs_event(hapd, None, 5)
+ if "AP-ENABLED" not in ev:
+ raise Exception("Unexpected DFS event: " + ev)
+ dev[0].connect("dfs", key_mgmt="NONE", scan_freq="5660")
+ dev[0].wait_regdom(country_ie=True)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5660" not in sig or "WIDTH=40 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value: " + str(sig))
+ finally:
+ clear_regdom(hapd, dev)
+
+@long_duration_test
+def test_dfs_chan_switch(dev, apdev):
+ """DFS channel switch"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], country="US")
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5260" not in ev:
+ raise Exception("Unexpected DFS freq result")
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+ freq = hapd.get_status_field("freq")
+ if freq != "5260":
+ raise Exception("Unexpected frequency")
+
+ dev[0].connect("dfs", key_mgmt="NONE", scan_freq="5260 5280")
+ dev[0].wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ if "OK" not in hapd.request("CHAN_SWITCH 5 5280 ht"):
+ raise Exception("CHAN_SWITCH failed")
+ # This results in BSS going down before restart, so the STA is expected
+ # to report disconnection.
+ dev[0].wait_disconnected()
+ ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ if "freq=5280" not in ev:
+ raise Exception("Unexpected channel: " + ev)
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5280" not in ev:
+ raise Exception("Unexpected DFS freq result")
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+ freq = hapd.get_status_field("freq")
+ if freq != "5280":
+ raise Exception("Unexpected frequency")
+
+ dev[0].wait_connected(timeout=30)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ clear_regdom(hapd, dev)
+
+@long_duration_test
+def test_dfs_no_available_channel(dev, apdev):
+ """DFS and no available channel after radar detection"""
+ try:
+ hapd = None
+ hapd = start_dfs_ap(apdev[0], chanlist="56")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=70)
+ if not ev:
+ raise Exception("AP2 setup timed out")
+
+ dfs_simulate_radar(hapd)
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5260 ht_enabled=1 chan_offset=0 chan_width=1" not in ev:
+ raise Exception("Unexpected DFS radar detection freq from AP")
+
+ ev = wait_dfs_event(hapd, "DFS-NEW-CHANNEL", 5)
+ if "freq=5280 chan=56" not in ev:
+ raise Exception("Unexpected DFS new freq: " + ev)
+ ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ if "freq=5280" not in ev:
+ raise Exception("Unexpected channel: " + ev)
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5280" not in ev:
+ raise Exception("Unexpected DFS freq result")
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ dfs_simulate_radar(hapd)
+ ev = wait_dfs_event(hapd, "DFS-RADAR-DETECTED", 5)
+ if "freq=5280 ht_enabled=1 chan_offset=0 chan_width=1" not in ev:
+ raise Exception("Unexpected DFS radar detection freq from AP [2]")
+
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("AP was not disabled")
+ finally:
+ clear_regdom(hapd, dev)
+
+def dfs_chan_switch_precac(dev, apdev, country):
+ """DFS channel switch pre CAC"""
+ try:
+ hapd = None
+
+ # Toggle regulatory - clean all preCAC
+ hostapd.cmd_execute(apdev[0], ['iw', 'reg', 'set', 'US'])
+
+ hapd = start_dfs_ap(apdev[0], country=country)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5260" not in ev:
+ raise Exception("Unexpected DFS freq result")
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+ freq = hapd.get_status_field("freq")
+ if freq != "5260":
+ raise Exception("Unexpected frequency")
+
+ # TODO add/connect station here
+ # Today skip this step while dev[0].connect()
+ # for some reason toggle regulatory to US
+ # and clean preCAC
+
+ # Back to non DFS channel
+ if "OK" not in hapd.request("CHAN_SWITCH 5 5180 ht"):
+ raise Exception("CHAN_SWITCH 5180 failed")
+
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=5)
+ if not ev:
+ raise Exception("No CSA finished event - 5180")
+ freq = hapd.get_status_field("freq")
+ if freq != "5180":
+ raise Exception("Unexpected frequency")
+
+ # Today cfg80211 first send AP-CSA-FINISHED and next
+ # DFS-PRE-CAC-EXPIRED
+ ev = hapd.wait_event(["DFS-PRE-CAC-EXPIRED"], timeout=3)
+ if not ev and country == 'US':
+ raise Exception("US - no CAC-EXPIRED event")
+
+ # Back again to DFS channel (CAC passed)
+ if "OK" not in hapd.request("CHAN_SWITCH 5 5260 ht"):
+ raise Exception("CHAN_SWITCH 5260 failed")
+
+ if country == 'US':
+ # For non EU we should start CAC again
+ ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ if not ev:
+ raise Exception("No DFS CAC start event")
+ else:
+ # For EU preCAC should be used
+ ev = wait_dfs_event(hapd, "AP-CSA-FINISHED", 5)
+ if not ev:
+ raise Exception("No CSA finished event - 5260")
+ finally:
+ clear_regdom(hapd, dev)
+
+@long_duration_test
+def test_dfs_eu_chan_switch_precac(dev, apdev):
+ """DFS channel switch pre CAC - ETSI domain"""
+ dfs_chan_switch_precac(dev, apdev, 'PL')
+
+@long_duration_test
+def test_dfs_us_chan_switch_precac(dev, apdev):
+ """DFS channel switch pre CAC - FCC domain"""
+ dfs_chan_switch_precac(dev, apdev, 'US')
diff --git a/contrib/wpa/tests/hwsim/test_dpp.py b/contrib/wpa/tests/hwsim/test_dpp.py
new file mode 100644
index 000000000000..b696c5d1dc2e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_dpp.py
@@ -0,0 +1,6874 @@
+# Test cases for Device Provisioning Protocol (DPP)
+# Copyright (c) 2017, Qualcomm Atheros, Inc.
+# Copyright (c) 2018-2019, The Linux Foundation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import base64
+import binascii
+import hashlib
+import logging
+logger = logging.getLogger()
+import os
+import socket
+import struct
+import subprocess
+import time
+
+import hostapd
+import hwsim_utils
+from hwsim import HWSimRadio
+from utils import *
+from wpasupplicant import WpaSupplicant
+from wlantest import WlantestCapture
+
+try:
+ import OpenSSL
+ openssl_imported = True
+except ImportError:
+ openssl_imported = False
+
+def check_dpp_capab(dev, brainpool=False, min_ver=1):
+ if "UNKNOWN COMMAND" in dev.request("DPP_BOOTSTRAP_GET_URI 0"):
+ raise HwsimSkip("DPP not supported")
+ if brainpool:
+ tls = dev.request("GET tls_library")
+ if not tls.startswith("OpenSSL") or "run=BoringSSL" in tls:
+ raise HwsimSkip("Crypto library does not support Brainpool curves: " + tls)
+ capa = dev.request("GET_CAPABILITY dpp")
+ ver = 1
+ if capa.startswith("DPP="):
+ ver = int(capa[4:])
+ if ver < min_ver:
+ raise HwsimSkip("DPP version %d not supported" % min_ver)
+ return ver
+
+def wait_dpp_fail(dev, expected=None):
+ ev = dev.wait_event(["DPP-FAIL"], timeout=5)
+ if ev is None:
+ raise Exception("Failure not reported")
+ if expected and expected not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+def test_dpp_qr_code_parsing(dev, apdev):
+ """DPP QR Code parsing"""
+ check_dpp_capab(dev[0])
+ id = []
+
+ tests = ["DPP:C:81/1,115/36;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;",
+ "DPP:C:81/1,81/2,81/3,81/4,81/5,81/6,81/7,81/8,81/9,81/10,81/11,81/12,81/13,82/14,83/1,83/2,83/3,83/4,83/5,83/6,83/7,83/8,83/9,84/5,84/6,84/7,84/8,84/9,84/10,84/11,84/12,84/13,115/36;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;",
+ "DPP:C:81/1,2,3,4,5,6,7,8,9,10,11,12,13,82/14,83/1,2,3,4,5,6,7,8,9,84/5,6,7,8,9,10,11,12,13,115/36;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;",
+ "DPP:C:81/1,2,3;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkq/24e0rsrfMP9K1Tm8gx+ovP0I=;;",
+ "DPP:I:SN=4774LH2b4044;M:010203040506;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;",
+ "DPP:I:;M:010203040506;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;"]
+ for uri in tests:
+ id.append(dev[0].dpp_qr_code(uri))
+
+ uri2 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id[-1])
+ if uri != uri2:
+ raise Exception("Returned URI does not match")
+
+ tests = ["foo",
+ "DPP:",
+ "DPP:;;",
+ "DPP:C:1/2;M:;K;;",
+ "DPP:I:;M:01020304050;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;",
+ "DPP:K:" + base64.b64encode(b"hello").decode() + ";;",
+ "DPP:K:MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEXiJuIWt1Q/CPCkuULechh37UsXPmbUANOeN5U9sOQROE4o/NEFeFEejROHYwwehF;;",
+ "DPP:K:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANNZaZA4T/kRDjnmpI1ACOJhAuTIIEk2KFOpS6XPpGF+EVr/ao3XemkE0/nzXmGaLzLqTUCJknSdxTnVPeWfCVsCAwEAAQ==;;",
+ "DPP:K:MIIBCjCB0wYHKoZIzj0CATCBxwIBATAkBgcqhkjOPQEBAhkA/////////////////////v//////////MEsEGP////////////////////7//////////AQYZCEFGeWcgOcPp+mrciQwSf643uzBRrmxAxUAMEWub8hCL2TtV5Uo04Eg6uEhltUEMQQYjagOsDCQ9ny/IOtDoYgA9P8K/YL/EBIHGSuV/8jaeGMQEe1rJM3Vc/l3oR55SBECGQD///////////////+Z3vg2FGvJsbTSKDECAQEDMgAEXiJuIWt1Q/CPCkuULechh37UsXPmbUANOeN5U9sOQROE4o/NEFeFEejROHYwwehF;;",
+ "DPP:I:foo\tbar;M:010203040506;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;",
+ "DPP:C:1;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkqa24e0rsrfMP9K1Tm8gx+ovP0I=;;",
+ "DPP:C:81/1a;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkqa24e0rsrfMP9K1Tm8gx+ovP0I=;;",
+ "DPP:C:1/2000,81/-1;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkqa24e0rsrfMP9K1Tm8gx+ovP0I=;;",
+ "DPP:C:-1/1;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADM2206avxHJaHXgLMkqa24e0rsrfMP9K1Tm8gx+ovP0I=;;"]
+ for t in tests:
+ res = dev[0].request("DPP_QR_CODE " + t)
+ if "FAIL" not in res:
+ raise Exception("Accepted invalid QR Code: " + t)
+
+ logger.info("ID: " + str(id))
+ if id[0] == id[1] or id[0] == id[2] or id[1] == id[2]:
+ raise Exception("Duplicate ID returned")
+
+ if "FAIL" not in dev[0].request("DPP_BOOTSTRAP_REMOVE 12345678"):
+ raise Exception("DPP_BOOTSTRAP_REMOVE accepted unexpectedly")
+ if "OK" not in dev[0].request("DPP_BOOTSTRAP_REMOVE %d" % id[1]):
+ raise Exception("DPP_BOOTSTRAP_REMOVE failed")
+
+ id = dev[0].dpp_bootstrap_gen()
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ logger.info("Generated URI: " + uri)
+
+ dev[0].dpp_qr_code(uri)
+
+ id = dev[0].dpp_bootstrap_gen(chan="81/1,115/36", mac="010203040506",
+ info="foo")
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ logger.info("Generated URI: " + uri)
+
+ dev[0].dpp_qr_code(uri)
+
+def test_dpp_uri_version(dev, apdev):
+ """DPP URI version information"""
+ check_dpp_capab(dev[0], min_ver=2)
+
+ id0 = dev[0].dpp_bootstrap_gen()
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("Generated URI: " + uri)
+
+ id1 = dev[0].dpp_qr_code(uri)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+ info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id1)
+ logger.info("Parsed URI info:\n" + info)
+ if "version=2" not in info.splitlines():
+ raise Exception("Unexpected version information (v2)")
+
+ dev[0].set("dpp_version_override", "1")
+ id0 = dev[0].dpp_bootstrap_gen()
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("Generated URI: " + uri)
+
+ id1 = dev[0].dpp_qr_code(uri)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+ info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id1)
+ logger.info("Parsed URI info:\n" + info)
+ if "version=0" not in info.splitlines():
+ raise Exception("Unexpected version information (without indication)")
+
+def test_dpp_qr_code_parsing_fail(dev, apdev):
+ """DPP QR Code parsing local failure"""
+ check_dpp_capab(dev[0])
+ with alloc_fail(dev[0], 1, "dpp_parse_uri_info"):
+ if "FAIL" not in dev[0].request("DPP_QR_CODE DPP:I:SN=4774LH2b4044;M:010203040506;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;"):
+ raise Exception("DPP_QR_CODE failure not reported")
+
+ with alloc_fail(dev[0], 1, "dpp_parse_uri_pk"):
+ if "FAIL" not in dev[0].request("DPP_QR_CODE DPP:K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;"):
+ raise Exception("DPP_QR_CODE failure not reported")
+
+ with fail_test(dev[0], 1, "dpp_parse_uri_pk"):
+ if "FAIL" not in dev[0].request("DPP_QR_CODE DPP:K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;"):
+ raise Exception("DPP_QR_CODE failure not reported")
+
+ with alloc_fail(dev[0], 1, "dpp_parse_uri"):
+ if "FAIL" not in dev[0].request("DPP_QR_CODE DPP:I:SN=4774LH2b4044;M:010203040506;K:MDkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDIgADURzxmttZoIRIPWGoQMV00XHWCAQIhXruVWOz0NjlkIA=;;"):
+ raise Exception("DPP_QR_CODE failure not reported")
+
+dpp_key_p256 = "30570201010420777fc55dc51e967c10ec051b91d860b5f1e6c934e48d5daffef98d032c64b170a00a06082a8648ce3d030107a124032200020c804188c7f85beb6e91070d2b3e5e39b90ca77b4d3c5251bc1844d6ca29dcad"
+dpp_key_p384 = "307402010104302f56fdd83b5345cacb630eb7c22fa5ad5daba37307c95191e2a75756d137003bd8b32dbcb00eb5650c1eb499ecfcaec0a00706052b81040022a13403320003615ec2141b5b77aebb6523f8a012755f9a34405a8398d2ceeeebca7f5ce868bf55056cba4c4ec62fad3ed26dd29e0f23"
+dpp_key_p521 = "308198020101044200c8010d5357204c252551aaf4e210343111e503fd1dc615b257058997c49b6b643c975226e93be8181cca3d83a7072defd161dfbdf433c19abe1f2ad51867a05761a00706052b81040023a1460344000301cdf3608b1305fe34a1f976095dcf001182b9973354efe156291a66830292f9babd8f412ad462958663e7a75d1d0610abdfc3dd95d40669f7ab3bc001668cfb3b7c"
+dpp_key_bp256 = "3058020101042057133a676fb60bf2a3e6797e19833c7b0f89dc192ab99ab5fa377ae23a157765a00b06092b2403030208010107a12403220002945d9bf7ce30c9c1ac0ff21ca62b984d5bb80ff69d2be8c9716ab39a10d2caf0"
+dpp_key_bp384 = "307802010104304902df9f3033a9b7128554c0851dc7127c3573eed150671dae74c0013e9896a9b1c22b6f7d43d8a2ebb7cd474dc55039a00b06092b240303020801010ba13403320003623cb5e68787f351faababf3425161571560add2e6f9a306fcbffb507735bf955bb46dd20ba246b0d5cadce73e5bd6a6"
+dpp_key_bp512 = "30819802010104405803494226eb7e50bf0e90633f37e7e35d33f5fa502165eeba721d927f9f846caf12e925701d18e123abaaaf4a7edb4fc4de21ce18bc10c4d12e8b3439f74e40a00b06092b240303020801010da144034200033b086ccd47486522d35dc16fbb2229642c2e9e87897d45abbf21f9fb52acb5a6272b31d1b227c3e53720769cc16b4cb181b26cd0d35fe463218aaedf3b6ec00a"
+
+def test_dpp_qr_code_curves(dev, apdev):
+ """DPP QR Code and supported curves"""
+ check_dpp_capab(dev[0])
+ tests = [("prime256v1", dpp_key_p256),
+ ("secp384r1", dpp_key_p384),
+ ("secp521r1", dpp_key_p521)]
+ for curve, hex in tests:
+ id = dev[0].dpp_bootstrap_gen(key=hex)
+ info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id)
+ if "FAIL" in info:
+ raise Exception("Failed to get info for " + curve)
+ if "curve=" + curve not in info:
+ raise Exception("Curve mismatch for " + curve)
+
+def test_dpp_qr_code_curves_brainpool(dev, apdev):
+ """DPP QR Code and supported Brainpool curves"""
+ check_dpp_capab(dev[0], brainpool=True)
+ tests = [("brainpoolP256r1", dpp_key_bp256),
+ ("brainpoolP384r1", dpp_key_bp384),
+ ("brainpoolP512r1", dpp_key_bp512)]
+ for curve, hex in tests:
+ id = dev[0].dpp_bootstrap_gen(key=hex)
+ info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id)
+ if "FAIL" in info:
+ raise Exception("Failed to get info for " + curve)
+ if "curve=" + curve not in info:
+ raise Exception("Curve mismatch for " + curve)
+
+def test_dpp_qr_code_unsupported_curve(dev, apdev):
+ """DPP QR Code and unsupported curve"""
+ check_dpp_capab(dev[0])
+
+ id = dev[0].request("DPP_BOOTSTRAP_GEN type=qrcode curve=unsupported")
+ if "FAIL" not in id:
+ raise Exception("Unsupported curve accepted")
+
+ tests = ["30",
+ "305f02010104187f723ed9e1b41979ec5cd02eb82696efc76b40e277661049a00a06082a8648ce3d030101a134033200043f292614dea97c43f500f069e79ae9fb48f8b07369180de5eec8fa2bc9eea5af7a46dc335f52f10cb1c0e9464201d41b"]
+ for hex in tests:
+ id = dev[0].request("DPP_BOOTSTRAP_GEN type=qrcode key=" + hex)
+ if "FAIL" not in id:
+ raise Exception("Unsupported/invalid curve accepted")
+
+def test_dpp_qr_code_keygen_fail(dev, apdev):
+ """DPP QR Code and keygen failure"""
+ check_dpp_capab(dev[0])
+
+ with alloc_fail(dev[0], 1, "dpp_bootstrap_key_der;dpp_keygen"):
+ if "FAIL" not in dev[0].request("DPP_BOOTSTRAP_GEN type=qrcode"):
+ raise Exception("Failure not reported")
+
+ with alloc_fail(dev[0], 1, "base64_gen_encode;dpp_keygen"):
+ if "FAIL" not in dev[0].request("DPP_BOOTSTRAP_GEN type=qrcode"):
+ raise Exception("Failure not reported")
+
+def test_dpp_qr_code_curve_select(dev, apdev):
+ """DPP QR Code and curve selection"""
+ check_dpp_capab(dev[0], brainpool=True)
+ check_dpp_capab(dev[1], brainpool=True)
+
+ bi = []
+ for key in [dpp_key_p256, dpp_key_p384, dpp_key_p521,
+ dpp_key_bp256, dpp_key_bp384, dpp_key_bp512]:
+ id = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True, key=key)
+ info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id)
+ for i in info.splitlines():
+ if '=' in i:
+ name, val = i.split('=')
+ if name == "curve":
+ curve = val
+ break
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ bi.append((curve, uri))
+
+ for curve, uri in bi:
+ logger.info("Curve: " + curve)
+ logger.info("URI: " + uri)
+
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri)
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
+ allow_enrollee_failure=True, stop_responder=True,
+ stop_initiator=True)
+
+def test_dpp_qr_code_auth_broadcast(dev, apdev):
+ """DPP QR Code and authentication exchange (broadcast)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1")
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0)
+ wait_auth_success(dev[0], dev[1], stop_responder=True)
+
+def test_dpp_qr_code_auth_unicast(dev, apdev):
+ """DPP QR Code and authentication exchange (unicast)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, None)
+
+def test_dpp_qr_code_auth_unicast_ap_enrollee(dev, apdev):
+ """DPP QR Code and authentication exchange (AP enrollee)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, None, netrole="ap")
+
+def run_dpp_configurator_enrollee(dev, apdev, conf_curve=None):
+ run_dpp_qr_code_auth_unicast(dev, apdev, None, netrole="configurator",
+ configurator=True, conf_curve=conf_curve,
+ conf="configurator")
+ ev = dev[0].wait_event(["DPP-CONFIGURATOR-ID"], timeout=2)
+ if ev is None:
+ raise Exception("No Configurator instance added")
+
+def test_dpp_configurator_enrollee(dev, apdev):
+ """DPP Configurator enrolling"""
+ run_dpp_configurator_enrollee(dev, apdev)
+
+def test_dpp_configurator_enrollee_prime256v1(dev, apdev):
+ """DPP Configurator enrolling (prime256v1)"""
+ run_dpp_configurator_enrollee(dev, apdev, conf_curve="prime256v1")
+
+def test_dpp_configurator_enrollee_secp384r1(dev, apdev):
+ """DPP Configurator enrolling (secp384r1)"""
+ run_dpp_configurator_enrollee(dev, apdev, conf_curve="secp384r1")
+
+def test_dpp_configurator_enrollee_secp521r1(dev, apdev):
+ """DPP Configurator enrolling (secp521r1)"""
+ run_dpp_configurator_enrollee(dev, apdev, conf_curve="secp521r1")
+
+def test_dpp_configurator_enrollee_brainpoolP256r1(dev, apdev):
+ """DPP Configurator enrolling (brainpoolP256r1)"""
+ run_dpp_configurator_enrollee(dev, apdev, conf_curve="brainpoolP256r1")
+
+def test_dpp_configurator_enrollee_brainpoolP384r1(dev, apdev):
+ """DPP Configurator enrolling (brainpoolP384r1)"""
+ run_dpp_configurator_enrollee(dev, apdev, conf_curve="brainpoolP384r1")
+
+def test_dpp_configurator_enrollee_brainpoolP512r1(dev, apdev):
+ """DPP Configurator enrolling (brainpoolP512r1)"""
+ run_dpp_configurator_enrollee(dev, apdev, conf_curve="brainpoolP512r1")
+
+def test_dpp_configurator_enroll_conf(dev, apdev):
+ """DPP Configurator enrolling followed by use of the new Configurator"""
+ check_dpp_capab(dev[0], min_ver=2)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+ run_dpp_configurator_enroll_conf(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_configurator_enroll_conf(dev, apdev):
+ run_dpp_qr_code_auth_unicast(dev, apdev, None, netrole="configurator",
+ configurator=True, conf="configurator",
+ qr="mutual", stop_responder=False)
+ ev = dev[0].wait_event(["DPP-CONFIGURATOR-ID"], timeout=2)
+ if ev is None:
+ raise Exception("No Configurator instance added")
+ dev[1].reset()
+ dev[0].dump_monitor()
+
+ ssid = "test-network"
+ passphrase = "test-passphrase"
+ dev[0].set("dpp_configurator_params",
+ "conf=sta-psk ssid=%s pass=%s" % (binascii.hexlify(ssid.encode()).decode(), binascii.hexlify(passphrase.encode()).decode()))
+ dev[0].dpp_listen(2412, role="configurator")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1")
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[1].dpp_auth_init(uri=uri0, role="enrollee")
+ wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1])
+
+def test_dpp_qr_code_curve_prime256v1(dev, apdev):
+ """DPP QR Code and curve prime256v1"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1")
+
+def test_dpp_qr_code_curve_secp384r1(dev, apdev):
+ """DPP QR Code and curve secp384r1"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1")
+
+def test_dpp_qr_code_curve_secp521r1(dev, apdev):
+ """DPP QR Code and curve secp521r1"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1")
+
+def test_dpp_qr_code_curve_brainpoolP256r1(dev, apdev):
+ """DPP QR Code and curve brainpoolP256r1"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "brainpoolP256r1")
+
+def test_dpp_qr_code_curve_brainpoolP384r1(dev, apdev):
+ """DPP QR Code and curve brainpoolP384r1"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "brainpoolP384r1")
+
+def test_dpp_qr_code_curve_brainpoolP512r1(dev, apdev):
+ """DPP QR Code and curve brainpoolP512r1"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "brainpoolP512r1")
+
+def test_dpp_qr_code_set_key(dev, apdev):
+ """DPP QR Code and fixed bootstrapping key"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, None, key="30770201010420e5143ac74682cc6869a830e8f5301a5fa569130ac329b1d7dd6f2a7495dbcbe1a00a06082a8648ce3d030107a144034200045e13e167c33dbc7d85541e5509600aa8139bbb3e39e25898992c5d01be92039ee2850f17e71506ded0d6b25677441eae249f8e225c68dd15a6354dca54006383")
+
+def run_dpp_qr_code_auth_unicast(dev, apdev, curve, netrole=None, key=None,
+ require_conf_success=False, init_extra=None,
+ require_conf_failure=False,
+ configurator=False, conf_curve=None,
+ conf=None, qr=None, stop_responder=True):
+ check_dpp_capab(dev[0], curve and "brainpool" in curve)
+ check_dpp_capab(dev[1], curve and "brainpool" in curve)
+ if configurator:
+ conf_id = dev[1].dpp_configurator_add(curve=conf_curve)
+ else:
+ conf_id = None
+
+ if qr == "mutual":
+ logger.info("dev1 displays QR Code and dev0 scans it")
+ id1 = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True, curve=curve)
+ uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+ id1c = dev[0].dpp_qr_code(uri1)
+ else:
+ id1 = None
+
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True, curve=curve, key=key)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412, netrole=netrole, qr=qr)
+ dev[1].dpp_auth_init(uri=uri0, extra=init_extra, configurator=conf_id,
+ conf=conf, own=id1)
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
+ allow_enrollee_failure=True,
+ allow_configurator_failure=not require_conf_success,
+ require_configurator_failure=require_conf_failure,
+ stop_responder=stop_responder)
+
+def test_dpp_qr_code_auth_mutual(dev, apdev):
+ """DPP QR Code and authentication exchange (mutual)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ logger.info("dev1 displays QR Code")
+ id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
+
+ logger.info("dev0 scans QR Code")
+ id0b = dev[0].dpp_qr_code(uri1b)
+
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0, own=id1b)
+
+ ev = dev[1].wait_event(["DPP-AUTH-DIRECTION"], timeout=5)
+ if ev is None:
+ raise Exception("DPP authentication direction not indicated (Initiator)")
+ if "mutual=1" not in ev:
+ raise Exception("Mutual authentication not used")
+
+ wait_auth_success(dev[0], dev[1], stop_responder=True)
+
+def test_dpp_qr_code_auth_mutual2(dev, apdev):
+ """DPP QR Code and authentication exchange (mutual2)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ logger.info("dev1 displays QR Code")
+ id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
+
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412, qr="mutual")
+ dev[1].dpp_auth_init(uri=uri0, own=id1b)
+
+ ev = dev[1].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
+ if ev is None:
+ raise Exception("Pending response not reported")
+ ev = dev[0].wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
+ if ev is None:
+ raise Exception("QR Code scan for mutual authentication not requested")
+
+ logger.info("dev0 scans QR Code")
+ id0b = dev[0].dpp_qr_code(uri1b)
+
+ ev = dev[1].wait_event(["DPP-AUTH-DIRECTION"], timeout=5)
+ if ev is None:
+ raise Exception("DPP authentication direction not indicated (Initiator)")
+ if "mutual=1" not in ev:
+ raise Exception("Mutual authentication not used")
+
+ wait_auth_success(dev[0], dev[1], stop_responder=True)
+
+def test_dpp_qr_code_auth_mutual_p_256(dev, apdev):
+ """DPP QR Code and authentication exchange (mutual, autogen P-256)"""
+ run_dpp_qr_code_auth_mutual(dev, apdev, "P-256")
+
+def test_dpp_qr_code_auth_mutual_p_384(dev, apdev):
+ """DPP QR Code and authentication exchange (mutual, autogen P-384)"""
+ run_dpp_qr_code_auth_mutual(dev, apdev, "P-384")
+
+def test_dpp_qr_code_auth_mutual_p_521(dev, apdev):
+ """DPP QR Code and authentication exchange (mutual, autogen P-521)"""
+ run_dpp_qr_code_auth_mutual(dev, apdev, "P-521")
+
+def test_dpp_qr_code_auth_mutual_bp_256(dev, apdev):
+ """DPP QR Code and authentication exchange (mutual, autogen BP-256)"""
+ run_dpp_qr_code_auth_mutual(dev, apdev, "BP-256")
+
+def test_dpp_qr_code_auth_mutual_bp_384(dev, apdev):
+ """DPP QR Code and authentication exchange (mutual, autogen BP-384)"""
+ run_dpp_qr_code_auth_mutual(dev, apdev, "BP-384")
+
+def test_dpp_qr_code_auth_mutual_bp_512(dev, apdev):
+ """DPP QR Code and authentication exchange (mutual, autogen BP-512)"""
+ run_dpp_qr_code_auth_mutual(dev, apdev, "BP-512")
+
+def run_dpp_qr_code_auth_mutual(dev, apdev, curve):
+ check_dpp_capab(dev[0], curve and "BP-" in curve)
+ check_dpp_capab(dev[1], curve and "BP-" in curve)
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True, curve=curve)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412, qr="mutual")
+ dev[1].dpp_auth_init(uri=uri0)
+
+ ev = dev[1].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
+ if ev is None:
+ raise Exception("Pending response not reported")
+ uri = ev.split(' ')[1]
+
+ ev = dev[0].wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
+ if ev is None:
+ raise Exception("QR Code scan for mutual authentication not requested")
+
+ logger.info("dev0 scans QR Code")
+ dev[0].dpp_qr_code(uri)
+
+ ev = dev[1].wait_event(["DPP-AUTH-DIRECTION"], timeout=5)
+ if ev is None:
+ raise Exception("DPP authentication direction not indicated (Initiator)")
+ if "mutual=1" not in ev:
+ raise Exception("Mutual authentication not used")
+
+ wait_auth_success(dev[0], dev[1], stop_responder=True)
+
+def test_dpp_auth_resp_retries(dev, apdev):
+ """DPP Authentication Response retries"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].set("dpp_resp_max_tries", "3")
+ dev[0].set("dpp_resp_retry_time", "100")
+
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("dev1 displays QR Code")
+ id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412, qr="mutual")
+ dev[1].dpp_auth_init(uri=uri0, own=id1b)
+
+ ev = dev[1].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
+ if ev is None:
+ raise Exception("Pending response not reported")
+ ev = dev[0].wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
+ if ev is None:
+ raise Exception("QR Code scan for mutual authentication not requested")
+
+ # Stop Initiator from listening to frames to force retransmission of the
+ # DPP Authentication Response frame with Status=0
+ dev[1].request("DPP_STOP_LISTEN")
+
+ dev[1].dump_monitor()
+ dev[0].dump_monitor()
+
+ logger.info("dev0 scans QR Code")
+ id0b = dev[0].dpp_qr_code(uri1b)
+
+ ev = dev[0].wait_event(["DPP-TX "], timeout=5)
+ if ev is None or "type=1" not in ev:
+ raise Exception("DPP Authentication Response not sent")
+ ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("TX status for DPP Authentication Response not reported")
+ if "result=no-ACK" not in ev:
+ raise Exception("Unexpected TX status for Authentication Response: " + ev)
+
+ ev = dev[0].wait_event(["DPP-TX "], timeout=15)
+ if ev is None or "type=1" not in ev:
+ raise Exception("DPP Authentication Response retransmission not sent")
+
+def test_dpp_qr_code_auth_mutual_not_used(dev, apdev):
+ """DPP QR Code and authentication exchange (mutual not used)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("dev1 displays QR Code")
+ id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
+ logger.info("dev0 does not scan QR Code")
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0, own=id1b)
+
+ ev = dev[1].wait_event(["DPP-AUTH-DIRECTION"], timeout=5)
+ if ev is None:
+ raise Exception("DPP authentication direction not indicated (Initiator)")
+ if "mutual=0" not in ev:
+ raise Exception("Mutual authentication not used")
+
+ wait_auth_success(dev[0], dev[1], stop_responder=True)
+
+def test_dpp_qr_code_auth_mutual_curve_mismatch(dev, apdev):
+ """DPP QR Code and authentication exchange (mutual/mismatch)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("dev1 displays QR Code")
+ id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True, curve="secp384r1")
+ uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
+ logger.info("dev0 scans QR Code")
+ id0b = dev[0].dpp_qr_code(uri1b)
+ logger.info("dev1 scans QR Code")
+ dev[1].dpp_auth_init(uri=uri0, own=id1b, expect_fail=True)
+
+def test_dpp_qr_code_auth_hostapd_mutual2(dev, apdev):
+ """DPP QR Code and authentication exchange (hostapd mutual2)"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+ logger.info("AP displays QR Code")
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri_h = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+ logger.info("dev0 displays QR Code")
+ id0b = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0b)
+ logger.info("dev0 scans QR Code and initiates DPP Authentication")
+ hapd.dpp_listen(2412, qr="mutual")
+ dev[0].dpp_auth_init(uri=uri_h, own=id0b)
+
+ ev = dev[0].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
+ if ev is None:
+ raise Exception("Pending response not reported")
+ ev = hapd.wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
+ if ev is None:
+ raise Exception("QR Code scan for mutual authentication not requested")
+
+ logger.info("AP scans QR Code")
+ hapd.dpp_qr_code(uri0)
+
+ wait_auth_success(hapd, dev[0], stop_responder=True)
+
+def test_dpp_qr_code_listen_continue(dev, apdev):
+ """DPP QR Code and listen operation needing continuation"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_listen(2412)
+ logger.info("Wait for listen to expire and get restarted")
+ time.sleep(5.5)
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[1].dpp_auth_init(uri=uri0)
+ wait_auth_success(dev[0], dev[1], stop_responder=True)
+
+def test_dpp_qr_code_auth_initiator_enrollee(dev, apdev):
+ """DPP QR Code and authentication exchange (Initiator in Enrollee role)"""
+ try:
+ run_dpp_qr_code_auth_initiator_enrollee(dev, apdev)
+ finally:
+ dev[0].set("gas_address3", "0")
+ dev[1].set("gas_address3", "0")
+
+def run_dpp_qr_code_auth_initiator_enrollee(dev, apdev):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].request("SET gas_address3 1")
+ dev[1].request("SET gas_address3 1")
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0, role="enrollee")
+ wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1],
+ allow_enrollee_failure=True, stop_responder=True)
+
+def test_dpp_qr_code_auth_initiator_either_1(dev, apdev):
+ """DPP QR Code and authentication exchange (Initiator in either role)"""
+ run_dpp_qr_code_auth_initiator_either(dev, apdev, None, dev[1], dev[0])
+
+def test_dpp_qr_code_auth_initiator_either_2(dev, apdev):
+ """DPP QR Code and authentication exchange (Initiator in either role)"""
+ run_dpp_qr_code_auth_initiator_either(dev, apdev, "enrollee",
+ dev[1], dev[0])
+
+def test_dpp_qr_code_auth_initiator_either_3(dev, apdev):
+ """DPP QR Code and authentication exchange (Initiator in either role)"""
+ run_dpp_qr_code_auth_initiator_either(dev, apdev, "configurator",
+ dev[0], dev[1])
+
+def run_dpp_qr_code_auth_initiator_either(dev, apdev, resp_role,
+ conf_dev, enrollee_dev):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412, role=resp_role)
+ dev[1].dpp_auth_init(uri=uri0, role="either")
+ wait_auth_success(dev[0], dev[1], configurator=conf_dev,
+ enrollee=enrollee_dev, allow_enrollee_failure=True,
+ stop_responder=True)
+
+def run_init_incompatible_roles(dev, role="enrollee"):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ logger.info("dev1 scans QR Code")
+ id1 = dev[1].dpp_qr_code(uri0)
+
+ logger.info("dev1 initiates DPP Authentication")
+ dev[0].dpp_listen(2412, role=role)
+ return id1
+
+def test_dpp_qr_code_auth_incompatible_roles(dev, apdev):
+ """DPP QR Code and authentication exchange (incompatible roles)"""
+ id1 = run_init_incompatible_roles(dev)
+ dev[1].dpp_auth_init(peer=id1, role="enrollee")
+ ev = dev[1].wait_event(["DPP-NOT-COMPATIBLE"], timeout=5)
+ if ev is None:
+ raise Exception("DPP-NOT-COMPATIBLE event on initiator timed out")
+ ev = dev[0].wait_event(["DPP-NOT-COMPATIBLE"], timeout=1)
+ if ev is None:
+ raise Exception("DPP-NOT-COMPATIBLE event on responder timed out")
+ dev[1].dpp_auth_init(peer=id1, role="configurator")
+ wait_auth_success(dev[0], dev[1], stop_responder=True)
+
+def test_dpp_qr_code_auth_incompatible_roles2(dev, apdev):
+ """DPP QR Code and authentication exchange (incompatible roles 2)"""
+ id1 = run_init_incompatible_roles(dev, role="configurator")
+ dev[1].dpp_auth_init(peer=id1, role="configurator")
+ ev = dev[1].wait_event(["DPP-NOT-COMPATIBLE"], timeout=5)
+ if ev is None:
+ raise Exception("DPP-NOT-COMPATIBLE event on initiator timed out")
+ ev = dev[0].wait_event(["DPP-NOT-COMPATIBLE"], timeout=1)
+ if ev is None:
+ raise Exception("DPP-NOT-COMPATIBLE event on responder timed out")
+
+def test_dpp_qr_code_auth_incompatible_roles_failure(dev, apdev):
+ """DPP QR Code and authentication exchange (incompatible roles failure)"""
+ id1 = run_init_incompatible_roles(dev, role="configurator")
+ with alloc_fail(dev[0], 1, "dpp_auth_build_resp_status"):
+ dev[1].dpp_auth_init(peer=id1, role="configurator")
+ ev = dev[0].wait_event(["DPP-NOT-COMPATIBLE"], timeout=1)
+ if ev is None:
+ raise Exception("DPP-NOT-COMPATIBLE event on responder timed out")
+
+def test_dpp_qr_code_auth_incompatible_roles_failure2(dev, apdev):
+ """DPP QR Code and authentication exchange (incompatible roles failure 2)"""
+ id1 = run_init_incompatible_roles(dev, role="configurator")
+ with alloc_fail(dev[1], 1, "dpp_auth_resp_rx_status"):
+ dev[1].dpp_auth_init(peer=id1, role="configurator")
+ wait_fail_trigger(dev[1], "GET_ALLOC_FAIL")
+
+def test_dpp_qr_code_auth_incompatible_roles_failure3(dev, apdev):
+ """DPP QR Code and authentication exchange (incompatible roles failure 3)"""
+ id1 = run_init_incompatible_roles(dev, role="configurator")
+ with fail_test(dev[1], 1, "dpp_auth_resp_rx_status"):
+ dev[1].dpp_auth_init(peer=id1, role="configurator")
+ wait_dpp_fail(dev[1], "AES-SIV decryption failed")
+
+def test_dpp_qr_code_auth_neg_chan(dev, apdev):
+ """DPP QR Code and authentication exchange with requested different channel"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ conf_id = dev[1].dpp_configurator_add()
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0, conf="sta-dpp", neg_freq=2462,
+ configurator=conf_id)
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("DPP Authentication Request not sent")
+ if "freq=2412 type=0" not in ev:
+ raise Exception("Unexpected TX data for Authentication Request: " + ev)
+
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None:
+ raise Exception("DPP Authentication Request not received")
+ if "freq=2412 type=0" not in ev:
+ raise Exception("Unexpected RX data for Authentication Request: " + ev)
+
+ ev = dev[1].wait_event(["DPP-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("TX status for DPP Authentication Request not reported")
+ if "freq=2412 result=SUCCESS" not in ev:
+ raise Exception("Unexpected TX status for Authentication Request: " + ev)
+
+ ev = dev[0].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("DPP Authentication Response not sent")
+ if "freq=2462 type=1" not in ev:
+ raise Exception("Unexpected TX data for Authentication Response: " + ev)
+
+ ev = dev[1].wait_event(["DPP-RX"], timeout=5)
+ if ev is None:
+ raise Exception("DPP Authentication Response not received")
+ if "freq=2462 type=1" not in ev:
+ raise Exception("Unexpected RX data for Authentication Response: " + ev)
+
+ ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("TX status for DPP Authentication Response not reported")
+ if "freq=2462 result=SUCCESS" not in ev:
+ raise Exception("Unexpected TX status for Authentication Response: " + ev)
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("DPP Authentication Confirm not sent")
+ if "freq=2462 type=2" not in ev:
+ raise Exception("Unexpected TX data for Authentication Confirm: " + ev)
+
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None:
+ raise Exception("DPP Authentication Confirm not received")
+ if "freq=2462 type=2" not in ev:
+ raise Exception("Unexpected RX data for Authentication Confirm: " + ev)
+
+ ev = dev[1].wait_event(["DPP-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("TX status for DPP Authentication Confirm not reported")
+ if "freq=2462 result=SUCCESS" not in ev:
+ raise Exception("Unexpected TX status for Authentication Confirm: " + ev)
+
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
+ stop_responder=True)
+
+def test_dpp_config_legacy(dev, apdev):
+ """DPP Config Object for legacy network using passphrase"""
+ check_dpp_capab(dev[1])
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}'
+ dev[1].set("dpp_config_obj_override", conf)
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ require_conf_success=True)
+
+def test_dpp_config_legacy_psk_hex(dev, apdev):
+ """DPP Config Object for legacy network using PSK"""
+ check_dpp_capab(dev[1])
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","psk_hex":"' + 32*"12" + '"}}'
+ dev[1].set("dpp_config_obj_override", conf)
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ require_conf_success=True)
+
+def test_dpp_config_fragmentation(dev, apdev):
+ """DPP Config Object for legacy network requiring fragmentation"""
+ check_dpp_capab(dev[1])
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[1].set("dpp_config_obj_override", conf)
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ require_conf_success=True)
+
+def test_dpp_config_legacy_gen(dev, apdev):
+ """Generate DPP Config Object for legacy network"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ init_extra="conf=sta-psk pass=%s" % binascii.hexlify(b"passphrase").decode(),
+ require_conf_success=True)
+
+def test_dpp_config_legacy_gen_psk(dev, apdev):
+ """Generate DPP Config Object for legacy network (PSK)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ init_extra="conf=sta-psk psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ require_conf_success=True)
+
+def test_dpp_config_dpp_gen_prime256v1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-256)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True)
+
+def test_dpp_config_dpp_gen_secp384r1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-384)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True)
+
+def test_dpp_config_dpp_gen_secp521r1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-521)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True)
+
+def test_dpp_config_dpp_gen_prime256v1_prime256v1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-256 + P-256)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True,
+ conf_curve="prime256v1")
+
+def test_dpp_config_dpp_gen_prime256v1_secp384r1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-256 + P-384)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True,
+ conf_curve="secp384r1")
+
+def test_dpp_config_dpp_gen_prime256v1_secp521r1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-256 + P-521)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True,
+ conf_curve="secp521r1")
+
+def test_dpp_config_dpp_gen_secp384r1_prime256v1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-384 + P-256)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True,
+ conf_curve="prime256v1")
+
+def test_dpp_config_dpp_gen_secp384r1_secp384r1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-384 + P-384)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True,
+ conf_curve="secp384r1")
+
+def test_dpp_config_dpp_gen_secp384r1_secp521r1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-384 + P-521)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True,
+ conf_curve="secp521r1")
+
+def test_dpp_config_dpp_gen_secp521r1_prime256v1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-521 + P-256)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True,
+ conf_curve="prime256v1")
+
+def test_dpp_config_dpp_gen_secp521r1_secp384r1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-521 + P-384)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True,
+ conf_curve="secp384r1")
+
+def test_dpp_config_dpp_gen_secp521r1_secp521r1(dev, apdev):
+ """Generate DPP Config Object for DPP network (P-521 + P-521)"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True,
+ conf_curve="secp521r1")
+
+def test_dpp_config_dpp_gen_expiry(dev, apdev):
+ """Generate DPP Config Object for DPP network with expiry value"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ init_extra="conf=sta-dpp expiry=%d" % (time.time() + 1000),
+ require_conf_success=True,
+ configurator=True)
+
+def test_dpp_config_dpp_gen_expired_key(dev, apdev):
+ """Generate DPP Config Object for DPP network with expiry value"""
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ init_extra="conf=sta-dpp expiry=%d" % (time.time() - 10),
+ require_conf_failure=True,
+ configurator=True)
+
+def test_dpp_config_dpp_override_prime256v1(dev, apdev):
+ """DPP Config Object override (P-256)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"dpp","signedConnector":"eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJUbkdLaklsTlphYXRyRUFZcmJiamlCNjdyamtMX0FHVldYTzZxOWhESktVIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6InN0YSJ9XSwibmV0QWNjZXNzS2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiYVRGNEpFR0lQS1NaMFh2OXpkQ01qbS10bjVYcE1zWUlWWjl3eVNBejFnSSIsInkiOiJRR2NIV0FfNnJiVTlYRFhBenRvWC1NNVEzc3VUbk1hcUVoVUx0bjdTU1h3In19._sm6YswxMf6hJLVTyYoU1uYUeY2VVkUNjrzjSiEhY42StD_RWowStEE-9CRsdCvLmsTptZ72_g40vTFwdId20A","csign":{"kty":"EC","crv":"P-256","x":"W4-Y5N1Pkos3UWb9A5qme0KUYRtY3CVUpekx_MapZ9s","y":"Et-M4NSF4NGjvh2VCh4B1sJ9eSCZ4RNzP2DBdP137VE","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU"}}}'
+ dev[0].set("dpp_ignore_netaccesskey_mismatch", "1")
+ dev[1].set("dpp_config_obj_override", conf)
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ require_conf_success=True)
+
+def test_dpp_config_dpp_override_secp384r1(dev, apdev):
+ """DPP Config Object override (P-384)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"dpp","signedConnector":"eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJabi1iMndjbjRLM2pGQklkYmhGZkpVTHJTXzdESS0yMWxFQi02R3gxNjl3IiwiYWxnIjoiRVMzODQifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6InN0YSJ9XSwibmV0QWNjZXNzS2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC0zODQiLCJ4IjoickdrSGg1UUZsOUtfWjdqYUZkVVhmbThoY1RTRjM1b25Xb1NIRXVsbVNzWW9oX1RXZGpoRjhiVGdiS0ZRN2tBViIsInkiOiJBbU1QVDA5VmFENWpGdzMwTUFKQlp2VkZXeGNlVVlKLXR5blQ0bVJ5N0xOZWxhZ0dEWHpfOExaRlpOU2FaNUdLIn19.Yn_F7m-bbOQ5PlaYQJ9-1qsuqYQ6V-rAv8nWw1COKiCYwwbt3WFBJ8DljY0dPrlg5CHJC4saXwkytpI-CpELW1yUdzYb4Lrun07d20Eo_g10ICyOl5sqQCAUElKMe_Xr","csign":{"kty":"EC","crv":"P-384","x":"dmTyXXiPV2Y8a01fujL-jo08gvzyby23XmzOtzjAiujKQZZgPJsbhfEKrZDlc6ey","y":"H5Z0av5c7bqInxYb2_OOJdNiMhVf3zlcULR0516ZZitOY4U31KhL4wl4KGV7g2XW","kid":"Zn-b2wcn4K3jFBIdbhFfJULrS_7DI-21lEB-6Gx169w"}}}'
+ dev[0].set("dpp_ignore_netaccesskey_mismatch", "1")
+ dev[1].set("dpp_config_obj_override", conf)
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp384r1",
+ require_conf_success=True)
+
+def test_dpp_config_dpp_override_secp521r1(dev, apdev):
+ """DPP Config Object override (P-521)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"dpp","signedConnector":"eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJMZkhKY3hnV2ZKcG1uS2IwenZRT0F2VDB2b0ZKc0JjZnBmYzgxY3Y5ZXFnIiwiYWxnIjoiRVM1MTIifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6InN0YSJ9XSwibmV0QWNjZXNzS2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC01MjEiLCJ4IjoiQVJlUFBrMFNISkRRR2NWbnlmM3lfbTlaQllHNjFJeElIbDN1NkdwRHVhMkU1WVd4TE1BSUtMMnZuUGtlSGFVRXljRmZaZlpYZ2JlNkViUUxMVkRVUm1VUSIsInkiOiJBWUtaYlNwUkFFNjJVYm9YZ2c1ZWRBVENzbEpzTlpwcm9RR1dUcW9Md04weXkzQkVoT3ZRZmZrOWhaR2lKZ295TzFobXFRRVRrS0pXb2tIYTBCQUpLSGZtIn19.ACEZLyPk13cM_OFScpLoCElQ2t1sxq5z2d_W_3_QslTQQe5SFiH_o8ycL4632YLAH4RV0gZcMKKRMtZdHgBYHjkzASDqgY-_aYN2SBmpfl8hw0YdDlUJWX3DJf-ofqNAlTbnGmhpSg69cEAhFn41Xgvx2MdwYcPVncxxESVOtWl5zNLK","csign":{"kty":"EC","crv":"P-521","x":"ADiOI_YJOAipEXHB-SpGl4KqokX8m8h3BVYCc8dgiwssZ061-nIIY3O1SIO6Re4Jjfy53RPgzDG6jitOgOGLtzZs","y":"AZKggKaQi0ExutSpJAU3-lqDV03sBQLA9C7KabfWoAn8qD6Vk4jU0WAJdt-wBBTF9o1nVuiqS2OxMVYrxN4lOz79","kid":"LfHJcxgWfJpmnKb0zvQOAvT0voFJsBcfpfc81cv9eqg"}}}'
+ dev[0].set("dpp_ignore_netaccesskey_mismatch", "1")
+ dev[1].set("dpp_config_obj_override", conf)
+ run_dpp_qr_code_auth_unicast(dev, apdev, "secp521r1",
+ require_conf_success=True)
+
+def test_dpp_config_override_objects(dev, apdev):
+ """Generate DPP Config Object and override objects)"""
+ check_dpp_capab(dev[1])
+ discovery = '{\n"ssid":"mywifi"\n}'
+ groups = '[\n {"groupId":"home","netRole":"sta"},\n {"groupId":"cottage","netRole":"sta"}\n]'
+ dev[1].set("dpp_discovery_override", discovery)
+ dev[1].set("dpp_groups_override", groups)
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True)
+
+def build_conf_obj(kty="EC", crv="P-256",
+ x="W4-Y5N1Pkos3UWb9A5qme0KUYRtY3CVUpekx_MapZ9s",
+ y="Et-M4NSF4NGjvh2VCh4B1sJ9eSCZ4RNzP2DBdP137VE",
+ kid="TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU",
+ prot_hdr='{"typ":"dppCon","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU","alg":"ES256"}',
+ signed_connector=None,
+ no_signed_connector=False,
+ csign=True):
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{'
+ conf += '"akm":"dpp",'
+
+ if signed_connector:
+ conn = signed_connector
+ conf += '"signedConnector":"%s",' % conn
+ elif not no_signed_connector:
+ payload = '{"groups":[{"groupId":"*","netRole":"sta"}],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
+ sign = "_sm6YswxMf6hJLVTyYoU1uYUeY2VVkUNjrzjSiEhY42StD_RWowStEE-9CRsdCvLmsTptZ72_g40vTFwdId20A"
+ conn = base64.urlsafe_b64encode(prot_hdr.encode()).decode().rstrip('=') + '.'
+ conn += base64.urlsafe_b64encode(payload.encode()).decode().rstrip('=') + '.'
+ conn += sign
+ conf += '"signedConnector":"%s",' % conn
+
+ if csign:
+ conf += '"csign":{'
+ if kty:
+ conf += '"kty":"%s",' % kty
+ if crv:
+ conf += '"crv":"%s",' % crv
+ if x:
+ conf += '"x":"%s",' % x
+ if y:
+ conf += '"y":"%s",' % y
+ if kid:
+ conf += '"kid":"%s"' % kid
+ conf = conf.rstrip(',')
+ conf += '}'
+ else:
+ conf = conf.rstrip(',')
+
+ conf += '}}'
+
+ return conf
+
+def run_dpp_config_error(dev, apdev, conf,
+ skip_net_access_key_mismatch=True,
+ conf_failure=True):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ if skip_net_access_key_mismatch:
+ dev[0].set("dpp_ignore_netaccesskey_mismatch", "1")
+ dev[1].set("dpp_config_obj_override", conf)
+ run_dpp_qr_code_auth_unicast(dev, apdev, "prime256v1",
+ require_conf_success=not conf_failure,
+ require_conf_failure=conf_failure)
+
+def test_dpp_config_jwk_error_no_kty(dev, apdev):
+ """DPP Config Object JWK error - no kty"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(kty=None))
+
+def test_dpp_config_jwk_error_unexpected_kty(dev, apdev):
+ """DPP Config Object JWK error - unexpected kty"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(kty="unknown"))
+
+def test_dpp_config_jwk_error_no_crv(dev, apdev):
+ """DPP Config Object JWK error - no crv"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(crv=None))
+
+def test_dpp_config_jwk_error_unsupported_crv(dev, apdev):
+ """DPP Config Object JWK error - unsupported curve"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(crv="unsupported"))
+
+def test_dpp_config_jwk_error_no_x(dev, apdev):
+ """DPP Config Object JWK error - no x"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(x=None))
+
+def test_dpp_config_jwk_error_invalid_x(dev, apdev):
+ """DPP Config Object JWK error - invalid x"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(x="MTIz"))
+
+def test_dpp_config_jwk_error_no_y(dev, apdev):
+ """DPP Config Object JWK error - no y"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(y=None))
+
+def test_dpp_config_jwk_error_invalid_y(dev, apdev):
+ """DPP Config Object JWK error - invalid y"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(y="MTIz"))
+
+def test_dpp_config_jwk_error_invalid_xy(dev, apdev):
+ """DPP Config Object JWK error - invalid x,y"""
+ conf = build_conf_obj(x="MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY",
+ y="MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY")
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_jwk_error_no_kid(dev, apdev):
+ """DPP Config Object JWK error - no kid"""
+ # csign kid is optional field, so this results in success
+ run_dpp_config_error(dev, apdev, build_conf_obj(kid=None),
+ conf_failure=False)
+
+def test_dpp_config_jws_error_prot_hdr_not_an_object(dev, apdev):
+ """DPP Config Object JWS error - protected header not an object"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr="1"))
+
+def test_dpp_config_jws_error_prot_hdr_no_typ(dev, apdev):
+ """DPP Config Object JWS error - protected header - no typ"""
+ prot_hdr = '{"kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU","alg":"ES256"}'
+ run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
+
+def test_dpp_config_jws_error_prot_hdr_unsupported_typ(dev, apdev):
+ """DPP Config Object JWS error - protected header - unsupported typ"""
+ prot_hdr = '{"typ":"unsupported","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU","alg":"ES256"}'
+ run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
+
+def test_dpp_config_jws_error_prot_hdr_no_alg(dev, apdev):
+ """DPP Config Object JWS error - protected header - no alg"""
+ prot_hdr = '{"typ":"dppCon","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU"}'
+ run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
+
+def test_dpp_config_jws_error_prot_hdr_unexpected_alg(dev, apdev):
+ """DPP Config Object JWS error - protected header - unexpected alg"""
+ prot_hdr = '{"typ":"dppCon","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU","alg":"unexpected"}'
+ run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
+
+def test_dpp_config_jws_error_prot_hdr_no_kid(dev, apdev):
+ """DPP Config Object JWS error - protected header - no kid"""
+ prot_hdr = '{"typ":"dppCon","alg":"ES256"}'
+ run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
+
+def test_dpp_config_jws_error_prot_hdr_unexpected_kid(dev, apdev):
+ """DPP Config Object JWS error - protected header - unexpected kid"""
+ prot_hdr = '{"typ":"dppCon","kid":"MTIz","alg":"ES256"}'
+ run_dpp_config_error(dev, apdev, build_conf_obj(prot_hdr=prot_hdr))
+
+def test_dpp_config_signed_connector_error_no_dot_1(dev, apdev):
+ """DPP Config Object signedConnector error - no dot(1)"""
+ conn = "MTIz"
+ run_dpp_config_error(dev, apdev, build_conf_obj(signed_connector=conn))
+
+def test_dpp_config_signed_connector_error_no_dot_2(dev, apdev):
+ """DPP Config Object signedConnector error - no dot(2)"""
+ conn = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJUbkdLaklsTlphYXRyRUFZcmJiamlCNjdyamtMX0FHVldYTzZxOWhESktVIiwiYWxnIjoiRVMyNTYifQ.MTIz"
+ run_dpp_config_error(dev, apdev, build_conf_obj(signed_connector=conn))
+
+def test_dpp_config_signed_connector_error_unexpected_signature_len(dev, apdev):
+ """DPP Config Object signedConnector error - unexpected signature length"""
+ conn = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJUbkdLaklsTlphYXRyRUFZcmJiamlCNjdyamtMX0FHVldYTzZxOWhESktVIiwiYWxnIjoiRVMyNTYifQ.MTIz.MTIz"
+ run_dpp_config_error(dev, apdev, build_conf_obj(signed_connector=conn))
+
+def test_dpp_config_signed_connector_error_invalid_signature_der(dev, apdev):
+ """DPP Config Object signedConnector error - invalid signature DER"""
+ conn = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJUbkdLaklsTlphYXRyRUFZcmJiamlCNjdyamtMX0FHVldYTzZxOWhESktVIiwiYWxnIjoiRVMyNTYifQ.MTIz.MTI"
+ run_dpp_config_error(dev, apdev, build_conf_obj(signed_connector=conn))
+
+def test_dpp_config_no_csign(dev, apdev):
+ """DPP Config Object error - no csign"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(csign=False))
+
+def test_dpp_config_no_signed_connector(dev, apdev):
+ """DPP Config Object error - no signedConnector"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(no_signed_connector=True))
+
+def test_dpp_config_unexpected_signed_connector_char(dev, apdev):
+ """DPP Config Object error - unexpected signedConnector character"""
+ run_dpp_config_error(dev, apdev, build_conf_obj(signed_connector='a\nb'))
+
+def test_dpp_config_root_not_an_object(dev, apdev):
+ """DPP Config Object error - root not an object"""
+ conf = "1"
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_no_wi_fi_tech(dev, apdev):
+ """DPP Config Object error - no wi-fi_tech"""
+ conf = "{}"
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_unsupported_wi_fi_tech(dev, apdev):
+ """DPP Config Object error - unsupported wi-fi_tech"""
+ conf = '{"wi-fi_tech":"unsupported"}'
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_no_discovery(dev, apdev):
+ """DPP Config Object error - no discovery"""
+ conf = '{"wi-fi_tech":"infra"}'
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_no_discovery_ssid(dev, apdev):
+ """DPP Config Object error - no discovery::ssid"""
+ conf = '{"wi-fi_tech":"infra","discovery":{}}'
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_too_long_discovery_ssid(dev, apdev):
+ """DPP Config Object error - too long discovery::ssid"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"%s"}}' % (33*'A')
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_no_cred(dev, apdev):
+ """DPP Config Object error - no cred"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"}}'
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_no_cred_akm(dev, apdev):
+ """DPP Config Object error - no cred::akm"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{}}'
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_unsupported_cred_akm(dev, apdev):
+ """DPP Config Object error - unsupported cred::akm"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"unsupported"}}'
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_error_legacy_no_pass(dev, apdev):
+ """DPP Config Object legacy error - no pass/psk"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk"}}'
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_error_legacy_too_short_pass(dev, apdev):
+ """DPP Config Object legacy error - too short pass/psk"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"1"}}'
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_error_legacy_too_long_pass(dev, apdev):
+ """DPP Config Object legacy error - too long pass/psk"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"%s"}}' % (64*'A')
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_error_legacy_psk_with_sae(dev, apdev):
+ """DPP Config Object legacy error - psk_hex with SAE"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"sae","psk_hex":"%s"}}' % (32*"12")
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_error_legacy_no_pass_for_sae(dev, apdev):
+ """DPP Config Object legacy error - no pass for SAE"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk+sae","psk_hex":"%s"}}' % (32*"12")
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_error_legacy_invalid_psk(dev, apdev):
+ """DPP Config Object legacy error - invalid psk_hex"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk","psk_hex":"%s"}}' % (32*"qa")
+ run_dpp_config_error(dev, apdev, conf)
+
+def test_dpp_config_error_legacy_too_short_psk(dev, apdev):
+ """DPP Config Object legacy error - too short psk_hex"""
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"psk","psk_hex":"%s"}}' % (31*"12")
+ run_dpp_config_error(dev, apdev, conf)
+
+def get_der_int_32(val):
+ a, b = struct.unpack('BB', val[0:2])
+ if a != 0x02:
+ raise Exception("Invalid DER encoding of INTEGER")
+ if b > len(val) - 2:
+ raise Exception("Invalid length of INTEGER (truncated)")
+ val = val[2:]
+ if b == 32:
+ r = val[0:32]
+ elif b == 33:
+ if val[0] != 0:
+ raise Exception("Too large INTEGER (32)")
+ r = val[1:33]
+ elif b < 32:
+ r = (32 - b) * b'\x00' + val[0:b]
+ else:
+ raise Exception("Invalid length of INTEGER (32): %d" % b)
+ return r, val[b:]
+
+def ecdsa_sign(pkey, message, alg="sha256"):
+ sign = OpenSSL.crypto.sign(pkey, message, alg)
+ logger.debug("sign=" + binascii.hexlify(sign).decode())
+ a, b = struct.unpack('BB', sign[0:2])
+ if a != 0x30:
+ raise Exception("Invalid DER encoding of ECDSA signature")
+ if b != len(sign) - 2:
+ raise Exception("Invalid length of ECDSA signature")
+ sign = sign[2:]
+
+ r, sign = get_der_int_32(sign)
+ s, sign = get_der_int_32(sign)
+ if len(sign) != 0:
+ raise Exception("Extra data at the end of ECDSA signature")
+
+ logger.info("r=" + binascii.hexlify(r).decode())
+ logger.info("s=" + binascii.hexlify(s).decode())
+ raw_sign = r + s
+ return base64.urlsafe_b64encode(raw_sign).decode().rstrip('=')
+
+p256_priv_key = """-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIBVQij9ah629f1pu3tarDQGQvrzHgAkgYd1jHGiLxNajoAoGCCqGSM49
+AwEHoUQDQgAEAC9d2/JirKu72F2qLuv5jEFMD1Cqu9EiyGk7cOzn/2DJ51p2mEoW
+n03N6XRvTC+G7WPol9Ng97NAM2sK57+F/Q==
+-----END EC PRIVATE KEY-----"""
+p256_pub_key_x = binascii.unhexlify("002f5ddbf262acabbbd85daa2eebf98c414c0f50aabbd122c8693b70ece7ff60")
+p256_pub_key_y = binascii.unhexlify("c9e75a76984a169f4dcde9746f4c2f86ed63e897d360f7b340336b0ae7bf85fd")
+
+def run_dpp_config_connector(dev, apdev, expiry=None, payload=None,
+ skip_net_access_key_mismatch=True,
+ conf_failure=True):
+ if not openssl_imported:
+ raise HwsimSkip("OpenSSL python method not available")
+ pkey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM,
+ p256_priv_key)
+ x = base64.urlsafe_b64encode(p256_pub_key_x).decode().rstrip('=')
+ y = base64.urlsafe_b64encode(p256_pub_key_y).decode().rstrip('=')
+
+ pubkey = b'\x04' + p256_pub_key_x + p256_pub_key_y
+ kid = base64.urlsafe_b64encode(hashlib.sha256(pubkey).digest()).decode().rstrip('=')
+
+ prot_hdr = '{"typ":"dppCon","kid":"%s","alg":"ES256"}' % kid
+
+ if not payload:
+ payload = '{"groups":[{"groupId":"*","netRole":"sta"}],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}'
+ if expiry:
+ payload += ',"expiry":"%s"' % expiry
+ payload += '}'
+ conn = base64.urlsafe_b64encode(prot_hdr.encode()).decode().rstrip('=') + '.'
+ conn += base64.urlsafe_b64encode(payload.encode()).decode().rstrip('=')
+ sign = ecdsa_sign(pkey, conn)
+ conn += '.' + sign
+ run_dpp_config_error(dev, apdev,
+ build_conf_obj(x=x, y=y, signed_connector=conn),
+ skip_net_access_key_mismatch=skip_net_access_key_mismatch,
+ conf_failure=conf_failure)
+
+def test_dpp_config_connector_error_ext_sign(dev, apdev):
+ """DPP Config Object connector error - external signature calculation"""
+ run_dpp_config_connector(dev, apdev, conf_failure=False)
+
+def test_dpp_config_connector_error_too_short_timestamp(dev, apdev):
+ """DPP Config Object connector error - too short timestamp"""
+ run_dpp_config_connector(dev, apdev, expiry="1")
+
+def test_dpp_config_connector_error_invalid_timestamp(dev, apdev):
+ """DPP Config Object connector error - invalid timestamp"""
+ run_dpp_config_connector(dev, apdev, expiry=19*"1")
+
+def test_dpp_config_connector_error_invalid_timestamp_date(dev, apdev):
+ """DPP Config Object connector error - invalid timestamp date"""
+ run_dpp_config_connector(dev, apdev, expiry="9999-99-99T99:99:99Z")
+
+def test_dpp_config_connector_error_invalid_time_zone(dev, apdev):
+ """DPP Config Object connector error - invalid time zone"""
+ run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00*")
+
+def test_dpp_config_connector_error_invalid_time_zone_2(dev, apdev):
+ """DPP Config Object connector error - invalid time zone 2"""
+ run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00+")
+
+def test_dpp_config_connector_error_expired_1(dev, apdev):
+ """DPP Config Object connector error - expired 1"""
+ run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00")
+
+def test_dpp_config_connector_error_expired_2(dev, apdev):
+ """DPP Config Object connector error - expired 2"""
+ run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00Z")
+
+def test_dpp_config_connector_error_expired_3(dev, apdev):
+ """DPP Config Object connector error - expired 3"""
+ run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00+01")
+
+def test_dpp_config_connector_error_expired_4(dev, apdev):
+ """DPP Config Object connector error - expired 4"""
+ run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00+01:02")
+
+def test_dpp_config_connector_error_expired_5(dev, apdev):
+ """DPP Config Object connector error - expired 5"""
+ run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00-01")
+
+def test_dpp_config_connector_error_expired_6(dev, apdev):
+ """DPP Config Object connector error - expired 6"""
+ run_dpp_config_connector(dev, apdev, expiry="2018-01-01T00:00:00-01:02")
+
+def test_dpp_config_connector_error_no_groups(dev, apdev):
+ """DPP Config Object connector error - no groups"""
+ payload = '{"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
+ run_dpp_config_connector(dev, apdev, payload=payload)
+
+def test_dpp_config_connector_error_empty_groups(dev, apdev):
+ """DPP Config Object connector error - empty groups"""
+ payload = '{"groups":[],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
+ run_dpp_config_connector(dev, apdev, payload=payload)
+
+def test_dpp_config_connector_error_missing_group_id(dev, apdev):
+ """DPP Config Object connector error - missing groupId"""
+ payload = '{"groups":[{"netRole":"sta"}],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
+ run_dpp_config_connector(dev, apdev, payload=payload)
+
+def test_dpp_config_connector_error_missing_net_role(dev, apdev):
+ """DPP Config Object connector error - missing netRole"""
+ payload = '{"groups":[{"groupId":"*"}],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
+ run_dpp_config_connector(dev, apdev, payload=payload)
+
+def test_dpp_config_connector_error_missing_net_access_key(dev, apdev):
+ """DPP Config Object connector error - missing netAccessKey"""
+ payload = '{"groups":[{"groupId":"*","netRole":"sta"}]}'
+ run_dpp_config_connector(dev, apdev, payload=payload)
+
+def test_dpp_config_connector_error_net_access_key_mismatch(dev, apdev):
+ """DPP Config Object connector error - netAccessKey mismatch"""
+ payload = '{"groups":[{"groupId":"*","netRole":"sta"}],"netAccessKey":{"kty":"EC","crv":"P-256","x":"aTF4JEGIPKSZ0Xv9zdCMjm-tn5XpMsYIVZ9wySAz1gI","y":"QGcHWA_6rbU9XDXAztoX-M5Q3suTnMaqEhULtn7SSXw"}}'
+ run_dpp_config_connector(dev, apdev, payload=payload,
+ skip_net_access_key_mismatch=False)
+
+def test_dpp_gas_timeout(dev, apdev):
+ """DPP and GAS server timeout for a query"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2412)
+
+ # Force GAS fragmentation
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[1].set("dpp_config_obj_override", conf)
+
+ dev[1].dpp_auth_init(uri=uri0)
+
+ # DPP Authentication Request
+ msg = dev[0].mgmt_rx()
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
+ msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ # DPP Authentication Confirmation
+ msg = dev[0].mgmt_rx()
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
+ msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ wait_auth_success(dev[0], dev[1])
+
+ # DPP Configuration Response (GAS Initial Response frame)
+ msg = dev[0].mgmt_rx()
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
+ msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ # GAS Comeback Response frame
+ msg = dev[0].mgmt_rx()
+ # Do not continue to force timeout on GAS server
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS result not reported (Enrollee)")
+ if "result=TIMEOUT" not in ev:
+ raise Exception("Unexpected GAS result (Enrollee): " + ev)
+ dev[0].set("ext_mgmt_frame_handling", "0")
+
+ ev = dev[1].wait_event(["DPP-CONF-FAILED"], timeout=15)
+ if ev is None:
+ raise Exception("DPP configuration failure not reported (Configurator)")
+
+ ev = dev[0].wait_event(["DPP-CONF-FAILED"], timeout=1)
+ if ev is None:
+ raise Exception("DPP configuration failure not reported (Enrollee)")
+
+def test_dpp_akm_sha256(dev, apdev):
+ """DPP AKM (SHA256)"""
+ run_dpp_akm(dev, apdev, 32)
+
+def test_dpp_akm_sha384(dev, apdev):
+ """DPP AKM (SHA384)"""
+ run_dpp_akm(dev, apdev, 48)
+
+def test_dpp_akm_sha512(dev, apdev):
+ """DPP AKM (SHA512)"""
+ run_dpp_akm(dev, apdev, 64)
+
+def run_dpp_akm(dev, apdev, pmk_len):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ params = {"ssid": "dpp",
+ "wpa": "2",
+ "wpa_key_mgmt": "DPP",
+ "rsn_pairwise": "CCMP",
+ "ieee80211w": "2"}
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except:
+ raise HwsimSkip("DPP not supported")
+
+ conf = hapd.request("GET_CONFIG")
+ if "key_mgmt=DPP" not in conf.splitlines():
+ logger.info("GET_CONFIG:\n" + conf)
+ raise Exception("GET_CONFIG did not report correct key_mgmt")
+
+ id = dev[0].connect("dpp", key_mgmt="DPP", ieee80211w="2", scan_freq="2412",
+ dpp_pfs="2", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=2)
+ if not ev:
+ raise Exception("Network mismatch not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+
+ bssid = hapd.own_addr()
+ pmkid = 16*'11'
+ akmp = 2**23
+ pmk = pmk_len*'22'
+ cmd = "PMKSA_ADD %d %s %s %s 30240 43200 %d 0" % (id, bssid, pmkid, pmk, akmp)
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("PMKSA_ADD failed (wpa_supplicant)")
+ dev[0].select_network(id, freq="2412")
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=2)
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+ if not ev:
+ raise Exception("Association attempt was not rejected")
+ if "status_code=53" not in ev:
+ raise Exception("Unexpected status code: " + ev)
+
+ addr = dev[0].own_addr()
+ cmd = "PMKSA_ADD %s %s %s 0 %d" % (addr, pmkid, pmk, akmp)
+ if "OK" not in hapd.request(cmd):
+ raise Exception("PMKSA_ADD failed (hostapd)")
+
+ dev[0].select_network(id, freq="2412")
+ dev[0].wait_connected()
+ val = dev[0].get_status_field("key_mgmt")
+ if val != "DPP":
+ raise Exception("Unexpected key_mgmt: " + val)
+
+params1_csign = "3059301306072a8648ce3d020106082a8648ce3d03010703420004d02e5bd81a120762b5f0f2994777f5d40297238a6c294fd575cdf35fabec44c050a6421c401d98d659fd2ed13c961cc8287944dd3202f516977800d3ab2f39ee"
+params1_ap_connector = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJzOEFrYjg5bTV4UGhoYk5UbTVmVVo0eVBzNU5VMkdxYXNRY3hXUWhtQVFRIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6ImFwIn1dLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiIwOHF4TlNYRzRWemdCV3BjVUdNSmc1czNvbElOVFJsRVQ1aERpNkRKY3ZjIiwieSI6IlVhaGFYQXpKRVpRQk1YaHRUQnlZZVlrOWtJYjk5UDA3UV9NcW9TVVZTVEkifX0.a5_nfMVr7Qe1SW0ZL3u6oQRm5NUCYUSfixDAJOUFN3XUfECBZ6E8fm8xjeSfdOytgRidTz0CTlIRjzPQo82dmQ"
+params1_ap_netaccesskey = "30770201010420f6531d17f29dfab655b7c9e923478d5a345164c489aadd44a3519c3e9dcc792da00a06082a8648ce3d030107a14403420004d3cab13525c6e15ce0056a5c506309839b37a2520d4d19444f98438ba0c972f751a85a5c0cc911940131786d4c1c9879893d9086fdf4fd3b43f32aa125154932"
+params1_sta_connector = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJzOEFrYjg5bTV4UGhoYk5UbTVmVVo0eVBzNU5VMkdxYXNRY3hXUWhtQVFRIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6InN0YSJ9XSwibmV0QWNjZXNzS2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiZWMzR3NqQ3lQMzVBUUZOQUJJdEltQnN4WXVyMGJZX1dES1lfSE9zUGdjNCIsInkiOiJTRS1HVllkdWVnTFhLMU1TQXZNMEx2QWdLREpTNWoyQVhCbE9PMTdUSTRBIn19.PDK9zsGlK-e1pEOmNxVeJfCS8pNeay6ckIS1TXCQsR64AR-9wFPCNVjqOxWvVKltehyMFqVAtOcv0IrjtMJFqQ"
+params1_sta_netaccesskey = "30770201010420bc33380c26fd2168b69cd8242ed1df07ba89aa4813f8d4e8523de6ca3f8dd28ba00a06082a8648ce3d030107a1440342000479cdc6b230b23f7e40405340048b48981b3162eaf46d8fd60ca63f1ceb0f81ce484f8655876e7a02d72b531202f3342ef020283252e63d805c194e3b5ed32380"
+
+def test_dpp_network_introduction(dev, apdev):
+ """DPP network introduction"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ params = {"ssid": "dpp",
+ "wpa": "2",
+ "wpa_key_mgmt": "DPP",
+ "ieee80211w": "2",
+ "rsn_pairwise": "CCMP",
+ "dpp_connector": params1_ap_connector,
+ "dpp_csign": params1_csign,
+ "dpp_netaccesskey": params1_ap_netaccesskey}
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except:
+ raise HwsimSkip("DPP not supported")
+
+ id = dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
+ ieee80211w="2",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey)
+ val = dev[0].get_status_field("key_mgmt")
+ if val != "DPP":
+ raise Exception("Unexpected key_mgmt: " + val)
+
+def test_dpp_network_introduction_expired(dev, apdev):
+ """DPP network introduction with expired netaccesskey"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ params = {"ssid": "dpp",
+ "wpa": "2",
+ "wpa_key_mgmt": "DPP",
+ "ieee80211w": "2",
+ "rsn_pairwise": "CCMP",
+ "dpp_connector": params1_ap_connector,
+ "dpp_csign": params1_csign,
+ "dpp_netaccesskey": params1_ap_netaccesskey,
+ "dpp_netaccesskey_expiry": "1565530889"}
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except:
+ raise HwsimSkip("DPP not supported")
+
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
+ ieee80211w="2",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey,
+ wait_connect=False)
+ ev = hapd.wait_event(["DPP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("No DPP Peer Discovery Request seen")
+ if "type=5" not in ev:
+ raise Exception("Unexpected DPP message received: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ dev[0].request("DISCONNECT")
+ if ev:
+ raise Exception("Connection reported")
+
+ hapd.disable()
+ hapd.set("dpp_netaccesskey_expiry", "2565530889")
+ hapd.enable()
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+def test_dpp_and_sae_akm(dev, apdev):
+ """DPP and SAE AKMs"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ if "SAE" not in dev[1].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ params = {"ssid": "dpp+sae",
+ "wpa": "2",
+ "wpa_key_mgmt": "DPP SAE",
+ "ieee80211w": "2",
+ "rsn_pairwise": "CCMP",
+ "sae_password": "sae-password",
+ "dpp_connector": params1_ap_connector,
+ "dpp_csign": params1_csign,
+ "dpp_netaccesskey": params1_ap_netaccesskey}
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except:
+ raise HwsimSkip("DPP not supported")
+
+ id = dev[0].connect("dpp+sae", key_mgmt="DPP", scan_freq="2412",
+ ieee80211w="2",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey)
+ val = dev[0].get_status_field("key_mgmt")
+ if val != "DPP":
+ raise Exception("Unexpected key_mgmt for DPP: " + val)
+
+ dev[1].request("SET sae_groups ")
+ id = dev[1].connect("dpp+sae", key_mgmt="SAE", scan_freq="2412",
+ ieee80211w="2", psk="sae-password")
+ val = dev[1].get_status_field("key_mgmt")
+ if val != "SAE":
+ raise Exception("Unexpected key_mgmt for SAE: " + val)
+
+def test_dpp_ap_config(dev, apdev):
+ """DPP and AP configuration"""
+ run_dpp_ap_config(dev, apdev)
+
+def test_dpp_ap_config_p256_p256(dev, apdev):
+ """DPP and AP configuration (P-256 + P-256)"""
+ run_dpp_ap_config(dev, apdev, curve="P-256", conf_curve="P-256")
+
+def test_dpp_ap_config_p256_p384(dev, apdev):
+ """DPP and AP configuration (P-256 + P-384)"""
+ run_dpp_ap_config(dev, apdev, curve="P-256", conf_curve="P-384")
+
+def test_dpp_ap_config_p256_p521(dev, apdev):
+ """DPP and AP configuration (P-256 + P-521)"""
+ run_dpp_ap_config(dev, apdev, curve="P-256", conf_curve="P-521")
+
+def test_dpp_ap_config_p384_p256(dev, apdev):
+ """DPP and AP configuration (P-384 + P-256)"""
+ run_dpp_ap_config(dev, apdev, curve="P-384", conf_curve="P-256")
+
+def test_dpp_ap_config_p384_p384(dev, apdev):
+ """DPP and AP configuration (P-384 + P-384)"""
+ run_dpp_ap_config(dev, apdev, curve="P-384", conf_curve="P-384")
+
+def test_dpp_ap_config_p384_p521(dev, apdev):
+ """DPP and AP configuration (P-384 + P-521)"""
+ run_dpp_ap_config(dev, apdev, curve="P-384", conf_curve="P-521")
+
+def test_dpp_ap_config_p521_p256(dev, apdev):
+ """DPP and AP configuration (P-521 + P-256)"""
+ run_dpp_ap_config(dev, apdev, curve="P-521", conf_curve="P-256")
+
+def test_dpp_ap_config_p521_p384(dev, apdev):
+ """DPP and AP configuration (P-521 + P-384)"""
+ run_dpp_ap_config(dev, apdev, curve="P-521", conf_curve="P-384")
+
+def test_dpp_ap_config_p521_p521(dev, apdev):
+ """DPP and AP configuration (P-521 + P-521)"""
+ run_dpp_ap_config(dev, apdev, curve="P-521", conf_curve="P-521")
+
+def test_dpp_ap_config_bp256_bp256(dev, apdev):
+ """DPP and AP configuration (BP-256 + BP-256)"""
+ run_dpp_ap_config(dev, apdev, curve="BP-256", conf_curve="BP-256")
+
+def test_dpp_ap_config_bp384_bp384(dev, apdev):
+ """DPP and AP configuration (BP-384 + BP-384)"""
+ run_dpp_ap_config(dev, apdev, curve="BP-384", conf_curve="BP-384")
+
+def test_dpp_ap_config_bp512_bp512(dev, apdev):
+ """DPP and AP configuration (BP-512 + BP-512)"""
+ run_dpp_ap_config(dev, apdev, curve="BP-512", conf_curve="BP-512")
+
+def test_dpp_ap_config_p256_bp256(dev, apdev):
+ """DPP and AP configuration (P-256 + BP-256)"""
+ run_dpp_ap_config(dev, apdev, curve="P-256", conf_curve="BP-256")
+
+def test_dpp_ap_config_bp256_p256(dev, apdev):
+ """DPP and AP configuration (BP-256 + P-256)"""
+ run_dpp_ap_config(dev, apdev, curve="BP-256", conf_curve="P-256")
+
+def test_dpp_ap_config_p521_bp512(dev, apdev):
+ """DPP and AP configuration (P-521 + BP-512)"""
+ run_dpp_ap_config(dev, apdev, curve="P-521", conf_curve="BP-512")
+
+def test_dpp_ap_config_bp512_p521(dev, apdev):
+ """DPP and AP configuration (BP-512 + P-521)"""
+ run_dpp_ap_config(dev, apdev, curve="BP-512", conf_curve="P-521")
+
+def test_dpp_ap_config_reconfig_configurator(dev, apdev):
+ """DPP and AP configuration with Configurator reconfiguration"""
+ run_dpp_ap_config(dev, apdev, reconf_configurator=True)
+
+def update_hapd_config(hapd):
+ ev = hapd.wait_event(["DPP-CONFOBJ-SSID"], timeout=1)
+ if ev is None:
+ raise Exception("SSID not reported (AP)")
+ ssid = ev.split(' ')[1]
+
+ ev = hapd.wait_event(["DPP-CONNECTOR"], timeout=1)
+ if ev is None:
+ raise Exception("Connector not reported (AP)")
+ connector = ev.split(' ')[1]
+
+ ev = hapd.wait_event(["DPP-C-SIGN-KEY"], timeout=1)
+ if ev is None:
+ raise Exception("C-sign-key not reported (AP)")
+ p = ev.split(' ')
+ csign = p[1]
+
+ ev = hapd.wait_event(["DPP-NET-ACCESS-KEY"], timeout=1)
+ if ev is None:
+ raise Exception("netAccessKey not reported (AP)")
+ p = ev.split(' ')
+ net_access_key = p[1]
+ net_access_key_expiry = p[2] if len(p) > 2 else None
+
+ logger.info("Update AP configuration to use key_mgmt=DPP")
+ hapd.disable()
+ hapd.set("ssid", ssid)
+ hapd.set("utf8_ssid", "1")
+ hapd.set("wpa", "2")
+ hapd.set("wpa_key_mgmt", "DPP")
+ hapd.set("ieee80211w", "2")
+ hapd.set("rsn_pairwise", "CCMP")
+ hapd.set("dpp_connector", connector)
+ hapd.set("dpp_csign", csign)
+ hapd.set("dpp_netaccesskey", net_access_key)
+ if net_access_key_expiry:
+ hapd.set("dpp_netaccesskey_expiry", net_access_key_expiry)
+ hapd.enable()
+
+def run_dpp_ap_config(dev, apdev, curve=None, conf_curve=None,
+ reconf_configurator=False):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True, curve=curve)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+
+ conf_id = dev[0].dpp_configurator_add(curve=conf_curve)
+
+ if reconf_configurator:
+ csign = dev[0].request("DPP_CONFIGURATOR_GET_KEY %d" % conf_id)
+ if "FAIL" in csign or len(csign) == 0:
+ raise Exception("DPP_CONFIGURATOR_GET_KEY failed")
+
+ dev[0].dpp_auth_init(uri=uri, conf="ap-dpp", configurator=conf_id)
+ wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd)
+ update_hapd_config(hapd)
+
+ id1 = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True, curve=curve)
+ uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ if reconf_configurator:
+ dev[0].dpp_configurator_remove(conf_id)
+ conf_id = dev[0].dpp_configurator_add(curve=conf_curve, key=csign)
+
+ dev[1].dpp_listen(2412)
+ dev[0].dpp_auth_init(uri=uri1, conf="sta-dpp", configurator=conf_id)
+ wait_auth_success(dev[1], dev[0], configurator=dev[0], enrollee=dev[1],
+ stop_responder=True)
+
+ ev = dev[1].wait_event(["DPP-CONFOBJ-SSID"], timeout=1)
+ if ev is None:
+ raise Exception("SSID not reported")
+ ssid = ev.split(' ')[1]
+
+ ev = dev[1].wait_event(["DPP-CONNECTOR"], timeout=1)
+ if ev is None:
+ raise Exception("Connector not reported")
+ connector = ev.split(' ')[1]
+
+ ev = dev[1].wait_event(["DPP-C-SIGN-KEY"], timeout=1)
+ if ev is None:
+ raise Exception("C-sign-key not reported")
+ p = ev.split(' ')
+ csign = p[1]
+
+ ev = dev[1].wait_event(["DPP-NET-ACCESS-KEY"], timeout=1)
+ if ev is None:
+ raise Exception("netAccessKey not reported")
+ p = ev.split(' ')
+ net_access_key = p[1]
+ net_access_key_expiry = p[2] if len(p) > 2 else None
+
+ dev[1].dump_monitor()
+
+ id = dev[1].connect(ssid, key_mgmt="DPP", ieee80211w="2", scan_freq="2412",
+ only_add_network=True)
+ dev[1].set_network_quoted(id, "dpp_connector", connector)
+ dev[1].set_network(id, "dpp_csign", csign)
+ dev[1].set_network(id, "dpp_netaccesskey", net_access_key)
+ if net_access_key_expiry:
+ dev[1].set_network(id, "dpp_netaccess_expiry", net_access_key_expiry)
+
+ logger.info("Check data connection")
+ dev[1].select_network(id, freq="2412")
+ dev[1].wait_connected()
+
+def test_dpp_auto_connect_1(dev, apdev):
+ """DPP and auto connect (1)"""
+ try:
+ run_dpp_auto_connect(dev, apdev, 1)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_2(dev, apdev):
+ """DPP and auto connect (2)"""
+ try:
+ run_dpp_auto_connect(dev, apdev, 2)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_2_connect_cmd(dev, apdev):
+ """DPP and auto connect (2) using connect_cmd"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ dev_new = [wpas, dev[1]]
+ try:
+ run_dpp_auto_connect(dev_new, apdev, 2)
+ finally:
+ wpas.set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_2_sta_ver1(dev, apdev):
+ """DPP and auto connect (2; STA using ver 1)"""
+ try:
+ run_dpp_auto_connect(dev, apdev, 2, sta_version=1)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_2_ap_ver1(dev, apdev):
+ """DPP and auto connect (2; AP using ver 1)"""
+ try:
+ run_dpp_auto_connect(dev, apdev, 2, ap_version=1)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_2_ver1(dev, apdev):
+ """DPP and auto connect (2; AP and STA using ver 1)"""
+ try:
+ run_dpp_auto_connect(dev, apdev, 2, ap_version=1, sta_version=1)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_2_conf_ver1(dev, apdev):
+ """DPP and auto connect (2; Configurator using ver 1)"""
+ try:
+ run_dpp_auto_connect(dev, apdev, 2, sta1_version=1)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_auto_connect(dev, apdev, processing, ap_version=0, sta_version=0,
+ sta1_version=0):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ csign = "30770201010420768240a3fc89d6662d9782f120527fe7fb9edc6366ab0b9c7dde96125cfd250fa00a06082a8648ce3d030107a144034200042908e1baf7bf413cc66f9e878a03e8bb1835ba94b033dbe3d6969fc8575d5eb5dfda1cb81c95cee21d0cd7d92ba30541ffa05cb6296f5dd808b0c1c2a83c0708"
+ csign_pub = "3059301306072a8648ce3d020106082a8648ce3d030107034200042908e1baf7bf413cc66f9e878a03e8bb1835ba94b033dbe3d6969fc8575d5eb5dfda1cb81c95cee21d0cd7d92ba30541ffa05cb6296f5dd808b0c1c2a83c0708"
+ ap_connector = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJwYWtZbXVzd1dCdWpSYTl5OEsweDViaTVrT3VNT3dzZHRlaml2UG55ZHZzIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6ImFwIn1dLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiIybU5vNXZuRkI5bEw3d1VWb1hJbGVPYzBNSEE1QXZKbnpwZXZULVVTYzVNIiwieSI6IlhzS3dqVHJlLTg5WWdpU3pKaG9CN1haeUttTU05OTl3V2ZaSVl0bi01Q3MifX0.XhjFpZgcSa7G2lHy0OCYTvaZFRo5Hyx6b7g7oYyusLC7C_73AJ4_BxEZQVYJXAtDuGvb3dXSkHEKxREP9Q6Qeg"
+ ap_netaccesskey = "30770201010420ceba752db2ad5200fa7bc565b9c05c69b7eb006751b0b329b0279de1c19ca67ca00a06082a8648ce3d030107a14403420004da6368e6f9c507d94bef0515a1722578e73430703902f267ce97af4fe51273935ec2b08d3adefbcf588224b3261a01ed76722a630cf7df7059f64862d9fee42b"
+
+ params = {"ssid": "test",
+ "wpa": "2",
+ "wpa_key_mgmt": "DPP",
+ "ieee80211w": "2",
+ "rsn_pairwise": "CCMP",
+ "dpp_connector": ap_connector,
+ "dpp_csign": csign_pub,
+ "dpp_netaccesskey": ap_netaccesskey}
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ if ap_version:
+ hapd.set("dpp_version_override", str(ap_version))
+ except:
+ raise HwsimSkip("DPP not supported")
+
+ if sta_version:
+ dev[0].set("dpp_version_override", str(sta_version))
+ if sta1_version:
+ dev[1].set("dpp_version_override", str(sta1_version))
+ conf_id = dev[1].dpp_configurator_add(key=csign)
+ dev[0].set("dpp_config_processing", str(processing))
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0, conf="sta-dpp", configurator=conf_id)
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0])
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("DPP network profile not generated")
+ id = ev.split(' ')[1]
+
+ if processing == 1:
+ dev[0].select_network(id, freq=2412)
+
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_dpp_auto_connect_legacy(dev, apdev):
+ """DPP and auto connect (legacy)"""
+ try:
+ run_dpp_auto_connect_legacy(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_legacy_ssid_charset(dev, apdev):
+ """DPP and auto connect (legacy, ssid_charset)"""
+ try:
+ run_dpp_auto_connect_legacy(dev, apdev, ssid_charset=12345)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_legacy_sae_1(dev, apdev):
+ """DPP and auto connect (legacy SAE)"""
+ try:
+ run_dpp_auto_connect_legacy(dev, apdev, conf='sta-sae', psk_sae=True)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_legacy_sae_2(dev, apdev):
+ """DPP and auto connect (legacy SAE)"""
+ try:
+ run_dpp_auto_connect_legacy(dev, apdev, conf='sta-sae', sae_only=True)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_legacy_psk_sae_1(dev, apdev):
+ """DPP and auto connect (legacy PSK+SAE)"""
+ try:
+ run_dpp_auto_connect_legacy(dev, apdev, conf='sta-psk-sae',
+ psk_sae=True)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_legacy_psk_sae_2(dev, apdev):
+ """DPP and auto connect (legacy PSK+SAE)"""
+ try:
+ run_dpp_auto_connect_legacy(dev, apdev, conf='sta-psk-sae',
+ sae_only=True)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_auto_connect_legacy_psk_sae_3(dev, apdev):
+ """DPP and auto connect (legacy PSK+SAE)"""
+ try:
+ run_dpp_auto_connect_legacy(dev, apdev, conf='sta-psk-sae')
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_auto_connect_legacy(dev, apdev, conf='sta-psk',
+ ssid_charset=None,
+ psk_sae=False, sae_only=False):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ params = hostapd.wpa2_params(ssid="dpp-legacy",
+ passphrase="secret passphrase")
+ if sae_only:
+ params['wpa_key_mgmt'] = 'SAE'
+ params['ieee80211w'] = '2'
+ elif psk_sae:
+ params['wpa_key_mgmt'] = 'WPA-PSK SAE'
+ params['ieee80211w'] = '1'
+ params['sae_require_mfp'] = '1'
+
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].set("dpp_config_processing", "2")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0, conf=conf, ssid="dpp-legacy",
+ ssid_charset=ssid_charset,
+ passphrase="secret passphrase")
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0])
+ if ssid_charset:
+ ev = dev[0].wait_event(["DPP-CONFOBJ-SSID-CHARSET"], timeout=1)
+ if ev is None:
+ raise Exception("ssid_charset not reported")
+ charset = ev.split(' ')[1]
+ if charset != str(ssid_charset):
+ raise Exception("Incorrect ssid_charset reported: " + ev)
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("DPP network profile not generated")
+ id = ev.split(' ')[1]
+
+ dev[0].wait_connected()
+
+def test_dpp_auto_connect_legacy_pmf_required(dev, apdev):
+ """DPP and auto connect (legacy, PMF required)"""
+ try:
+ run_dpp_auto_connect_legacy_pmf_required(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_auto_connect_legacy_pmf_required(dev, apdev):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ params = hostapd.wpa2_params(ssid="dpp-legacy",
+ passphrase="secret passphrase")
+ params['wpa_key_mgmt'] = "WPA-PSK-SHA256"
+ params['ieee80211w'] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].set("dpp_config_processing", "2")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0, conf="sta-psk", ssid="dpp-legacy",
+ passphrase="secret passphrase")
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0])
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("DPP network profile not generated")
+ dev[0].wait_connected()
+
+def test_dpp_qr_code_auth_responder_configurator(dev, apdev):
+ """DPP QR Code and responder as the configurator"""
+ run_dpp_qr_code_auth_responder_configurator(dev, apdev, "")
+
+def test_dpp_qr_code_auth_responder_configurator_group_id(dev, apdev):
+ """DPP QR Code and responder as the configurator with group_id)"""
+ run_dpp_qr_code_auth_responder_configurator(dev, apdev,
+ " group_id=test-group")
+
+def run_dpp_qr_code_auth_responder_configurator(dev, apdev, extra):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ " conf=sta-dpp configurator=%d%s" % (conf_id, extra))
+ dev[0].dpp_listen(2412, role="configurator")
+ dev[1].dpp_auth_init(uri=uri0, role="enrollee")
+ wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1],
+ stop_responder=True)
+
+def test_dpp_qr_code_auth_enrollee_init_netrole(dev, apdev):
+ """DPP QR Code and enrollee initiating with netrole specified"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ " conf=configurator configurator=%d" % conf_id)
+ dev[0].dpp_listen(2412, role="configurator")
+ dev[1].dpp_auth_init(uri=uri0, role="enrollee", netrole="configurator")
+ wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1],
+ stop_responder=True)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ # verify that netrole resets back to sta, if not explicitly stated
+ dev[0].set("dpp_configurator_params",
+ "conf=sta-dpp configurator=%d" % conf_id)
+ dev[0].dpp_listen(2412, role="configurator")
+ dev[1].dpp_auth_init(uri=uri0, role="enrollee")
+ wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1],
+ stop_responder=True)
+
+def test_dpp_qr_code_hostapd_init(dev, apdev):
+ """DPP QR Code and hostapd as initiator"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ " conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ wait_auth_success(dev[0], hapd, configurator=dev[0], enrollee=hapd,
+ stop_responder=True)
+
+def test_dpp_qr_code_hostapd_init_offchannel(dev, apdev):
+ """DPP QR Code and hostapd as initiator (offchannel)"""
+ run_dpp_qr_code_hostapd_init_offchannel(dev, apdev, None)
+
+def test_dpp_qr_code_hostapd_init_offchannel_neg_freq(dev, apdev):
+ """DPP QR Code and hostapd as initiator (offchannel, neg_freq)"""
+ run_dpp_qr_code_hostapd_init_offchannel(dev, apdev, "neg_freq=2437")
+
+def run_dpp_qr_code_hostapd_init_offchannel(dev, apdev, extra):
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1,81/11", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ " conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].dpp_listen(2462, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee", extra=extra)
+ wait_auth_success(dev[0], hapd, configurator=dev[0], enrollee=hapd,
+ stop_responder=True)
+
+def test_dpp_qr_code_hostapd_ignore_mismatch(dev, apdev):
+ """DPP QR Code and hostapd ignoring netaccessKey mismatch"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ conf = '{"wi-fi_tech":"infra","discovery":{"ssid":"test"},"cred":{"akm":"dpp","signedConnector":"eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJUbkdLaklsTlphYXRyRUFZcmJiamlCNjdyamtMX0FHVldYTzZxOWhESktVIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6InN0YSJ9XSwibmV0QWNjZXNzS2V5Ijp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiYVRGNEpFR0lQS1NaMFh2OXpkQ01qbS10bjVYcE1zWUlWWjl3eVNBejFnSSIsInkiOiJRR2NIV0FfNnJiVTlYRFhBenRvWC1NNVEzc3VUbk1hcUVoVUx0bjdTU1h3In19._sm6YswxMf6hJLVTyYoU1uYUeY2VVkUNjrzjSiEhY42StD_RWowStEE-9CRsdCvLmsTptZ72_g40vTFwdId20A","csign":{"kty":"EC","crv":"P-256","x":"W4-Y5N1Pkos3UWb9A5qme0KUYRtY3CVUpekx_MapZ9s","y":"Et-M4NSF4NGjvh2VCh4B1sJ9eSCZ4RNzP2DBdP137VE","kid":"TnGKjIlNZaatrEAYrbbjiB67rjkL_AGVWXO6q9hDJKU"}}}'
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.set("dpp_ignore_netaccesskey_mismatch", "1")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ wait_auth_success(dev[0], hapd, configurator=dev[0], enrollee=hapd,
+ stop_responder=True)
+
+def test_dpp_test_vector_p_256(dev, apdev):
+ """DPP P-256 test vector (mutual auth)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ # Responder bootstrapping key
+ priv = "54ce181a98525f217216f59b245f60e9df30ac7f6b26c939418cfc3c42d1afa0"
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/11", mac=True, key="30310201010420" + priv + "a00a06082a8648ce3d030107")
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ # Responder protocol keypair override
+ priv = "f798ed2e19286f6a6efe210b1863badb99af2a14b497634dbfd2a97394fb5aa5"
+ dev[0].set("dpp_protocol_key_override",
+ "30310201010420" + priv + "a00a06082a8648ce3d030107")
+
+ dev[0].set("dpp_nonce_override", "3d0cfb011ca916d796f7029ff0b43393")
+
+ # Initiator bootstrapping key
+ priv = "15b2a83c5a0a38b61f2aa8200ee4994b8afdc01c58507d10d0a38f7eedf051bb"
+ id1 = dev[1].dpp_bootstrap_gen(key="30310201010420" + priv + "a00a06082a8648ce3d030107")
+ uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ # Initiator protocol keypair override
+ priv = "a87de9afbb406c96e5f79a3df895ecac3ad406f95da66314c8cb3165e0c61783"
+ dev[1].set("dpp_protocol_key_override",
+ "30310201010420" + priv + "a00a06082a8648ce3d030107")
+
+ dev[1].set("dpp_nonce_override", "13f4602a16daeb69712263b9c46cba31")
+
+ dev[0].dpp_qr_code(uri1)
+ dev[0].dpp_listen(2462, qr="mutual")
+ dev[1].dpp_auth_init(uri=uri0, own=id1, neg_freq=2412)
+ wait_auth_success(dev[0], dev[1])
+
+def test_dpp_test_vector_p_256_b(dev, apdev):
+ """DPP P-256 test vector (Responder-only auth)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ # Responder bootstrapping key
+ priv = "54ce181a98525f217216f59b245f60e9df30ac7f6b26c939418cfc3c42d1afa0"
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/11", mac=True, key="30310201010420" + priv + "a00a06082a8648ce3d030107")
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ # Responder protocol keypair override
+ priv = "f798ed2e19286f6a6efe210b1863badb99af2a14b497634dbfd2a97394fb5aa5"
+ dev[0].set("dpp_protocol_key_override",
+ "30310201010420" + priv + "a00a06082a8648ce3d030107")
+
+ dev[0].set("dpp_nonce_override", "3d0cfb011ca916d796f7029ff0b43393")
+
+ # Initiator bootstrapping key
+ priv = "15b2a83c5a0a38b61f2aa8200ee4994b8afdc01c58507d10d0a38f7eedf051bb"
+ id1 = dev[1].dpp_bootstrap_gen(key="30310201010420" + priv + "a00a06082a8648ce3d030107")
+ uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ # Initiator protocol keypair override
+ priv = "a87de9afbb406c96e5f79a3df895ecac3ad406f95da66314c8cb3165e0c61783"
+ dev[1].set("dpp_protocol_key_override",
+ "30310201010420" + priv + "a00a06082a8648ce3d030107")
+
+ dev[1].set("dpp_nonce_override", "13f4602a16daeb69712263b9c46cba31")
+
+ dev[0].dpp_listen(2462)
+ dev[1].dpp_auth_init(uri=uri0, own=id1, neg_freq=2412)
+ wait_auth_success(dev[0], dev[1])
+
+def der_priv_key_p_521(priv):
+ if len(priv) != 2 * 66:
+ raise Exception("Unexpected der_priv_key_p_521 parameter: " + priv)
+ der_prefix = "3081500201010442"
+ der_postfix = "a00706052b81040023"
+ return der_prefix + priv + der_postfix
+
+def test_dpp_test_vector_p_521(dev, apdev):
+ """DPP P-521 test vector (mutual auth)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ # Responder bootstrapping key
+ priv = "0061e54f518cdf859735da3dd64c6f72c2f086f41a6fd52915152ea2fe0f24ddaecd8883730c9c9fd82cf7c043a41021696388cf5190b731dd83638bcd56d8b6c743"
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/11", mac=True,
+ key=der_priv_key_p_521(priv))
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ # Responder protocol keypair override
+ priv = "01d8b7b17cd1b0a33f7c66fb4220999329cdaf4f8b44b2ffadde8ab8ed8abffa9f5358c5b1caae26709ca4fb78e52a4d08f2e4f24111a36a6f440d20a0000ff51597"
+ dev[0].set("dpp_protocol_key_override", der_priv_key_p_521(priv))
+
+ dev[0].set("dpp_nonce_override",
+ "d749a782012eb0a8595af30b2dfc8d0880d004ebddb55ecc5afbdef18c400e01")
+
+ # Initiator bootstrapping key
+ priv = "0060c10df14af5ef27f6e362d31bdd9eeb44be77a323ba64b08f3f03d58b92cbfe05c182a91660caa081ca344243c47b5aa088bcdf738840eb35f0218b9f26881e02"
+ id1 = dev[1].dpp_bootstrap_gen(key=der_priv_key_p_521(priv))
+ uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ # Initiator protocol keypair override
+ priv = "019c1c08caaeec38fb931894699b095bc3ab8c1ec7ef0622d2e3eba821477c8c6fca41774f21166ad98aebda37c067d9aa08a8a2e1b5c44c61f2bae02a61f85d9661"
+ dev[1].set("dpp_protocol_key_override", der_priv_key_p_521(priv))
+
+ dev[1].set("dpp_nonce_override",
+ "de972af3847bec3ba2aedd9f5c21cfdec7bf0bc5fe8b276cbcd0267807fb15b0")
+
+ dev[0].dpp_qr_code(uri1)
+ dev[0].dpp_listen(2462, qr="mutual")
+ dev[1].dpp_auth_init(uri=uri0, own=id1, neg_freq=2412)
+ wait_auth_success(dev[0], dev[1])
+
+def test_dpp_pkex(dev, apdev):
+ """DPP and PKEX"""
+ run_dpp_pkex(dev, apdev)
+
+def test_dpp_pkex_p256(dev, apdev):
+ """DPP and PKEX (P-256)"""
+ run_dpp_pkex(dev, apdev, "P-256")
+
+def test_dpp_pkex_p384(dev, apdev):
+ """DPP and PKEX (P-384)"""
+ run_dpp_pkex(dev, apdev, "P-384")
+
+def test_dpp_pkex_p521(dev, apdev):
+ """DPP and PKEX (P-521)"""
+ run_dpp_pkex(dev, apdev, "P-521")
+
+def test_dpp_pkex_bp256(dev, apdev):
+ """DPP and PKEX (BP-256)"""
+ run_dpp_pkex(dev, apdev, "brainpoolP256r1")
+
+def test_dpp_pkex_bp384(dev, apdev):
+ """DPP and PKEX (BP-384)"""
+ run_dpp_pkex(dev, apdev, "brainpoolP384r1")
+
+def test_dpp_pkex_bp512(dev, apdev):
+ """DPP and PKEX (BP-512)"""
+ run_dpp_pkex(dev, apdev, "brainpoolP512r1")
+
+def test_dpp_pkex_config(dev, apdev):
+ """DPP and PKEX with initiator as the configurator"""
+ check_dpp_capab(dev[1])
+ conf_id = dev[1].dpp_configurator_add()
+ run_dpp_pkex(dev, apdev,
+ init_extra="conf=sta-dpp configurator=%d" % (conf_id),
+ check_config=True)
+
+def test_dpp_pkex_no_identifier(dev, apdev):
+ """DPP and PKEX without identifier"""
+ run_dpp_pkex(dev, apdev, identifier_i=None, identifier_r=None)
+
+def test_dpp_pkex_identifier_mismatch(dev, apdev):
+ """DPP and PKEX with different identifiers"""
+ run_dpp_pkex(dev, apdev, identifier_i="foo", identifier_r="bar",
+ expect_no_resp=True)
+
+def test_dpp_pkex_identifier_mismatch2(dev, apdev):
+ """DPP and PKEX with initiator using identifier and the responder not"""
+ run_dpp_pkex(dev, apdev, identifier_i="foo", identifier_r=None,
+ expect_no_resp=True)
+
+def test_dpp_pkex_identifier_mismatch3(dev, apdev):
+ """DPP and PKEX with responder using identifier and the initiator not"""
+ run_dpp_pkex(dev, apdev, identifier_i=None, identifier_r="bar",
+ expect_no_resp=True)
+
+def run_dpp_pkex(dev, apdev, curve=None, init_extra=None, check_config=False,
+ identifier_i="test", identifier_r="test",
+ expect_no_resp=False):
+ check_dpp_capab(dev[0], curve and "brainpool" in curve)
+ check_dpp_capab(dev[1], curve and "brainpool" in curve)
+ dev[0].dpp_pkex_resp(2437, identifier=identifier_r, code="secret",
+ curve=curve)
+ dev[1].dpp_pkex_init(identifier=identifier_i, code="secret", curve=curve,
+ extra=init_extra)
+
+ if expect_no_resp:
+ ev = dev[0].wait_event(["DPP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("DPP PKEX frame not received")
+ ev = dev[1].wait_event(["DPP-AUTH-SUCCESS"], timeout=1)
+ if ev is not None:
+ raise Exception("DPP authentication succeeded")
+ ev = dev[0].wait_event(["DPP-AUTH-SUCCESS"], timeout=0.1)
+ if ev is not None:
+ raise Exception("DPP authentication succeeded")
+ return
+
+ wait_auth_success(dev[0], dev[1],
+ configurator=dev[1] if check_config else None,
+ enrollee=dev[0] if check_config else None)
+
+def test_dpp_pkex_5ghz(dev, apdev):
+ """DPP and PKEX on 5 GHz"""
+ try:
+ dev[0].request("SET country US")
+ dev[1].request("SET country US")
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+ if ev is None:
+ ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"],
+ timeout=1)
+ run_dpp_pkex_5ghz(dev, apdev)
+ finally:
+ dev[0].request("SET country 00")
+ dev[1].request("SET country 00")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ time.sleep(0.1)
+
+def run_dpp_pkex_5ghz(dev, apdev):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].dpp_pkex_resp(5745, identifier="test", code="secret")
+ dev[1].dpp_pkex_init(identifier="test", code="secret")
+ wait_auth_success(dev[0], dev[1], timeout=20)
+
+def test_dpp_pkex_test_vector(dev, apdev):
+ """DPP and PKEX (P-256) test vector"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ init_addr = "ac:64:91:f4:52:07"
+ resp_addr = "6e:5e:ce:6e:f3:dd"
+
+ identifier = "joes_key"
+ code = "thisisreallysecret"
+
+ # Initiator bootstrapping private key
+ init_priv = "5941b51acfc702cdc1c347264beb2920db88eb1a0bf03a211868b1632233c269"
+
+ # Responder bootstrapping private key
+ resp_priv = "2ae8956293f49986b6d0b8169a86805d9232babb5f6813fdfe96f19d59536c60"
+
+ # Initiator x/X keypair override
+ init_x_priv = "8365c5ed93d751bef2d92b410dc6adfd95670889183fac1bd66759ad85c3187a"
+
+ # Responder y/Y keypair override
+ resp_y_priv = "d98faa24d7dd3f592665d71a95c862bfd02c4c48acb0c515a41cbc6e929675ea"
+
+ p256_prefix = "30310201010420"
+ p256_postfix = "a00a06082a8648ce3d030107"
+
+ dev[0].set("dpp_pkex_own_mac_override", resp_addr)
+ dev[0].set("dpp_pkex_peer_mac_override", init_addr)
+ dev[1].set("dpp_pkex_own_mac_override", init_addr)
+ dev[1].set("dpp_pkex_peer_mac_override", resp_addr)
+
+ # Responder y/Y keypair override
+ dev[0].set("dpp_pkex_ephemeral_key_override",
+ p256_prefix + resp_y_priv + p256_postfix)
+
+ # Initiator x/X keypair override
+ dev[1].set("dpp_pkex_ephemeral_key_override",
+ p256_prefix + init_x_priv + p256_postfix)
+
+ dev[0].dpp_pkex_resp(2437, identifier=identifier, code=code,
+ key=p256_prefix + resp_priv + p256_postfix)
+ dev[1].dpp_pkex_init(identifier=identifier, code=code,
+ key=p256_prefix + init_priv + p256_postfix)
+ wait_auth_success(dev[0], dev[1])
+
+def test_dpp_pkex_code_mismatch(dev, apdev):
+ """DPP and PKEX with mismatching code"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].dpp_pkex_resp(2437, identifier="test", code="secret")
+ id1 = dev[1].dpp_pkex_init(identifier="test", code="unknown")
+ wait_dpp_fail(dev[0], "possible PKEX code mismatch")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[1].dpp_pkex_init(identifier="test", code="secret", use_id=id1)
+ wait_auth_success(dev[0], dev[1])
+
+def test_dpp_pkex_code_mismatch_limit(dev, apdev):
+ """DPP and PKEX with mismatching code limit"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].dpp_pkex_resp(2437, identifier="test", code="secret")
+
+ id1 = None
+ for i in range(5):
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ id1 = dev[1].dpp_pkex_init(identifier="test", code="unknown",
+ use_id=id1)
+ wait_dpp_fail(dev[0], "possible PKEX code mismatch")
+
+ ev = dev[0].wait_event(["DPP-PKEX-T-LIMIT"], timeout=1)
+ if ev is None:
+ raise Exception("PKEX t limit not reported")
+
+def test_dpp_pkex_curve_mismatch(dev, apdev):
+ """DPP and PKEX with mismatching curve"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].dpp_pkex_resp(2437, identifier="test", code="secret", curve="P-256")
+ dev[1].dpp_pkex_init(identifier="test", code="secret", curve="P-384")
+ wait_dpp_fail(dev[0], "Mismatching PKEX curve: peer=20 own=19")
+ wait_dpp_fail(dev[1], "Peer indicated mismatching PKEX group - proposed 19")
+
+def test_dpp_pkex_curve_mismatch_failure(dev, apdev):
+ """DPP and PKEX with mismatching curve (local failure)"""
+ run_dpp_pkex_curve_mismatch_failure(dev, apdev, "=dpp_pkex_rx_exchange_req")
+
+def test_dpp_pkex_curve_mismatch_failure2(dev, apdev):
+ """DPP and PKEX with mismatching curve (local failure 2)"""
+ run_dpp_pkex_curve_mismatch_failure(dev, apdev,
+ "dpp_pkex_build_exchange_resp")
+
+def run_dpp_pkex_curve_mismatch_failure(dev, apdev, func):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].dpp_pkex_resp(2437, identifier="test", code="secret", curve="P-256")
+
+ with alloc_fail(dev[0], 1, func):
+ dev[1].dpp_pkex_init(identifier="test", code="secret", curve="P-384")
+
+ ev = dev[0].wait_event(["DPP-FAIL"], timeout=5)
+ if ev is None:
+ raise Exception("Failure not reported (dev 0)")
+ if "Mismatching PKEX curve: peer=20 own=19" not in ev:
+ raise Exception("Unexpected result: " + ev)
+ wait_dpp_fail(dev[0], "Mismatching PKEX curve: peer=20 own=19")
+
+def test_dpp_pkex_exchange_resp_processing_failure(dev, apdev):
+ """DPP and PKEX with local failure in processing Exchange Resp"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].dpp_pkex_resp(2437, identifier="test", code="secret")
+
+ with fail_test(dev[1], 1, "dpp_pkex_derive_Qr;dpp_pkex_rx_exchange_resp"):
+ dev[1].dpp_pkex_init(identifier="test", code="secret")
+ wait_fail_trigger(dev[1], "GET_FAIL")
+
+def test_dpp_pkex_commit_reveal_req_processing_failure(dev, apdev):
+ """DPP and PKEX with local failure in processing Commit Reveal Req"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].dpp_pkex_resp(2437, identifier="test", code="secret")
+
+ with alloc_fail(dev[0], 1,
+ "dpp_get_pubkey_point;dpp_pkex_rx_commit_reveal_req"):
+ dev[1].dpp_pkex_init(identifier="test", code="secret")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+def test_dpp_pkex_config2(dev, apdev):
+ """DPP and PKEX with responder as the configurator"""
+ check_dpp_capab(dev[0])
+ conf_id = dev[0].dpp_configurator_add()
+ dev[0].set("dpp_configurator_params",
+ " conf=sta-dpp configurator=%d" % conf_id)
+ run_dpp_pkex2(dev, apdev)
+
+def run_dpp_pkex2(dev, apdev, curve=None, init_extra=""):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].dpp_pkex_resp(2437, identifier="test", code="secret", curve=curve,
+ listen_role="configurator")
+ dev[1].dpp_pkex_init(identifier="test", code="secret", role="enrollee",
+ curve=curve, extra=init_extra)
+ wait_auth_success(dev[0], dev[1], configurator=dev[0], enrollee=dev[1])
+
+def test_dpp_pkex_no_responder(dev, apdev):
+ """DPP and PKEX with no responder (retry behavior)"""
+ check_dpp_capab(dev[0])
+ dev[0].dpp_pkex_init(identifier="test", code="secret")
+
+ for i in range(15):
+ ev = dev[0].wait_event(["DPP-TX ", "DPP-FAIL"], timeout=5)
+ if ev is None:
+ raise Exception("DPP PKEX failure not reported")
+ if "DPP-FAIL" not in ev:
+ continue
+ if "No response from PKEX peer" not in ev:
+ raise Exception("Unexpected failure reason: " + ev)
+ break
+
+def test_dpp_pkex_after_retry(dev, apdev):
+ """DPP and PKEX completing after retry"""
+ check_dpp_capab(dev[0])
+ dev[0].dpp_pkex_init(identifier="test", code="secret")
+ time.sleep(0.1)
+ dev[1].dpp_pkex_resp(2437, identifier="test", code="secret")
+ wait_auth_success(dev[1], dev[0], configurator=dev[0], enrollee=dev[1],
+ allow_enrollee_failure=True)
+
+def test_dpp_pkex_hostapd_responder(dev, apdev):
+ """DPP PKEX with hostapd as responder"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ hapd.dpp_pkex_resp(2437, identifier="test", code="secret")
+ conf_id = dev[0].dpp_configurator_add()
+ dev[0].dpp_pkex_init(identifier="test", code="secret",
+ extra="conf=ap-dpp configurator=%d" % conf_id)
+ wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd,
+ stop_initiator=True)
+
+def test_dpp_pkex_hostapd_initiator(dev, apdev):
+ """DPP PKEX with hostapd as initiator"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ dev[0].set("dpp_configurator_params",
+ " conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].dpp_pkex_resp(2437, identifier="test", code="secret",
+ listen_role="configurator")
+ hapd.dpp_pkex_init(identifier="test", code="secret", role="enrollee")
+ wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd,
+ stop_initiator=True)
+
+def test_dpp_pkex_hostapd_errors(dev, apdev):
+ """DPP PKEX errors with hostapd"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ id0 = hapd.dpp_bootstrap_gen(type="pkex")
+ tests = ["own=%d" % id0,
+ "own=%d identifier=foo" % id0,
+ ""]
+ for t in tests:
+ if "FAIL" not in hapd.request("DPP_PKEX_ADD " + t):
+ raise Exception("Invalid DPP_PKEX_ADD accepted: " + t)
+
+ res = hapd.request("DPP_PKEX_ADD own=%d code=foo" % id0)
+ if "FAIL" in res:
+ raise Exception("Failed to add PKEX responder")
+ if "OK" not in hapd.request("DPP_PKEX_REMOVE " + res):
+ raise Exception("Failed to remove PKEX responder")
+ if "FAIL" not in hapd.request("DPP_PKEX_REMOVE " + res):
+ raise Exception("Unknown PKEX responder removal accepted")
+
+ res = hapd.request("DPP_PKEX_ADD own=%d code=foo" % id0)
+ if "FAIL" in res:
+ raise Exception("Failed to add PKEX responder")
+ if "OK" not in hapd.request("DPP_PKEX_REMOVE *"):
+ raise Exception("Failed to flush PKEX responders")
+ hapd.request("DPP_PKEX_REMOVE *")
+
+def test_dpp_hostapd_configurator(dev, apdev):
+ """DPP with hostapd as configurator/initiator"""
+ run_dpp_hostapd_configurator(dev, apdev)
+
+def test_dpp_hostapd_configurator_enrollee_v1(dev, apdev):
+ """DPP with hostapd as configurator/initiator with v1 enrollee"""
+ dev[0].set("dpp_version_override", "1")
+ run_dpp_hostapd_configurator(dev, apdev)
+
+def run_dpp_hostapd_configurator(dev, apdev):
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "1"})
+ check_dpp_capab(hapd)
+ conf_id = hapd.dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ id1 = hapd.dpp_qr_code(uri0)
+ res = hapd.request("DPP_BOOTSTRAP_INFO %d" % id1)
+ if "FAIL" in res:
+ raise Exception("DPP_BOOTSTRAP_INFO failed")
+ if "type=QRCODE" not in res:
+ raise Exception("DPP_BOOTSTRAP_INFO did not report correct type")
+ if "mac_addr=" + dev[0].own_addr() not in res:
+ raise Exception("DPP_BOOTSTRAP_INFO did not report correct mac_addr")
+ dev[0].dpp_listen(2412)
+ hapd.dpp_auth_init(peer=id1, configurator=conf_id, conf="sta-dpp")
+ wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0],
+ stop_responder=True)
+
+def test_dpp_hostapd_configurator_responder(dev, apdev):
+ """DPP with hostapd as configurator/responder"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "1"})
+ check_dpp_capab(hapd)
+ conf_id = hapd.dpp_configurator_add()
+ hapd.set("dpp_configurator_params",
+ " conf=sta-dpp configurator=%d" % conf_id)
+ id0 = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_auth_init(uri=uri0, role="enrollee")
+ wait_auth_success(hapd, dev[0], configurator=hapd, enrollee=dev[0],
+ stop_initiator=True)
+
+def test_dpp_hostapd_configurator_fragmentation(dev, apdev):
+ """DPP with hostapd as configurator/initiator requiring fragmentation"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "1"})
+ check_dpp_capab(hapd)
+ conf_id = hapd.dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ id1 = hapd.dpp_qr_code(uri0)
+ res = hapd.request("DPP_BOOTSTRAP_INFO %d" % id1)
+ if "FAIL" in res:
+ raise Exception("DPP_BOOTSTRAP_INFO failed")
+ if "type=QRCODE" not in res:
+ raise Exception("DPP_BOOTSTRAP_INFO did not report correct type")
+ if "mac_addr=" + dev[0].own_addr() not in res:
+ raise Exception("DPP_BOOTSTRAP_INFO did not report correct mac_addr")
+ dev[0].dpp_listen(2412)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ hapd.set("dpp_config_obj_override", conf)
+ hapd.dpp_auth_init(peer=id1, configurator=conf_id, conf="sta-dpp")
+ wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0],
+ stop_responder=True)
+
+def test_dpp_hostapd_enrollee_fragmentation(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS fragmentation"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].set("dpp_configurator_params",
+ " conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ wait_auth_success(dev[0], hapd, configurator=dev[0], enrollee=hapd,
+ stop_responder=True)
+
+def test_dpp_hostapd_enrollee_gas_timeout(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS timeout"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0])
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if "result=TIMEOUT" not in ev:
+ raise Exception("GAS timeout not reported")
+
+def test_dpp_hostapd_enrollee_gas_timeout_comeback(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS timeout during comeback"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=4)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if "result=TIMEOUT" not in ev:
+ raise Exception("GAS timeout not reported")
+
+def process_dpp_frames(dev, count=3):
+ for i in range(count):
+ msg = dev.mgmt_rx()
+ cmd = "MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())
+ if "OK" not in dev.request(cmd):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+def test_dpp_hostapd_enrollee_gas_errors(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS query local errors"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+
+ # GAS without comeback
+ tests = [(1, "gas_query_append;gas_query_rx_initial", 3, True),
+ (1, "gas_query_rx_initial", 3, True),
+ (1, "gas_query_tx_initial_req", 2, True),
+ (1, "gas_query_ap_req", 2, False)]
+ for count, func, frame_count, wait_ev in tests:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[0].dpp_listen(2437, role="configurator")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ with alloc_fail(hapd, count, func):
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=frame_count)
+ if wait_ev:
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=INTERNAL_ERROR" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+
+ # GAS with comeback
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+
+ tests = [(1, "gas_query_append;gas_query_rx_comeback", 4),
+ (1, "wpabuf_alloc;gas_query_tx_comeback_req", 3),
+ (1, "hostapd_drv_send_action;gas_query_tx_comeback_req", 3)]
+ for count, func, frame_count in tests:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[0].dpp_listen(2437, role="configurator")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ with alloc_fail(hapd, count, func):
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=frame_count)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=INTERNAL_ERROR" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+
+def test_dpp_hostapd_enrollee_gas_proto(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS query protocol testing"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ bssid = hapd.own_addr()
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=3)
+ msg = dev[0].mgmt_rx()
+ payload = msg['payload']
+ dialog_token, = struct.unpack('B', payload[2:3])
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x80, 0)
+ # GAS: Advertisement Protocol changed between initial and comeback response from 02:00:00:00:00:00
+ adv_proto = "6c087fdd05506f9a1a02"
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=PEER_ERROR" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+ dev[0].request("DPP_STOP_LISTEN")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=3)
+ msg = dev[0].mgmt_rx()
+ payload = msg['payload']
+ dialog_token, = struct.unpack('B', payload[2:3])
+ # Another comeback delay
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x80, 1)
+ adv_proto = "6c087fdd05506f9a1a01"
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ msg = dev[0].mgmt_rx()
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x81, 1)
+ # GAS: Invalid comeback response with non-zero frag_id and comeback_delay from 02:00:00:00:00:00
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=PEER_ERROR" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+ dev[0].request("DPP_STOP_LISTEN")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=3)
+ msg = dev[0].mgmt_rx()
+ payload = msg['payload']
+ dialog_token, = struct.unpack('B', payload[2:3])
+ # Valid comeback response
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x80, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ msg = dev[0].mgmt_rx()
+ # GAS: Drop frame as possible retry of previous fragment
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x80, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Unexpected frag_id in response from 02:00:00:00:00:00
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x82, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=PEER_ERROR" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+ dev[0].request("DPP_STOP_LISTEN")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=3)
+ msg = dev[0].mgmt_rx()
+ payload = msg['payload']
+ dialog_token, = struct.unpack('B', payload[2:3])
+ # GAS: Unexpected initial response from 02:00:00:00:00:00 dialog token 3 when waiting for comeback response
+ hdr = struct.pack('<BBBHBH', 4, 11, dialog_token, 0, 0x80, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Allow non-zero status for outstanding comeback response
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 95, 0x80, 0)
+ # GAS: Ignore 1 octets of extra data after Query Response from 02:00:00:00:00:00
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001" + "ff"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: No pending query found for 02:00:00:00:00:00 dialog token 4
+ hdr = struct.pack('<BBBHBH', 4, 13, (dialog_token + 1) % 256, 0, 0x80, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Truncated Query Response in response from 02:00:00:00:00:00
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x81, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "0010"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: No room for GAS Response Length
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x81, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "03"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Unexpected Advertisement Protocol element ID 0 in response from 02:00:00:00:00:00
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x81, 0)
+ adv_proto_broken = "0000"
+ action = binascii.hexlify(hdr).decode() + adv_proto_broken + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: No room for Advertisement Protocol element in the response from 02:00:00:00:00:00
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x81, 0)
+ adv_proto_broken = "00ff"
+ action = binascii.hexlify(hdr).decode() + adv_proto_broken + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # No room for Comeback Delay
+ hdr = struct.pack('<BBBHBB', 4, 13, dialog_token, 0, 0x81, 0)
+ action = binascii.hexlify(hdr).decode()
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # No room for frag_id
+ hdr = struct.pack('<BBBH', 4, 13, dialog_token, 0)
+ action = binascii.hexlify(hdr).decode()
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Query to 02:00:00:00:00:00 dialog token 3 failed - status code 1
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 1, 0x81, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=FAILURE" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+ dev[0].request("DPP_STOP_LISTEN")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=2)
+ msg = dev[0].mgmt_rx()
+ payload = msg['payload']
+ dialog_token, = struct.unpack('B', payload[2:3])
+ # Unexpected comeback delay
+ hdr = struct.pack('<BBBHBH', 4, 13, dialog_token, 0, 0x80, 0)
+ adv_proto = "6c087fdd05506f9a1a01"
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ # GAS: Query to 02:00:00:00:00:00 dialog token 3 failed - status code 1
+ hdr = struct.pack('<BBBHBH', 4, 11, dialog_token, 1, 0x80, 0)
+ action = binascii.hexlify(hdr).decode() + adv_proto + "0300" + "001001"
+ cmd = "MGMT_TX %s %s freq=2437 wait_time=100 action=%s" % (bssid, bssid, action)
+ dev[0].request(cmd)
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if not ev or "result=FAILURE" not in ev:
+ raise Exception("Unexpect GAS query result: " + str(ev))
+ dev[0].request("DPP_STOP_LISTEN")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+def test_dpp_hostapd_enrollee_gas_tx_status_errors(dev, apdev):
+ """DPP and hostapd as Enrollee with GAS TX status errors"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ conf_id = dev[0].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2437, role="configurator")
+ hapd.dpp_auth_init(uri=uri0, role="enrollee")
+ process_dpp_frames(dev[0], count=3)
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ # GAS: TX status for unexpected destination
+ frame = "d0003a01" + "222222222222"
+ frame += hapd.own_addr().replace(':', '') + "ffffffffffff"
+ frame += "5000" + "040a"
+ hapd.request("MGMT_TX_STATUS_PROCESS stype=13 ok=1 buf=" + frame)
+
+ # GAS: No ACK to GAS request
+ frame = "d0003a01" + dev[0].own_addr().replace(':', '')
+ frame += hapd.own_addr().replace(':', '') + "ffffffffffff"
+ frame += "5000" + "040a"
+ hapd.request("MGMT_TX_STATUS_PROCESS stype=13 ok=0 buf=" + frame)
+
+ ev = hapd.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if "result=TIMEOUT" not in ev:
+ raise Exception("GAS timeout not reported")
+
+ # GAS: Unexpected TX status: dst=02:00:00:00:00:00 ok=1 - no query in progress
+ hapd.request("MGMT_TX_STATUS_PROCESS stype=13 ok=1 buf=" + frame)
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+def test_dpp_hostapd_configurator_override_objects(dev, apdev):
+ """DPP with hostapd as configurator and override objects"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "1"})
+ check_dpp_capab(hapd)
+ conf_id = hapd.dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ id1 = hapd.dpp_qr_code(uri0)
+ res = hapd.request("DPP_BOOTSTRAP_INFO %d" % id1)
+ if "FAIL" in res:
+ raise Exception("DPP_BOOTSTRAP_INFO failed")
+ dev[0].dpp_listen(2412)
+ discovery = '{\n"ssid":"mywifi"\n}'
+ groups = '[\n {"groupId":"home","netRole":"sta"},\n {"groupId":"cottage","netRole":"sta"}\n]'
+ hapd.set("dpp_discovery_override", discovery)
+ hapd.set("dpp_groups_override", groups)
+ hapd.dpp_auth_init(peer=id1, configurator=conf_id, conf="sta-dpp")
+ wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0],
+ stop_responder=True)
+
+def test_dpp_own_config(dev, apdev):
+ """DPP configurator signing own connector"""
+ try:
+ run_dpp_own_config(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_own_config_group_id(dev, apdev):
+ """DPP configurator signing own connector"""
+ try:
+ run_dpp_own_config(dev, apdev, extra=" group_id=test-group")
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_own_config_curve_mismatch(dev, apdev):
+ """DPP configurator signing own connector using mismatching curve"""
+ try:
+ run_dpp_own_config(dev, apdev, own_curve="BP-384", expect_failure=True)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_own_config(dev, apdev, own_curve=None, expect_failure=False,
+ extra=None):
+ check_dpp_capab(dev[0], own_curve and "BP" in own_curve)
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+ conf_id = dev[0].dpp_configurator_add()
+ dev[0].dpp_auth_init(uri=uri, conf="ap-dpp", configurator=conf_id,
+ extra=extra)
+ wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd)
+ update_hapd_config(hapd)
+
+ dev[0].set("dpp_config_processing", "1")
+ cmd = "DPP_CONFIGURATOR_SIGN conf=sta-dpp configurator=%d%s" % (conf_id, extra)
+ if own_curve:
+ cmd += " curve=" + own_curve
+ res = dev[0].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to generate own configuration")
+
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("DPP network profile not generated")
+ id = ev.split(' ')[1]
+ dev[0].select_network(id, freq="2412")
+ if expect_failure:
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+ dev[0].request("DISCONNECT")
+ else:
+ dev[0].wait_connected()
+
+def test_dpp_own_config_ap(dev, apdev):
+ """DPP configurator (AP) signing own connector"""
+ try:
+ run_dpp_own_config_ap(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_own_config_ap_group_id(dev, apdev):
+ """DPP configurator (AP) signing own connector (group_id)"""
+ try:
+ run_dpp_own_config_ap(dev, apdev, extra=" group_id=test-group")
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_own_config_ap_reconf(dev, apdev):
+ """DPP configurator (AP) signing own connector and configurator reconf"""
+ try:
+ run_dpp_own_config_ap(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_own_config_ap(dev, apdev, reconf_configurator=False, extra=None):
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+ conf_id = hapd.dpp_configurator_add()
+ if reconf_configurator:
+ csign = hapd.request("DPP_CONFIGURATOR_GET_KEY %d" % conf_id)
+ if "FAIL" in csign or len(csign) == 0:
+ raise Exception("DPP_CONFIGURATOR_GET_KEY failed")
+
+ cmd = "DPP_CONFIGURATOR_SIGN conf=ap-dpp configurator=%d%s" % (conf_id, extra)
+ res = hapd.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to generate own configuration")
+ update_hapd_config(hapd)
+
+ if reconf_configurator:
+ hapd.dpp_configurator_remove(conf_id)
+ conf_id = hapd.dpp_configurator_add(key=csign)
+
+ id = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ dev[0].set("dpp_config_processing", "2")
+ dev[0].dpp_listen(2412)
+ hapd.dpp_auth_init(uri=uri, conf="sta-dpp", configurator=conf_id,
+ extra=extra)
+ wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0])
+ dev[0].wait_connected()
+
+def test_dpp_intro_mismatch(dev, apdev):
+ """DPP network introduction mismatch cases"""
+ try:
+ wpas = None
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ check_dpp_capab(wpas)
+ run_dpp_intro_mismatch(dev, apdev, wpas)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+ dev[2].set("dpp_config_processing", "0", allow_fail=True)
+ if wpas:
+ wpas.set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_intro_mismatch(dev, apdev, wpas):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ check_dpp_capab(dev[2])
+ logger.info("Start AP in unconfigured state")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+ logger.info("Provision AP with DPP configuration")
+ conf_id = dev[1].dpp_configurator_add()
+ dev[1].set("dpp_groups_override", '[{"groupId":"a","netRole":"ap"}]')
+ dev[1].dpp_auth_init(uri=uri, conf="ap-dpp", configurator=conf_id)
+ update_hapd_config(hapd)
+
+ logger.info("Provision STA0 with DPP Connector that has mismatching groupId")
+ dev[0].set("dpp_config_processing", "2")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_listen(2412)
+ dev[1].set("dpp_groups_override", '[{"groupId":"b","netRole":"sta"}]')
+ dev[1].dpp_auth_init(uri=uri0, conf="sta-dpp", configurator=conf_id)
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0])
+
+ logger.info("Provision STA2 with DPP Connector that has mismatching C-sign-key")
+ dev[2].set("dpp_config_processing", "2")
+ id2 = dev[2].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri2 = dev[2].request("DPP_BOOTSTRAP_GET_URI %d" % id2)
+ dev[2].dpp_listen(2412)
+ conf_id_2 = dev[1].dpp_configurator_add()
+ dev[1].set("dpp_groups_override", '')
+ dev[1].dpp_auth_init(uri=uri2, conf="sta-dpp", configurator=conf_id_2)
+ wait_auth_success(dev[2], dev[1], configurator=dev[1], enrollee=dev[2])
+
+ logger.info("Provision STA5 with DPP Connector that has mismatching netAccessKey EC group")
+ wpas.set("dpp_config_processing", "2")
+ id5 = wpas.dpp_bootstrap_gen(chan="81/1", mac=True, curve="P-521")
+ uri5 = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % id5)
+ wpas.dpp_listen(2412)
+ dev[1].set("dpp_groups_override", '')
+ dev[1].dpp_auth_init(uri=uri5, conf="sta-dpp", configurator=conf_id)
+ wait_auth_success(wpas, dev[1], configurator=dev[1], enrollee=wpas)
+
+ logger.info("Verify network introduction results")
+ ev = dev[0].wait_event(["DPP-INTRO"], timeout=10)
+ if ev is None:
+ raise Exception("DPP network introduction result not seen on STA0")
+ if "status=8" not in ev:
+ raise Exception("Unexpected network introduction result on STA0: " + ev)
+
+ ev = dev[2].wait_event(["DPP-INTRO"], timeout=5)
+ if ev is None:
+ raise Exception("DPP network introduction result not seen on STA2")
+ if "status=8" not in ev:
+ raise Exception("Unexpected network introduction result on STA2: " + ev)
+
+ ev = wpas.wait_event(["DPP-INTRO"], timeout=10)
+ if ev is None:
+ raise Exception("DPP network introduction result not seen on STA5")
+ if "status=7" not in ev:
+ raise Exception("Unexpected network introduction result on STA5: " + ev)
+
+def run_dpp_proto_init(dev, test_dev, test, mutual=False, unicast=True,
+ listen=True, chan="81/1", init_enrollee=False,
+ incompatible_roles=False):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[test_dev].set("dpp_test", str(test))
+ if init_enrollee:
+ conf_id = dev[0].dpp_configurator_add()
+ else:
+ conf_id = dev[1].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan=chan, mac=unicast)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ if mutual:
+ id1b = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri1b = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1b)
+
+ id0b = dev[0].dpp_qr_code(uri1b)
+ qr = "mutual"
+ else:
+ qr = None
+
+ if init_enrollee:
+ if incompatible_roles:
+ role = "enrollee"
+ else:
+ role = "configurator"
+ dev[0].set("dpp_configurator_params",
+ " conf=sta-dpp configurator=%d" % conf_id)
+ elif incompatible_roles:
+ role = "enrollee"
+ else:
+ role = None
+
+ if listen:
+ dev[0].dpp_listen(2412, qr=qr, role=role)
+
+ role = None
+ configurator = None
+ conf = None
+ own = None
+
+ if init_enrollee:
+ role="enrollee"
+ else:
+ configurator=conf_id
+ conf="sta-dpp"
+ if incompatible_roles:
+ role="enrollee"
+ if mutual:
+ own = id1b
+ dev[1].dpp_auth_init(uri=uri0, role=role, configurator=configurator,
+ conf=conf, own=own)
+ return uri0, role, configurator, conf, own
+
+def test_dpp_proto_after_wrapped_data_auth_req(dev, apdev):
+ """DPP protocol testing - attribute after Wrapped Data in Auth Req"""
+ run_dpp_proto_init(dev, 1, 1)
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None:
+ raise Exception("DPP Authentication Request not seen")
+ if "type=0" not in ev or "ignore=invalid-attributes" not in ev:
+ raise Exception("Unexpected RX info: " + ev)
+ ev = dev[1].wait_event(["DPP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected DPP message seen")
+
+def test_dpp_auth_req_stop_after_ack(dev, apdev):
+ """DPP initiator stopping after ACK, but no response"""
+ run_dpp_proto_init(dev, 1, 1, listen=True)
+ ev = dev[1].wait_event(["DPP-AUTH-INIT-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Authentication failure not reported")
+
+def test_dpp_auth_req_retries(dev, apdev):
+ """DPP initiator retries with no ACK"""
+ check_dpp_capab(dev[1])
+ dev[1].set("dpp_init_max_tries", "3")
+ dev[1].set("dpp_init_retry_time", "1000")
+ dev[1].set("dpp_resp_wait_time", "100")
+ run_dpp_proto_init(dev, 1, 1, unicast=False, listen=False)
+
+ for i in range(3):
+ ev = dev[1].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("Auth Req not sent (%d)" % i)
+
+ ev = dev[1].wait_event(["DPP-AUTH-INIT-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Authentication failure not reported")
+
+def test_dpp_auth_req_retries_multi_chan(dev, apdev):
+ """DPP initiator retries with no ACK and multiple channels"""
+ check_dpp_capab(dev[1])
+ dev[1].set("dpp_init_max_tries", "3")
+ dev[1].set("dpp_init_retry_time", "1000")
+ dev[1].set("dpp_resp_wait_time", "100")
+ run_dpp_proto_init(dev, 1, 1, unicast=False, listen=False,
+ chan="81/1,81/6,81/11")
+
+ for i in range(3 * 3):
+ ev = dev[1].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("Auth Req not sent (%d)" % i)
+
+ ev = dev[1].wait_event(["DPP-AUTH-INIT-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Authentication failure not reported")
+
+def test_dpp_proto_after_wrapped_data_auth_resp(dev, apdev):
+ """DPP protocol testing - attribute after Wrapped Data in Auth Resp"""
+ run_dpp_proto_init(dev, 0, 2)
+ ev = dev[1].wait_event(["DPP-RX"], timeout=5)
+ if ev is None:
+ raise Exception("DPP Authentication Response not seen")
+ if "type=1" not in ev or "ignore=invalid-attributes" not in ev:
+ raise Exception("Unexpected RX info: " + ev)
+ ev = dev[0].wait_event(["DPP-RX"], timeout=1)
+ if ev is None or "type=0" not in ev:
+ raise Exception("DPP Authentication Request not seen")
+ ev = dev[0].wait_event(["DPP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected DPP message seen")
+
+def test_dpp_proto_after_wrapped_data_auth_conf(dev, apdev):
+ """DPP protocol testing - attribute after Wrapped Data in Auth Conf"""
+ run_dpp_proto_init(dev, 1, 3)
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "type=0" not in ev:
+ raise Exception("DPP Authentication Request not seen")
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None:
+ raise Exception("DPP Authentication Confirm not seen")
+ if "type=2" not in ev or "ignore=invalid-attributes" not in ev:
+ raise Exception("Unexpected RX info: " + ev)
+
+def test_dpp_proto_after_wrapped_data_conf_req(dev, apdev):
+ """DPP protocol testing - attribute after Wrapped Data in Conf Req"""
+ run_dpp_proto_init(dev, 0, 6)
+ ev = dev[1].wait_event(["DPP-CONF-FAILED"], timeout=10)
+ if ev is None:
+ raise Exception("DPP Configuration failure not seen")
+
+def test_dpp_proto_after_wrapped_data_conf_resp(dev, apdev):
+ """DPP protocol testing - attribute after Wrapped Data in Conf Resp"""
+ run_dpp_proto_init(dev, 1, 7)
+ ev = dev[0].wait_event(["DPP-CONF-FAILED"], timeout=10)
+ if ev is None:
+ raise Exception("DPP Configuration failure not seen")
+
+def test_dpp_proto_zero_i_capab(dev, apdev):
+ """DPP protocol testing - zero I-capability in Auth Req"""
+ run_dpp_proto_init(dev, 1, 8)
+ wait_dpp_fail(dev[0], "Invalid role in I-capabilities 0x00")
+ ev = dev[1].wait_event(["DPP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected DPP message seen")
+
+def test_dpp_proto_zero_r_capab(dev, apdev):
+ """DPP protocol testing - zero R-capability in Auth Resp"""
+ run_dpp_proto_init(dev, 0, 9)
+ wait_dpp_fail(dev[1], "Unexpected role in R-capabilities 0x00")
+ ev = dev[0].wait_event(["DPP-RX"], timeout=1)
+ if ev is None or "type=0" not in ev:
+ raise Exception("DPP Authentication Request not seen")
+ ev = dev[0].wait_event(["DPP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected DPP message seen")
+
+def run_dpp_proto_auth_req_missing(dev, test, reason, mutual=False):
+ run_dpp_proto_init(dev, 1, test, mutual=mutual)
+ wait_dpp_fail(dev[0], reason)
+ ev = dev[1].wait_event(["DPP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected DPP message seen")
+
+def test_dpp_proto_auth_req_no_r_bootstrap_key(dev, apdev):
+ """DPP protocol testing - no R-bootstrap key in Auth Req"""
+ run_dpp_proto_auth_req_missing(dev, 10, "Missing or invalid required Responder Bootstrapping Key Hash attribute")
+
+def test_dpp_proto_auth_req_invalid_r_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid R-bootstrap key in Auth Req"""
+ run_dpp_proto_auth_req_missing(dev, 68, "No matching own bootstrapping key found - ignore message")
+
+def test_dpp_proto_auth_req_no_i_bootstrap_key(dev, apdev):
+ """DPP protocol testing - no I-bootstrap key in Auth Req"""
+ run_dpp_proto_auth_req_missing(dev, 11, "Missing or invalid required Initiator Bootstrapping Key Hash attribute")
+
+def test_dpp_proto_auth_req_invalid_i_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid I-bootstrap key in Auth Req"""
+ run_dpp_proto_init(dev, 1, 69, mutual=True)
+ ev = dev[0].wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
+ if ev is None:
+ raise Exception("DPP scan request not seen")
+ ev = dev[1].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
+ if ev is None:
+ raise Exception("DPP response pending indivation not seen")
+
+def test_dpp_proto_auth_req_no_i_proto_key(dev, apdev):
+ """DPP protocol testing - no I-proto key in Auth Req"""
+ run_dpp_proto_auth_req_missing(dev, 12, "Missing required Initiator Protocol Key attribute")
+
+def test_dpp_proto_auth_req_invalid_i_proto_key(dev, apdev):
+ """DPP protocol testing - invalid I-proto key in Auth Req"""
+ run_dpp_proto_auth_req_missing(dev, 66, "Invalid Initiator Protocol Key")
+
+def test_dpp_proto_auth_req_no_i_nonce(dev, apdev):
+ """DPP protocol testing - no I-nonce in Auth Req"""
+ run_dpp_proto_auth_req_missing(dev, 13, "Missing or invalid I-nonce")
+
+def test_dpp_proto_auth_req_invalid_i_nonce(dev, apdev):
+ """DPP protocol testing - invalid I-nonce in Auth Req"""
+ run_dpp_proto_auth_req_missing(dev, 81, "Missing or invalid I-nonce")
+
+def test_dpp_proto_auth_req_no_i_capab(dev, apdev):
+ """DPP protocol testing - no I-capab in Auth Req"""
+ run_dpp_proto_auth_req_missing(dev, 14, "Missing or invalid I-capab")
+
+def test_dpp_proto_auth_req_no_wrapped_data(dev, apdev):
+ """DPP protocol testing - no Wrapped Data in Auth Req"""
+ run_dpp_proto_auth_req_missing(dev, 15, "Missing or invalid required Wrapped Data attribute")
+
+def run_dpp_proto_auth_resp_missing(dev, test, reason,
+ incompatible_roles=False):
+ run_dpp_proto_init(dev, 0, test, mutual=True,
+ incompatible_roles=incompatible_roles)
+ if reason is None:
+ if incompatible_roles:
+ ev = dev[0].wait_event(["DPP-NOT-COMPATIBLE"], timeout=5)
+ if ev is None:
+ raise Exception("DPP-NOT-COMPATIBLE not reported")
+ time.sleep(0.1)
+ return
+ wait_dpp_fail(dev[1], reason)
+ ev = dev[0].wait_event(["DPP-RX"], timeout=1)
+ if ev is None or "type=0" not in ev:
+ raise Exception("DPP Authentication Request not seen")
+ ev = dev[0].wait_event(["DPP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected DPP message seen")
+
+def test_dpp_proto_auth_resp_no_status(dev, apdev):
+ """DPP protocol testing - no Status in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 16, "Missing or invalid required DPP Status attribute")
+
+def test_dpp_proto_auth_resp_status_no_status(dev, apdev):
+ """DPP protocol testing - no Status in Auth Resp(status)"""
+ run_dpp_proto_auth_resp_missing(dev, 16,
+ "Missing or invalid required DPP Status attribute",
+ incompatible_roles=True)
+
+def test_dpp_proto_auth_resp_invalid_status(dev, apdev):
+ """DPP protocol testing - invalid Status in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 74, "Responder reported failure")
+
+def test_dpp_proto_auth_resp_no_r_bootstrap_key(dev, apdev):
+ """DPP protocol testing - no R-bootstrap key in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 17, "Missing or invalid required Responder Bootstrapping Key Hash attribute")
+
+def test_dpp_proto_auth_resp_status_no_r_bootstrap_key(dev, apdev):
+ """DPP protocol testing - no R-bootstrap key in Auth Resp(status)"""
+ run_dpp_proto_auth_resp_missing(dev, 17,
+ "Missing or invalid required Responder Bootstrapping Key Hash attribute",
+ incompatible_roles=True)
+
+def test_dpp_proto_auth_resp_invalid_r_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid R-bootstrap key in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 70, "Unexpected Responder Bootstrapping Key Hash value")
+
+def test_dpp_proto_auth_resp_status_invalid_r_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid R-bootstrap key in Auth Resp(status)"""
+ run_dpp_proto_auth_resp_missing(dev, 70,
+ "Unexpected Responder Bootstrapping Key Hash value",
+ incompatible_roles=True)
+
+def test_dpp_proto_auth_resp_no_i_bootstrap_key(dev, apdev):
+ """DPP protocol testing - no I-bootstrap key in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 18, None)
+
+def test_dpp_proto_auth_resp_status_no_i_bootstrap_key(dev, apdev):
+ """DPP protocol testing - no I-bootstrap key in Auth Resp(status)"""
+ run_dpp_proto_auth_resp_missing(dev, 18, None, incompatible_roles=True)
+
+def test_dpp_proto_auth_resp_invalid_i_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid I-bootstrap key in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 71, "Initiator Bootstrapping Key Hash attribute did not match")
+
+def test_dpp_proto_auth_resp_status_invalid_i_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid I-bootstrap key in Auth Resp(status)"""
+ run_dpp_proto_auth_resp_missing(dev, 71,
+ "Initiator Bootstrapping Key Hash attribute did not match",
+ incompatible_roles=True)
+
+def test_dpp_proto_auth_resp_no_r_proto_key(dev, apdev):
+ """DPP protocol testing - no R-Proto Key in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 19, "Missing required Responder Protocol Key attribute")
+
+def test_dpp_proto_auth_resp_invalid_r_proto_key(dev, apdev):
+ """DPP protocol testing - invalid R-Proto Key in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 67, "Invalid Responder Protocol Key")
+
+def test_dpp_proto_auth_resp_no_r_nonce(dev, apdev):
+ """DPP protocol testing - no R-nonce in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 20, "Missing or invalid R-nonce")
+
+def test_dpp_proto_auth_resp_no_i_nonce(dev, apdev):
+ """DPP protocol testing - no I-nonce in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 21, "Missing or invalid I-nonce")
+
+def test_dpp_proto_auth_resp_status_no_i_nonce(dev, apdev):
+ """DPP protocol testing - no I-nonce in Auth Resp(status)"""
+ run_dpp_proto_auth_resp_missing(dev, 21, "Missing or invalid I-nonce",
+ incompatible_roles=True)
+
+def test_dpp_proto_auth_resp_no_r_capab(dev, apdev):
+ """DPP protocol testing - no R-capab in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 22, "Missing or invalid R-capabilities")
+
+def test_dpp_proto_auth_resp_no_r_auth(dev, apdev):
+ """DPP protocol testing - no R-auth in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 23, "Missing or invalid Secondary Wrapped Data")
+
+def test_dpp_proto_auth_resp_no_wrapped_data(dev, apdev):
+ """DPP protocol testing - no Wrapped Data in Auth Resp"""
+ run_dpp_proto_auth_resp_missing(dev, 24, "Missing or invalid required Wrapped Data attribute")
+
+def test_dpp_proto_auth_resp_i_nonce_mismatch(dev, apdev):
+ """DPP protocol testing - I-nonce mismatch in Auth Resp"""
+ run_dpp_proto_init(dev, 0, 30, mutual=True)
+ wait_dpp_fail(dev[1], "I-nonce mismatch")
+ ev = dev[0].wait_event(["DPP-RX"], timeout=1)
+ if ev is None or "type=0" not in ev:
+ raise Exception("DPP Authentication Request not seen")
+ ev = dev[0].wait_event(["DPP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected DPP message seen")
+
+def test_dpp_proto_auth_resp_incompatible_r_capab(dev, apdev):
+ """DPP protocol testing - Incompatible R-capab in Auth Resp"""
+ run_dpp_proto_init(dev, 0, 31, mutual=True)
+ wait_dpp_fail(dev[1], "Unexpected role in R-capabilities 0x02")
+ wait_dpp_fail(dev[0], "Peer reported incompatible R-capab role")
+
+def test_dpp_proto_auth_resp_r_auth_mismatch(dev, apdev):
+ """DPP protocol testing - R-auth mismatch in Auth Resp"""
+ run_dpp_proto_init(dev, 0, 32, mutual=True)
+ wait_dpp_fail(dev[1], "Mismatching Responder Authenticating Tag")
+ wait_dpp_fail(dev[0], "Peer reported authentication failure")
+
+def test_dpp_proto_auth_resp_r_auth_mismatch_failure(dev, apdev):
+ """DPP protocol testing - Auth Conf RX processing failure"""
+ with alloc_fail(dev[0], 1, "dpp_auth_conf_rx_failure"):
+ run_dpp_proto_init(dev, 0, 32, mutual=True)
+ wait_dpp_fail(dev[0], "Authentication failed")
+
+def test_dpp_proto_auth_resp_r_auth_mismatch_failure2(dev, apdev):
+ """DPP protocol testing - Auth Conf RX processing failure 2"""
+ with fail_test(dev[0], 1, "dpp_auth_conf_rx_failure"):
+ run_dpp_proto_init(dev, 0, 32, mutual=True)
+ wait_dpp_fail(dev[0], "AES-SIV decryption failed")
+
+def run_dpp_proto_auth_conf_missing(dev, test, reason):
+ run_dpp_proto_init(dev, 1, test, mutual=True)
+ if reason is None:
+ time.sleep(0.1)
+ return
+ wait_dpp_fail(dev[0], reason)
+
+def test_dpp_proto_auth_conf_no_status(dev, apdev):
+ """DPP protocol testing - no Status in Auth Conf"""
+ run_dpp_proto_auth_conf_missing(dev, 25, "Missing or invalid required DPP Status attribute")
+
+def test_dpp_proto_auth_conf_invalid_status(dev, apdev):
+ """DPP protocol testing - invalid Status in Auth Conf"""
+ run_dpp_proto_auth_conf_missing(dev, 75, "Authentication failed")
+
+def test_dpp_proto_auth_conf_no_r_bootstrap_key(dev, apdev):
+ """DPP protocol testing - no R-bootstrap key in Auth Conf"""
+ run_dpp_proto_auth_conf_missing(dev, 26, "Missing or invalid required Responder Bootstrapping Key Hash attribute")
+
+def test_dpp_proto_auth_conf_invalid_r_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid R-bootstrap key in Auth Conf"""
+ run_dpp_proto_auth_conf_missing(dev, 72, "Responder Bootstrapping Key Hash mismatch")
+
+def test_dpp_proto_auth_conf_no_i_bootstrap_key(dev, apdev):
+ """DPP protocol testing - no I-bootstrap key in Auth Conf"""
+ run_dpp_proto_auth_conf_missing(dev, 27, "Missing Initiator Bootstrapping Key Hash attribute")
+
+def test_dpp_proto_auth_conf_invalid_i_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid I-bootstrap key in Auth Conf"""
+ run_dpp_proto_auth_conf_missing(dev, 73, "Initiator Bootstrapping Key Hash mismatch")
+
+def test_dpp_proto_auth_conf_no_i_auth(dev, apdev):
+ """DPP protocol testing - no I-Auth in Auth Conf"""
+ run_dpp_proto_auth_conf_missing(dev, 28, "Missing or invalid Initiator Authenticating Tag")
+
+def test_dpp_proto_auth_conf_no_wrapped_data(dev, apdev):
+ """DPP protocol testing - no Wrapped Data in Auth Conf"""
+ run_dpp_proto_auth_conf_missing(dev, 29, "Missing or invalid required Wrapped Data attribute")
+
+def test_dpp_proto_auth_conf_i_auth_mismatch(dev, apdev):
+ """DPP protocol testing - I-auth mismatch in Auth Conf"""
+ run_dpp_proto_init(dev, 1, 33, mutual=True)
+ wait_dpp_fail(dev[0], "Mismatching Initiator Authenticating Tag")
+
+def test_dpp_proto_auth_conf_replaced_by_resp(dev, apdev):
+ """DPP protocol testing - Auth Conf replaced by Resp"""
+ run_dpp_proto_init(dev, 1, 65, mutual=True)
+ wait_dpp_fail(dev[0], "Unexpected Authentication Response")
+
+def run_dpp_proto_conf_req_missing(dev, test, reason):
+ run_dpp_proto_init(dev, 0, test)
+ wait_dpp_fail(dev[1], reason)
+
+def test_dpp_proto_conf_req_no_e_nonce(dev, apdev):
+ """DPP protocol testing - no E-nonce in Conf Req"""
+ run_dpp_proto_conf_req_missing(dev, 51,
+ "Missing or invalid Enrollee Nonce attribute")
+
+def test_dpp_proto_conf_req_invalid_e_nonce(dev, apdev):
+ """DPP protocol testing - invalid E-nonce in Conf Req"""
+ run_dpp_proto_conf_req_missing(dev, 83,
+ "Missing or invalid Enrollee Nonce attribute")
+
+def test_dpp_proto_conf_req_no_config_attr_obj(dev, apdev):
+ """DPP protocol testing - no Config Attr Obj in Conf Req"""
+ run_dpp_proto_conf_req_missing(dev, 52,
+ "Missing or invalid Config Attributes attribute")
+
+def test_dpp_proto_conf_req_invalid_config_attr_obj(dev, apdev):
+ """DPP protocol testing - invalid Config Attr Obj in Conf Req"""
+ run_dpp_proto_conf_req_missing(dev, 76,
+ "Unsupported wi-fi_tech")
+
+def test_dpp_proto_conf_req_no_wrapped_data(dev, apdev):
+ """DPP protocol testing - no Wrapped Data in Conf Req"""
+ run_dpp_proto_conf_req_missing(dev, 53,
+ "Missing or invalid required Wrapped Data attribute")
+
+def run_dpp_proto_conf_resp_missing(dev, test, reason):
+ run_dpp_proto_init(dev, 1, test)
+ wait_dpp_fail(dev[0], reason)
+
+def test_dpp_proto_conf_resp_no_e_nonce(dev, apdev):
+ """DPP protocol testing - no E-nonce in Conf Resp"""
+ run_dpp_proto_conf_resp_missing(dev, 54,
+ "Missing or invalid Enrollee Nonce attribute")
+
+def test_dpp_proto_conf_resp_no_config_obj(dev, apdev):
+ """DPP protocol testing - no Config Object in Conf Resp"""
+ run_dpp_proto_conf_resp_missing(dev, 55,
+ "Missing required Configuration Object attribute")
+
+def test_dpp_proto_conf_resp_no_status(dev, apdev):
+ """DPP protocol testing - no Status in Conf Resp"""
+ run_dpp_proto_conf_resp_missing(dev, 56,
+ "Missing or invalid required DPP Status attribute")
+
+def test_dpp_proto_conf_resp_no_wrapped_data(dev, apdev):
+ """DPP protocol testing - no Wrapped Data in Conf Resp"""
+ run_dpp_proto_conf_resp_missing(dev, 57,
+ "Missing or invalid required Wrapped Data attribute")
+
+def test_dpp_proto_conf_resp_invalid_status(dev, apdev):
+ """DPP protocol testing - invalid Status in Conf Resp"""
+ run_dpp_proto_conf_resp_missing(dev, 58,
+ "Configurator rejected configuration")
+
+def test_dpp_proto_conf_resp_e_nonce_mismatch(dev, apdev):
+ """DPP protocol testing - E-nonce mismatch in Conf Resp"""
+ run_dpp_proto_conf_resp_missing(dev, 59,
+ "Enrollee Nonce mismatch")
+
+def test_dpp_proto_stop_at_auth_req(dev, apdev):
+ """DPP protocol testing - stop when receiving Auth Req"""
+ run_dpp_proto_init(dev, 0, 87)
+ ev = dev[1].wait_event(["DPP-AUTH-INIT-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Authentication init failure not reported")
+
+def test_dpp_proto_stop_at_auth_resp(dev, apdev):
+ """DPP protocol testing - stop when receiving Auth Resp"""
+ uri0, role, configurator, conf, own = run_dpp_proto_init(dev, 1, 88)
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("Auth Req TX not seen")
+
+ ev = dev[0].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("Auth Resp TX not seen")
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected Auth Conf TX")
+
+ ev = dev[0].wait_event(["DPP-FAIL"], timeout=2)
+ if ev is None or "No Auth Confirm received" not in ev:
+ raise Exception("DPP-FAIL for missing Auth Confirm not reported")
+ time.sleep(0.1)
+
+ # Try again without special testing behavior to confirm Responder is able
+ # to accept a new provisioning attempt.
+ dev[1].set("dpp_test", "0")
+ dev[1].dpp_auth_init(uri=uri0, role=role, configurator=configurator,
+ conf=conf, own=own)
+ wait_auth_success(dev[0], dev[1])
+
+def test_dpp_proto_stop_at_auth_conf(dev, apdev):
+ """DPP protocol testing - stop when receiving Auth Conf"""
+ run_dpp_proto_init(dev, 0, 89, init_enrollee=True)
+ ev = dev[1].wait_event(["GAS-QUERY-START"], timeout=10)
+ if ev is None:
+ raise Exception("Enrollee did not start GAS")
+ ev = dev[1].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("Enrollee did not time out GAS")
+ if "result=TIMEOUT" not in ev:
+ raise Exception("Unexpected GAS result: " + ev)
+
+def test_dpp_proto_stop_at_auth_conf_tx(dev, apdev):
+ """DPP protocol testing - stop when transmitting Auth Conf (Registrar)"""
+ run_dpp_proto_init(dev, 1, 89, init_enrollee=True)
+ wait_auth_success(dev[0], dev[1], timeout=10)
+ ev = dev[1].wait_event(["GAS-QUERY-START"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected GAS query")
+
+ # There is currently no timeout on GAS server side, so no event to wait for
+ # in this case.
+
+def test_dpp_proto_stop_at_auth_conf_tx2(dev, apdev):
+ """DPP protocol testing - stop when transmitting Auth Conf (Enrollee)"""
+ run_dpp_proto_init(dev, 1, 89)
+ wait_auth_success(dev[0], dev[1], timeout=10)
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+ if ev is None or "result=TIMEOUT" not in ev:
+ raise Exception("GAS query did not time out")
+
+def test_dpp_proto_stop_at_conf_req(dev, apdev):
+ """DPP protocol testing - stop when receiving Auth Req"""
+ run_dpp_proto_init(dev, 1, 90)
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=10)
+ if ev is None:
+ raise Exception("Enrollee did not start GAS")
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("Enrollee did not time out GAS")
+ if "result=TIMEOUT" not in ev:
+ raise Exception("Unexpected GAS result: " + ev)
+
+def run_dpp_proto_init_pkex(dev, test_dev, test):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[test_dev].set("dpp_test", str(test))
+ dev[0].dpp_pkex_resp(2437, identifier="test", code="secret")
+ dev[1].dpp_pkex_init(identifier="test", code="secret")
+
+def test_dpp_proto_after_wrapped_data_pkex_cr_req(dev, apdev):
+ """DPP protocol testing - attribute after Wrapped Data in PKEX CR Req"""
+ run_dpp_proto_init_pkex(dev, 1, 4)
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "type=7" not in ev:
+ raise Exception("PKEX Exchange Request not seen")
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "type=9" not in ev:
+ raise Exception("PKEX Commit-Reveal Request not seen")
+ if "ignore=invalid-attributes" not in ev:
+ raise Exception("Unexpected RX info: " + ev)
+
+def test_dpp_proto_after_wrapped_data_pkex_cr_resp(dev, apdev):
+ """DPP protocol testing - attribute after Wrapped Data in PKEX CR Resp"""
+ run_dpp_proto_init_pkex(dev, 0, 5)
+ ev = dev[1].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "type=8" not in ev:
+ raise Exception("PKEX Exchange Response not seen")
+ ev = dev[1].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "type=10" not in ev:
+ raise Exception("PKEX Commit-Reveal Response not seen")
+ if "ignore=invalid-attributes" not in ev:
+ raise Exception("Unexpected RX info: " + ev)
+
+def run_dpp_proto_pkex_req_missing(dev, test, reason):
+ run_dpp_proto_init_pkex(dev, 1, test)
+ wait_dpp_fail(dev[0], reason)
+
+def run_dpp_proto_pkex_resp_missing(dev, test, reason):
+ run_dpp_proto_init_pkex(dev, 0, test)
+ wait_dpp_fail(dev[1], reason)
+
+def test_dpp_proto_pkex_exchange_req_no_finite_cyclic_group(dev, apdev):
+ """DPP protocol testing - no Finite Cyclic Group in PKEX Exchange Request"""
+ run_dpp_proto_pkex_req_missing(dev, 34,
+ "Missing or invalid Finite Cyclic Group attribute")
+
+def test_dpp_proto_pkex_exchange_req_no_encrypted_key(dev, apdev):
+ """DPP protocol testing - no Encrypted Key in PKEX Exchange Request"""
+ run_dpp_proto_pkex_req_missing(dev, 35,
+ "Missing Encrypted Key attribute")
+
+def test_dpp_proto_pkex_exchange_resp_no_status(dev, apdev):
+ """DPP protocol testing - no Status in PKEX Exchange Response"""
+ run_dpp_proto_pkex_resp_missing(dev, 36, "No DPP Status attribute")
+
+def test_dpp_proto_pkex_exchange_resp_no_encrypted_key(dev, apdev):
+ """DPP protocol testing - no Encrypted Key in PKEX Exchange Response"""
+ run_dpp_proto_pkex_resp_missing(dev, 37, "Missing Encrypted Key attribute")
+
+def test_dpp_proto_pkex_cr_req_no_bootstrap_key(dev, apdev):
+ """DPP protocol testing - no Bootstrap Key in PKEX Commit-Reveal Request"""
+ run_dpp_proto_pkex_req_missing(dev, 38,
+ "No valid peer bootstrapping key found")
+
+def test_dpp_proto_pkex_cr_req_no_i_auth_tag(dev, apdev):
+ """DPP protocol testing - no I-Auth Tag in PKEX Commit-Reveal Request"""
+ run_dpp_proto_pkex_req_missing(dev, 39, "No valid u (I-Auth tag) found")
+
+def test_dpp_proto_pkex_cr_req_no_wrapped_data(dev, apdev):
+ """DPP protocol testing - no Wrapped Data in PKEX Commit-Reveal Request"""
+ run_dpp_proto_pkex_req_missing(dev, 40, "Missing or invalid required Wrapped Data attribute")
+
+def test_dpp_proto_pkex_cr_resp_no_bootstrap_key(dev, apdev):
+ """DPP protocol testing - no Bootstrap Key in PKEX Commit-Reveal Response"""
+ run_dpp_proto_pkex_resp_missing(dev, 41,
+ "No valid peer bootstrapping key found")
+
+def test_dpp_proto_pkex_cr_resp_no_r_auth_tag(dev, apdev):
+ """DPP protocol testing - no R-Auth Tag in PKEX Commit-Reveal Response"""
+ run_dpp_proto_pkex_resp_missing(dev, 42, "No valid v (R-Auth tag) found")
+
+def test_dpp_proto_pkex_cr_resp_no_wrapped_data(dev, apdev):
+ """DPP protocol testing - no Wrapped Data in PKEX Commit-Reveal Response"""
+ run_dpp_proto_pkex_resp_missing(dev, 43, "Missing or invalid required Wrapped Data attribute")
+
+def test_dpp_proto_pkex_exchange_req_invalid_encrypted_key(dev, apdev):
+ """DPP protocol testing - invalid Encrypted Key in PKEX Exchange Request"""
+ run_dpp_proto_pkex_req_missing(dev, 44,
+ "Invalid Encrypted Key value")
+
+def test_dpp_proto_pkex_exchange_resp_invalid_encrypted_key(dev, apdev):
+ """DPP protocol testing - invalid Encrypted Key in PKEX Exchange Response"""
+ run_dpp_proto_pkex_resp_missing(dev, 45,
+ "Invalid Encrypted Key value")
+
+def test_dpp_proto_pkex_exchange_resp_invalid_status(dev, apdev):
+ """DPP protocol testing - invalid Status in PKEX Exchange Response"""
+ run_dpp_proto_pkex_resp_missing(dev, 46,
+ "PKEX failed (peer indicated failure)")
+
+def test_dpp_proto_pkex_cr_req_invalid_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Request"""
+ run_dpp_proto_pkex_req_missing(dev, 47,
+ "Peer bootstrapping key is invalid")
+
+def test_dpp_proto_pkex_cr_resp_invalid_bootstrap_key(dev, apdev):
+ """DPP protocol testing - invalid Bootstrap Key in PKEX Commit-Reveal Response"""
+ run_dpp_proto_pkex_resp_missing(dev, 48,
+ "Peer bootstrapping key is invalid")
+
+def test_dpp_proto_pkex_cr_req_i_auth_tag_mismatch(dev, apdev):
+ """DPP protocol testing - I-auth tag mismatch in PKEX Commit-Reveal Request"""
+ run_dpp_proto_pkex_req_missing(dev, 49, "No valid u (I-Auth tag) found")
+
+def test_dpp_proto_pkex_cr_resp_r_auth_tag_mismatch(dev, apdev):
+ """DPP protocol testing - R-auth tag mismatch in PKEX Commit-Reveal Response"""
+ run_dpp_proto_pkex_resp_missing(dev, 50, "No valid v (R-Auth tag) found")
+
+def test_dpp_proto_stop_at_pkex_exchange_resp(dev, apdev):
+ """DPP protocol testing - stop when receiving PKEX Exchange Response"""
+ run_dpp_proto_init_pkex(dev, 1, 84)
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("PKEX Exchange Req TX not seen")
+
+ ev = dev[0].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("PKEX Exchange Resp not seen")
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected PKEX CR Req TX")
+
+def test_dpp_proto_stop_at_pkex_cr_req(dev, apdev):
+ """DPP protocol testing - stop when receiving PKEX CR Request"""
+ run_dpp_proto_init_pkex(dev, 0, 85)
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("PKEX Exchange Req TX not seen")
+
+ ev = dev[0].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("PKEX Exchange Resp not seen")
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("PKEX CR Req TX not seen")
+
+ ev = dev[0].wait_event(["DPP-TX "], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected PKEX CR Resp TX")
+
+def test_dpp_proto_stop_at_pkex_cr_resp(dev, apdev):
+ """DPP protocol testing - stop when receiving PKEX CR Response"""
+ run_dpp_proto_init_pkex(dev, 1, 86)
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("PKEX Exchange Req TX not seen")
+
+ ev = dev[0].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("PKEX Exchange Resp not seen")
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("PKEX CR Req TX not seen")
+
+ ev = dev[0].wait_event(["DPP-TX "], timeout=5)
+ if ev is None:
+ raise Exception("PKEX CR Resp TX not seen")
+
+ ev = dev[1].wait_event(["DPP-TX "], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected Auth Req TX")
+
+def test_dpp_proto_network_introduction(dev, apdev):
+ """DPP protocol testing - network introduction"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ params = {"ssid": "dpp",
+ "wpa": "2",
+ "wpa_key_mgmt": "DPP",
+ "ieee80211w": "2",
+ "rsn_pairwise": "CCMP",
+ "dpp_connector": params1_ap_connector,
+ "dpp_csign": params1_csign,
+ "dpp_netaccesskey": params1_ap_netaccesskey}
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except:
+ raise HwsimSkip("DPP not supported")
+
+ for test in [60, 61, 80, 82]:
+ dev[0].set("dpp_test", str(test))
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412", ieee80211w="2",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey,
+ wait_connect=False)
+
+ ev = dev[0].wait_event(["DPP-TX "], timeout=10)
+ if ev is None or "type=5" not in ev:
+ raise Exception("Peer Discovery Request TX not reported")
+ ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=2)
+ if ev is None or "result=SUCCESS" not in ev:
+ raise Exception("Peer Discovery Request TX status not reported")
+
+ ev = hapd.wait_event(["DPP-RX"], timeout=10)
+ if ev is None or "type=5" not in ev:
+ raise Exception("Peer Discovery Request RX not reported")
+
+ if test == 80:
+ ev = dev[0].wait_event(["DPP-INTRO"], timeout=10)
+ if ev is None:
+ raise Exception("DPP-INTRO not reported for test 80")
+ if "status=7" not in ev:
+ raise Exception("Unexpected result in test 80: " + ev)
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ dev[0].set("dpp_test", "0")
+
+ for test in [62, 63, 64, 77, 78, 79]:
+ hapd.set("dpp_test", str(test))
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412", ieee80211w="2",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey,
+ wait_connect=False)
+
+ ev = dev[0].wait_event(["DPP-INTRO"], timeout=10)
+ if ev is None:
+ raise Exception("Peer introduction result not reported (test %d)" % test)
+ if test == 77:
+ if "fail=transaction_id_mismatch" not in ev:
+ raise Exception("Connector validation failure not reported")
+ elif test == 78:
+ if "status=254" not in ev:
+ raise Exception("Invalid status value not reported")
+ elif test == 79:
+ if "fail=peer_connector_validation_failed" not in ev:
+ raise Exception("Connector validation failure not reported")
+ elif "status=" in ev:
+ raise Exception("Unexpected peer introduction result (test %d): " % test + ev)
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ hapd.set("dpp_test", "0")
+
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412", ieee80211w="2",
+ dpp_csign=params1_csign, dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey)
+
+def test_dpp_hostapd_auth_conf_timeout(dev, apdev):
+ """DPP Authentication Confirm timeout in hostapd"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri_h = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+ hapd.dpp_listen(2412)
+ dev[0].set("dpp_test", "88")
+ dev[0].dpp_auth_init(uri=uri_h)
+ ev = hapd.wait_event(["DPP-FAIL"], timeout=10)
+ if ev is None:
+ raise Exception("DPP-FAIL not reported")
+ if "No Auth Confirm received" not in ev:
+ raise Exception("Unexpected failure reason: " + ev)
+
+def test_dpp_hostapd_auth_resp_retries(dev, apdev):
+ """DPP Authentication Response retries in hostapd"""
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+
+ hapd.set("dpp_resp_max_tries", "3")
+ hapd.set("dpp_resp_retry_time", "100")
+
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri_h = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+ id0b = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0b = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0b)
+ hapd.dpp_listen(2412, qr="mutual")
+ dev[0].dpp_auth_init(uri=uri_h, own=id0b)
+
+ ev = dev[0].wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
+ if ev is None:
+ raise Exception("Pending response not reported")
+ ev = hapd.wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
+ if ev is None:
+ raise Exception("QR Code scan for mutual authentication not requested")
+
+ # Stop Initiator from listening to frames to force retransmission of the
+ # DPP Authentication Response frame with Status=0
+ dev[0].request("DPP_STOP_LISTEN")
+
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+ id0b = hapd.dpp_qr_code(uri0b)
+
+ ev = hapd.wait_event(["DPP-TX "], timeout=5)
+ if ev is None or "type=1" not in ev:
+ raise Exception("DPP Authentication Response not sent")
+ ev = hapd.wait_event(["DPP-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("TX status for DPP Authentication Response not reported")
+ if "result=FAILED" not in ev:
+ raise Exception("Unexpected TX status for Authentication Response: " + ev)
+
+ ev = hapd.wait_event(["DPP-TX "], timeout=15)
+ if ev is None or "type=1" not in ev:
+ raise Exception("DPP Authentication Response retransmission not sent")
+
+def test_dpp_qr_code_no_chan_list_unicast(dev, apdev):
+ """DPP QR Code and no channel list (unicast)"""
+ run_dpp_qr_code_chan_list(dev, apdev, True, 2417, None)
+
+def test_dpp_qr_code_chan_list_unicast(dev, apdev):
+ """DPP QR Code and 2.4 GHz channels (unicast)"""
+ run_dpp_qr_code_chan_list(dev, apdev, True, 2417,
+ "81/1,81/2,81/3,81/4,81/5,81/6,81/7,81/8,81/9,81/10,81/11,81/12,81/13")
+
+def test_dpp_qr_code_chan_list_unicast2(dev, apdev):
+ """DPP QR Code and 2.4 GHz channels (unicast 2)"""
+ run_dpp_qr_code_chan_list(dev, apdev, True, 2417,
+ "81/1,2,3,4,5,6,7,8,9,10,11,12,13")
+
+def test_dpp_qr_code_chan_list_no_peer_unicast(dev, apdev):
+ """DPP QR Code and channel list and no peer (unicast)"""
+ run_dpp_qr_code_chan_list(dev, apdev, True, 2417, "81/1,81/6,81/11",
+ no_wait=True)
+ ev = dev[1].wait_event(["DPP-AUTH-INIT-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Initiation failure not reported")
+
+def test_dpp_qr_code_no_chan_list_broadcast(dev, apdev):
+ """DPP QR Code and no channel list (broadcast)"""
+ run_dpp_qr_code_chan_list(dev, apdev, False, 2412, None)
+
+def test_dpp_qr_code_chan_list_broadcast(dev, apdev):
+ """DPP QR Code and some 2.4 GHz channels (broadcast)"""
+ run_dpp_qr_code_chan_list(dev, apdev, False, 2412, "81/1,81/6,81/11",
+ timeout=10)
+
+def run_dpp_qr_code_chan_list(dev, apdev, unicast, listen_freq, chanlist,
+ no_wait=False, timeout=5):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[1].set("dpp_init_max_tries", "3")
+ dev[1].set("dpp_init_retry_time", "100")
+ dev[1].set("dpp_resp_wait_time", "1000")
+
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan=chanlist, mac=unicast)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(listen_freq)
+ dev[1].dpp_auth_init(uri=uri0)
+ if no_wait:
+ return
+ wait_auth_success(dev[0], dev[1], timeout=timeout, configurator=dev[1],
+ enrollee=dev[0], allow_enrollee_failure=True,
+ stop_responder=True)
+
+def test_dpp_qr_code_chan_list_no_match(dev, apdev):
+ """DPP QR Code and no matching supported channel"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ id0 = dev[0].dpp_bootstrap_gen(chan="123/123")
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[1].dpp_auth_init(uri=uri0, expect_fail=True)
+
+def test_dpp_pkex_alloc_fail(dev, apdev):
+ """DPP/PKEX and memory allocation failures"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ tests = [(1, "=dpp_keygen_configurator"),
+ (1, "base64_gen_encode;dpp_keygen_configurator")]
+ for count, func in tests:
+ with alloc_fail(dev[1], count, func):
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev[1].request(cmd)
+ if "FAIL" not in res:
+ raise Exception("Unexpected DPP_CONFIGURATOR_ADD success")
+
+ conf_id = dev[1].dpp_configurator_add()
+
+ id0 = None
+ id1 = None
+
+ # Local error cases on the Initiator
+ tests = [(1, "dpp_get_pubkey_point"),
+ (1, "dpp_alloc_msg;dpp_pkex_build_exchange_req"),
+ (1, "dpp_alloc_msg;dpp_pkex_build_commit_reveal_req"),
+ (1, "dpp_alloc_msg;dpp_auth_build_req"),
+ (1, "dpp_alloc_msg;dpp_auth_build_conf"),
+ (1, "dpp_bootstrap_key_hash"),
+ (1, "dpp_auth_init"),
+ (1, "dpp_alloc_auth"),
+ (1, "=dpp_auth_resp_rx"),
+ (1, "dpp_build_conf_start"),
+ (1, "dpp_build_conf_obj_dpp"),
+ (2, "dpp_build_conf_obj_dpp"),
+ (3, "dpp_build_conf_obj_dpp"),
+ (4, "dpp_build_conf_obj_dpp"),
+ (5, "dpp_build_conf_obj_dpp"),
+ (6, "dpp_build_conf_obj_dpp"),
+ (7, "dpp_build_conf_obj_dpp"),
+ (8, "dpp_build_conf_obj_dpp"),
+ (1, "dpp_conf_req_rx"),
+ (2, "dpp_conf_req_rx"),
+ (3, "dpp_conf_req_rx"),
+ (4, "dpp_conf_req_rx"),
+ (5, "dpp_conf_req_rx"),
+ (6, "dpp_conf_req_rx"),
+ (7, "dpp_conf_req_rx"),
+ (1, "dpp_pkex_init"),
+ (2, "dpp_pkex_init"),
+ (3, "dpp_pkex_init"),
+ (1, "dpp_pkex_derive_z"),
+ (1, "=dpp_pkex_rx_commit_reveal_resp"),
+ (1, "dpp_get_pubkey_point;dpp_build_jwk"),
+ (2, "dpp_get_pubkey_point;dpp_build_jwk"),
+ (1, "dpp_get_pubkey_point;dpp_auth_init")]
+ for count, func in tests:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ id0 = dev[0].dpp_pkex_resp(2437, identifier="test", code="secret",
+ use_id=id0)
+
+ with alloc_fail(dev[1], count, func):
+ id1 = dev[1].dpp_pkex_init(identifier="test", code="secret",
+ use_id=id1,
+ extra="conf=sta-dpp configurator=%d" % conf_id,
+ allow_fail=True)
+ wait_fail_trigger(dev[1], "GET_ALLOC_FAIL", max_iter=100)
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=0.01)
+ if ev:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[0].wait_event(["GAS-QUERY-DONE"], timeout=3)
+
+ # Local error cases on the Responder
+ tests = [(1, "dpp_get_pubkey_point"),
+ (1, "dpp_alloc_msg;dpp_pkex_build_exchange_resp"),
+ (1, "dpp_alloc_msg;dpp_pkex_build_commit_reveal_resp"),
+ (1, "dpp_alloc_msg;dpp_auth_build_resp"),
+ (1, "dpp_get_pubkey_point;dpp_auth_build_resp_ok"),
+ (1, "dpp_alloc_auth"),
+ (1, "=dpp_auth_req_rx"),
+ (1, "=dpp_auth_conf_rx"),
+ (1, "json_parse;dpp_parse_jws_prot_hdr"),
+ (1, "json_get_member_base64url;dpp_parse_jws_prot_hdr"),
+ (1, "json_get_member_base64url;dpp_parse_jwk"),
+ (2, "json_get_member_base64url;dpp_parse_jwk"),
+ (1, "json_parse;dpp_parse_connector"),
+ (1, "dpp_parse_jwk;dpp_parse_connector"),
+ (1, "dpp_parse_jwk;dpp_parse_cred_dpp"),
+ (1, "dpp_get_pubkey_point;dpp_check_pubkey_match"),
+ (1, "base64_gen_decode;dpp_process_signed_connector"),
+ (1, "dpp_parse_jws_prot_hdr;dpp_process_signed_connector"),
+ (2, "base64_gen_decode;dpp_process_signed_connector"),
+ (3, "base64_gen_decode;dpp_process_signed_connector"),
+ (4, "base64_gen_decode;dpp_process_signed_connector"),
+ (1, "json_parse;dpp_parse_conf_obj"),
+ (1, "dpp_conf_resp_rx"),
+ (1, "=dpp_pkex_derive_z"),
+ (1, "=dpp_pkex_rx_exchange_req"),
+ (2, "=dpp_pkex_rx_exchange_req"),
+ (3, "=dpp_pkex_rx_exchange_req"),
+ (1, "=dpp_pkex_rx_commit_reveal_req"),
+ (1, "dpp_get_pubkey_point;dpp_pkex_rx_commit_reveal_req"),
+ (1, "dpp_bootstrap_key_hash")]
+ for count, func in tests:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ id0 = dev[0].dpp_pkex_resp(2437, identifier="test", code="secret",
+ use_id=id0)
+
+ with alloc_fail(dev[0], count, func):
+ id1 = dev[1].dpp_pkex_init(identifier="test", code="secret",
+ use_id=id1,
+ extra="conf=sta-dpp configurator=%d" % conf_id)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL", max_iter=100)
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=0.01)
+ if ev:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[0].wait_event(["GAS-QUERY-DONE"], timeout=3)
+
+def test_dpp_pkex_test_fail(dev, apdev):
+ """DPP/PKEX and local failures"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ tests = [(1, "dpp_keygen_configurator")]
+ for count, func in tests:
+ with fail_test(dev[1], count, func):
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev[1].request(cmd)
+ if "FAIL" not in res:
+ raise Exception("Unexpected DPP_CONFIGURATOR_ADD success")
+
+ tests = [(1, "dpp_keygen")]
+ for count, func in tests:
+ with fail_test(dev[1], count, func):
+ cmd = "DPP_BOOTSTRAP_GEN type=pkex"
+ res = dev[1].request(cmd)
+ if "FAIL" not in res:
+ raise Exception("Unexpected DPP_BOOTSTRAP_GEN success")
+
+ conf_id = dev[1].dpp_configurator_add()
+
+ id0 = None
+ id1 = None
+
+ # Local error cases on the Initiator
+ tests = [(1, "aes_siv_encrypt;dpp_auth_build_req"),
+ (1, "os_get_random;dpp_auth_init"),
+ (1, "dpp_derive_k1;dpp_auth_init"),
+ (1, "dpp_hkdf_expand;dpp_derive_k1;dpp_auth_init"),
+ (1, "dpp_gen_i_auth;dpp_auth_build_conf"),
+ (1, "aes_siv_encrypt;dpp_auth_build_conf"),
+ (1, "dpp_derive_k2;dpp_auth_resp_rx"),
+ (1, "dpp_hkdf_expand;dpp_derive_k2;dpp_auth_resp_rx"),
+ (1, "dpp_derive_bk_ke;dpp_auth_resp_rx"),
+ (1, "dpp_hkdf_expand;dpp_derive_bk_ke;dpp_auth_resp_rx"),
+ (1, "dpp_gen_r_auth;dpp_auth_resp_rx"),
+ (1, "aes_siv_encrypt;dpp_build_conf_resp"),
+ (1, "dpp_pkex_derive_Qi;dpp_pkex_build_exchange_req"),
+ (1, "aes_siv_encrypt;dpp_pkex_build_commit_reveal_req"),
+ (1, "hmac_sha256_vector;dpp_pkex_rx_exchange_resp"),
+ (1, "aes_siv_decrypt;dpp_pkex_rx_commit_reveal_resp"),
+ (1, "hmac_sha256_vector;dpp_pkex_rx_commit_reveal_resp"),
+ (1, "dpp_bootstrap_key_hash")]
+ for count, func in tests:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ id0 = dev[0].dpp_pkex_resp(2437, identifier="test", code="secret",
+ use_id=id0)
+
+ with fail_test(dev[1], count, func):
+ id1 = dev[1].dpp_pkex_init(identifier="test", code="secret",
+ use_id=id1,
+ extra="conf=sta-dpp configurator=%d" % conf_id,
+ allow_fail=True)
+ wait_fail_trigger(dev[1], "GET_FAIL", max_iter=100)
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=0.01)
+ if ev:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[0].wait_event(["GAS-QUERY-DONE"], timeout=3)
+
+ # Local error cases on the Responder
+ tests = [(1, "aes_siv_encrypt;dpp_auth_build_resp"),
+ (1, "aes_siv_encrypt;dpp_auth_build_resp;dpp_auth_build_resp_ok"),
+ (1, "os_get_random;dpp_build_conf_req"),
+ (1, "aes_siv_encrypt;dpp_build_conf_req"),
+ (1, "os_get_random;dpp_auth_build_resp_ok"),
+ (1, "dpp_derive_k2;dpp_auth_build_resp_ok"),
+ (1, "dpp_derive_bk_ke;dpp_auth_build_resp_ok"),
+ (1, "dpp_gen_r_auth;dpp_auth_build_resp_ok"),
+ (1, "aes_siv_encrypt;dpp_auth_build_resp_ok"),
+ (1, "dpp_derive_k1;dpp_auth_req_rx"),
+ (1, "aes_siv_decrypt;dpp_auth_req_rx"),
+ (1, "aes_siv_decrypt;dpp_auth_conf_rx"),
+ (1, "dpp_gen_i_auth;dpp_auth_conf_rx"),
+ (1, "dpp_check_pubkey_match"),
+ (1, "aes_siv_decrypt;dpp_conf_resp_rx"),
+ (1, "hmac_sha256_kdf;dpp_pkex_derive_z"),
+ (1, "dpp_pkex_derive_Qi;dpp_pkex_rx_exchange_req"),
+ (1, "dpp_pkex_derive_Qr;dpp_pkex_rx_exchange_req"),
+ (1, "aes_siv_encrypt;dpp_pkex_build_commit_reveal_resp"),
+ (1, "aes_siv_decrypt;dpp_pkex_rx_commit_reveal_req"),
+ (1, "hmac_sha256_vector;dpp_pkex_rx_commit_reveal_req"),
+ (2, "hmac_sha256_vector;dpp_pkex_rx_commit_reveal_req")]
+ for count, func in tests:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ id0 = dev[0].dpp_pkex_resp(2437, identifier="test", code="secret",
+ use_id=id0)
+
+ with fail_test(dev[0], count, func):
+ id1 = dev[1].dpp_pkex_init(identifier="test", code="secret",
+ use_id=id1,
+ extra="conf=sta-dpp configurator=%d" % conf_id)
+ wait_fail_trigger(dev[0], "GET_FAIL", max_iter=100)
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=0.01)
+ if ev:
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[0].wait_event(["GAS-QUERY-DONE"], timeout=3)
+
+def test_dpp_keygen_configurator_error(dev, apdev):
+ """DPP Configurator keygen error case"""
+ check_dpp_capab(dev[0])
+ if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD curve=unknown"):
+ raise Exception("Unexpected success of invalid DPP_CONFIGURATOR_ADD")
+
+def rx_process_frame(dev):
+ msg = dev.mgmt_rx()
+ if msg is None:
+ raise Exception("No management frame RX reported")
+ if "OK" not in dev.request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
+ msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())):
+ raise Exception("MGMT_RX_PROCESS failed")
+ return msg
+
+def wait_auth_success(responder, initiator, configurator=None, enrollee=None,
+ allow_enrollee_failure=False,
+ allow_configurator_failure=False,
+ require_configurator_failure=False,
+ timeout=5, stop_responder=False, stop_initiator=False):
+ res = {}
+ ev = responder.wait_event(["DPP-AUTH-SUCCESS", "DPP-FAIL"], timeout=timeout)
+ if ev is None or "DPP-AUTH-SUCCESS" not in ev:
+ raise Exception("DPP authentication did not succeed (Responder)")
+ ev = initiator.wait_event(["DPP-AUTH-SUCCESS", "DPP-FAIL"], timeout=5)
+ if ev is None or "DPP-AUTH-SUCCESS" not in ev:
+ raise Exception("DPP authentication did not succeed (Initiator)")
+ if configurator:
+ ev = configurator.wait_event(["DPP-CONF-SENT",
+ "DPP-CONF-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Configurator)")
+ if "DPP-CONF-FAILED" in ev and not allow_configurator_failure:
+ raise Exception("DPP configuration did not succeed (Configurator)")
+ if "DPP-CONF-SENT" in ev and require_configurator_failure:
+ raise Exception("DPP configuration succeeded (Configurator)")
+ if "DPP-CONF-SENT" in ev and "wait_conn_status=1" in ev:
+ res['wait_conn_status'] = True
+ if enrollee:
+ ev = enrollee.wait_event(["DPP-CONF-RECEIVED",
+ "DPP-CONF-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Enrollee)")
+ if "DPP-CONF-FAILED" in ev and not allow_enrollee_failure:
+ raise Exception("DPP configuration did not succeed (Enrollee)")
+ if stop_responder:
+ responder.request("DPP_STOP_LISTEN")
+ if stop_initiator:
+ initiator.request("DPP_STOP_LISTEN")
+ return res
+
+def wait_conf_completion(configurator, enrollee):
+ ev = configurator.wait_event(["DPP-CONF-SENT"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Configurator)")
+ ev = enrollee.wait_event(["DPP-CONF-RECEIVED", "DPP-CONF-FAILED"],
+ timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Enrollee)")
+
+def start_dpp(dev):
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"test"},"cred":{"akm":"psk","pass":"secret passphrase"}}' + 3000*' '
+ dev[0].set("dpp_config_obj_override", conf)
+
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0, role="enrollee")
+
+def test_dpp_gas_timeout_handling(dev, apdev):
+ """DPP and GAS timeout handling"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ start_dpp(dev)
+
+ # DPP Authentication Request
+ rx_process_frame(dev[0])
+
+ # DPP Authentication Confirmation
+ rx_process_frame(dev[0])
+
+ wait_auth_success(dev[0], dev[1])
+
+ # DPP Configuration Request (GAS Initial Request frame)
+ rx_process_frame(dev[0])
+
+ # DPP Configuration Request (GAS Comeback Request frame)
+ rx_process_frame(dev[0])
+
+ # Wait for GAS timeout
+ ev = dev[1].wait_event(["DPP-CONF-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Enrollee)")
+
+def test_dpp_gas_comeback_after_failure(dev, apdev):
+ """DPP and GAS comeback after failure"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ start_dpp(dev)
+
+ # DPP Authentication Request
+ rx_process_frame(dev[0])
+
+ # DPP Authentication Confirmation
+ rx_process_frame(dev[0])
+
+ wait_auth_success(dev[0], dev[1])
+
+ # DPP Configuration Request (GAS Initial Request frame)
+ rx_process_frame(dev[0])
+
+ # DPP Configuration Request (GAS Comeback Request frame)
+ msg = dev[0].mgmt_rx()
+ frame = binascii.hexlify(msg['frame']).decode()
+ with alloc_fail(dev[0], 1, "gas_build_comeback_resp;gas_server_handle_rx_comeback_req"):
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
+ raise Exception("MGMT_RX_PROCESS failed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ # Try the same frame again - this is expected to fail since the response has
+ # already been freed.
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ # DPP Configuration Request (GAS Comeback Request frame retry)
+ msg = dev[0].mgmt_rx()
+
+def test_dpp_gas(dev, apdev):
+ """DPP and GAS protocol testing"""
+ ver0 = check_dpp_capab(dev[0])
+ ver1 = check_dpp_capab(dev[1])
+ start_dpp(dev)
+
+ # DPP Authentication Request
+ rx_process_frame(dev[0])
+
+ # DPP Authentication Confirmation
+ rx_process_frame(dev[0])
+
+ wait_auth_success(dev[0], dev[1])
+
+ # DPP Configuration Request (GAS Initial Request frame)
+ msg = dev[0].mgmt_rx()
+
+ # Protected Dual of GAS Initial Request frame (dropped by GAS server)
+ if msg == None:
+ raise Exception("MGMT_RX_PROCESS failed. <Please retry>")
+ frame = binascii.hexlify(msg['frame'])
+ frame = frame[0:48] + b"09" + frame[50:]
+ frame = frame.decode()
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ with alloc_fail(dev[0], 1, "gas_server_send_resp"):
+ frame = binascii.hexlify(msg['frame']).decode()
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
+ raise Exception("MGMT_RX_PROCESS failed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with alloc_fail(dev[0], 1, "gas_build_initial_resp;gas_server_send_resp"):
+ frame = binascii.hexlify(msg['frame']).decode()
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
+ raise Exception("MGMT_RX_PROCESS failed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ # Add extra data after Query Request field to trigger
+ # "GAS: Ignored extra data after Query Request field"
+ frame = binascii.hexlify(msg['frame']).decode() + "00"
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ # DPP Configuration Request (GAS Comeback Request frame)
+ rx_process_frame(dev[0])
+
+ # DPP Configuration Request (GAS Comeback Request frame)
+ rx_process_frame(dev[0])
+
+ # DPP Configuration Request (GAS Comeback Request frame)
+ rx_process_frame(dev[0])
+
+ if ver0 >= 2 and ver1 >= 2:
+ # DPP Configuration Result
+ rx_process_frame(dev[0])
+
+ wait_conf_completion(dev[0], dev[1])
+
+def test_dpp_truncated_attr(dev, apdev):
+ """DPP and truncated attribute"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ start_dpp(dev)
+
+ # DPP Authentication Request
+ msg = dev[0].mgmt_rx()
+ frame = msg['frame']
+
+ # DPP: Truncated message - not enough room for the attribute - dropped
+ frame1 = binascii.hexlify(frame[0:36]).decode()
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame1)):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "ignore=invalid-attributes" not in ev:
+ raise Exception("Invalid attribute error not reported")
+
+ # DPP: Unexpected octets (3) after the last attribute
+ frame2 = binascii.hexlify(frame).decode() + "000000"
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame2)):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "ignore=invalid-attributes" not in ev:
+ raise Exception("Invalid attribute error not reported")
+
+def test_dpp_bootstrap_key_autogen_issues(dev, apdev):
+ """DPP bootstrap key autogen issues"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ logger.info("dev1 scans QR Code")
+ id1 = dev[1].dpp_qr_code(uri0)
+
+ logger.info("dev1 initiates DPP Authentication")
+ dev[0].dpp_listen(2412)
+ with alloc_fail(dev[1], 1, "dpp_autogen_bootstrap_key"):
+ dev[1].dpp_auth_init(peer=id1, expect_fail=True)
+ with alloc_fail(dev[1], 1, "dpp_gen_uri;dpp_autogen_bootstrap_key"):
+ dev[1].dpp_auth_init(peer=id1, expect_fail=True)
+ with fail_test(dev[1], 1, "dpp_keygen;dpp_autogen_bootstrap_key"):
+ dev[1].dpp_auth_init(peer=id1, expect_fail=True)
+ dev[0].request("DPP_STOP_LISTEN")
+
+def test_dpp_auth_resp_status_failure(dev, apdev):
+ """DPP and Auth Resp(status) build failure"""
+ with alloc_fail(dev[0], 1, "dpp_auth_build_resp"):
+ run_dpp_proto_auth_resp_missing(dev, 99999, None,
+ incompatible_roles=True)
+
+def test_dpp_auth_resp_aes_siv_issue(dev, apdev):
+ """DPP Auth Resp AES-SIV issue"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ logger.info("dev0 displays QR Code")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("dev1 scans QR Code and initiates DPP Authentication")
+ dev[0].dpp_listen(2412)
+ with fail_test(dev[1], 1, "aes_siv_decrypt;dpp_auth_resp_rx"):
+ dev[1].dpp_auth_init(uri=uri0)
+ wait_dpp_fail(dev[1], "AES-SIV decryption failed")
+ dev[0].request("DPP_STOP_LISTEN")
+
+def test_dpp_invalid_legacy_params(dev, apdev):
+ """DPP invalid legacy parameters"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ # No pass/psk
+ dev[1].dpp_auth_init(uri=uri0, conf="sta-psk", ssid="dpp-legacy",
+ expect_fail=True)
+
+def test_dpp_invalid_legacy_params2(dev, apdev):
+ """DPP invalid legacy parameters 2"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ " conf=sta-psk ssid=%s" % (binascii.hexlify(b"dpp-legacy").decode()))
+ dev[0].dpp_listen(2412, role="configurator")
+ dev[1].dpp_auth_init(uri=uri0, role="enrollee")
+ # No pass/psk
+ ev = dev[0].wait_event(["DPP: Failed to set configurator parameters"],
+ timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration failure not reported")
+
+def test_dpp_legacy_params_failure(dev, apdev):
+ """DPP legacy parameters local failure"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_listen(2412)
+ with alloc_fail(dev[1], 1, "dpp_build_conf_obj_legacy"):
+ dev[1].dpp_auth_init(uri=uri0, conf="sta-psk", passphrase="passphrase",
+ ssid="dpp-legacy")
+ ev = dev[0].wait_event(["DPP-CONF-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration failure not reported")
+
+def test_dpp_invalid_configurator_key(dev, apdev):
+ """DPP invalid configurator key"""
+ check_dpp_capab(dev[0])
+
+ if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD key=aa"):
+ raise Exception("Invalid key accepted")
+
+ with alloc_fail(dev[0], 1, "dpp_keygen_configurator"):
+ if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD key=" + dpp_key_p256):
+ raise Exception("Error not reported")
+
+ with alloc_fail(dev[0], 1, "dpp_get_pubkey_point;dpp_keygen_configurator"):
+ if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD key=" + dpp_key_p256):
+ raise Exception("Error not reported")
+
+ with alloc_fail(dev[0], 1, "base64_gen_encode;dpp_keygen_configurator"):
+ if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD key=" + dpp_key_p256):
+ raise Exception("Error not reported")
+
+ with fail_test(dev[0], 1, "dpp_keygen_configurator"):
+ if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_ADD key=" + dpp_key_p256):
+ raise Exception("Error not reported")
+
+def test_dpp_own_config_sign_fail(dev, apdev):
+ """DPP own config signing failure"""
+ check_dpp_capab(dev[0])
+ conf_id = dev[0].dpp_configurator_add()
+ tests = ["",
+ " ",
+ " conf=sta-dpp",
+ " configurator=%d" % conf_id,
+ " conf=sta-dpp configurator=%d curve=unsupported" % conf_id]
+ for t in tests:
+ if "FAIL" not in dev[0].request("DPP_CONFIGURATOR_SIGN " + t):
+ raise Exception("Invalid command accepted: " + t)
+
+def test_dpp_peer_intro_failures(dev, apdev):
+ """DPP peer introduction failures"""
+ try:
+ run_dpp_peer_intro_failures(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_peer_intro_failures(dev, apdev):
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+
+ conf_id = hapd.dpp_configurator_add(key=dpp_key_p256)
+ csign = hapd.request("DPP_CONFIGURATOR_GET_KEY %d" % conf_id)
+ if "FAIL" in csign or len(csign) == 0:
+ raise Exception("DPP_CONFIGURATOR_GET_KEY failed")
+
+ conf_id2 = dev[0].dpp_configurator_add(key=csign)
+ csign2 = dev[0].request("DPP_CONFIGURATOR_GET_KEY %d" % conf_id2)
+
+ if csign != csign2:
+ raise Exception("Unexpected difference in configurator key")
+
+ cmd = "DPP_CONFIGURATOR_SIGN conf=ap-dpp configurator=%d" % conf_id
+ res = hapd.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to generate own configuration")
+ update_hapd_config(hapd)
+
+ dev[0].set("dpp_config_processing", "1")
+ cmd = "DPP_CONFIGURATOR_SIGN conf=sta-dpp configurator=%d" % conf_id
+ res = dev[0].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to generate own configuration")
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("DPP network profile not generated")
+ id = ev.split(' ')[1]
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ tests = ["eyJ0eXAiOiJkcHBDb24iLCJraWQiOiIwTlNSNTlxRTc0alFfZTFLVGVPV1lYY1pTWnFUaDdNXzU0aHJPcFRpaFJnIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOltdLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJiVmFMRGlBT09OQmFjcVFVN1pYamFBVEtEMVhhbDVlUExqOUZFZUl3VkN3IiwieSI6Il95c25JR1hTYjBvNEsyMWg0anZmSkZxMHdVNnlPNWp1VUFPd3FuM0dHVHMifX0.WgzZBOJaisWBRxvtXPbVYPXU7OIZxs6sZD-cPOLmJVTIYZKdMkSOMvP5b6si_j61FIrjhm43tmGq1P6cpoxB_g",
+ "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiIwTlNSNTlxRTc0alFfZTFLVGVPV1lYY1pTWnFUaDdNXzU0aHJPcFRpaFJnIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7fV0sIm5ldEFjY2Vzc0tleSI6eyJrdHkiOiJFQyIsImNydiI6IlAtMjU2IiwieCI6IkJhY3BWSDNpNDBrZklNS0RHa1FFRzhCODBCaEk4cEFmTWpLbzM5NlFZT2ciLCJ5IjoiMjBDYjhDNjRsSjFzQzV2NXlKMnBFZXRRempxMjI4YVV2cHMxNmQ0M3EwQSJ9fQ.dG2y8VvZQJ5hfob8E5F2FAeR7Nd700qstYkxDgA2QfARaNMZ0_SfKfoG-yKXsIZNM-TvGBfACgfhagG9Oaw_Xw",
+ "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiIwTlNSNTlxRTc0alFfZTFLVGVPV1lYY1pTWnFUaDdNXzU0aHJPcFRpaFJnIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIn1dLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiJkc2VmcmJWWlhad0RMWHRpLWlObDBBYkFIOXpqeFFKd0R1SUd5NzNuZGU0IiwieSI6IjZFQnExN3cwYW1fZlh1OUQ4UGxWYk9XZ2I3b19DcTUxWHlmSG8wcHJyeDQifX0.caBvdDUtXrhnS61-juVZ_2FQdprepv0yZjC04G4ERvLUpeX7cgu0Hp-A1aFDogP1PEFGpkaEdcAWRQnSSRiIKQ"]
+ for t in tests:
+ dev[0].set_network_quoted(id, "dpp_connector", t)
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["DPP-INTRO"], timeout=5)
+ if ev is None or "status=8" not in ev:
+ raise Exception("Introduction failure not reported")
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+
+def test_dpp_peer_intro_local_failures(dev, apdev):
+ """DPP peer introduction local failures"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ params = {"ssid": "dpp",
+ "wpa": "2",
+ "wpa_key_mgmt": "DPP",
+ "ieee80211w": "2",
+ "rsn_pairwise": "CCMP",
+ "dpp_connector": params1_ap_connector,
+ "dpp_csign": params1_csign,
+ "dpp_netaccesskey": params1_ap_netaccesskey}
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except:
+ raise HwsimSkip("DPP not supported")
+
+ tests = ["dpp_derive_pmk",
+ "dpp_hkdf_expand;dpp_derive_pmk",
+ "dpp_derive_pmkid"]
+ for func in tests:
+ with fail_test(dev[0], 1, func):
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
+ ieee80211w="2",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey,
+ wait_connect=False)
+ ev = dev[0].wait_event(["DPP-INTRO"], timeout=10)
+ if ev is None or "fail=peer_connector_validation_failed" not in ev:
+ raise Exception("Introduction failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = [(1, "base64_gen_decode;dpp_peer_intro"),
+ (1, "json_parse;dpp_peer_intro"),
+ (50, "json_parse;dpp_peer_intro"),
+ (1, "=dpp_check_signed_connector;dpp_peer_intro"),
+ (1, "dpp_parse_jwk")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
+ ieee80211w="2",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey,
+ wait_connect=False)
+ ev = dev[0].wait_event(["DPP-INTRO"], timeout=10)
+ if ev is None or "fail=peer_connector_validation_failed" not in ev:
+ raise Exception("Introduction failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ parts = params1_ap_connector.split('.')
+ for ap_connector in ['.'.join(parts[0:2]), '.'.join(parts[0:1])]:
+ hapd.set("dpp_connector", ap_connector)
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
+ ieee80211w="2",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey,
+ wait_connect=False)
+ ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=10)
+ if ev is None:
+ raise Exception("No TX status reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ hapd.set("dpp_netaccesskey", "00")
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
+ ieee80211w="2",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey,
+ wait_connect=False)
+ ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=10)
+ if ev is None:
+ raise Exception("No TX status reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ hapd.set("dpp_csign", "00")
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
+ ieee80211w="2",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey,
+ wait_connect=False)
+ ev = dev[0].wait_event(["DPP-TX-STATUS"], timeout=10)
+ if ev is None:
+ raise Exception("No TX status reported")
+ dev[0].request("REMOVE_NETWORK all")
+
+def run_dpp_configurator_id_unknown(dev):
+ check_dpp_capab(dev)
+ conf_id = dev.dpp_configurator_add()
+ if "FAIL" not in dev.request("DPP_CONFIGURATOR_GET_KEY %d" % (conf_id + 1)):
+ raise Exception("DPP_CONFIGURATOR_GET_KEY with incorrect id accepted")
+
+ cmd = "DPP_CONFIGURATOR_SIGN conf=sta-dpp configurator=%d" % (conf_id + 1)
+ if "FAIL" not in dev.request(cmd):
+ raise Exception("DPP_CONFIGURATOR_SIGN with incorrect id accepted")
+
+def test_dpp_configurator_id_unknown(dev, apdev):
+ """DPP and unknown configurator id"""
+ run_dpp_configurator_id_unknown(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ run_dpp_configurator_id_unknown(hapd)
+
+def run_dpp_bootstrap_gen_failures(dev):
+ check_dpp_capab(dev)
+
+ tests = ["type=unsupported",
+ "type=qrcode chan=-1",
+ "type=qrcode mac=a",
+ "type=qrcode key=qq",
+ "type=qrcode key=",
+ "type=qrcode info=abc\tdef"]
+ for t in tests:
+ if "FAIL" not in dev.request("DPP_BOOTSTRAP_GEN " + t):
+ raise Exception("Command accepted unexpectedly")
+
+ id = dev.dpp_bootstrap_gen()
+ uri = dev.request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ if not uri.startswith("DPP:"):
+ raise Exception("Could not get URI")
+ if "FAIL" not in dev.request("DPP_BOOTSTRAP_GET_URI 0"):
+ raise Exception("Failure not reported")
+ info = dev.request("DPP_BOOTSTRAP_INFO %d" % id)
+ if not info.startswith("type=QRCODE"):
+ raise Exception("Could not get info")
+ if "FAIL" not in dev.request("DPP_BOOTSTRAP_REMOVE 0"):
+ raise Exception("Failure not reported")
+ if "FAIL" in dev.request("DPP_BOOTSTRAP_REMOVE *"):
+ raise Exception("Failed to remove bootstrap info")
+ if "FAIL" not in dev.request("DPP_BOOTSTRAP_GET_URI %d" % id):
+ raise Exception("Failure not reported")
+ if "FAIL" not in dev.request("DPP_BOOTSTRAP_INFO %d" % id):
+ raise Exception("Failure not reported")
+
+ func = "dpp_bootstrap_gen"
+ with alloc_fail(dev, 1, "=" + func):
+ if "FAIL" not in dev.request("DPP_BOOTSTRAP_GEN type=qrcode"):
+ raise Exception("Command accepted unexpectedly")
+
+ with alloc_fail(dev, 1, "dpp_gen_uri;dpp_bootstrap_gen"):
+ if "FAIL" not in dev.request("DPP_BOOTSTRAP_GEN type=qrcode"):
+ raise Exception("Command accepted unexpectedly")
+
+ with alloc_fail(dev, 1, "get_param"):
+ dev.request("DPP_BOOTSTRAP_GEN type=qrcode curve=foo")
+
+def test_dpp_bootstrap_gen_failures(dev, apdev):
+ """DPP_BOOTSTRAP_GEN/REMOVE/GET_URI/INFO error cases"""
+ run_dpp_bootstrap_gen_failures(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ run_dpp_bootstrap_gen_failures(hapd)
+
+def test_dpp_listen_continue(dev, apdev):
+ """DPP and continue listen state"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ id = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ dev[0].dpp_listen(2412)
+ time.sleep(5.1)
+ dev[1].dpp_auth_init(uri=uri)
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
+ allow_enrollee_failure=True, stop_responder=True,
+ stop_initiator=True)
+
+def test_dpp_network_addition_failure(dev, apdev):
+ """DPP network addition failure"""
+ try:
+ run_dpp_network_addition_failure(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_network_addition_failure(dev, apdev):
+ check_dpp_capab(dev[0])
+ conf_id = dev[0].dpp_configurator_add()
+ dev[0].set("dpp_config_processing", "1")
+ cmd = "DPP_CONFIGURATOR_SIGN conf=sta-dpp configurator=%d" % conf_id
+ tests = [(1, "=wpas_dpp_add_network"),
+ (2, "=wpas_dpp_add_network"),
+ (3, "=wpas_dpp_add_network"),
+ (4, "=wpas_dpp_add_network"),
+ (1, "wpa_config_add_network;wpas_dpp_add_network")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ res = dev[0].request(cmd)
+ if "OK" in res:
+ ev = dev[0].wait_event(["DPP-NET-ACCESS-KEY"], timeout=2)
+ if ev is None:
+ raise Exception("Config object not processed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].dump_monitor()
+
+ cmd = "DPP_CONFIGURATOR_SIGN conf=sta-psk pass=%s configurator=%d" % (binascii.hexlify(b"passphrase").decode(), conf_id)
+ tests = [(1, "wpa_config_set_quoted;wpas_dpp_add_network")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ res = dev[0].request(cmd)
+ if "OK" in res:
+ ev = dev[0].wait_event(["DPP-NET-ACCESS-KEY"], timeout=2)
+ if ev is None:
+ raise Exception("Config object not processed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].dump_monitor()
+
+def test_dpp_two_initiators(dev, apdev):
+ """DPP and two initiators"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ check_dpp_capab(dev[2])
+ id = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri)
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None:
+ raise Exeption("No DPP Authentication Request seen")
+ dev[2].dpp_auth_init(uri=uri)
+ wait_dpp_fail(dev[0],
+ "DPP-FAIL Already in DPP authentication exchange - ignore new one")
+
+ ev = dev[0].wait_event(["DPP-CONF-FAILED"], timeout=2)
+ if ev is None:
+ raise Exception("DPP configuration result not seen (Enrollee)")
+ ev = dev[1].wait_event(["DPP-CONF-SENT"], timeout=2)
+ if ev is None:
+ raise Exception("DPP configuration result not seen (Responder)")
+
+ dev[0].request("DPP_STOP_LISTEN")
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[2].request("DPP_STOP_LISTEN")
+
+def test_dpp_conf_file_update(dev, apdev, params):
+ """DPP provisioning updating wpa_supplicant configuration file"""
+ config = os.path.join(params['logdir'], 'dpp_conf_file_update.conf')
+ with open(config, "w") as f:
+ f.write("update_config=1\n")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", config=config)
+ check_dpp_capab(wpas)
+ wpas.set("dpp_config_processing", "1")
+ run_dpp_qr_code_auth_unicast([wpas, dev[1]], apdev, None,
+ init_extra="conf=sta-dpp",
+ require_conf_success=True,
+ configurator=True)
+ wpas.interface_remove("wlan5")
+
+ with open(config, "r") as f:
+ res = f.read()
+ for i in ["network={", "dpp_connector=", "key_mgmt=DPP", "ieee80211w=2",
+ "dpp_netaccesskey=", "dpp_csign="]:
+ if i not in res:
+ raise Exception("Configuration file missing '%s'" % i)
+
+ wpas.interface_add("wlan5", config=config)
+ if len(wpas.list_networks()) != 1:
+ raise Exception("Unexpected number of networks")
+
+def test_dpp_duplicated_auth_resp(dev, apdev):
+ """DPP and duplicated Authentication Response"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[1].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0)
+
+ # DPP Authentication Request
+ rx_process_frame(dev[0])
+
+ # DPP Authentication Response
+ msg = rx_process_frame(dev[1])
+ frame = binascii.hexlify(msg['frame']).decode()
+ # Duplicated frame
+ if "OK" not in dev[1].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame)):
+ raise Exception("MGMT_RX_PROCESS failed")
+ # Modified frame - nonzero status
+ if frame[2*32:2*37] != "0010010000":
+ raise Exception("Could not find Status attribute")
+ frame2 = frame[0:2*32] + "0010010001" + frame[2*37:]
+ if "OK" not in dev[1].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame2)):
+ raise Exception("MGMT_RX_PROCESS failed")
+ frame2 = frame[0:2*32] + "00100100ff" + frame[2*37:]
+ if "OK" not in dev[1].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], frame2)):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ # DPP Authentication Confirmation
+ rx_process_frame(dev[0])
+
+ wait_auth_success(dev[0], dev[1])
+
+ # DPP Configuration Request
+ rx_process_frame(dev[1])
+
+ # DPP Configuration Response
+ rx_process_frame(dev[0])
+
+ wait_conf_completion(dev[1], dev[0])
+
+def test_dpp_duplicated_auth_conf(dev, apdev):
+ """DPP and duplicated Authentication Confirmation"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("ext_mgmt_frame_handling", "1")
+ dev[1].set("ext_mgmt_frame_handling", "1")
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0)
+
+ # DPP Authentication Request
+ rx_process_frame(dev[0])
+
+ # DPP Authentication Response
+ rx_process_frame(dev[1])
+
+ # DPP Authentication Confirmation
+ msg = rx_process_frame(dev[0])
+ # Duplicated frame
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(msg['freq'], msg['datarate'], msg['ssi_signal'], binascii.hexlify(msg['frame']).decode())):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ wait_auth_success(dev[0], dev[1])
+
+ # DPP Configuration Request
+ rx_process_frame(dev[1])
+
+ # DPP Configuration Response
+ rx_process_frame(dev[0])
+
+ wait_conf_completion(dev[1], dev[0])
+
+def test_dpp_enrollee_reject_config(dev, apdev):
+ """DPP and Enrollee rejecting Config Object"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ dev[0].set("dpp_test", "91")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0, conf="sta-sae", ssid="dpp-legacy",
+ passphrase="secret passphrase")
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
+ allow_enrollee_failure=True,
+ allow_configurator_failure=True)
+
+def test_dpp_enrollee_ap_reject_config(dev, apdev):
+ """DPP and Enrollee AP rejecting Config Object"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+ hapd.set("dpp_test", "91")
+ conf_id = dev[0].dpp_configurator_add()
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+ dev[0].dpp_auth_init(uri=uri, conf="ap-dpp", configurator=conf_id)
+ wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd,
+ allow_enrollee_failure=True,
+ allow_configurator_failure=True)
+
+def test_dpp_legacy_and_dpp_akm(dev, apdev):
+ """DPP and provisoning DPP and legacy AKMs"""
+ try:
+ run_dpp_legacy_and_dpp_akm(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_legacy_and_dpp_akm(dev, apdev):
+ check_dpp_capab(dev[0], min_ver=2)
+ check_dpp_capab(dev[1], min_ver=2)
+
+ csign = "30770201010420768240a3fc89d6662d9782f120527fe7fb9edc6366ab0b9c7dde96125cfd250fa00a06082a8648ce3d030107a144034200042908e1baf7bf413cc66f9e878a03e8bb1835ba94b033dbe3d6969fc8575d5eb5dfda1cb81c95cee21d0cd7d92ba30541ffa05cb6296f5dd808b0c1c2a83c0708"
+ csign_pub = "3059301306072a8648ce3d020106082a8648ce3d030107034200042908e1baf7bf413cc66f9e878a03e8bb1835ba94b033dbe3d6969fc8575d5eb5dfda1cb81c95cee21d0cd7d92ba30541ffa05cb6296f5dd808b0c1c2a83c0708"
+ ap_connector = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJwYWtZbXVzd1dCdWpSYTl5OEsweDViaTVrT3VNT3dzZHRlaml2UG55ZHZzIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6ImFwIn1dLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiIybU5vNXZuRkI5bEw3d1VWb1hJbGVPYzBNSEE1QXZKbnpwZXZULVVTYzVNIiwieSI6IlhzS3dqVHJlLTg5WWdpU3pKaG9CN1haeUttTU05OTl3V2ZaSVl0bi01Q3MifX0.XhjFpZgcSa7G2lHy0OCYTvaZFRo5Hyx6b7g7oYyusLC7C_73AJ4_BxEZQVYJXAtDuGvb3dXSkHEKxREP9Q6Qeg"
+ ap_netaccesskey = "30770201010420ceba752db2ad5200fa7bc565b9c05c69b7eb006751b0b329b0279de1c19ca67ca00a06082a8648ce3d030107a14403420004da6368e6f9c507d94bef0515a1722578e73430703902f267ce97af4fe51273935ec2b08d3adefbcf588224b3261a01ed76722a630cf7df7059f64862d9fee42b"
+
+ ssid = "dpp-both"
+ passphrase = "secret passphrase"
+ params = {"ssid": ssid,
+ "wpa": "2",
+ "wpa_key_mgmt": "DPP WPA-PSK SAE",
+ "ieee80211w": "1",
+ "sae_require_mfp": '1',
+ "rsn_pairwise": "CCMP",
+ "wpa_passphrase": passphrase,
+ "dpp_connector": ap_connector,
+ "dpp_csign": csign_pub,
+ "dpp_netaccesskey": ap_netaccesskey}
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except:
+ raise HwsimSkip("DPP not supported")
+
+ dev[0].request("SET sae_groups ")
+ conf_id = dev[1].dpp_configurator_add(key=csign)
+ dev[0].set("dpp_config_processing", "1")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_listen(2412)
+ dev[1].dpp_auth_init(uri=uri0, conf="sta-psk-sae-dpp", ssid=ssid,
+ passphrase=passphrase, configurator=conf_id)
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0],
+ allow_enrollee_failure=True,
+ allow_configurator_failure=True)
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("DPP network profile not generated")
+ id0 = ev.split(' ')[1]
+
+ key_mgmt = dev[0].get_network(id0, "key_mgmt").split(' ')
+ for m in ["SAE", "WPA-PSK", "DPP"]:
+ if m not in key_mgmt:
+ raise Exception("%s missing from key_mgmt" % m)
+
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ dev[0].select_network(id0, freq=2412)
+ dev[0].wait_connected()
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.disable()
+
+ params = {"ssid": ssid,
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK SAE",
+ "ieee80211w": "1",
+ "sae_require_mfp": '1',
+ "rsn_pairwise": "CCMP",
+ "wpa_passphrase": passphrase}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].request("BSS_FLUSH 0")
+ dev[0].scan_for_bss(hapd2.own_addr(), freq=2412, force_scan=True,
+ only_new=True)
+ dev[0].select_network(id0, freq=2412)
+ dev[0].wait_connected()
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_dpp_controller_relay(dev, apdev, params):
+ """DPP Controller/Relay"""
+ try:
+ run_dpp_controller_relay(dev, apdev, params)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def test_dpp_controller_relay_chirp(dev, apdev, params):
+ """DPP Controller/Relay with chirping"""
+ try:
+ run_dpp_controller_relay(dev, apdev, params, chirp=True)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def run_dpp_controller_relay(dev, apdev, params, chirp=False):
+ check_dpp_capab(dev[0], min_ver=2)
+ check_dpp_capab(dev[1], min_ver=2)
+ prefix = "dpp_controller_relay"
+ if chirp:
+ prefix += "_chirp"
+ cap_lo = os.path.join(params['logdir'], prefix + ".lo.pcap")
+
+ wt = WlantestCapture('lo', cap_lo)
+
+ # Controller
+ conf_id = dev[1].dpp_configurator_add()
+ dev[1].set("dpp_configurator_params",
+ "conf=sta-dpp configurator=%d" % conf_id)
+ id_c = dev[1].dpp_bootstrap_gen()
+ uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ res = dev[1].request("DPP_BOOTSTRAP_INFO %d" % id_c)
+ pkhash = None
+ for line in res.splitlines():
+ name, value = line.split('=')
+ if name == "pkhash":
+ pkhash = value
+ break
+ if not pkhash:
+ raise Exception("Could not fetch public key hash from Controller")
+ if "OK" not in dev[1].request("DPP_CONTROLLER_START"):
+ raise Exception("Failed to start Controller")
+
+ # Relay
+ params = {"ssid": "unconfigured",
+ "channel": "6",
+ "dpp_controller": "ipaddr=127.0.0.1 pkhash=" + pkhash}
+ if chirp:
+ params["channel"] = "11"
+ params["dpp_configurator_connectivity"] = "1"
+ relay = hostapd.add_ap(apdev[1], params)
+ check_dpp_capab(relay)
+
+ # Enroll Relay to the network
+ # TODO: Do this over TCP once direct Enrollee-over-TCP case is supported
+ if chirp:
+ id_h = relay.dpp_bootstrap_gen(chan="81/11", mac=True)
+ else:
+ id_h = relay.dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri_r = relay.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+ dev[1].dpp_auth_init(uri=uri_r, conf="ap-dpp", configurator=conf_id)
+ wait_auth_success(relay, dev[1], configurator=dev[1], enrollee=relay)
+ update_hapd_config(relay)
+
+ # Initiate from Enrollee with broadcast DPP Authentication Request or
+ # using chirping
+ dev[0].set("dpp_config_processing", "2")
+ if chirp:
+ id1 = dev[0].dpp_bootstrap_gen()
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+ idc = dev[1].dpp_qr_code(uri)
+ dev[1].dpp_bootstrap_set(idc, conf="sta-dpp", configurator=conf_id)
+ if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=5" % id1):
+ raise Exception("DPP_CHIRP failed")
+ ev = relay.wait_event(["DPP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Presence Announcement not seen")
+ if "type=13" not in ev:
+ raise Exception("Unexpected DPP frame received: " + ev)
+ else:
+ dev[0].dpp_auth_init(uri=uri_c, role="enrollee")
+ wait_auth_success(dev[1], dev[0], configurator=dev[1], enrollee=dev[0],
+ allow_enrollee_failure=True,
+ allow_configurator_failure=True)
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("DPP network id not reported")
+ network = int(ev.split(' ')[1])
+ dev[0].wait_connected()
+ dev[0].dump_monitor()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ if "OK" not in dev[0].request("DPP_RECONFIG %s" % network):
+ raise Exception("Failed to start reconfiguration")
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=15)
+ if ev is None:
+ raise Exception("DPP network id not reported for reconfiguration")
+ network2 = int(ev.split(' ')[1])
+ if network == network2:
+ raise Exception("Network ID did not change")
+ dev[0].wait_connected()
+
+ time.sleep(0.5)
+ wt.close()
+
+def test_dpp_tcp(dev, apdev, params):
+ """DPP over TCP"""
+ prefix = "dpp_tcp"
+ cap_lo = os.path.join(params['logdir'], prefix + ".lo.pcap")
+ try:
+ run_dpp_tcp(dev[0], dev[1], cap_lo)
+ finally:
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def test_dpp_tcp_port(dev, apdev, params):
+ """DPP over TCP and specified port"""
+ prefix = "dpp_tcp_port"
+ cap_lo = os.path.join(params['logdir'], prefix + ".lo.pcap")
+ try:
+ run_dpp_tcp(dev[0], dev[1], cap_lo, port="23456")
+ finally:
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def test_dpp_tcp_mutual(dev, apdev, params):
+ """DPP over TCP (mutual)"""
+ cap_lo = os.path.join(params['prefix'], ".lo.pcap")
+ try:
+ run_dpp_tcp(dev[0], dev[1], cap_lo, mutual=True)
+ finally:
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def test_dpp_tcp_mutual_hostapd_conf(dev, apdev, params):
+ """DPP over TCP (mutual, hostapd as Configurator)"""
+ cap_lo = os.path.join(params['prefix'], ".lo.pcap")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ run_dpp_tcp(dev[0], hapd, cap_lo, mutual=True)
+
+def run_dpp_tcp(dev0, dev1, cap_lo, port=None, mutual=False):
+ check_dpp_capab(dev0)
+ check_dpp_capab(dev1)
+
+ wt = WlantestCapture('lo', cap_lo)
+ time.sleep(1)
+
+ # Controller
+ conf_id = dev1.dpp_configurator_add()
+ dev1.set("dpp_configurator_params",
+ " conf=sta-dpp configurator=%d" % conf_id)
+ id_c = dev1.dpp_bootstrap_gen()
+ uri_c = dev1.request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ res = dev1.request("DPP_BOOTSTRAP_INFO %d" % id_c)
+ pkhash = None
+ for line in res.splitlines():
+ name, value = line.split('=')
+ if name == "pkhash":
+ pkhash = value
+ break
+ if not pkhash:
+ raise Exception("Could not fetch public key hash from Controller")
+ req = "DPP_CONTROLLER_START"
+ if port:
+ req += " tcp_port=" + port
+ if mutual:
+ req += " qr=mutual"
+ id0 = dev0.dpp_bootstrap_gen()
+ uri0 = dev0.request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ own = id0
+ else:
+ own = None
+ if "OK" not in dev1.request(req):
+ raise Exception("Failed to start Controller")
+
+ # Initiate from Enrollee with broadcast DPP Authentication Request
+ dev0.dpp_auth_init(uri=uri_c, own=own, role="enrollee",
+ tcp_addr="127.0.0.1", tcp_port=port)
+
+ if mutual:
+ ev = dev0.wait_event(["DPP-RESPONSE-PENDING"], timeout=5)
+ if ev is None:
+ raise Exception("Pending response not reported")
+ ev = dev1.wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=5)
+ if ev is None:
+ raise Exception("QR Code scan for mutual authentication not requested")
+
+ id1 = dev1.dpp_qr_code(uri0)
+
+ ev = dev0.wait_event(["DPP-AUTH-DIRECTION"], timeout=5)
+ if ev is None:
+ raise Exception("DPP authentication direction not indicated (Initiator)")
+ if "mutual=1" not in ev:
+ raise Exception("Mutual authentication not used")
+
+ wait_auth_success(dev1, dev0, configurator=dev1, enrollee=dev0,
+ allow_enrollee_failure=True,
+ allow_configurator_failure=True)
+ time.sleep(0.5)
+ wt.close()
+
+def test_dpp_tcp_conf_init(dev, apdev, params):
+ """DPP over TCP (Configurator initiates)"""
+ cap_lo = os.path.join(params['prefix'], ".lo.pcap")
+ try:
+ run_dpp_tcp_conf_init(dev[0], dev[1], cap_lo)
+ finally:
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def test_dpp_tcp_conf_init_hostapd_enrollee(dev, apdev, params):
+ """DPP over TCP (Configurator initiates, hostapd as Enrollee)"""
+ cap_lo = os.path.join(params['prefix'], ".lo.pcap")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ run_dpp_tcp_conf_init(dev[0], hapd, cap_lo, conf="ap-dpp")
+
+def run_dpp_tcp_conf_init(dev0, dev1, cap_lo, port=None, conf="sta-dpp"):
+ check_dpp_capab(dev0, min_ver=2)
+ check_dpp_capab(dev1, min_ver=2)
+
+ wt = WlantestCapture('lo', cap_lo)
+ time.sleep(1)
+
+ id_c = dev1.dpp_bootstrap_gen()
+ uri_c = dev1.request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ res = dev1.request("DPP_BOOTSTRAP_INFO %d" % id_c)
+ req = "DPP_CONTROLLER_START role=enrollee"
+ if port:
+ req += " tcp_port=" + port
+ if "OK" not in dev1.request(req):
+ raise Exception("Failed to start Controller")
+
+ conf_id = dev0.dpp_configurator_add()
+ dev0.dpp_auth_init(uri=uri_c, role="configurator", conf=conf,
+ configurator=conf_id,
+ tcp_addr="127.0.0.1", tcp_port=port)
+ wait_auth_success(dev1, dev0, configurator=dev0, enrollee=dev1,
+ allow_enrollee_failure=True,
+ allow_configurator_failure=True)
+ time.sleep(0.5)
+ wt.close()
+
+def test_dpp_tcp_controller_management_hostapd(dev, apdev, params):
+ """DPP Controller management in hostapd"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+ conf_id = hapd.dpp_configurator_add()
+ if "OK" not in hapd.request("DPP_CONTROLLER_START"):
+ raise Exception("Failed to start Controller")
+ if "FAIL" not in hapd.request("DPP_CONTROLLER_START"):
+ raise Exception("DPP_CONTROLLER_START succeeded while already running Controller")
+ hapd.request("DPP_CONTROLLER_STOP")
+ hapd.dpp_configurator_remove(conf_id)
+ if "FAIL" not in hapd.request("DPP_CONFIGURATOR_REMOVE %d" % conf_id):
+ raise Exception("Removal of unknown Configurator accepted")
+
+def test_dpp_tcp_controller_start_failure(dev, apdev, params):
+ """DPP Controller startup failure"""
+ check_dpp_capab(dev[0])
+
+ try:
+ if "OK" not in dev[0].request("DPP_CONTROLLER_START"):
+ raise Exception("Could not start Controller")
+ if "OK" in dev[0].request("DPP_CONTROLLER_START"):
+ raise Exception("Second Controller start not rejected")
+ finally:
+ dev[0].request("DPP_CONTROLLER_STOP")
+
+ tests = ["dpp_controller_start",
+ "eloop_sock_table_add_sock;?eloop_register_sock;dpp_controller_start"]
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ if "FAIL" not in dev[0].request("DPP_CONTROLLER_START"):
+ raise Exception("Failure not reported during OOM")
+
+def test_dpp_tcp_init_failure(dev, apdev, params):
+ """DPP TCP init failure"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ id_c = dev[1].dpp_bootstrap_gen()
+ uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ peer = dev[0].dpp_qr_code(uri_c)
+ tests = ["dpp_tcp_init",
+ "eloop_sock_table_add_sock;?eloop_register_sock;dpp_tcp_init",
+ "dpp_tcp_encaps"]
+ cmd = "DPP_AUTH_INIT peer=%d tcp_addr=127.0.0.1" % peer
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ if "FAIL" not in dev[0].request(cmd):
+ raise Exception("DPP_AUTH_INIT accepted during OOM")
+
+def test_dpp_controller_rx_failure(dev, apdev, params):
+ """DPP Controller RX failure"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ try:
+ run_dpp_controller_rx_failure(dev, apdev)
+ finally:
+ dev[0].request("DPP_CONTROLLER_STOP")
+
+def run_dpp_controller_rx_failure(dev, apdev):
+ if "OK" not in dev[0].request("DPP_CONTROLLER_START"):
+ raise Exception("Could not start Controller")
+ id_c = dev[0].dpp_bootstrap_gen()
+ uri_c = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ peer = dev[1].dpp_qr_code(uri_c)
+ tests = ["dpp_controller_tcp_cb",
+ "eloop_sock_table_add_sock;?eloop_register_sock;dpp_controller_tcp_cb",
+ "dpp_controller_rx",
+ "dpp_controller_rx_auth_req",
+ "wpabuf_alloc;=dpp_tcp_send_msg;dpp_controller_rx_auth_req"]
+ cmd = "DPP_AUTH_INIT peer=%d tcp_addr=127.0.0.1" % peer
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to initiate TCP connection")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+def test_dpp_controller_rx_errors(dev, apdev, params):
+ """DPP Controller RX error cases"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ try:
+ run_dpp_controller_rx_errors(dev, apdev)
+ finally:
+ dev[0].request("DPP_CONTROLLER_STOP")
+
+def run_dpp_controller_rx_errors(dev, apdev):
+ if "OK" not in dev[0].request("DPP_CONTROLLER_START"):
+ raise Exception("Could not start Controller")
+
+ addr = ("127.0.0.1", 8908)
+
+ tests = [b"abc",
+ b"abcd",
+ b"\x00\x00\x00\x00",
+ b"\x00\x00\x00\x01",
+ b"\x00\x00\x00\x01\x09",
+ b"\x00\x00\x00\x07\x09\x50\x6f\x9a\x1a\xff\xff",
+ b"\x00\x00\x00\x07\x09\x50\x6f\x9a\x1a\x01\xff",
+ b"\x00\x00\x00\x07\x09\x50\x6f\x9a\x1a\x01\x00",
+ b"\x00\x00\x00\x08\x09\x50\x6f\x9a\x1a\x01\x00\xff",
+ b"\x00\x00\x00\x01\x0a",
+ b"\x00\x00\x00\x04\x0a\xff\xff\xff",
+ b"\x00\x00\x00\x01\x0b",
+ b"\x00\x00\x00\x08\x0b\xff\xff\xff\xff\xff\xff\xff",
+ b"\x00\x00\x00\x08\x0b\xff\x00\x00\xff\xff\xff\xff",
+ b"\x00\x00\x00\x08\x0b\xff\x00\x00\xff\xff\x6c\x00",
+ b"\x00\x00\x00\x0a\x0b\xff\x00\x00\xff\xff\x6c\x02\xff\xff",
+ b"\x00\x00\x00\x10\x0b\xff\x00\x00\xff\xff\x6c\x08\xff\xdd\x05\x50\x6f\x9a\x1a\x01",
+ b"\x00\x00\x00\x12\x0b\xff\x00\x00\xff\xff\x6c\x08\xff\xdd\x05\x50\x6f\x9a\x1a\x01\x00\x00",
+ b"\x00\x00\x00\x01\xff",
+ b"\x00\x00\x00\x01\xff\xee"]
+ #define WLAN_PA_GAS_INITIAL_REQ 10
+ #define WLAN_PA_GAS_INITIAL_RESP 11
+
+ for t in tests:
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+ socket.IPPROTO_TCP)
+ sock.settimeout(0.1)
+ sock.connect(addr)
+ sock.send(t)
+ sock.shutdown(1)
+ try:
+ sock.recv(10)
+ except socket.timeout:
+ pass
+ sock.close()
+
+def test_dpp_conn_status_success(dev, apdev):
+ """DPP connection status - success"""
+ try:
+ run_dpp_conn_status(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_conn_status_wrong_passphrase(dev, apdev):
+ """DPP connection status - wrong passphrase"""
+ try:
+ run_dpp_conn_status(dev, apdev, result=2)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_conn_status_no_ap(dev, apdev):
+ """DPP connection status - no AP"""
+ try:
+ run_dpp_conn_status(dev, apdev, result=10)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_conn_status_connector_mismatch(dev, apdev):
+ """DPP connection status - invalid Connector"""
+ try:
+ run_dpp_conn_status(dev, apdev, result=8)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_conn_status_assoc_reject(dev, apdev):
+ """DPP connection status - association rejection"""
+ try:
+ dev[0].request("TEST_ASSOC_IE 30020000")
+ run_dpp_conn_status(dev, apdev, assoc_reject=True)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_conn_status(dev, apdev, result=0, assoc_reject=False):
+ check_dpp_capab(dev[0], min_ver=2)
+ check_dpp_capab(dev[1], min_ver=2)
+
+ if result != 10:
+ if result == 7 or result == 8:
+ params = {"ssid": "dpp-status",
+ "wpa": "2",
+ "wpa_key_mgmt": "DPP",
+ "ieee80211w": "2",
+ "rsn_pairwise": "CCMP",
+ "dpp_connector": params1_ap_connector,
+ "dpp_csign": params1_csign,
+ "dpp_netaccesskey": params1_ap_netaccesskey}
+ else:
+ if result == 2:
+ passphrase = "wrong passphrase"
+ else:
+ passphrase = "secret passphrase"
+ params = hostapd.wpa2_params(ssid="dpp-status",
+ passphrase=passphrase)
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except:
+ raise HwsimSkip("DPP not supported")
+
+ dev[0].request("SET sae_groups ")
+ dev[0].set("dpp_config_processing", "2")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ dev[0].dpp_listen(2412)
+ if result == 7 or result == 8:
+ conf = 'sta-dpp'
+ passphrase = None
+ configurator = dev[1].dpp_configurator_add()
+ else:
+ conf = 'sta-psk'
+ passphrase = "secret passphrase"
+ configurator = None
+ dev[1].dpp_auth_init(uri=uri0, conf=conf, ssid="dpp-status",
+ passphrase=passphrase, configurator=configurator,
+ conn_status=True)
+ res = wait_auth_success(dev[0], dev[1], configurator=dev[1],
+ enrollee=dev[0])
+ if 'wait_conn_status' not in res:
+ raise Exception("Configurator did not request connection status")
+
+ if assoc_reject and result == 0:
+ result = 2
+ ev = dev[1].wait_event(["DPP-CONN-STATUS-RESULT"], timeout=20)
+ if ev is None:
+ raise Exception("No connection status reported")
+ if "timeout" in ev:
+ raise Exception("Connection status result timeout")
+ if "result=%d" % result not in ev:
+ raise Exception("Unexpected connection status result: " + ev)
+ if "ssid=dpp-status" not in ev:
+ raise Exception("SSID not reported")
+
+ if result == 0:
+ dev[0].wait_connected()
+ if result == 10 and "channel_list=" not in ev:
+ raise Exception("Channel list not reported for no-AP")
+
+def test_dpp_conn_status_success_hostapd_configurator(dev, apdev):
+ """DPP connection status - success with hostapd as Configurator"""
+ try:
+ run_dpp_conn_status_hostapd_configurator(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_conn_status_hostapd_configurator(dev, apdev):
+ check_dpp_capab(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "1"})
+ check_dpp_capab(hapd)
+ conf_id = hapd.dpp_configurator_add()
+
+ cmd = "DPP_CONFIGURATOR_SIGN conf=ap-dpp configurator=%d" % conf_id
+ res = hapd.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to generate own configuration")
+ update_hapd_config(hapd)
+
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ id1 = hapd.dpp_qr_code(uri0)
+ res = hapd.request("DPP_BOOTSTRAP_INFO %d" % id1)
+ if "FAIL" in res:
+ raise Exception("DPP_BOOTSTRAP_INFO failed")
+ if "type=QRCODE" not in res:
+ raise Exception("DPP_BOOTSTRAP_INFO did not report correct type")
+ if "mac_addr=" + dev[0].own_addr() not in res:
+ raise Exception("DPP_BOOTSTRAP_INFO did not report correct mac_addr")
+ dev[0].set("dpp_config_processing", "2")
+ dev[0].dpp_listen(2412)
+ hapd.dpp_auth_init(peer=id1, configurator=conf_id, conf="sta-dpp",
+ conn_status=True)
+ res = wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0])
+ if 'wait_conn_status' not in res:
+ raise Exception("Configurator did not request connection status")
+ ev = hapd.wait_event(["DPP-CONN-STATUS-RESULT"], timeout=20)
+ if ev is None:
+ raise Exception("No connection status reported")
+ if "result=0" not in ev:
+ raise Exception("Unexpected connection status: " + ev)
+
+def test_dpp_mud_url(dev, apdev):
+ """DPP MUD URL"""
+ check_dpp_capab(dev[0])
+ try:
+ dev[0].set("dpp_name", "Test Enrollee")
+ dev[0].set("dpp_mud_url", "https://example.com/mud")
+ run_dpp_qr_code_auth_unicast(dev, apdev, None)
+ finally:
+ dev[0].set("dpp_mud_url", "")
+ dev[0].set("dpp_name", "Test")
+
+def test_dpp_mud_url_hostapd(dev, apdev):
+ """DPP MUD URL from hostapd"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ params = {"ssid": "unconfigured",
+ "dpp_name": "AP Enrollee",
+ "dpp_mud_url": "https://example.com/mud"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_dpp_capab(hapd)
+
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+
+ conf_id = dev[0].dpp_configurator_add()
+ dev[0].dpp_auth_init(uri=uri, conf="ap-dpp", configurator=conf_id)
+ wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd)
+ update_hapd_config(hapd)
+
+def test_dpp_config_save(dev, apdev, params):
+ """DPP configuration saving"""
+ config = os.path.join(params['logdir'], 'dpp_config_save.conf')
+ run_dpp_config_save(dev, apdev, config, "test", '"test"')
+
+def test_dpp_config_save2(dev, apdev, params):
+ """DPP configuration saving (2)"""
+ config = os.path.join(params['logdir'], 'dpp_config_save2.conf')
+ run_dpp_config_save(dev, apdev, config, "\\u0001*", '012a')
+
+def test_dpp_config_save3(dev, apdev, params):
+ """DPP configuration saving (3)"""
+ config = os.path.join(params['logdir'], 'dpp_config_save3.conf')
+ run_dpp_config_save(dev, apdev, config, "\\u0001*\\u00c2\\u00bc\\u00c3\\u009e\\u00c3\\u00bf", '012ac2bcc39ec3bf')
+
+def run_dpp_config_save(dev, apdev, config, conf_ssid, exp_ssid):
+ check_dpp_capab(dev[1])
+ with open(config, "w") as f:
+ f.write("update_config=1\n" +
+ "dpp_config_processing=1\n")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", config=config)
+ check_dpp_capab(wpas)
+ conf = '{"wi-fi_tech":"infra", "discovery":{"ssid":"' + conf_ssid + '"},"cred":{"akm":"psk","pass":"secret passphrase"}}'
+ dev[1].set("dpp_config_obj_override", conf)
+ dpp_dev = [wpas, dev[1]]
+ run_dpp_qr_code_auth_unicast(dpp_dev, apdev, "prime256v1",
+ require_conf_success=True)
+ if "OK" not in wpas.request("SAVE_CONFIG"):
+ raise Exception("Failed to save configuration file")
+ with open(config, "r") as f:
+ data = f.read()
+ logger.info("Saved configuration:\n" + data)
+ if 'ssid=' + exp_ssid + '\n' not in data:
+ raise Exception("SSID not saved")
+ if 'psk="secret passphrase"' not in data:
+ raise Exception("Passphtase not saved")
+
+def test_dpp_nfc_uri(dev, apdev):
+ """DPP bootstrapping via NFC URI record"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ id = dev[0].dpp_bootstrap_gen(type="nfc-uri", chan="81/1", mac=True)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ logger.info("Generated URI: " + uri)
+ info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id)
+ logger.info("Bootstrapping info:\n" + info)
+ if "type=NFC-URI" not in info:
+ raise Exception("Unexpected bootstrapping info contents")
+
+ dev[0].dpp_listen(2412)
+ conf_id = dev[1].dpp_configurator_add()
+ dev[1].dpp_auth_init(nfc_uri=uri, configurator=conf_id, conf="sta-dpp")
+ wait_auth_success(dev[0], dev[1], configurator=dev[1], enrollee=dev[0])
+
+def test_dpp_nfc_uri_hostapd(dev, apdev):
+ """DPP bootstrapping via NFC URI record (hostapd)"""
+ check_dpp_capab(dev[0])
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+
+ id = hapd.dpp_bootstrap_gen(type="nfc-uri", chan="81/1", mac=True)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ logger.info("Generated URI: " + uri)
+ info = hapd.request("DPP_BOOTSTRAP_INFO %d" % id)
+ logger.info("Bootstrapping info:\n" + info)
+ if "type=NFC-URI" not in info:
+ raise Exception("Unexpected bootstrapping info contents")
+
+ hapd.dpp_listen(2412)
+ conf_id = dev[0].dpp_configurator_add()
+ dev[0].dpp_auth_init(nfc_uri=uri, configurator=conf_id, conf="ap-dpp")
+ wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd)
+
+def test_dpp_nfc_uri_hostapd_tag_read(dev, apdev):
+ """DPP bootstrapping via NFC URI record (hostapd reading tag)"""
+ check_dpp_capab(dev[0])
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+
+ id = dev[0].dpp_bootstrap_gen(type="nfc-uri", chan="81/1", mac=True)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ info = dev[0].request("DPP_BOOTSTRAP_INFO %d" % id)
+ conf_id = dev[0].dpp_configurator_add()
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ dev[0].dpp_listen(2412)
+
+ hapd.dpp_auth_init(nfc_uri=uri, role="enrollee")
+ wait_auth_success(dev[0], hapd, configurator=dev[0], enrollee=hapd)
+
+def test_dpp_nfc_negotiated_handover(dev, apdev):
+ """DPP bootstrapping via NFC negotiated handover"""
+ run_dpp_nfc_negotiated_handover(dev)
+
+def test_dpp_nfc_negotiated_handover_diff_curve(dev, apdev):
+ """DPP bootstrapping via NFC negotiated handover (different curve)"""
+ run_dpp_nfc_negotiated_handover(dev, curve0="prime256v1",
+ curve1="secp384r1")
+
+def test_dpp_nfc_negotiated_handover_hostapd_sel(dev, apdev):
+ """DPP bootstrapping via NFC negotiated handover (hostapd as selector)"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ run_dpp_nfc_negotiated_handover([dev[0], hapd], conf="ap-dpp")
+
+def test_dpp_nfc_negotiated_handover_hostapd_req(dev, apdev):
+ """DPP bootstrapping via NFC negotiated handover (hostapd as requestor)"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+ run_dpp_nfc_negotiated_handover([hapd, dev[0]])
+
+def run_dpp_nfc_negotiated_handover(dev, curve0=None, curve1=None,
+ conf="sta-dpp"):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ id0 = dev[0].dpp_bootstrap_gen(type="nfc-uri", chan="81/6,11", mac=True,
+ curve=curve0)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ logger.info("Generated URI[0]: " + uri0)
+ id1 = dev[1].dpp_bootstrap_gen(type="nfc-uri", chan="81/1,6,11", mac=True,
+ curve=curve1)
+ uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+ logger.info("Generated URI[1]: " + uri1)
+
+ # dev[0] acting as NFC Handover Requestor
+ # dev[1] acting as NFC Handover Selector
+ res = dev[1].request("DPP_NFC_HANDOVER_REQ own=%d uri=%s" % (id1, uri0))
+ if "FAIL" in res:
+ raise Exception("Failed to process NFC Handover Request")
+ info = dev[1].request("DPP_BOOTSTRAP_INFO %d" % id1)
+ logger.info("Updated local bootstrapping info:\n" + info)
+ freq = None
+ for line in info.splitlines():
+ if line.startswith("use_freq="):
+ freq = int(line.split('=')[1])
+ if freq is None:
+ raise Exception("Selected channel not indicated")
+ uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+ logger.info("Updated URI[1]: " + uri1)
+ dev[1].dpp_listen(freq)
+ res = dev[0].request("DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (id0, uri1))
+ if "FAIL" in res:
+ raise Exception("Failed to process NFC Handover Select")
+ peer = int(res)
+
+ conf_id = dev[0].dpp_configurator_add()
+ dev[0].dpp_auth_init(peer=peer, own=id0, configurator=conf_id,
+ conf=conf)
+ wait_auth_success(dev[1], dev[0], configurator=dev[0], enrollee=dev[1])
+
+def test_dpp_nfc_errors_hostapd(dev, apdev):
+ """DPP NFC operation failures in hostapd"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ id0 = dev[0].dpp_bootstrap_gen(type="nfc-uri", chan="81/11", mac=True,
+ curve="secp384r1")
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "channel": "6"})
+ check_dpp_capab(hapd)
+
+ id_h = hapd.dpp_bootstrap_gen(type="nfc-uri", chan="81/6", mac=True)
+ uri_h = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+
+ tests = ["",
+ "own=123456789",
+ "own=%d" % id_h,
+ "own=%d uri=%s" % (id_h, "foo")]
+ for t in tests:
+ if "FAIL" not in hapd.request("DPP_NFC_HANDOVER_REQ " + t):
+ raise Exception("Invalid DPP_NFC_HANDOVER_REQ accepted")
+ if "FAIL" not in hapd.request("DPP_NFC_HANDOVER_SEL " + t):
+ raise Exception("Invalid DPP_NFC_HANDOVER_SEL accepted")
+
+ # DPP: Peer (NFC Handover Selector) used different curve
+ if "FAIL" not in hapd.request("DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (id_h, uri0)):
+ raise Exception("Invalid DPP_NFC_HANDOVER_SEL accepted")
+
+ # DPP: No common channel found
+ if "FAIL" not in hapd.request("DPP_NFC_HANDOVER_REQ own=%d uri=%s" % (id_h, uri0)):
+ raise Exception("DPP_NFC_HANDOVER_REQ with local error accepted")
+
+def test_dpp_with_p2p_device(dev, apdev):
+ """DPP exchange when driver uses a separate P2P Device interface"""
+ check_dpp_capab(dev[0])
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ check_dpp_capab(wpas)
+ id1 = wpas.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri1 = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+ wpas.dpp_listen(2412)
+ time.sleep(7)
+ dev[0].dpp_auth_init(uri=uri1)
+ wait_auth_success(wpas, dev[0], configurator=dev[0], enrollee=wpas,
+ allow_enrollee_failure=True)
+
+@long_duration_test
+def test_dpp_chirp(dev, apdev):
+ """DPP chirp"""
+ check_dpp_capab(dev[0])
+ dev[0].flush_scan_cache()
+
+ params = {"ssid": "dpp",
+ "channel": "11"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_dpp_capab(hapd)
+ dpp_cc = False
+
+ id1 = dev[0].dpp_bootstrap_gen(chan="81/1")
+ if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=5" % id1):
+ raise Exception("DPP_CHIRP failed")
+ chan1 = 0
+ chan6 = 0
+ chan11 = 0
+ for i in range(30):
+ ev = dev[0].wait_event(["DPP-CHIRP-STOPPED",
+ "DPP-TX "], timeout=60)
+ if ev is None:
+ raise Exception("DPP chirp stop not reported")
+ if "DPP-CHIRP-STOPPED" in ev:
+ break
+ if "type=13" not in ev:
+ continue
+ freq = int(ev.split(' ')[2].split('=')[1])
+ if freq == 2412:
+ chan1 += 1
+ elif freq == 2437:
+ chan6 += 1
+ elif freq == 2462:
+ chan11 += 1
+ if not dpp_cc:
+ hapd.set("dpp_configurator_connectivity", "1")
+ if "OK" not in hapd.request("UPDATE_BEACON"):
+ raise Exception("UPDATE_BEACON failed")
+ dpp_cc = True
+ if chan1 != 5 or chan6 != 5 or chan11 != 1:
+ raise Exception("Unexpected number of presence announcements sent: %d %d %d" % (chan1, chan6, chan11))
+ ev = hapd.wait_event(["DPP-CHIRP-RX"], timeout=1)
+ if ev is None:
+ raise Exception("No chirp received on the AP")
+ if "freq=2462" not in ev:
+ raise Exception("Chirp reception reported on unexpected channel: " + ev)
+ if "src=" + dev[0].own_addr() not in ev:
+ raise Exception("Unexpected chirp source reported: " + ev)
+
+@long_duration_test
+def test_dpp_chirp_listen(dev, apdev):
+ """DPP chirp with listen"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ id1 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=2 listen=2412" % id1):
+ raise Exception("DPP_CHIRP failed")
+ for i in range(30):
+ ev = dev[0].wait_event(["DPP-CHIRP-STOPPED",
+ "DPP-TX "], timeout=60)
+ if ev is None:
+ raise Exception("DPP chirp stop not reported")
+ if "DPP-CHIRP-STOPPED" in ev:
+ break
+
+def test_dpp_chirp_configurator(dev, apdev):
+ """DPP chirp with a standalone Configurator"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ id1 = dev[0].dpp_bootstrap_gen(chan="81/1")
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ conf_id = dev[1].dpp_configurator_add()
+ idc = dev[1].dpp_qr_code(uri)
+ dev[1].dpp_bootstrap_set(idc, conf="sta-dpp", configurator=conf_id)
+ dev[1].dpp_listen(2437)
+
+ if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=2" % id1):
+ raise Exception("DPP_CHIRP failed")
+
+ ev = dev[1].wait_event(["DPP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Presence Announcement not seen")
+ if "type=13" not in ev:
+ raise Exception("Unexpected DPP frame received: " + ev)
+
+ ev = dev[1].wait_event(["DPP-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Authentication Request TX not seen")
+ if "type=0" not in ev:
+ raise Exception("Unexpected DPP frame TX: " + ev)
+ if "dst=" + dev[0].own_addr() not in ev:
+ raise Exception("Unexpected Authentication Request destination: " + ev)
+
+ wait_auth_success(dev[0], dev[1], dev[1], dev[0])
+
+def test_dpp_chirp_ap_as_configurator(dev, apdev):
+ """DPP chirp with an AP as a standalone Configurator"""
+ check_dpp_capab(dev[0], min_ver=2)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd, min_ver=2)
+
+ id1 = dev[0].dpp_bootstrap_gen(chan="81/1")
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ conf_id = hapd.dpp_configurator_add()
+ idc = hapd.dpp_qr_code(uri)
+ hapd.dpp_bootstrap_set(idc, conf="sta-dpp", configurator=conf_id)
+ hapd.dpp_listen(2412)
+
+ if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=2" % id1):
+ raise Exception("DPP_CHIRP failed")
+
+ wait_auth_success(dev[0], hapd, hapd, dev[0])
+
+def test_dpp_chirp_configurator_inits(dev, apdev):
+ """DPP chirp with a standalone Configurator initiating"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ id1 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ conf_id = dev[1].dpp_configurator_add()
+ idc = dev[1].dpp_qr_code(uri)
+
+ if "OK" not in dev[0].request("DPP_CHIRP own=%d iter=2 listen=2412" % id1):
+ raise Exception("DPP_CHIRP failed")
+ for i in range(2):
+ ev = dev[0].wait_event(["DPP-TX "], timeout=10)
+ if ev is None or "type=13" not in ev:
+ raise Exception("Presence Announcement not sent")
+
+ dev[1].dpp_auth_init(uri=uri, conf="sta-dpp", configurator=conf_id)
+ wait_auth_success(dev[0], dev[1], dev[1], dev[0])
+
+def test_dpp_chirp_ap(dev, apdev):
+ """DPP chirp by an AP"""
+ check_dpp_capab(dev[0], min_ver=2)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "start_disabled": "1"})
+ check_dpp_capab(hapd, min_ver=2)
+
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+
+ conf_id = dev[0].dpp_configurator_add()
+ idc = dev[0].dpp_qr_code(uri)
+ dev[0].dpp_bootstrap_set(idc, conf="ap-dpp", configurator=conf_id)
+ dev[0].dpp_listen(2437)
+ if "OK" not in hapd.request("DPP_CHIRP own=%d iter=5" % id_h):
+ raise Exception("DPP_CHIRP failed")
+ wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd,
+ timeout=20)
+ update_hapd_config(hapd)
+
+@long_duration_test
+def test_dpp_chirp_ap_5g(dev, apdev):
+ """DPP chirp by an AP on 5 GHz"""
+ check_dpp_capab(dev[0], min_ver=2)
+
+ try:
+ hapd = None
+ hapd2 = None
+
+ params = {"ssid": "unconfigured",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "40",
+ "dpp_configurator_connectivity": "1"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ check_dpp_capab(hapd2, min_ver=2)
+
+ params = {"ssid": "unconfigured",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "start_disabled": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_dpp_capab(hapd, min_ver=2)
+
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+
+ # First, check chirping iteration and timeout
+ if "OK" not in hapd.request("DPP_CHIRP own=%d iter=2" % id_h):
+ raise Exception("DPP_CHIRP failed")
+ chan1 = 0
+ chan6 = 0
+ chan40 = 0
+ chan149 = 0
+ for i in range(30):
+ ev = hapd.wait_event(["DPP-CHIRP-STOPPED", "DPP-TX "], timeout=60)
+ if ev is None:
+ raise Exception("DPP chirp stop not reported")
+ if "DPP-CHIRP-STOPPED" in ev:
+ break
+ if "type=13" not in ev:
+ continue
+ freq = int(ev.split(' ')[2].split('=')[1])
+ if freq == 2412:
+ chan1 += 1
+ elif freq == 2437:
+ chan6 += 1
+ elif freq == 5200:
+ chan40 += 1
+ elif freq == 5745:
+ chan149 += 1
+ if not chan1 or not chan6 or not chan40 or not chan149:
+ raise Exception("Chirp not sent on all channels")
+
+ # Then, check successful chirping
+ conf_id = dev[0].dpp_configurator_add()
+ idc = dev[0].dpp_qr_code(uri)
+ dev[0].dpp_bootstrap_set(idc, conf="ap-dpp", configurator=conf_id)
+ dev[0].dpp_listen(5200)
+ if "OK" not in hapd.request("DPP_CHIRP own=%d iter=5" % id_h):
+ raise Exception("DPP_CHIRP failed")
+ wait_auth_success(hapd, dev[0], configurator=dev[0], enrollee=hapd,
+ timeout=20)
+ update_hapd_config(hapd)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_dpp_chirp_ap_errors(dev, apdev):
+ """DPP chirp errors in hostapd"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured",
+ "start_disabled": "1"})
+ check_dpp_capab(hapd, min_ver=2)
+
+ id_h = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id_h)
+ tests = ["",
+ "own=%d" % (id_h + 1),
+ "own=%d iter=-1" % id_h,
+ "own=%d listen=0" % id_h]
+ for t in tests:
+ if "FAIL" not in hapd.request("DPP_CHIRP " + t):
+ raise Exception("Invalid DPP_CHIRP accepted: " + t)
+ if "OK" not in hapd.request("DPP_CHIRP own=%d iter=5" % id_h):
+ raise Exception("DPP_CHIRP failed")
+
+ hapd.request("DPP_STOP_CHIRP")
+
+def start_dpp_pfs_ap(apdev, pfs, sae=False):
+ params = {"ssid": "dpp",
+ "wpa": "2",
+ "wpa_key_mgmt": "DPP",
+ "dpp_pfs": str(pfs),
+ "ieee80211w": "2",
+ "rsn_pairwise": "CCMP",
+ "dpp_connector": params1_ap_connector,
+ "dpp_csign": params1_csign,
+ "dpp_netaccesskey": params1_ap_netaccesskey}
+ if sae:
+ params["wpa_key_mgmt"] = "DPP SAE"
+ params["sae_password"] = "sae-password"
+ try:
+ hapd = hostapd.add_ap(apdev, params)
+ except:
+ raise HwsimSkip("DPP not supported")
+ return hapd
+
+def run_dpp_pfs_sta(dev, pfs, fail=False, pfs_expected=None, sae=False):
+ key_mgmt = "DPP SAE" if sae else "DPP"
+ psk = "sae-password" if sae else None
+ dev.connect("dpp", key_mgmt=key_mgmt, scan_freq="2412",
+ ieee80211w="2", dpp_pfs=str(pfs),
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey,
+ psk=psk,
+ wait_connect=not fail)
+ if fail:
+ for i in range(2):
+ ev = dev.wait_event(["CTRL-EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection result not reported")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ dev.request("REMOVE_NETWORK all")
+ else:
+ if pfs_expected is not None:
+ res = dev.get_status_field("dpp_pfs")
+ pfs_used = res == "1"
+ if pfs_expected != pfs_used:
+ raise Exception("Unexpected PFS negotiation result")
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_dpp_pfs_ap_0(dev, apdev):
+ """DPP PFS AP default"""
+ check_dpp_capab(dev[0])
+ hapd = start_dpp_pfs_ap(apdev[0], 0)
+ run_dpp_pfs_sta(dev[0], 0, pfs_expected=True)
+ run_dpp_pfs_sta(dev[0], 1, pfs_expected=True)
+ run_dpp_pfs_sta(dev[0], 2, pfs_expected=False)
+
+def test_dpp_pfs_ap_1(dev, apdev):
+ """DPP PFS AP required"""
+ check_dpp_capab(dev[0])
+ hapd = start_dpp_pfs_ap(apdev[0], 1)
+ run_dpp_pfs_sta(dev[0], 0, pfs_expected=True)
+ run_dpp_pfs_sta(dev[0], 1, pfs_expected=True)
+ run_dpp_pfs_sta(dev[0], 2, fail=True)
+
+def test_dpp_pfs_ap_2(dev, apdev):
+ """DPP PFS AP not allowed"""
+ check_dpp_capab(dev[0])
+ hapd = start_dpp_pfs_ap(apdev[0], 2)
+ run_dpp_pfs_sta(dev[0], 0, pfs_expected=False)
+ run_dpp_pfs_sta(dev[0], 1, fail=True)
+ run_dpp_pfs_sta(dev[0], 2, pfs_expected=False)
+
+def test_dpp_pfs_connect_cmd(dev, apdev):
+ """DPP PFS and cfg80211 connect command"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ check_dpp_capab(wpas)
+ hapd = start_dpp_pfs_ap(apdev[0], 0)
+ run_dpp_pfs_sta(wpas, 0, pfs_expected=True)
+ run_dpp_pfs_sta(wpas, 1, pfs_expected=True)
+ run_dpp_pfs_sta(wpas, 2, pfs_expected=False)
+
+def test_dpp_pfs_connect_cmd_ap_2(dev, apdev):
+ """DPP PFS and cfg80211 connect command (PFS not allowed by AP)"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ check_dpp_capab(wpas)
+ hapd = start_dpp_pfs_ap(apdev[0], 2)
+ run_dpp_pfs_sta(wpas, 0, pfs_expected=False)
+ run_dpp_pfs_sta(wpas, 1, fail=True)
+ run_dpp_pfs_sta(wpas, 2, pfs_expected=False)
+
+def test_dpp_pfs_connect_cmd_ap_2_sae(dev, apdev):
+ """DPP PFS and cfg80211 connect command (PFS not allowed by AP; SAE enabled)"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ check_dpp_capab(wpas)
+ if "SAE" not in wpas.get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ hapd = start_dpp_pfs_ap(apdev[0], 2, sae=True)
+ run_dpp_pfs_sta(wpas, 0, pfs_expected=False, sae=True)
+ run_dpp_pfs_sta(wpas, 1, fail=True, sae=True)
+ run_dpp_pfs_sta(wpas, 2, pfs_expected=False, sae=True)
+
+def test_dpp_pfs_ap_0_sta_ver1(dev, apdev):
+ """DPP PFS AP default with version 1 STA"""
+ check_dpp_capab(dev[0])
+ dev[0].set("dpp_version_override", "1")
+ hapd = start_dpp_pfs_ap(apdev[0], 0)
+ run_dpp_pfs_sta(dev[0], 0, pfs_expected=False)
+
+def test_dpp_pfs_errors(dev, apdev):
+ """DPP PFS error cases"""
+ check_dpp_capab(dev[0], min_ver=2)
+ hapd = start_dpp_pfs_ap(apdev[0], 1)
+ tests = [(1, "dpp_pfs_init"),
+ (1, "crypto_ecdh_init;dpp_pfs_init"),
+ (1, "wpabuf_alloc;dpp_pfs_init")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("dpp", key_mgmt="DPP", scan_freq="2412",
+ ieee80211w="2", dpp_pfs="1",
+ dpp_csign=params1_csign,
+ dpp_connector=params1_sta_connector,
+ dpp_netaccesskey=params1_sta_netaccesskey)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+
+def test_dpp_reconfig_connector(dev, apdev):
+ """DPP reconfiguration connector"""
+ try:
+ run_dpp_reconfig_connector(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_dpp_reconfig_connector_different_groups(dev, apdev):
+ """DPP reconfiguration connector with different groups"""
+ try:
+ run_dpp_reconfig_connector(dev, apdev, conf_curve="secp384r1")
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+@long_duration_test
+def test_dpp_reconfig_retries(dev, apdev):
+ """DPP reconfiguration retries"""
+ try:
+ run_dpp_reconfig_connector(dev, apdev, test_retries=True)
+ for i in range(4):
+ ev = dev[0].wait_event(["DPP-TX "], timeout=120)
+ if ev is None or "type=14" not in ev:
+ raise Exception("Reconfig Announcement not sent")
+ dev[0].request("DPP_STOP_LISTEN")
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_reconfig_connector(dev, apdev, conf_curve=None,
+ test_retries=False):
+ check_dpp_capab(dev[0], min_ver=2)
+ check_dpp_capab(dev[1], min_ver=2)
+
+ ssid = "reconfig"
+ passphrase = "secret passphrase"
+ passphrase2 = "another secret passphrase"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].set("dpp_config_processing", "2")
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_listen(2412)
+ configurator = dev[1].dpp_configurator_add(curve=conf_curve)
+ conf = 'sta-psk'
+ dev[1].dpp_auth_init(uri=uri0, conf=conf, ssid=ssid,
+ passphrase=passphrase, configurator=configurator,
+ conn_status=True)
+ res = wait_auth_success(dev[0], dev[1], configurator=dev[1],
+ enrollee=dev[0])
+ if 'wait_conn_status' not in res:
+ raise Exception("Configurator did not request connection status")
+ ev = dev[1].wait_event(["DPP-CONN-STATUS-RESULT"], timeout=20)
+ if ev is None:
+ raise Exception("No connection status reported")
+ dev[1].dump_monitor()
+
+ ev = dev[0].wait_event(["DPP-CONFOBJ-SSID"], timeout=1)
+ if ev is None:
+ raise Exception("SSID not reported")
+ res_ssid = ev.split(' ')[1]
+ if res_ssid != ssid:
+ raise Exception("Unexpected SSID value")
+
+ ev = dev[0].wait_event(["DPP-CONNECTOR"], timeout=1)
+ if ev is None:
+ raise Exception("Connector not reported")
+ connector = ev.split(' ')[1]
+
+ ev = dev[0].wait_event(["DPP-C-SIGN-KEY"], timeout=1)
+ if ev is None:
+ raise Exception("C-sign-key not reported")
+ p = ev.split(' ')
+ csign = p[1]
+
+ ev = dev[0].wait_event(["DPP-NET-ACCESS-KEY"], timeout=1)
+ if ev is None:
+ raise Exception("netAccessKey not reported")
+ p = ev.split(' ')
+ net_access_key = p[1]
+ net_access_key_expiry = p[2] if len(p) > 2 else None
+
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("DPP network profile not generated")
+ id = ev.split(' ')[1]
+
+ dev[0].wait_connected()
+
+ n_key_mgmt = dev[0].get_network(id, "key_mgmt")
+ if n_key_mgmt != "WPA-PSK FT-PSK WPA-PSK-SHA256":
+ raise Exception("Unexpected key_mgmt: " + n_key_mgmt)
+ n_connector = dev[0].get_network(id, "dpp_connector")
+ if n_connector.strip('"') != connector:
+ raise Exception("Connector mismatch: %s %s" % (n_connector, connector))
+ n_csign = dev[0].get_network(id, "dpp_csign")
+ if n_csign.strip('"') != csign:
+ raise Exception("csign mismatch: %s %s" % (n_csign, csign))
+ n_net_access_key = dev[0].get_network(id, "dpp_netaccesskey")
+ if n_net_access_key.strip('"') != net_access_key:
+ raise Exception("net_access_key mismatch: %s %s" % (n_net_access_key,
+ net_access_key))
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ hapd.disable()
+ hapd.set("wpa_passphrase", passphrase2)
+ hapd.enable()
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ if test_retries:
+ dev[1].request("DPP_STOP_LISTEN")
+ if "OK" not in dev[0].request("DPP_RECONFIG %s iter=10" % id):
+ raise Exception("Failed to start reconfiguration")
+ return
+
+ dev[1].set("dpp_configurator_params",
+ "conf=sta-psk ssid=%s pass=%s conn_status=1" % (binascii.hexlify(ssid.encode()).decode(), binascii.hexlify(passphrase2.encode()).decode()))
+ dev[1].dpp_listen(2437)
+
+ if "OK" not in dev[0].request("DPP_RECONFIG %s" % id):
+ raise Exception("Failed to start reconfiguration")
+ ev = dev[0].wait_event(["DPP-TX "], timeout=10)
+ if ev is None or "type=14" not in ev:
+ raise Exception("Reconfig Announcement not sent")
+
+ ev = dev[1].wait_event(["DPP-RX"], timeout=5)
+ if ev is None:
+ raise Exception("DPP Reconfig Announcement not received")
+ if "freq=2437 type=14" not in ev:
+ raise Exception("Unexpected RX data for Reconfig Announcement: " + ev)
+
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "freq=2437 type=15" not in ev:
+ raise Exception("DPP Reconfig Authentication Request not received")
+
+ ev = dev[1].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "freq=2437 type=16" not in ev:
+ raise Exception("DPP Reconfig Authentication Response not received")
+
+ ev = dev[0].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "freq=2437 type=17" not in ev:
+ raise Exception("DPP Reconfig Authentication Confirm not received")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None or "freq=2437" not in ev:
+ raise Exception("DPP Config Request (GAS) not transmitted")
+
+ ev = dev[1].wait_event(["DPP-CONF-REQ-RX"], timeout=5)
+ if ev is None:
+ raise Exception("DPP Config Request (GAS) not received")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+ if ev is None or "freq=2437" not in ev:
+ raise Exception("DPP Config Response (GAS) not received")
+
+ ev = dev[1].wait_event(["DPP-RX"], timeout=5)
+ if ev is None or "freq=2437 type=11" not in ev:
+ raise Exception("DPP Config Result not received")
+
+ ev = dev[1].wait_event(["DPP-CONF-SENT"], timeout=5)
+ if ev is None:
+ raise Exception("DPP Config Response (GAS) not transmitted")
+
+ ev = dev[0].wait_event(["DPP-CONF-RECEIVED", "DPP-CONF-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("DPP config response reception result not indicated")
+ if "DPP-CONF-RECEIVED" not in ev:
+ raise Exception("Reconfiguration failed")
+
+ dev[0].wait_connected()
+
+ ev = dev[1].wait_event(["DPP-CONN-STATUS-RESULT"], timeout=20)
+ if ev is None:
+ raise Exception("No connection status reported")
+
+def test_dpp_reconfig_hostapd_configurator(dev, apdev):
+ """DPP reconfiguration with hostapd as configurator"""
+ try:
+ run_dpp_reconfig_hostapd_configurator(dev, apdev)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_reconfig_hostapd_configurator(dev, apdev):
+ ssid = "reconfig-ap"
+ check_dpp_capab(dev[0], min_ver=2)
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd, min_ver=2)
+ conf_id = hapd.dpp_configurator_add()
+
+ cmd = "DPP_CONFIGURATOR_SIGN conf=ap-dpp configurator=%d ssid=%s" % (conf_id, binascii.hexlify(ssid.encode()).decode())
+ res = hapd.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to generate own configuration")
+ hapd.set("dpp_configurator_connectivity", "1")
+ update_hapd_config(hapd)
+
+ id = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ dev[0].set("dpp_config_processing", "2")
+ dev[0].dpp_listen(2412)
+ hapd.dpp_auth_init(uri=uri, conf="sta-dpp", configurator=conf_id,
+ extra="expiry=%d" % (time.time() + 10), ssid=ssid)
+ wait_auth_success(dev[0], hapd, configurator=hapd, enrollee=dev[0])
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("DPP network id not reported")
+ network = int(ev.split(' ')[1])
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ time.sleep(10)
+ if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["DPP-MISSING-CONNECTOR", "CTRL-EVENT-CONNECTED"],
+ timeout=15)
+ if ev is None or "DPP-MISSING-CONNECTOR" not in ev:
+ raise Exception("Missing Connector not reported")
+ if "netAccessKey expired" not in ev:
+ raise Exception("netAccessKey expiry not indicated")
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+
+ hapd.set("dpp_configurator_params",
+ "conf=sta-dpp configurator=%d ssid=%s" % (conf_id, binascii.hexlify(ssid.encode()).decode()))
+
+ if "OK" not in dev[0].request("DPP_RECONFIG %s" % network):
+ raise Exception("Failed to start reconfiguration")
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=15)
+ if ev is None:
+ raise Exception("DPP network id not reported for reconfiguration")
+ network2 = int(ev.split(' ')[1])
+ if network == network2:
+ raise Exception("Network ID did not change")
+ dev[0].wait_connected()
+
+def test_dpp_qr_code_auth_rand_mac_addr(dev, apdev):
+ """DPP QR Code and authentication exchange (rand_mac_addr=1)"""
+ flags = int(dev[0].get_driver_status_field('capa.flags'), 16)
+ if flags & 0x0000400000000000 == 0:
+ raise HwsimSkip("Driver does not support random GAS TA")
+
+ try:
+ dev[0].set("gas_rand_mac_addr", "1")
+ run_dpp_qr_code_auth_unicast(dev, apdev, None)
+ finally:
+ dev[0].set("gas_rand_mac_addr", "0")
+
+def dpp_sign_cert(cacert, cakey, csr_der):
+ csr = OpenSSL.crypto.load_certificate_request(OpenSSL.crypto.FILETYPE_ASN1,
+ csr_der)
+ cert = OpenSSL.crypto.X509()
+ cert.set_serial_number(12345)
+ cert.gmtime_adj_notBefore(-10)
+ cert.gmtime_adj_notAfter(100000)
+ cert.set_pubkey(csr.get_pubkey())
+ dn = csr.get_subject()
+ cert.set_subject(dn)
+ cert.set_version(2)
+ cert.add_extensions([
+ OpenSSL.crypto.X509Extension(b"basicConstraints", True,
+ b"CA:FALSE"),
+ OpenSSL.crypto.X509Extension(b"subjectKeyIdentifier", False,
+ b"hash", subject=cert),
+ OpenSSL.crypto.X509Extension(b"authorityKeyIdentifier", False,
+ b"keyid:always", issuer=cacert),
+ ])
+ cert.set_issuer(cacert.get_subject())
+ cert.sign(cakey, "sha256")
+ return cert
+
+def test_dpp_enterprise(dev, apdev, params):
+ """DPP and enterprise EAP-TLS provisioning"""
+ check_dpp_capab(dev[0], min_ver=2)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+ run_dpp_enterprise(dev, apdev, params)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_dpp_enterprise(dev, apdev, params):
+ if not openssl_imported:
+ raise HwsimSkip("OpenSSL python method not available")
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ cert_file = params['prefix'] + ".cert.pem"
+ pkcs7_file = params['prefix'] + ".pkcs7.der"
+
+ params = {"ssid": "dpp-ent",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP",
+ "ieee8021x": "1",
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ec-ca.pem",
+ "server_cert": "auth_serv/ec-server.pem",
+ "private_key": "auth_serv/ec-server.key"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with open("auth_serv/ec-ca.pem", "rb") as f:
+ res = f.read()
+ cacert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
+ res)
+
+ with open("auth_serv/ec-ca.key", "rb") as f:
+ res = f.read()
+ cakey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, res)
+
+ conf_id = dev[1].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_listen(2412)
+ csrattrs = "MAsGCSqGSIb3DQEJBw=="
+ id1 = dev[1].dpp_auth_init(uri=uri0, configurator=conf_id, conf="sta-dot1x",
+ csrattrs=csrattrs, ssid="dpp-ent")
+
+ ev = dev[1].wait_event(["DPP-CSR"], timeout=10)
+ if ev is None:
+ raise Exception("Configurator did not receive CSR")
+ id1_csr = int(ev.split(' ')[1].split('=')[1])
+ if id1 != id1_csr:
+ raise Exception("Peer bootstrapping ID mismatch in CSR event")
+ csr = ev.split(' ')[2]
+ if not csr.startswith("csr="):
+ raise Exception("Could not parse CSR event: " + ev)
+ csr = csr[4:]
+ csr = base64.b64decode(csr.encode())
+ logger.info("CSR: " + binascii.hexlify(csr).decode())
+
+ cert = dpp_sign_cert(cacert, cakey, csr)
+ with open(cert_file, 'wb') as f:
+ f.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
+ cert))
+ subprocess.check_call(['openssl', 'crl2pkcs7', '-nocrl',
+ '-certfile', cert_file,
+ '-certfile', 'auth_serv/ec-ca.pem',
+ '-outform', 'DER', '-out', pkcs7_file])
+
+ #caCert = base64.b64encode(b"TODO").decode()
+ #res = dev[1].request("DPP_CA_SET peer=%d name=caCert value=%s" % (id1, caCert))
+ #if "OK" not in res:
+ # raise Exception("Failed to set caCert")
+
+ name = "server.w1.fi"
+ res = dev[1].request("DPP_CA_SET peer=%d name=trustedEapServerName value=%s" % (id1, name))
+ if "OK" not in res:
+ raise Exception("Failed to set trustedEapServerName")
+
+ with open(pkcs7_file, 'rb') as f:
+ pkcs7_der = f.read()
+ certbag = base64.b64encode(pkcs7_der).decode()
+ res = dev[1].request("DPP_CA_SET peer=%d name=certBag value=%s" % (id1, certbag))
+ if "OK" not in res:
+ raise Exception("Failed to set certBag")
+
+ ev = dev[1].wait_event(["DPP-CONF-SENT", "DPP-CONF-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Configurator)")
+ if "DPP-CONF-FAILED" in ev:
+ raise Exception("DPP configuration did not succeed (Configurator)")
+
+ ev = dev[0].wait_event(["DPP-CONF-RECEIVED", "DPP-CONF-FAILED"],
+ timeout=1)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Enrollee)")
+ if "DPP-CONF-FAILED" in ev:
+ raise Exception("DPP configuration did not succeed (Enrollee)")
+
+ ev = dev[0].wait_event(["DPP-CERTBAG"], timeout=1)
+ if ev is None:
+ raise Exception("DPP-CERTBAG not reported")
+ certbag = base64.b64decode(ev.split(' ')[1].encode())
+ if certbag != pkcs7_der:
+ raise Exception("DPP-CERTBAG mismatch")
+
+ #ev = dev[0].wait_event(["DPP-CACERT"], timeout=1)
+ #if ev is None:
+ # raise Exception("DPP-CACERT not reported")
+
+ ev = dev[0].wait_event(["DPP-SERVER-NAME"], timeout=1)
+ if ev is None:
+ raise Exception("DPP-SERVER-NAME not reported")
+ if ev.split(' ')[1] != name:
+ raise Exception("DPP-SERVER-NAME mismatch: " + ev)
+
+ ev = dev[0].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("DPP network profile not generated")
+ id = ev.split(' ')[1]
+
+ dev[0].wait_connected()
+
+def test_dpp_enterprise_reject(dev, apdev, params):
+ """DPP and enterprise EAP-TLS provisioning and CSR getting rejected"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ conf_id = dev[1].dpp_configurator_add()
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].dpp_listen(2412)
+ csrattrs = "MAsGCSqGSIb3DQEJBw=="
+ id1 = dev[1].dpp_auth_init(uri=uri0, configurator=conf_id, conf="sta-dot1x",
+ csrattrs=csrattrs, ssid="dpp-ent")
+
+ ev = dev[1].wait_event(["DPP-CSR"], timeout=10)
+ if ev is None:
+ raise Exception("Configurator did not receive CSR")
+
+ res = dev[1].request("DPP_CA_SET peer=%d name=status value=5" % id1)
+ if "OK" not in res:
+ raise Exception("Failed to set status")
+
+ ev = dev[1].wait_event(["DPP-CONF-SENT", "DPP-CONF-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Configurator)")
+ if "DPP-CONF-FAILED" in ev:
+ raise Exception("DPP configuration did not succeed (Configurator)")
+
+ ev = dev[0].wait_event(["DPP-CONF-RECEIVED", "DPP-CONF-FAILED"],
+ timeout=1)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Enrollee)")
+ if "DPP-CONF-FAILED" not in ev:
+ raise Exception("DPP configuration did not fail (Enrollee)")
+
+def test_dpp_enterprise_tcp(dev, apdev, params):
+ """DPP over TCP for enterprise provisioning"""
+ if not openssl_imported:
+ raise HwsimSkip("OpenSSL python method not available")
+
+ try:
+ run_dpp_enterprise_tcp(dev, apdev, params)
+ finally:
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def run_dpp_enterprise_tcp(dev, apdev, params):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ cap_lo = params['prefix'] + ".lo.pcap"
+
+ wt = WlantestCapture('lo', cap_lo)
+ time.sleep(1)
+
+ # Controller
+ conf_id = dev[1].dpp_configurator_add()
+ csrattrs = "MAsGCSqGSIb3DQEJBw=="
+ dev[1].set("dpp_configurator_params",
+ "conf=sta-dot1x configurator=%d csrattrs=%s" % (conf_id, csrattrs))
+ id_c = dev[1].dpp_bootstrap_gen()
+ uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ res = dev[1].request("DPP_BOOTSTRAP_INFO %d" % id_c)
+ req = "DPP_CONTROLLER_START"
+ if "OK" not in dev[1].request(req):
+ raise Exception("Failed to start Controller")
+
+ dev[0].dpp_auth_init(uri=uri_c, role="enrollee", tcp_addr="127.0.0.1")
+ run_dpp_enterprise_tcp_end(params, dev, wt)
+
+def run_dpp_enterprise_tcp_end(params, dev, wt):
+ cert_file = params['prefix'] + ".cert.pem"
+ pkcs7_file = params['prefix'] + ".pkcs7.der"
+
+ with open("auth_serv/ec-ca.pem", "rb") as f:
+ res = f.read()
+ cacert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
+ res)
+
+ with open("auth_serv/ec-ca.key", "rb") as f:
+ res = f.read()
+ cakey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, res)
+
+ ev = dev[1].wait_event(["DPP-CSR"], timeout=10)
+ if ev is None:
+ raise Exception("Configurator did not receive CSR")
+ id1_csr = int(ev.split(' ')[1].split('=')[1])
+ csr = ev.split(' ')[2]
+ if not csr.startswith("csr="):
+ raise Exception("Could not parse CSR event: " + ev)
+ csr = csr[4:]
+ csr = base64.b64decode(csr.encode())
+ logger.info("CSR: " + binascii.hexlify(csr).decode())
+
+ cert = dpp_sign_cert(cacert, cakey, csr)
+ with open(cert_file, 'wb') as f:
+ f.write(OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM,
+ cert))
+ subprocess.check_call(['openssl', 'crl2pkcs7', '-nocrl',
+ '-certfile', cert_file,
+ '-certfile', 'auth_serv/ec-ca.pem',
+ '-outform', 'DER', '-out', pkcs7_file])
+
+ with open(pkcs7_file, 'rb') as f:
+ pkcs7_der = f.read()
+ certbag = base64.b64encode(pkcs7_der).decode()
+ res = dev[1].request("DPP_CA_SET peer=%d name=certBag value=%s" % (id1_csr, certbag))
+ if "OK" not in res:
+ raise Exception("Failed to set certBag")
+
+ ev = dev[1].wait_event(["DPP-CONF-SENT", "DPP-CONF-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Configurator)")
+ if "DPP-CONF-FAILED" in ev:
+ raise Exception("DPP configuration did not succeed (Configurator)")
+
+ ev = dev[0].wait_event(["DPP-CONF-RECEIVED", "DPP-CONF-FAILED"],
+ timeout=1)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Enrollee)")
+ if "DPP-CONF-RECEIVED" not in ev:
+ raise Exception("DPP configuration did not succeed (Enrollee)")
+
+ time.sleep(0.5)
+ wt.close()
+
+def test_dpp_enterprise_tcp2(dev, apdev, params):
+ """DPP over TCP for enterprise provisioning (Controller initiating)"""
+ if not openssl_imported:
+ raise HwsimSkip("OpenSSL python method not available")
+
+ try:
+ run_dpp_enterprise_tcp2(dev, apdev, params)
+ finally:
+ dev[0].request("DPP_CONTROLLER_STOP")
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def run_dpp_enterprise_tcp2(dev, apdev, params):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ cap_lo = params['prefix'] + ".lo.pcap"
+ cert_file = params['prefix'] + ".cert.pem"
+ pkcs7_file = params['prefix'] + ".pkcs7.der"
+
+ with open("auth_serv/ec-ca.pem", "rb") as f:
+ res = f.read()
+ cacert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM,
+ res)
+
+ with open("auth_serv/ec-ca.key", "rb") as f:
+ res = f.read()
+ cakey = OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, res)
+
+ wt = WlantestCapture('lo', cap_lo)
+ time.sleep(1)
+
+ # Client/Enrollee/Responder
+ id_e = dev[0].dpp_bootstrap_gen()
+ uri_e = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id_e)
+ req = "DPP_CONTROLLER_START"
+ if "OK" not in dev[0].request(req):
+ raise Exception("Failed to start Client/Enrollee")
+
+ # Controller/Configurator/Initiator
+ conf_id = dev[1].dpp_configurator_add()
+ csrattrs = "MAsGCSqGSIb3DQEJBw=="
+ dev[1].dpp_auth_init(uri=uri_e, role="configurator", configurator=conf_id,
+ conf="sta-dot1x", csrattrs=csrattrs,
+ tcp_addr="127.0.0.1")
+
+ run_dpp_enterprise_tcp_end(params, dev, wt)
diff --git a/contrib/wpa/tests/hwsim/test_eap.py b/contrib/wpa/tests/hwsim/test_eap.py
new file mode 100644
index 000000000000..144e4d314070
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_eap.py
@@ -0,0 +1,602 @@
+# EAP authentication tests
+# Copyright (c) 2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hostapd
+
+from utils import alloc_fail, fail_test, wait_fail_trigger, HwsimSkip
+from test_ap_eap import check_eap_capa, int_eap_server_params, eap_connect, \
+ eap_reauth
+
+def int_teap_server_params(eap_teap_auth=None, eap_teap_pac_no_inner=None,
+ eap_teap_separate_result=None, eap_teap_id=None):
+ params = int_eap_server_params()
+ params['pac_opaque_encr_key'] = "000102030405060708090a0b0c0dff00"
+ params['eap_fast_a_id'] = "101112131415161718191a1b1c1dff00"
+ params['eap_fast_a_id_info'] = "test server 0"
+ if eap_teap_auth:
+ params['eap_teap_auth'] = eap_teap_auth
+ if eap_teap_pac_no_inner:
+ params['eap_teap_pac_no_inner'] = eap_teap_pac_no_inner
+ if eap_teap_separate_result:
+ params['eap_teap_separate_result'] = eap_teap_separate_result
+ if eap_teap_id:
+ params['eap_teap_id'] = eap_teap_id
+ return params
+
+def test_eap_teap_eap_mschapv2(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac")
+ eap_reauth(dev[0], "TEAP")
+
+def test_eap_teap_eap_pwd(dev, apdev):
+ """EAP-TEAP with inner EAP-PWD"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "PWD")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user-pwd-2",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PWD",
+ pac_file="blob://teap_pac")
+
+def test_eap_teap_eap_eke(dev, apdev):
+ """EAP-TEAP with inner EAP-EKE"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "EKE")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user-eke-2",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=EKE",
+ pac_file="blob://teap_pac")
+
+def test_eap_teap_basic_password_auth(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac")
+
+def test_eap_teap_basic_password_auth_failure(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth failure"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="incorrect",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac", expect_failure=True)
+
+def test_eap_teap_basic_password_auth_no_password(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth and no password configured"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac", expect_failure=True)
+
+def test_eap_teap_basic_password_auth_id0(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth (eap_teap_id=0)"""
+ run_eap_teap_basic_password_auth_id(dev, apdev, 0)
+
+def test_eap_teap_basic_password_auth_id1(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth (eap_teap_id=1)"""
+ run_eap_teap_basic_password_auth_id(dev, apdev, 1)
+
+def test_eap_teap_basic_password_auth_id2(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth (eap_teap_id=2)"""
+ run_eap_teap_basic_password_auth_id(dev, apdev, 2, failure=True)
+
+def test_eap_teap_basic_password_auth_id3(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth (eap_teap_id=3)"""
+ run_eap_teap_basic_password_auth_id(dev, apdev, 3)
+
+def test_eap_teap_basic_password_auth_id4(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth (eap_teap_id=4)"""
+ run_eap_teap_basic_password_auth_id(dev, apdev, 4)
+
+def run_eap_teap_basic_password_auth_id(dev, apdev, eap_teap_id, failure=False):
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1",
+ eap_teap_id=str(eap_teap_id))
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac",
+ expect_failure=failure)
+
+def test_eap_teap_basic_password_auth_machine(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth using machine credential"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1", eap_teap_id="2")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "",
+ anonymous_identity="TEAP",
+ machine_identity="machine", machine_password="machine-password",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac")
+
+def test_eap_teap_basic_password_auth_user_and_machine(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth using user and machine credentials"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1", eap_teap_id="5")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user", password="password",
+ anonymous_identity="TEAP",
+ machine_identity="machine", machine_password="machine-password",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac")
+
+def test_eap_teap_basic_password_auth_user_and_machine_fail_user(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth using user and machine credentials (fail user)"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1", eap_teap_id="5")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user", password="wrong-password",
+ anonymous_identity="TEAP",
+ machine_identity="machine", machine_password="machine-password",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac",
+ expect_failure=True)
+
+def test_eap_teap_basic_password_auth_user_and_machine_fail_machine(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth using user and machine credentials (fail machine)"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1", eap_teap_id="5")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user", password="password",
+ anonymous_identity="TEAP",
+ machine_identity="machine",
+ machine_password="wrong-machine-password",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac",
+ expect_failure=True)
+
+def test_eap_teap_basic_password_auth_user_and_machine_no_machine(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth using user and machine credentials (no machine)"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1", eap_teap_id="5")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user", password="password",
+ anonymous_identity="TEAP",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac",
+ expect_failure=True)
+
+def test_eap_teap_peer_outer_tlvs(dev, apdev):
+ """EAP-TEAP with peer Outer TLVs"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac", phase1="teap_test_outer_tlvs=1")
+
+def test_eap_teap_eap_mschapv2_pac(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 and PAC provisioning"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ phase1="teap_provisioning=2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac")
+ res = eap_reauth(dev[0], "TEAP")
+ if res['tls_session_reused'] != '1':
+ raise Exception("EAP-TEAP could not use PAC session ticket")
+
+def test_eap_teap_eap_mschapv2_pac_no_inner_eap(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 and PAC without inner EAP"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_teap_server_params(eap_teap_pac_no_inner="1")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ phase1="teap_provisioning=2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac")
+ res = eap_reauth(dev[0], "TEAP")
+ if res['tls_session_reused'] != '1':
+ raise Exception("EAP-TEAP could not use PAC session ticket")
+
+def test_eap_teap_eap_mschapv2_separate_result(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 and separate message for Result TLV"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_teap_server_params(eap_teap_separate_result="1")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac")
+
+def test_eap_teap_eap_mschapv2_pac_no_ca_cert(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 and PAC provisioning attempt without ca_cert"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ phase1="teap_provisioning=2",
+ phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac")
+ res = eap_reauth(dev[0], "TEAP")
+ if res['tls_session_reused'] == '1':
+ raise Exception("Unexpected use of PAC session ticket")
+
+def test_eap_teap_eap_mschapv2_id0(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 (eap_teap_id=0)"""
+ run_eap_teap_eap_mschapv2_id(dev, apdev, 0)
+
+def test_eap_teap_eap_mschapv2_id1(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 (eap_teap_id=1)"""
+ run_eap_teap_eap_mschapv2_id(dev, apdev, 1)
+
+def test_eap_teap_eap_mschapv2_id2(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 (eap_teap_id=2)"""
+ run_eap_teap_eap_mschapv2_id(dev, apdev, 2, failure=True)
+
+def test_eap_teap_eap_mschapv2_id3(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 (eap_teap_id=3)"""
+ run_eap_teap_eap_mschapv2_id(dev, apdev, 3)
+
+def test_eap_teap_eap_mschapv2_id4(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 (eap_teap_id=4)"""
+ run_eap_teap_eap_mschapv2_id(dev, apdev, 4)
+
+def run_eap_teap_eap_mschapv2_id(dev, apdev, eap_teap_id, failure=False):
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_teap_server_params(eap_teap_id=str(eap_teap_id))
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac",
+ expect_failure=failure)
+
+def test_eap_teap_eap_mschapv2_machine(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 using machine credential"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_teap_server_params(eap_teap_id="2")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "",
+ anonymous_identity="TEAP",
+ machine_identity="machine", machine_password="machine-password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac")
+
+def test_eap_teap_eap_mschapv2_user_and_machine(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 using user and machine credentials"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_teap_server_params(eap_teap_id="5")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user", password="password",
+ anonymous_identity="TEAP",
+ machine_identity="machine", machine_password="machine-password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac")
+
+def test_eap_teap_eap_mschapv2_user_and_machine_fail_user(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 using user and machine credentials (fail user)"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_teap_server_params(eap_teap_id="5")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user", password="wrong-password",
+ anonymous_identity="TEAP",
+ machine_identity="machine", machine_password="machine-password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac",
+ expect_failure=True)
+
+def test_eap_teap_eap_mschapv2_user_and_machine_fail_machine(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 using user and machine credentials (fail machine)"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_teap_server_params(eap_teap_id="5")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user", password="password",
+ anonymous_identity="TEAP",
+ machine_identity="machine",
+ machine_password="wrong-machine-password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac",
+ expect_failure=True)
+
+def test_eap_teap_eap_mschapv2_user_and_machine_no_machine(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 using user and machine credentials (no machine)"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_teap_server_params(eap_teap_id="5")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user", password="password",
+ anonymous_identity="TEAP",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac",
+ expect_failure=True)
+
+def test_eap_teap_eap_mschapv2_user_and_eap_tls_machine(dev, apdev):
+ """EAP-TEAP with inner EAP-MSCHAPv2 user and EAP-TLS machine credentials"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ check_eap_capa(dev[0], "TLS")
+ params = int_teap_server_params(eap_teap_id="5")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user", password="password",
+ anonymous_identity="TEAP",
+ machine_identity="cert user",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ machine_phase2="auth=TLS",
+ machine_ca_cert="auth_serv/ca.pem",
+ machine_client_cert="auth_serv/user.pem",
+ machine_private_key="auth_serv/user.key",
+ pac_file="blob://teap_pac")
+
+def test_eap_teap_basic_password_auth_pac(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth and PAC"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ phase1="teap_provisioning=2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac")
+ res = eap_reauth(dev[0], "TEAP")
+ if res['tls_session_reused'] != '1':
+ raise Exception("EAP-TEAP could not use PAC session ticket")
+
+def test_eap_teap_basic_password_auth_pac_binary(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth and PAC (binary)"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ phase1="teap_provisioning=2 teap_max_pac_list_len=2 teap_pac_format=binary",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac_bin")
+ res = eap_reauth(dev[0], "TEAP")
+ if res['tls_session_reused'] != '1':
+ raise Exception("EAP-TEAP could not use PAC session ticket")
+
+def test_eap_teap_basic_password_auth_pac_no_inner_eap(dev, apdev):
+ """EAP-TEAP with Basic-Password-Auth and PAC without inner auth"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="1",
+ eap_teap_pac_no_inner="1")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ phase1="teap_provisioning=2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac")
+ res = eap_reauth(dev[0], "TEAP")
+ if res['tls_session_reused'] != '1':
+ raise Exception("EAP-TEAP could not use PAC session ticket")
+
+def test_eap_teap_eap_eke_unauth_server_prov(dev, apdev):
+ """EAP-TEAP with inner EAP-EKE and unauthenticated server provisioning"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "EKE")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user-eke-2",
+ anonymous_identity="TEAP", password="password",
+ phase1="teap_provisioning=1",
+ phase2="auth=EKE", pac_file="blob://teap_pac")
+ res = eap_reauth(dev[0], "TEAP")
+ if res['tls_session_reused'] != '1':
+ raise Exception("EAP-TEAP could not use PAC session ticket")
+
+def test_eap_teap_fragmentation(dev, apdev):
+ """EAP-TEAP with fragmentation"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac", fragment_size="100")
+
+def test_eap_teap_tls_cs_sha1(dev, apdev):
+ """EAP-TEAP with TLS cipher suite that uses SHA-1"""
+ run_eap_teap_tls_cs(dev, apdev, "AES128-SHA")
+
+def test_eap_teap_tls_cs_sha256(dev, apdev):
+ """EAP-TEAP with TLS cipher suite that uses SHA-256"""
+ run_eap_teap_tls_cs(dev, apdev, "AES128-SHA256")
+
+def test_eap_teap_tls_cs_sha384(dev, apdev):
+ """EAP-TEAP with TLS cipher suite that uses SHA-384"""
+ run_eap_teap_tls_cs(dev, apdev, "AES256-GCM-SHA384")
+
+def run_eap_teap_tls_cs(dev, apdev, cipher):
+ check_eap_capa(dev[0], "TEAP")
+ tls = dev[0].request("GET tls_library")
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("TLS library not supported for TLS CS configuration: " + tls)
+ params = int_teap_server_params(eap_teap_auth="1")
+ params['openssl_ciphers'] = cipher
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac")
+
+def wait_eap_proposed(dev, wait_trigger=None):
+ ev = dev.wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ if wait_trigger:
+ wait_fail_trigger(dev, wait_trigger)
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_eap_teap_errors(dev, apdev):
+ """EAP-TEAP local errors"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="TEAP", identity="user", password="password",
+ anonymous_identity="TEAP",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ wait_connect=False)
+ wait_eap_proposed(dev[0])
+
+ dev[0].set("blob", "teap_broken_pac 11")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="TEAP", identity="user", password="password",
+ anonymous_identity="TEAP",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_broken_pac", wait_connect=False)
+ wait_eap_proposed(dev[0])
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="TEAP", identity="user", password="password",
+ anonymous_identity="TEAP",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="teap_pac_format=binary",
+ pac_file="blob://teap_broken_pac", wait_connect=False)
+ wait_eap_proposed(dev[0])
+
+ tests = [(1, "eap_teap_tlv_eap_payload"),
+ (1, "eap_teap_process_eap_payload_tlv"),
+ (1, "eap_teap_compound_mac"),
+ (1, "eap_teap_tlv_result"),
+ (1, "eap_peer_select_phase2_methods"),
+ (1, "eap_peer_tls_ssl_init"),
+ (1, "eap_teap_session_id"),
+ (1, "wpabuf_alloc;=eap_teap_process_crypto_binding"),
+ (1, "eap_peer_tls_encrypt"),
+ (1, "eap_peer_tls_decrypt"),
+ (1, "eap_teap_getKey"),
+ (1, "eap_teap_session_id"),
+ (1, "eap_teap_init")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="TEAP", identity="user", password="password",
+ anonymous_identity="TEAP",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac", wait_connect=False)
+ wait_eap_proposed(dev[0], wait_trigger="GET_ALLOC_FAIL")
+
+ tests = [(1, "eap_teap_derive_eap_msk"),
+ (1, "eap_teap_derive_eap_emsk"),
+ (1, "eap_teap_write_crypto_binding"),
+ (1, "eap_teap_process_crypto_binding"),
+ (1, "eap_teap_derive_msk;eap_teap_process_crypto_binding"),
+ (1, "eap_teap_compound_mac;eap_teap_process_crypto_binding"),
+ (1, "eap_teap_derive_imck")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="TEAP", identity="user", password="password",
+ anonymous_identity="TEAP",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac", wait_connect=False)
+ wait_eap_proposed(dev[0], wait_trigger="GET_FAIL")
+
+def test_eap_teap_errors2(dev, apdev):
+ """EAP-TEAP local errors 2 (Basic-Password-Auth specific)"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_teap_server_params(eap_teap_auth="1")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tests = [(1, "eap_teap_tlv_pac_ack"),
+ (1, "eap_teap_process_basic_auth_req")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="TEAP", identity="user", password="password",
+ anonymous_identity="TEAP",
+ phase1="teap_provisioning=2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac", wait_connect=False)
+ wait_eap_proposed(dev[0], wait_trigger="GET_ALLOC_FAIL")
+
+ tests = [(1, "eap_teap_derive_cmk_basic_pw_auth")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="TEAP", identity="user", password="password",
+ anonymous_identity="TEAP",
+ phase1="teap_provisioning=2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac", wait_connect=False)
+ wait_eap_proposed(dev[0], wait_trigger="GET_FAIL")
+
+def test_eap_teap_eap_vendor(dev, apdev):
+ """EAP-TEAP with inner EAP-vendor"""
+ check_eap_capa(dev[0], "TEAP")
+ check_eap_capa(dev[0], "VENDOR-TEST")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "TEAP", "vendor-test-2",
+ anonymous_identity="TEAP",
+ ca_cert="auth_serv/ca.pem", phase2="auth=VENDOR-TEST",
+ pac_file="blob://teap_pac")
+
+def test_eap_teap_client_cert(dev, apdev):
+ """EAP-TEAP with client certificate in Phase 1"""
+ check_eap_capa(dev[0], "TEAP")
+ params = int_teap_server_params(eap_teap_auth="2")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # verify server accept a client with certificate, but no Phase 2
+ # configuration
+ eap_connect(dev[0], hapd, "TEAP", "user",
+ anonymous_identity="TEAP",
+ phase1="teap_provisioning=2",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ ca_cert="auth_serv/ca.pem",
+ pac_file="blob://teap_pac")
+ dev[0].dump_monitor()
+ res = eap_reauth(dev[0], "TEAP")
+ if res['tls_session_reused'] != '1':
+ raise Exception("EAP-TEAP could not use PAC session ticket")
+
+ # verify server accepts a client without certificate
+ eap_connect(dev[1], hapd, "TEAP", "user",
+ anonymous_identity="TEAP", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ pac_file="blob://teap_pac")
diff --git a/contrib/wpa/tests/hwsim/test_eap_proto.py b/contrib/wpa/tests/hwsim/test_eap_proto.py
new file mode 100644
index 000000000000..afdc45d70ee2
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_eap_proto.py
@@ -0,0 +1,10377 @@
+# EAP protocol tests
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import hashlib
+import hmac
+import logging
+logger = logging.getLogger()
+import os
+import select
+import struct
+import threading
+import time
+
+import hostapd
+from utils import *
+from test_ap_eap import check_eap_capa, check_hlr_auc_gw_support, int_eap_server_params
+
+try:
+ import OpenSSL
+ openssl_imported = True
+except ImportError:
+ openssl_imported = False
+
+EAP_CODE_REQUEST = 1
+EAP_CODE_RESPONSE = 2
+EAP_CODE_SUCCESS = 3
+EAP_CODE_FAILURE = 4
+EAP_CODE_INITIATE = 5
+EAP_CODE_FINISH = 6
+
+EAP_TYPE_IDENTITY = 1
+EAP_TYPE_NOTIFICATION = 2
+EAP_TYPE_NAK = 3
+EAP_TYPE_MD5 = 4
+EAP_TYPE_OTP = 5
+EAP_TYPE_GTC = 6
+EAP_TYPE_TLS = 13
+EAP_TYPE_LEAP = 17
+EAP_TYPE_SIM = 18
+EAP_TYPE_TTLS = 21
+EAP_TYPE_AKA = 23
+EAP_TYPE_PEAP = 25
+EAP_TYPE_MSCHAPV2 = 26
+EAP_TYPE_TLV = 33
+EAP_TYPE_TNC = 38
+EAP_TYPE_FAST = 43
+EAP_TYPE_PAX = 46
+EAP_TYPE_PSK = 47
+EAP_TYPE_SAKE = 48
+EAP_TYPE_IKEV2 = 49
+EAP_TYPE_AKA_PRIME = 50
+EAP_TYPE_GPSK = 51
+EAP_TYPE_PWD = 52
+EAP_TYPE_EKE = 53
+EAP_TYPE_EXPANDED = 254
+
+# Type field in EAP-Initiate and EAP-Finish messages
+EAP_ERP_TYPE_REAUTH_START = 1
+EAP_ERP_TYPE_REAUTH = 2
+
+EAP_ERP_TLV_KEYNAME_NAI = 1
+EAP_ERP_TV_RRK_LIFETIME = 2
+EAP_ERP_TV_RMSK_LIFETIME = 3
+EAP_ERP_TLV_DOMAIN_NAME = 4
+EAP_ERP_TLV_CRYPTOSUITES = 5
+EAP_ERP_TLV_AUTHORIZATION_INDICATION = 6
+EAP_ERP_TLV_CALLED_STATION_ID = 128
+EAP_ERP_TLV_CALLING_STATION_ID = 129
+EAP_ERP_TLV_NAS_IDENTIFIER = 130
+EAP_ERP_TLV_NAS_IP_ADDRESS = 131
+EAP_ERP_TLV_NAS_IPV6_ADDRESS = 132
+
+def run_pyrad_server(srv, t_stop, eap_handler):
+ srv.RunWithStop(t_stop, eap_handler)
+
+def start_radius_server(eap_handler):
+ try:
+ import pyrad.server
+ import pyrad.packet
+ import pyrad.dictionary
+ except ImportError:
+ raise HwsimSkip("No pyrad modules available")
+
+ class TestServer(pyrad.server.Server):
+ def _HandleAuthPacket(self, pkt):
+ pyrad.server.Server._HandleAuthPacket(self, pkt)
+ eap = b''
+ for p in pkt[79]:
+ eap += p
+ eap_req = self.eap_handler(self.ctx, eap)
+ reply = self.CreateReplyPacket(pkt)
+ if eap_req:
+ while True:
+ if len(eap_req) > 253:
+ reply.AddAttribute("EAP-Message", eap_req[0:253])
+ eap_req = eap_req[253:]
+ else:
+ reply.AddAttribute("EAP-Message", eap_req)
+ break
+ else:
+ logger.info("No EAP request available")
+ reply.code = pyrad.packet.AccessChallenge
+
+ hmac_obj = hmac.new(reply.secret, digestmod=hashlib.md5)
+ hmac_obj.update(struct.pack("B", reply.code))
+ hmac_obj.update(struct.pack("B", reply.id))
+
+ # reply attributes
+ reply.AddAttribute("Message-Authenticator", 16*b'\x00')
+ attrs = reply._PktEncodeAttributes()
+
+ # Length
+ flen = 4 + 16 + len(attrs)
+ hmac_obj.update(struct.pack(">H", flen))
+ hmac_obj.update(pkt.authenticator)
+ hmac_obj.update(attrs)
+ del reply[80]
+ reply.AddAttribute("Message-Authenticator", hmac_obj.digest())
+
+ self.SendReplyPacket(pkt.fd, reply)
+
+ def RunWithStop(self, t_stop, eap_handler):
+ self._poll = select.poll()
+ self._fdmap = {}
+ self._PrepareSockets()
+ self.t_stop = t_stop
+ self.eap_handler = eap_handler
+ self.ctx = {}
+
+ while not t_stop.is_set():
+ for (fd, event) in self._poll.poll(200):
+ if event == select.POLLIN:
+ try:
+ fdo = self._fdmap[fd]
+ self._ProcessInput(fdo)
+ except pyrad.server.ServerPacketError as err:
+ logger.info("pyrad server dropping packet: " + str(err))
+ except pyrad.packet.PacketError as err:
+ logger.info("pyrad server received invalid packet: " + str(err))
+ else:
+ logger.error("Unexpected event in pyrad server main loop")
+
+ for fd in self.authfds + self.acctfds:
+ fd.close()
+
+ srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
+ authport=18138, acctport=18139)
+ srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
+ b"radius",
+ "localhost")
+ srv.BindToAddress("")
+ t_stop = threading.Event()
+ t = threading.Thread(target=run_pyrad_server, args=(srv, t_stop, eap_handler))
+ t.start()
+
+ return {'srv': srv, 'stop': t_stop, 'thread': t}
+
+def stop_radius_server(srv):
+ srv['stop'].set()
+ srv['thread'].join()
+
+def start_ap(ap):
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ params['auth_server_port'] = "18138"
+ hapd = hostapd.add_ap(ap, params)
+ return hapd
+
+def test_eap_proto(dev, apdev):
+ """EAP protocol tests"""
+ check_eap_capa(dev[0], "MD5")
+ def eap_handler(ctx, req):
+ logger.info("eap_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: MD5 challenge")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_MD5,
+ 1, 0xaa, ord('n'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Success - id off by 2")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] + 1, 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: MD5 challenge")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_MD5,
+ 1, 0xaa, ord('n'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Success - id off by 3")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] + 2, 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: MD5 challenge")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_MD5,
+ 1, 0xaa, ord('n'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Notification/Request")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_NOTIFICATION,
+ ord('A'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 1, 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Notification/Request")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_NOTIFICATION,
+ ord('B'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: MD5 challenge")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_MD5,
+ 1, 0xaa, ord('n'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 1, 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Notification/Request")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_NOTIFICATION,
+ ord('C'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: MD5 challenge")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_MD5,
+ 1, 0xaa, ord('n'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Notification/Request")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_NOTIFICATION,
+ ord('D'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 1, 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Notification/Request")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_NOTIFICATION,
+ ord('E'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Notification/Request (same id)")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'] - 1,
+ 4 + 1 + 1,
+ EAP_TYPE_NOTIFICATION,
+ ord('F'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected EAP-Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'] - 2, 4)
+
+ return None
+
+ srv = start_radius_server(eap_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP success")
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected EAP success")
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP notification")
+ if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION A":
+ raise Exception("Unexpected notification contents: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP success")
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP notification")
+ if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION B":
+ raise Exception("Unexpected notification contents: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP success")
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP notification")
+ if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION C":
+ raise Exception("Unexpected notification contents: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP notification")
+ if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION D":
+ raise Exception("Unexpected notification contents: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP success")
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP notification")
+ if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION E":
+ raise Exception("Unexpected notification contents: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-NOTIFICATION"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP notification")
+ if ev != "<3>CTRL-EVENT-EAP-NOTIFICATION F":
+ raise Exception("Unexpected notification contents: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ dev[0].request("REMOVE_NETWORK all")
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_notification_errors(dev, apdev):
+ """EAP Notification errors"""
+ def eap_handler(ctx, req):
+ logger.info("eap_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: MD5 challenge")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_MD5,
+ 1, 0xaa, ord('n'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Notification/Request")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_NOTIFICATION,
+ ord('A'))
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: MD5 challenge")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_MD5,
+ 1, 0xaa, ord('n'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Notification/Request")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_NOTIFICATION,
+ ord('A'))
+
+ return None
+
+ srv = start_radius_server(eap_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1, "eap_sm_processNotify"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_msg_alloc;sm_EAP_NOTIFICATION_Enter"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ finally:
+ stop_radius_server(srv)
+
+EAP_SAKE_VERSION = 2
+
+EAP_SAKE_SUBTYPE_CHALLENGE = 1
+EAP_SAKE_SUBTYPE_CONFIRM = 2
+EAP_SAKE_SUBTYPE_AUTH_REJECT = 3
+EAP_SAKE_SUBTYPE_IDENTITY = 4
+
+EAP_SAKE_AT_RAND_S = 1
+EAP_SAKE_AT_RAND_P = 2
+EAP_SAKE_AT_MIC_S = 3
+EAP_SAKE_AT_MIC_P = 4
+EAP_SAKE_AT_SERVERID = 5
+EAP_SAKE_AT_PEERID = 6
+EAP_SAKE_AT_SPI_S = 7
+EAP_SAKE_AT_SPI_P = 8
+EAP_SAKE_AT_ANY_ID_REQ = 9
+EAP_SAKE_AT_PERM_ID_REQ = 10
+EAP_SAKE_AT_ENCR_DATA = 128
+EAP_SAKE_AT_IV = 129
+EAP_SAKE_AT_PADDING = 130
+EAP_SAKE_AT_NEXT_TMPID = 131
+EAP_SAKE_AT_MSK_LIFE = 132
+
+def test_eap_proto_sake(dev, apdev):
+ """EAP-SAKE protocol tests"""
+ global eap_proto_sake_test_done
+ eap_proto_sake_test_done = False
+
+ def sake_challenge(ctx):
+ logger.info("Test: Challenge subtype")
+ return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 18,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE,
+ EAP_SAKE_AT_RAND_S, 18, 0, 0, 0, 0)
+
+ def sake_handler(ctx, req):
+ logger.info("sake_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] += 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'], 4 + 1,
+ EAP_TYPE_SAKE)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype without any attributes")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype")
+ return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
+ EAP_SAKE_AT_ANY_ID_REQ, 4, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype (different session id)")
+ return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 1, EAP_SAKE_SUBTYPE_IDENTITY,
+ EAP_SAKE_AT_PERM_ID_REQ, 4, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype with too short attribute")
+ return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 2,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
+ EAP_SAKE_AT_ANY_ID_REQ, 2)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype with truncated attribute")
+ return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 2,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
+ EAP_SAKE_AT_ANY_ID_REQ, 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype with too short attribute header")
+ payload = struct.pack("B", EAP_SAKE_AT_ANY_ID_REQ)
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + len(payload),
+ EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
+ EAP_SAKE_SUBTYPE_IDENTITY) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype with AT_IV but not AT_ENCR_DATA")
+ payload = struct.pack("BB", EAP_SAKE_AT_IV, 2)
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + len(payload),
+ EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
+ EAP_SAKE_SUBTYPE_IDENTITY) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype with skippable and non-skippable unknown attribute")
+ payload = struct.pack("BBBB", 255, 2, 127, 2)
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + len(payload),
+ EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
+ EAP_SAKE_SUBTYPE_IDENTITY) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype: AT_RAND_P with invalid payload length")
+ payload = struct.pack("BB", EAP_SAKE_AT_RAND_P, 2)
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + len(payload),
+ EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
+ EAP_SAKE_SUBTYPE_IDENTITY) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype: AT_MIC_P with invalid payload length")
+ payload = struct.pack("BB", EAP_SAKE_AT_MIC_P, 2)
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + len(payload),
+ EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
+ EAP_SAKE_SUBTYPE_IDENTITY) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype: AT_PERM_ID_REQ with invalid payload length")
+ payload = struct.pack("BBBBBBBBBBBBBB",
+ EAP_SAKE_AT_SPI_S, 2,
+ EAP_SAKE_AT_SPI_P, 2,
+ EAP_SAKE_AT_ENCR_DATA, 2,
+ EAP_SAKE_AT_NEXT_TMPID, 2,
+ EAP_SAKE_AT_PERM_ID_REQ, 4, 0, 0,
+ EAP_SAKE_AT_PERM_ID_REQ, 2)
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + len(payload),
+ EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
+ EAP_SAKE_SUBTYPE_IDENTITY) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype: AT_PADDING")
+ payload = struct.pack("BBBBBB",
+ EAP_SAKE_AT_PADDING, 3, 0,
+ EAP_SAKE_AT_PADDING, 3, 1)
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + len(payload),
+ EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
+ EAP_SAKE_SUBTYPE_IDENTITY) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype: AT_MSK_LIFE")
+ payload = struct.pack(">BBLBBH",
+ EAP_SAKE_AT_MSK_LIFE, 6, 0,
+ EAP_SAKE_AT_MSK_LIFE, 4, 0)
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + len(payload),
+ EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
+ EAP_SAKE_SUBTYPE_IDENTITY) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype with invalid attribute length")
+ payload = struct.pack("BB", EAP_SAKE_AT_ANY_ID_REQ, 0)
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + len(payload),
+ EAP_TYPE_SAKE, EAP_SAKE_VERSION, 0,
+ EAP_SAKE_SUBTYPE_IDENTITY) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unknown subtype")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, 123)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge subtype without any attributes")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge subtype with too short AT_RAND_S")
+ return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 2,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE,
+ EAP_SAKE_AT_RAND_S, 2)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return sake_challenge(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Identity subtype")
+ return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
+ EAP_SAKE_AT_ANY_ID_REQ, 4, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return sake_challenge(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Challenge subtype")
+ return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 18,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CHALLENGE,
+ EAP_SAKE_AT_RAND_S, 18, 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return sake_challenge(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Confirm subtype without any attributes")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return sake_challenge(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Confirm subtype with too short AT_MIC_S")
+ return struct.pack(">BBHBBBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 2,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM,
+ EAP_SAKE_AT_MIC_S, 2)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Confirm subtype")
+ return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 18,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM,
+ EAP_SAKE_AT_MIC_S, 18, 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return sake_challenge(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Confirm subtype with incorrect AT_MIC_S")
+ return struct.pack(">BBHBBBBBBLLLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 18,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_CONFIRM,
+ EAP_SAKE_AT_MIC_S, 18, 0, 0, 0, 0)
+
+ global eap_proto_sake_test_done
+ if eap_proto_sake_test_done:
+ return sake_challenge(ctx)
+
+ logger.info("No more test responses available - test case completed")
+ eap_proto_sake_test_done = True
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(sake_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ while not eap_proto_sake_test_done:
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SAKE", identity="sake user",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+
+ logger.info("Too short password")
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SAKE", identity="sake user",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcd",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_sake_errors(dev, apdev):
+ """EAP-SAKE local error cases"""
+ check_eap_capa(dev[0], "SAKE")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(1, 3):
+ with alloc_fail(dev[0], i, "eap_sake_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SAKE", identity="sake user",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ tests = [(1, "eap_msg_alloc;eap_sake_build_msg;eap_sake_process_challenge"),
+ (1, "=eap_sake_process_challenge"),
+ (1, "eap_sake_compute_mic;eap_sake_process_challenge"),
+ (1, "eap_sake_build_msg;eap_sake_process_confirm"),
+ (1, "eap_sake_compute_mic;eap_sake_process_confirm"),
+ (2, "eap_sake_compute_mic;=eap_sake_process_confirm"),
+ (1, "eap_sake_getKey"),
+ (1, "eap_sake_get_emsk"),
+ (1, "eap_sake_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SAKE", identity="sake user@domain",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ erp="1",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ tests = [(1, "os_get_random;eap_sake_process_challenge"),
+ (1, "eap_sake_derive_keys;eap_sake_process_challenge"),
+ (2, "eap_sake_derive_keys;eap_sake_process_challenge"),
+ (3, "eap_sake_derive_keys;eap_sake_process_challenge"),
+ (4, "eap_sake_derive_keys;eap_sake_process_challenge"),
+ (5, "eap_sake_derive_keys;eap_sake_process_challenge"),
+ (6, "eap_sake_derive_keys;eap_sake_process_challenge")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SAKE", identity="sake user",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def test_eap_proto_sake_errors2(dev, apdev):
+ """EAP-SAKE protocol tests (2)"""
+ def sake_handler(ctx, req):
+ logger.info("sake_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] += 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity subtype")
+ return struct.pack(">BBHBBBBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SAKE,
+ EAP_SAKE_VERSION, 0, EAP_SAKE_SUBTYPE_IDENTITY,
+ EAP_SAKE_AT_ANY_ID_REQ, 4, 0)
+
+ srv = start_radius_server(sake_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_sake_build_msg;eap_sake_process_identity"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SAKE", identity="sake user",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ finally:
+ stop_radius_server(srv)
+
+def run_eap_sake_connect(dev):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SAKE", identity="sake user",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-DISCONNECTED"],
+ timeout=1)
+ dev.request("REMOVE_NETWORK all")
+ if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_eap_proto_sake_errors_server(dev, apdev):
+ """EAP-SAKE local error cases on server"""
+ check_eap_capa(dev[0], "SAKE")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ tests = [(1, "eap_sake_init"),
+ (1, "eap_sake_build_msg;eap_sake_build_challenge"),
+ (1, "eap_sake_build_msg;eap_sake_build_confirm"),
+ (1, "eap_sake_compute_mic;eap_sake_build_confirm"),
+ (1, "eap_sake_process_challenge"),
+ (1, "eap_sake_getKey"),
+ (1, "eap_sake_get_emsk"),
+ (1, "eap_sake_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ run_eap_sake_connect(dev[0])
+
+ tests = [(1, "eap_sake_init"),
+ (1, "eap_sake_build_challenge"),
+ (1, "eap_sake_build_confirm"),
+ (1, "eap_sake_derive_keys;eap_sake_process_challenge"),
+ (1, "eap_sake_compute_mic;eap_sake_process_challenge"),
+ (1, "eap_sake_compute_mic;eap_sake_process_confirm"),
+ (1, "eap_sake_compute_mic;eap_sake_build_confirm"),
+ (1, "eap_sake_process_confirm")]
+ for count, func in tests:
+ with fail_test(hapd, count, func):
+ run_eap_sake_connect(dev[0])
+
+def start_sake_assoc(dev, hapd):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SAKE", identity="sake user",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ proxy_msg(hapd, dev) # EAP-Identity/Request
+ proxy_msg(dev, hapd) # EAP-Identity/Response
+ proxy_msg(hapd, dev) # SAKE/Challenge/Request
+
+def stop_sake_assoc(dev, hapd):
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+ hapd.dump_monitor()
+
+def test_eap_proto_sake_server(dev, apdev):
+ """EAP-SAKE protocol testing for the server"""
+ check_eap_capa(dev[0], "SAKE")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+
+ # Successful exchange to verify proxying mechanism
+ start_sake_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # SAKE/Challenge/Response
+ proxy_msg(hapd, dev[0]) # SAKE/Confirm/Request
+ proxy_msg(dev[0], hapd) # SAKE/Confirm/Response
+ proxy_msg(hapd, dev[0]) # EAP-Success
+ proxy_msg(hapd, dev[0]) # EAPOL-Key msg 1/4
+ proxy_msg(dev[0], hapd) # EAPOL-Key msg 2/4
+ proxy_msg(hapd, dev[0]) # EAPOL-Key msg 3/4
+ proxy_msg(dev[0], hapd) # EAPOL-Key msg 4/4
+ dev[0].wait_connected()
+ stop_sake_assoc(dev[0], hapd)
+
+ start_sake_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short EAP-SAKE header
+ # --> EAP-SAKE: Invalid frame
+ msg = resp[0:4] + "0007" + resp[8:12] + "0007" + "300200"
+ tx_msg(dev[0], hapd, msg)
+ # Unknown version
+ # --> EAP-SAKE: Unknown version 1
+ msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "30010000"
+ tx_msg(dev[0], hapd, msg)
+ # Unknown session
+ # --> EAP-SAKE: Session ID mismatch
+ sess, = struct.unpack('B', binascii.unhexlify(resp[20:22]))
+ sess = binascii.hexlify(struct.pack('B', sess + 1)).decode()
+ msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "3002" + sess + "00"
+ tx_msg(dev[0], hapd, msg)
+ # Unknown subtype
+ # --> EAP-SAKE: Unexpected subtype=5 in state=1
+ msg = resp[0:22] + "05" + resp[24:]
+ tx_msg(dev[0], hapd, msg)
+ # Empty challenge
+ # --> EAP-SAKE: Response/Challenge did not include AT_RAND_P or AT_MIC_P
+ msg = resp[0:4] + "0008" + resp[8:12] + "0008" + resp[16:24]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_sake_assoc(dev[0], hapd)
+
+ start_sake_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Invalid attribute in challenge
+ # --> EAP-SAKE: Too short attribute
+ msg = resp[0:4] + "0009" + resp[8:12] + "0009" + resp[16:26]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_sake_assoc(dev[0], hapd)
+
+ start_sake_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # SAKE/Challenge/Response
+ proxy_msg(hapd, dev[0]) # SAKE/Confirm/Request
+ resp = rx_msg(dev[0])
+ # Empty confirm
+ # --> EAP-SAKE: Response/Confirm did not include AT_MIC_P
+ msg = resp[0:4] + "0008" + resp[8:12] + "0008" + resp[16:26]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_sake_assoc(dev[0], hapd)
+
+ start_sake_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # SAKE/Challenge/Response
+ proxy_msg(hapd, dev[0]) # SAKE/Confirm/Request
+ resp = rx_msg(dev[0])
+ # Invalid attribute in confirm
+ # --> EAP-SAKE: Too short attribute
+ msg = resp[0:4] + "0009" + resp[8:12] + "0009" + resp[16:26]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_sake_assoc(dev[0], hapd)
+
+ start_sake_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # SAKE/Challenge/Response
+ proxy_msg(hapd, dev[0]) # SAKE/Confirm/Request
+ resp = rx_msg(dev[0])
+ # Corrupted AT_MIC_P value
+ # --> EAP-SAKE: Incorrect AT_MIC_P
+ msg = resp[0:30] + "000000000000" + resp[42:]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_sake_assoc(dev[0], hapd)
+
+def test_eap_proto_leap(dev, apdev):
+ """EAP-LEAP protocol tests"""
+ check_eap_capa(dev[0], "LEAP")
+ def leap_handler(ctx, req):
+ logger.info("leap_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ if ctx['num'] == 1:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_LEAP)
+
+ if ctx['num'] == 2:
+ logger.info("Test: Unexpected version")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_LEAP,
+ 0, 0, 0)
+
+ if ctx['num'] == 3:
+ logger.info("Test: Invalid challenge length")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_LEAP,
+ 1, 0, 0)
+
+ if ctx['num'] == 4:
+ logger.info("Test: Truncated challenge")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_LEAP,
+ 1, 0, 8)
+
+ if ctx['num'] == 5:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ if ctx['num'] == 6:
+ logger.info("Test: Missing payload in Response")
+ return struct.pack(">BBHB", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_LEAP)
+
+ if ctx['num'] == 7:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ if ctx['num'] == 8:
+ logger.info("Test: Unexpected version in Response")
+ return struct.pack(">BBHBBBB", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_LEAP,
+ 0, 0, 8)
+
+ if ctx['num'] == 9:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ if ctx['num'] == 10:
+ logger.info("Test: Invalid challenge length in Response")
+ return struct.pack(">BBHBBBB", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_LEAP,
+ 1, 0, 0)
+
+ if ctx['num'] == 11:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ if ctx['num'] == 12:
+ logger.info("Test: Truncated challenge in Response")
+ return struct.pack(">BBHBBBB", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_LEAP,
+ 1, 0, 24)
+
+ if ctx['num'] == 13:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ if ctx['num'] == 14:
+ logger.info("Test: Invalid challange value in Response")
+ return struct.pack(">BBHBBBB6L", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_LEAP,
+ 1, 0, 24,
+ 0, 0, 0, 0, 0, 0)
+
+ if ctx['num'] == 15:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ if ctx['num'] == 16:
+ logger.info("Test: Valid challange value in Response")
+ return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_LEAP,
+ 1, 0, 24,
+ 0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
+ 0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
+ 0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
+
+ if ctx['num'] == 17:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ if ctx['num'] == 18:
+ logger.info("Test: Success")
+ return struct.pack(">BBHB", EAP_CODE_SUCCESS, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_LEAP)
+ # hostapd will drop the next frame in the sequence
+
+ if ctx['num'] == 19:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ if ctx['num'] == 20:
+ logger.info("Test: Failure")
+ return struct.pack(">BBHB", EAP_CODE_FAILURE, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_LEAP)
+
+ return None
+
+ srv = start_radius_server(leap_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 12):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ if i == 10:
+ logger.info("Wait for additional roundtrip")
+ time.sleep(1)
+ dev[0].request("REMOVE_NETWORK all")
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_leap_errors(dev, apdev):
+ """EAP-LEAP protocol tests (error paths)"""
+ check_eap_capa(dev[0], "LEAP")
+
+ def leap_handler2(ctx, req):
+ logger.info("leap_handler2 - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challange value in Response")
+ return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_LEAP,
+ 1, 0, 24,
+ 0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
+ 0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
+ 0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challange value in Response")
+ return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_LEAP,
+ 1, 0, 24,
+ 0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
+ 0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
+ 0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challange value in Response")
+ return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_LEAP,
+ 1, 0, 24,
+ 0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
+ 0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
+ 0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challange value in Response")
+ return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_LEAP,
+ 1, 0, 24,
+ 0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
+ 0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
+ 0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challange value in Response")
+ return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_LEAP,
+ 1, 0, 24,
+ 0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
+ 0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
+ 0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challange value in Response")
+ return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_LEAP,
+ 1, 0, 24,
+ 0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
+ 0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
+ 0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challange value in Response")
+ return struct.pack(">BBHBBBB24B", EAP_CODE_RESPONSE, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_LEAP,
+ 1, 0, 24,
+ 0x48, 0x4e, 0x46, 0xe3, 0x88, 0x49, 0x46, 0xbd,
+ 0x28, 0x48, 0xf8, 0x53, 0x82, 0x50, 0x00, 0x04,
+ 0x93, 0x50, 0x30, 0xd7, 0x25, 0xea, 0x5f, 0x66)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid challenge")
+ return struct.pack(">BBHBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_LEAP,
+ 1, 0, 8, 0, 0)
+
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(leap_handler2)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1, "eap_leap_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_leap_process_request"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user",
+ password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_leap_process_success"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1, "os_get_random;eap_leap_process_success"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1, "eap_leap_process_response"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user",
+ password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1, "nt_password_hash;eap_leap_process_response"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1, "hash_nt_password_hash;eap_leap_process_response"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_leap_getKey"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user",
+ password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1, "eap_leap_getKey"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user",
+ password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1, "nt_password_hash;eap_leap_getKey"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1, "hash_nt_password_hash;eap_leap_getKey"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1,
+ "nt_challenge_response;eap_leap_process_request"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="LEAP", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_md5(dev, apdev):
+ """EAP-MD5 protocol tests"""
+ check_eap_capa(dev[0], "MD5")
+
+ def md5_handler(ctx, req):
+ logger.info("md5_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ if ctx['num'] == 1:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_MD5)
+
+ if ctx['num'] == 2:
+ logger.info("Test: Zero-length challenge")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_MD5,
+ 0)
+
+ if ctx['num'] == 3:
+ logger.info("Test: Truncated challenge")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_MD5,
+ 1)
+
+ if ctx['num'] == 4:
+ logger.info("Test: Shortest possible challenge and name")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_MD5,
+ 1, 0xaa, ord('n'))
+
+ return None
+
+ srv = start_radius_server(md5_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 4):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_md5_errors(dev, apdev):
+ """EAP-MD5 local error cases"""
+ check_eap_capa(dev[0], "MD5")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ with fail_test(dev[0], 1, "chap_md5"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="phase1-user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_md5_process"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="phase1-user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+
+def run_eap_md5_connect(dev):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="phase1-user", password="password",
+ wait_connect=False)
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-DISCONNECTED"],
+ timeout=1)
+ dev.request("REMOVE_NETWORK all")
+ if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_eap_proto_md5_errors_server(dev, apdev):
+ """EAP-MD5 local error cases on server"""
+ check_eap_capa(dev[0], "MD5")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ tests = [(1, "eap_md5_init")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ run_eap_md5_connect(dev[0])
+
+ tests = [(1, "os_get_random;eap_md5_buildReq"),
+ (1, "chap_md5;eap_md5_process")]
+ for count, func in tests:
+ with fail_test(hapd, count, func):
+ run_eap_md5_connect(dev[0])
+
+def start_md5_assoc(dev, hapd):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="phase1-user", password="password",
+ wait_connect=False)
+ proxy_msg(hapd, dev) # EAP-Identity/Request
+ proxy_msg(dev, hapd) # EAP-Identity/Response
+ proxy_msg(hapd, dev) # MSCHAPV2/Request
+ proxy_msg(dev, hapd) # NAK
+ proxy_msg(hapd, dev) # MD5 Request
+
+def stop_md5_assoc(dev, hapd):
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+ hapd.dump_monitor()
+
+def test_eap_proto_md5_server(dev, apdev):
+ """EAP-MD5 protocol testing for the server"""
+ check_eap_capa(dev[0], "MD5")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+
+ # Successful exchange to verify proxying mechanism
+ start_md5_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # MD5 Response
+ proxy_msg(hapd, dev[0]) # EAP-Success
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("No EAP-Success reported")
+ stop_md5_assoc(dev[0], hapd)
+
+ start_md5_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short EAP-MD5 header (no length field)
+ hapd.note("EAP-MD5: Invalid frame")
+ msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "04"
+ tx_msg(dev[0], hapd, msg)
+ # Too short EAP-MD5 header (no length field)
+ hapd.note("EAP-MD5: Invalid response (response_len=0 payload_len=1")
+ msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "0400"
+ tx_msg(dev[0], hapd, msg)
+ stop_md5_assoc(dev[0], hapd)
+
+def test_eap_proto_otp(dev, apdev):
+ """EAP-OTP protocol tests"""
+ def otp_handler(ctx, req):
+ logger.info("otp_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ if ctx['num'] == 1:
+ logger.info("Test: Empty payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_OTP)
+ if ctx['num'] == 2:
+ logger.info("Test: Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'],
+ 4)
+
+ if ctx['num'] == 3:
+ logger.info("Test: Challenge included")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_OTP,
+ ord('A'))
+ if ctx['num'] == 4:
+ logger.info("Test: Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'],
+ 4)
+
+ return None
+
+ srv = start_radius_server(otp_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 1):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="OTP", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="OTP", identity="user", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-REQ-OTP"])
+ if ev is None:
+ raise Exception("Request for password timed out")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-OTP-" + id + ":password")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"])
+ if ev is None:
+ raise Exception("Success not reported")
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_otp_errors(dev, apdev):
+ """EAP-OTP local error cases"""
+ def otp_handler2(ctx, req):
+ logger.info("otp_handler2 - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge included")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_OTP,
+ ord('A'))
+
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(otp_handler2)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_otp_process"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="OTP", identity="user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ finally:
+ stop_radius_server(srv)
+
+EAP_GPSK_OPCODE_GPSK_1 = 1
+EAP_GPSK_OPCODE_GPSK_2 = 2
+EAP_GPSK_OPCODE_GPSK_3 = 3
+EAP_GPSK_OPCODE_GPSK_4 = 4
+EAP_GPSK_OPCODE_FAIL = 5
+EAP_GPSK_OPCODE_PROTECTED_FAIL = 6
+
+def test_eap_proto_gpsk(dev, apdev):
+ """EAP-GPSK protocol tests"""
+ def gpsk_handler(ctx, req):
+ logger.info("gpsk_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_GPSK)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unknown opcode")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_GPSK,
+ 255)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected GPSK-3")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Too short GPSK-1")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Truncated ID_Server")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Missing RAND_Server")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Missing CSuite_List")
+ return struct.pack(">BBHBBH8L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Truncated CSuite_List")
+ return struct.pack(">BBHBBH8LH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Empty CSuite_List")
+ return struct.pack(">BBHBBH8LH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Invalid CSuite_List")
+ return struct.pack(">BBHBBH8LHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 1,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 No supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected GPSK-1")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite but too short key")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short GPSK-3")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Mismatch in RAND_Peer")
+ return struct.pack(">BBHBB8L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3,
+ 0, 0, 0, 0, 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Missing RAND_Server")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Mismatch in RAND_Server")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ msg += struct.pack(">8L", 1, 1, 1, 1, 1, 1, 1, 1)
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Missing ID_Server")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ msg += struct.pack(">8L", 0, 0, 0, 0, 0, 0, 0, 0)
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Truncated ID_Server")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32 + 2,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ msg += struct.pack(">8LH", 0, 0, 0, 0, 0, 0, 0, 0, 1)
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Mismatch in ID_Server")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32 + 3,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ msg += struct.pack(">8LHB", 0, 0, 0, 0, 0, 0, 0, 0, 1, ord('B'))
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBHB8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 3 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 1, ord('A'),
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Mismatch in ID_Server (same length)")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32 + 3,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[15:47]
+ msg += struct.pack(">8LHB", 0, 0, 0, 0, 0, 0, 0, 0, 1, ord('B'))
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Missing CSuite_Sel")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32 + 2,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ msg += struct.pack(">8LH", 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Mismatch in CSuite_Sel")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ msg += struct.pack(">8LHLH", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2)
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Missing len(PD_Payload_Block)")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ msg += struct.pack(">8LHLH", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1)
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Truncated PD_Payload_Block")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32 + 2 + 6 + 2,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ msg += struct.pack(">8LHLHH", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1)
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Missing MAC")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32 + 2 + 6 + 3,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ msg += struct.pack(">8LHLHHB",
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 123)
+ return msg
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-1 Supported CSuite")
+ return struct.pack(">BBHBBH8LHLH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 32 + 2 + 6,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_1, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 6, 0, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: GPSK-3 Incorrect MAC")
+ msg = struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 32 + 32 + 2 + 6 + 3 + 16,
+ EAP_TYPE_GPSK,
+ EAP_GPSK_OPCODE_GPSK_3)
+ msg += req[14:46]
+ msg += struct.pack(">8LHLHHB4L",
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 123,
+ 0, 0, 0, 0)
+ return msg
+
+ return None
+
+ srv = start_radius_server(gpsk_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 27):
+ if i == 12:
+ pw = "short"
+ else:
+ pw = "abcdefghijklmnop0123456789abcdef"
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="GPSK", identity="user", password=pw,
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.05)
+ dev[0].request("REMOVE_NETWORK all")
+ finally:
+ stop_radius_server(srv)
+
+def run_eap_gpsk_connect(dev):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ wait_connect=False)
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-DISCONNECTED"],
+ timeout=1)
+ dev.request("REMOVE_NETWORK all")
+ if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_eap_proto_gpsk_errors_server(dev, apdev):
+ """EAP-GPSK local error cases on server"""
+ check_eap_capa(dev[0], "GPSK")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ tests = [(1, "eap_gpsk_init"),
+ (1, "eap_msg_alloc;eap_gpsk_build_gpsk_1"),
+ (1, "eap_msg_alloc;eap_gpsk_build_gpsk_3"),
+ (1, "eap_gpsk_process_gpsk_2"),
+ (1, "eap_gpsk_derive_keys;eap_gpsk_process_gpsk_2"),
+ (1, "eap_gpsk_derive_session_id;eap_gpsk_process_gpsk_2"),
+ (1, "eap_gpsk_getKey"),
+ (1, "eap_gpsk_get_emsk"),
+ (1, "eap_gpsk_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ run_eap_gpsk_connect(dev[0])
+
+ tests = [(1, "os_get_random;eap_gpsk_build_gpsk_1"),
+ (1, "eap_gpsk_compute_mic;eap_gpsk_build_gpsk_3"),
+ (1, "eap_gpsk_derive_keys;eap_gpsk_process_gpsk_2"),
+ (1, "eap_gpsk_derive_session_id;eap_gpsk_process_gpsk_2"),
+ (1, "eap_gpsk_compute_mic;eap_gpsk_process_gpsk_2"),
+ (1, "eap_gpsk_compute_mic;eap_gpsk_process_gpsk_4")]
+ for count, func in tests:
+ with fail_test(hapd, count, func):
+ run_eap_gpsk_connect(dev[0])
+
+def start_gpsk_assoc(dev, hapd):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ wait_connect=False)
+ proxy_msg(hapd, dev) # EAP-Identity/Request
+ proxy_msg(dev, hapd) # EAP-Identity/Response
+ proxy_msg(hapd, dev) # GPSK-1
+
+def stop_gpsk_assoc(dev, hapd):
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+ hapd.dump_monitor()
+
+def test_eap_proto_gpsk_server(dev, apdev):
+ """EAP-GPSK protocol testing for the server"""
+ check_eap_capa(dev[0], "GPSK")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+
+ # Successful exchange to verify proxying mechanism
+ start_gpsk_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # GPSK-2
+ proxy_msg(hapd, dev[0]) # GPSK-3
+ proxy_msg(dev[0], hapd) # GPSK-4
+ proxy_msg(hapd, dev[0]) # EAP-Success
+ proxy_msg(hapd, dev[0]) # EAPOL-Key msg 1/4
+ proxy_msg(dev[0], hapd) # EAPOL-Key msg 2/4
+ proxy_msg(hapd, dev[0]) # EAPOL-Key msg 3/4
+ proxy_msg(dev[0], hapd) # EAPOL-Key msg 4/4
+ dev[0].wait_connected()
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short EAP-GPSK header (no OP-Code)
+ # --> EAP-GPSK: Invalid frame
+ msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "33"
+ tx_msg(dev[0], hapd, msg)
+ # Unknown OP-Code
+ # --> EAP-GPSK: Unexpected opcode=7 in state=0
+ msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "3307"
+ tx_msg(dev[0], hapd, msg)
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for ID_Peer length
+ msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "3302"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for ID_Peer
+ msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "33020001"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for ID_Server length
+ msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "33020000"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for ID_Server
+ msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "330200000001"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # ID_Server mismatch
+ # --> EAP-GPSK: ID_Server in GPSK-1 and GPSK-2 did not match
+ msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "330200000000"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for RAND_Peer
+ msg = resp[0:4] + "0011" + resp[8:12] + "0011" + "330200000007" + binascii.hexlify(b"hostapd").decode()
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for RAND_Server
+ msg = resp[0:4] + "0031" + resp[8:12] + "0031" + "330200000007" + binascii.hexlify(b"hostapd").decode() + 32*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # RAND_Server mismatch
+ # --> EAP-GPSK: RAND_Server in GPSK-1 and GPSK-2 did not match
+ msg = resp[0:4] + "0051" + resp[8:12] + "0051" + "330200000007" + binascii.hexlify(b"hostapd").decode() + 32*"00" + 32*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for CSuite_List length
+ msg = resp[0:4] + "005a" + resp[8:12] + "005a" + resp[16:188]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for CSuite_List
+ msg = resp[0:4] + "005c" + resp[8:12] + "005c" + resp[16:192]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: CSuite_List in GPSK-1 and GPSK-2 did not match
+ msg = resp[0:4] + "005c" + resp[8:12] + "005c" + resp[16:188] + "0000"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for CSuite_Sel
+ msg = resp[0:4] + "0068" + resp[8:12] + "0068" + resp[16:216]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Unsupported CSuite_Sel
+ # --> EAP-GPSK: Peer selected unsupported ciphersuite 0:255
+ msg = resp[0:4] + "006e" + resp[8:12] + "006e" + resp[16:226] + "ff"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for PD_Payload_1 length
+ msg = resp[0:4] + "006e" + resp[8:12] + "006e" + resp[16:228]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Too short message for PD_Payload_1
+ msg = resp[0:4] + "0070" + resp[8:12] + "0070" + resp[16:230] + "ff"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short GPSK-2
+ # --> EAP-GPSK: Message too short for MIC (left=0 miclen=16)
+ msg = resp[0:4] + "0070" + resp[8:12] + "0070" + resp[16:232]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Extra data in the end of GPSK-2
+ # --> EAP-GPSK: Ignored 1 bytes of extra data in the end of GPSK-2
+ msg = resp[0:4] + "0081" + resp[8:12] + "0081" + resp[16:264] + "00"
+ tx_msg(dev[0], hapd, msg)
+ proxy_msg(hapd, dev[0]) # GPSK-3
+ resp = rx_msg(dev[0])
+ # Too short GPSK-4
+ # --> EAP-GPSK: Too short message for PD_Payload_1 length
+ msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "3304"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd) # EAP-Failure
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # GPSK-2
+ proxy_msg(hapd, dev[0]) # GPSK-3
+ resp = rx_msg(dev[0])
+ # Too short GPSK-4
+ # --> EAP-GPSK: Too short message for PD_Payload_1
+ msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "33040001"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd) # EAP-Failure
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # GPSK-2
+ proxy_msg(hapd, dev[0]) # GPSK-3
+ resp = rx_msg(dev[0])
+ # Too short GPSK-4
+ # --> EAP-GPSK: Message too short for MIC (left=0 miclen=16)
+ msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "33040000"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd) # EAP-Failure
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # GPSK-2
+ proxy_msg(hapd, dev[0]) # GPSK-3
+ resp = rx_msg(dev[0])
+ # Incorrect MIC in GPSK-4
+ # --> EAP-GPSK: Incorrect MIC in GPSK-4
+ msg = resp[0:4] + "0018" + resp[8:12] + "0018" + "33040000" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd) # EAP-Failure
+ stop_gpsk_assoc(dev[0], hapd)
+
+ start_gpsk_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # GPSK-2
+ proxy_msg(hapd, dev[0]) # GPSK-3
+ resp = rx_msg(dev[0])
+ # Incorrect MIC in GPSK-4
+ # --> EAP-GPSK: Ignored 1 bytes of extra data in the end of GPSK-4
+ msg = resp[0:4] + "0019" + resp[8:12] + "0019" + resp[16:] + "00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd) # EAP-Success
+ stop_gpsk_assoc(dev[0], hapd)
+
+EAP_EKE_ID = 1
+EAP_EKE_COMMIT = 2
+EAP_EKE_CONFIRM = 3
+EAP_EKE_FAILURE = 4
+
+def test_eap_proto_eke(dev, apdev):
+ """EAP-EKE protocol tests"""
+ def eke_handler(ctx, req):
+ logger.info("eke_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_EKE)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unknown exchange")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_EKE,
+ 255)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No NumProposals in EAP-EKE-ID/Request")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: NumProposals=0 in EAP-EKE-ID/Request")
+ return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID,
+ 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated Proposals list in EAP-EKE-ID/Request")
+ return struct.pack(">BBHBBBB4B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 4,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID,
+ 2, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unsupported proposals in EAP-EKE-ID/Request")
+ return struct.pack(">BBHBBBB4B4B4B4B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 4 * 4,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID,
+ 4, 0,
+ 0, 0, 0, 0,
+ 3, 0, 0, 0,
+ 3, 1, 0, 0,
+ 3, 1, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing IDType/Identity in EAP-EKE-ID/Request")
+ return struct.pack(">BBHBBBB4B4B4B4B4B",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 5 * 4,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID,
+ 5, 0,
+ 0, 0, 0, 0,
+ 3, 0, 0, 0,
+ 3, 1, 0, 0,
+ 3, 1, 1, 0,
+ 3, 1, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid EAP-EKE-ID/Request")
+ return struct.pack(">BBHBBBB4BB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 4 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID,
+ 1, 0,
+ 3, 1, 1, 1,
+ 255)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected EAP-EKE-ID/Request")
+ return struct.pack(">BBHBBBB4BB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 4 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID,
+ 1, 0,
+ 3, 1, 1, 1,
+ 255)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid EAP-EKE-ID/Request")
+ return struct.pack(">BBHBBBB4BB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 4 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID,
+ 1, 0,
+ 3, 1, 1, 1,
+ 255)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected EAP-EKE-Confirm/Request")
+ return struct.pack(">BBHBB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_CONFIRM)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short EAP-EKE-Failure/Request")
+ return struct.pack(">BBHBB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_FAILURE)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected EAP-EKE-Commit/Request")
+ return struct.pack(">BBHBB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_COMMIT)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid EAP-EKE-ID/Request")
+ return struct.pack(">BBHBBBB4BB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 4 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID,
+ 1, 0,
+ 3, 1, 1, 1,
+ 255)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short EAP-EKE-Commit/Request")
+ return struct.pack(">BBHBB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_COMMIT)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid EAP-EKE-ID/Request")
+ return struct.pack(">BBHBBBB4BB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 4 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID,
+ 1, 0,
+ 1, 1, 1, 1,
+ 255)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: All zeroes DHComponent_S and empty CBvalue in EAP-EKE-Commit/Request")
+ return struct.pack(">BBHBB4L32L",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16 + 128,
+ EAP_TYPE_EKE,
+ EAP_EKE_COMMIT,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short EAP-EKE-Confirm/Request")
+ return struct.pack(">BBHBB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_CONFIRM)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid EAP-EKE-ID/Request")
+ return struct.pack(">BBHBBBB4BB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2 + 4 + 1,
+ EAP_TYPE_EKE,
+ EAP_EKE_ID,
+ 1, 0,
+ 1, 1, 1, 1,
+ 255)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: All zeroes DHComponent_S and empty CBvalue in EAP-EKE-Commit/Request")
+ return struct.pack(">BBHBB4L32L",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16 + 128,
+ EAP_TYPE_EKE,
+ EAP_EKE_COMMIT,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid PNonce_PS and Auth_S values in EAP-EKE-Confirm/Request")
+ return struct.pack(">BBHBB4L8L5L5L",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16 + 2 * 16 + 20 + 20,
+ EAP_TYPE_EKE,
+ EAP_EKE_CONFIRM,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ return None
+
+ srv = start_radius_server(eke_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 14):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="EKE", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ if i in [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ else:
+ time.sleep(0.05)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def eap_eke_test_fail(dev, phase1=None, success=False):
+ dev.connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="EKE", identity="eke user@domain", password="hello",
+ phase1=phase1, erp="1", wait_connect=False)
+ ev = dev.wait_event(["CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ if not success and "CTRL-EVENT-EAP-FAILURE" not in ev:
+ raise Exception("EAP did not fail during failure test")
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+
+def test_eap_proto_eke_errors(dev, apdev):
+ """EAP-EKE local error cases"""
+ check_eap_capa(dev[0], "EKE")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(1, 3):
+ with alloc_fail(dev[0], i, "eap_eke_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="EKE", identity="eke user", password="hello",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "eap_eke_dh_init", None),
+ (1, "eap_eke_prf_hmac_sha1", "dhgroup=3 encr=1 prf=1 mac=1"),
+ (1, "eap_eke_prf_hmac_sha256", "dhgroup=5 encr=1 prf=2 mac=2"),
+ (1, "eap_eke_prf", None),
+ (1, "os_get_random;eap_eke_dhcomp", None),
+ (1, "aes_128_cbc_encrypt;eap_eke_dhcomp", None),
+ (1, "aes_128_cbc_decrypt;eap_eke_shared_secret", None),
+ (1, "hmac_sha256_vector;eap_eke_shared_secret", None),
+ (1, "eap_eke_prf_hmac_sha256;eap_eke_derive_ke_ki", None),
+ (1, "eap_eke_prf_hmac_sha256;eap_eke_derive_ka", None),
+ (1, "eap_eke_prf_hmac_sha256;eap_eke_derive_msk", None),
+ (1, "os_get_random;eap_eke_prot", None),
+ (1, "aes_128_cbc_decrypt;eap_eke_decrypt_prot", None),
+ (1, "eap_eke_derive_key;eap_eke_process_commit", None),
+ (1, "eap_eke_dh_init;eap_eke_process_commit", None),
+ (1, "eap_eke_shared_secret;eap_eke_process_commit", None),
+ (1, "eap_eke_derive_ke_ki;eap_eke_process_commit", None),
+ (1, "eap_eke_dhcomp;eap_eke_process_commit", None),
+ (1, "os_get_random;eap_eke_process_commit", None),
+ (1, "os_get_random;=eap_eke_process_commit", None),
+ (1, "eap_eke_prot;eap_eke_process_commit", None),
+ (1, "eap_eke_decrypt_prot;eap_eke_process_confirm", None),
+ (1, "eap_eke_derive_ka;eap_eke_process_confirm", None),
+ (1, "eap_eke_auth;eap_eke_process_confirm", None),
+ (2, "eap_eke_auth;eap_eke_process_confirm", None),
+ (1, "eap_eke_prot;eap_eke_process_confirm", None),
+ (1, "aes_128_cbc_encrypt;eap_eke_prot;eap_eke_process_confirm", None),
+ (1, "hmac_sha256;eap_eke_prot;eap_eke_process_confirm", None),
+ (1, "eap_eke_derive_msk;eap_eke_process_confirm", None)]
+ for count, func, phase1 in tests:
+ with fail_test(dev[0], count, func):
+ eap_eke_test_fail(dev[0], phase1)
+
+ tests = [(1, "=eap_eke_derive_ke_ki", None),
+ (1, "=eap_eke_derive_ka", None),
+ (1, "=eap_eke_derive_msk", None),
+ (1, "eap_eke_build_msg;eap_eke_process_id", None),
+ (1, "wpabuf_alloc;eap_eke_process_id", None),
+ (1, "=eap_eke_process_id", None),
+ (1, "wpabuf_alloc;=eap_eke_process_id", None),
+ (1, "wpabuf_alloc;eap_eke_process_id", None),
+ (1, "eap_eke_build_msg;eap_eke_process_commit", None),
+ (1, "wpabuf_resize;eap_eke_process_commit", None),
+ (1, "eap_eke_build_msg;eap_eke_process_confirm", None)]
+ for count, func, phase1 in tests:
+ with alloc_fail(dev[0], count, func):
+ eap_eke_test_fail(dev[0], phase1)
+
+ tests = [(1, "eap_eke_getKey", None),
+ (1, "eap_eke_get_emsk", None),
+ (1, "eap_eke_get_session_id", None)]
+ for count, func, phase1 in tests:
+ with alloc_fail(dev[0], count, func):
+ eap_eke_test_fail(dev[0], phase1, success=True)
+
+EAP_PAX_OP_STD_1 = 0x01
+EAP_PAX_OP_STD_2 = 0x02
+EAP_PAX_OP_STD_3 = 0x03
+EAP_PAX_OP_SEC_1 = 0x11
+EAP_PAX_OP_SEC_2 = 0x12
+EAP_PAX_OP_SEC_3 = 0x13
+EAP_PAX_OP_SEC_4 = 0x14
+EAP_PAX_OP_SEC_5 = 0x15
+EAP_PAX_OP_ACK = 0x21
+
+EAP_PAX_FLAGS_MF = 0x01
+EAP_PAX_FLAGS_CE = 0x02
+EAP_PAX_FLAGS_AI = 0x04
+
+EAP_PAX_MAC_HMAC_SHA1_128 = 0x01
+EAP_PAX_HMAC_SHA256_128 = 0x02
+
+EAP_PAX_DH_GROUP_NONE = 0x00
+EAP_PAX_DH_GROUP_2048_MODP = 0x01
+EAP_PAX_DH_GROUP_3072_MODP = 0x02
+EAP_PAX_DH_GROUP_NIST_ECC_P_256 = 0x03
+
+EAP_PAX_PUBLIC_KEY_NONE = 0x00
+EAP_PAX_PUBLIC_KEY_RSAES_OAEP = 0x01
+EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5 = 0x02
+EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC = 0x03
+
+EAP_PAX_ADE_VENDOR_SPECIFIC = 0x01
+EAP_PAX_ADE_CLIENT_CHANNEL_BINDING = 0x02
+EAP_PAX_ADE_SERVER_CHANNEL_BINDING = 0x03
+
+def test_eap_proto_pax(dev, apdev):
+ """EAP-PAX protocol tests"""
+ def pax_std_1(ctx):
+ logger.info("Test: STD-1")
+ ctx['id'] = 10
+ return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 2 + 32 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x16, 0xc9, 0x08, 0x9d, 0x98, 0xa5, 0x6e, 0x1f,
+ 0xf0, 0xac, 0xcf, 0xc4, 0x66, 0xcd, 0x2d, 0xbf)
+
+ def pax_handler(ctx, req):
+ logger.info("pax_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_PAX)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Minimum length payload")
+ return struct.pack(">BBHB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 16,
+ EAP_TYPE_PAX,
+ 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unsupported MAC ID")
+ return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, 255, EAP_PAX_DH_GROUP_NONE,
+ EAP_PAX_PUBLIC_KEY_NONE,
+ 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unsupported DH Group ID")
+ return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ 255, EAP_PAX_PUBLIC_KEY_NONE,
+ 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unsupported Public Key ID")
+ return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, 255,
+ 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: More fragments")
+ return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, EAP_PAX_FLAGS_MF,
+ EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid ICV")
+ return struct.pack(">BBHBBBBBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid ICV in short frame")
+ return struct.pack(">BBHBBBBBB3L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 12,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Correct ICV - unsupported op_code")
+ ctx['id'] = 10
+ return struct.pack(">BBHBBBBBB16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 16,
+ EAP_TYPE_PAX,
+ 255, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 0x90, 0x78, 0x97, 0x38, 0x29, 0x94, 0x32, 0xd4,
+ 0x81, 0x27, 0xe0, 0xf6, 0x3b, 0x0d, 0xb2, 0xb2)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Correct ICV - CE flag in STD-1")
+ ctx['id'] = 10
+ return struct.pack(">BBHBBBBBB16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, EAP_PAX_FLAGS_CE,
+ EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 0x9c, 0x98, 0xb4, 0x0b, 0x94, 0x90, 0xde, 0x88,
+ 0xb7, 0x72, 0x63, 0x44, 0x1d, 0xe3, 0x7c, 0x5c)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Correct ICV - too short STD-1 payload")
+ ctx['id'] = 10
+ return struct.pack(">BBHBBBBBB16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 0xda, 0xab, 0x2c, 0xe7, 0x84, 0x41, 0xb5, 0x5c,
+ 0xee, 0xcf, 0x62, 0x03, 0xc5, 0x69, 0xcb, 0xf4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Correct ICV - incorrect A length in STD-1")
+ ctx['id'] = 10
+ return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 2 + 32 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0xc4, 0xb0, 0x81, 0xe4, 0x6c, 0x8c, 0x20, 0x23,
+ 0x60, 0x46, 0x89, 0xea, 0x94, 0x60, 0xf3, 0x2a)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Correct ICV - extra data in STD-1")
+ ctx['id'] = 10
+ return struct.pack(">BBHBBBBBBH8LB16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 2 + 32 + 1 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1,
+ 0x61, 0x49, 0x65, 0x37, 0x21, 0xe8, 0xd8, 0xbf,
+ 0xf3, 0x02, 0x01, 0xe5, 0x42, 0x51, 0xd3, 0x34)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected STD-1")
+ return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 2 + 32 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0xe5, 0x1d, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
+ 0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return pax_std_1(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: MAC ID changed during session")
+ return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 2 + 32 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_HMAC_SHA256_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0xee, 0x00, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
+ 0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return pax_std_1(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: DH Group ID changed during session")
+ return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 2 + 32 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_2048_MODP,
+ EAP_PAX_PUBLIC_KEY_NONE,
+ 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0xee, 0x01, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
+ 0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return pax_std_1(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Public Key ID changed during session")
+ return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 2 + 32 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_1, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE,
+ EAP_PAX_PUBLIC_KEY_RSAES_OAEP,
+ 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0xee, 0x02, 0xbf, 0xb8, 0x70, 0x20, 0x5c, 0xba,
+ 0x41, 0xbb, 0x34, 0xda, 0x1a, 0x08, 0xe6, 0x8d)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected STD-3")
+ ctx['id'] = 10
+ return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 2 + 32 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_3, 0, EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x47, 0xbb, 0xc0, 0xf9, 0xb9, 0x69, 0xf5, 0xcb,
+ 0x3a, 0xe8, 0xe7, 0xd6, 0x80, 0x28, 0xf2, 0x59)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return pax_std_1(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ # TODO: MAC calculation; for now, this gets dropped due to incorrect
+ # ICV
+ logger.info("Test: STD-3 with CE flag")
+ return struct.pack(">BBHBBBBBBH8L16B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 5 + 2 + 32 + 16,
+ EAP_TYPE_PAX,
+ EAP_PAX_OP_STD_3, EAP_PAX_FLAGS_CE,
+ EAP_PAX_MAC_HMAC_SHA1_128,
+ EAP_PAX_DH_GROUP_NONE, EAP_PAX_PUBLIC_KEY_NONE,
+ 32, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0x8a, 0xc2, 0xf9, 0xf4, 0x8b, 0x75, 0x72, 0xa2,
+ 0x4d, 0xd3, 0x1e, 0x54, 0x77, 0x04, 0x05, 0xe2)
+
+ idx += 1
+ if ctx['num'] & 0x1 == idx & 0x1:
+ logger.info("Test: Default request")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_PAX)
+ else:
+ logger.info("Test: Default EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(pax_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 18):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="user",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ logger.info("Waiting for EAP method to start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.05)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ logger.info("Too short password")
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="user",
+ password_hex="0123456789abcdef0123456789abcd",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ logger.info("No password")
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="user",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_pax_errors(dev, apdev):
+ """EAP-PAX local error cases"""
+ check_eap_capa(dev[0], "PAX")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(1, 3):
+ with alloc_fail(dev[0], i, "eap_pax_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = ["eap_msg_alloc;eap_pax_alloc_resp;eap_pax_process_std_1",
+ "eap_msg_alloc;eap_pax_alloc_resp;eap_pax_process_std_3",
+ "eap_pax_getKey",
+ "eap_pax_get_emsk",
+ "eap_pax_get_session_id"]
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "os_get_random;eap_pax_process_std_1"),
+ (1, "eap_pax_initial_key_derivation"),
+ (1, "eap_pax_mac;eap_pax_process_std_3"),
+ (2, "eap_pax_mac;eap_pax_process_std_3"),
+ (1, "eap_pax_kdf;eap_pax_getKey"),
+ (1, "eap_pax_kdf;eap_pax_get_emsk")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def run_eap_pax_connect(dev):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-DISCONNECTED"],
+ timeout=1)
+ dev.request("REMOVE_NETWORK all")
+ if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_eap_proto_pax_errors_server(dev, apdev):
+ """EAP-PAX local error cases on server"""
+ check_eap_capa(dev[0], "PAX")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ tests = [(1, "eap_pax_init"),
+ (1, "eap_msg_alloc;eap_pax_build_std_1"),
+ (1, "eap_msg_alloc;eap_pax_build_std_3"),
+ (1, "=eap_pax_process_std_2"),
+ (1, "eap_pax_getKey"),
+ (1, "eap_pax_get_emsk"),
+ (1, "eap_pax_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ run_eap_pax_connect(dev[0])
+
+ tests = [(1, "os_get_random;eap_pax_build_std_1"),
+ (1, "eap_pax_mac;eap_pax_build_std_1"),
+ (1, "eap_pax_mac;eap_pax_build_std_3"),
+ (2, "eap_pax_mac;=eap_pax_build_std_3"),
+ (1, "eap_pax_initial_key_derivation;eap_pax_process_std_2"),
+ (1, "eap_pax_mac;eap_pax_process_std_2"),
+ (2, "eap_pax_mac;=eap_pax_process_std_2"),
+ (1, "eap_pax_mac;eap_pax_check")]
+ for count, func in tests:
+ with fail_test(hapd, count, func):
+ run_eap_pax_connect(dev[0])
+
+def start_pax_assoc(dev, hapd):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ proxy_msg(hapd, dev) # EAP-Identity/Request
+ proxy_msg(dev, hapd) # EAP-Identity/Response
+ proxy_msg(hapd, dev) # PAX_STD-1
+
+def stop_pax_assoc(dev, hapd):
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+ hapd.dump_monitor()
+
+def test_eap_proto_pax_server(dev, apdev):
+ """EAP-PAX protocol testing for the server"""
+ check_eap_capa(dev[0], "PAX")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+
+ # Successful exchange to verify proxying mechanism
+ start_pax_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # PAX_STD-2
+ proxy_msg(hapd, dev[0]) # PAX_STD-3
+ proxy_msg(dev[0], hapd) # PAX-ACK
+ proxy_msg(hapd, dev[0]) # EAP-Success
+ proxy_msg(hapd, dev[0]) # EAPOL-Key msg 1/4
+ proxy_msg(dev[0], hapd) # EAPOL-Key msg 2/4
+ proxy_msg(hapd, dev[0]) # EAPOL-Key msg 3/4
+ proxy_msg(dev[0], hapd) # EAPOL-Key msg 4/4
+ dev[0].wait_connected()
+ stop_pax_assoc(dev[0], hapd)
+
+ start_pax_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short EAP-PAX header (no OP-Code)
+ hapd.note("EAP-PAX: Invalid frame")
+ msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "2e"
+ tx_msg(dev[0], hapd, msg)
+ # Too short EAP-PAX message (no payload)
+ hapd.note("EAP-PAX: Invalid frame")
+ msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "2e1100000000"
+ tx_msg(dev[0], hapd, msg)
+ # Unexpected PAX_SEC-2
+ hapd.note("EAP-PAX: Expected PAX_STD-2 - ignore op 17")
+ msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e1100000000" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ # Unexpected MAC ID
+ hapd.note("EAP-PAX: Expected MAC ID 0x1, received 0xff")
+ msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e0200ff0000" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ # Unexpected DH Group ID
+ hapd.note("EAP-PAX: Expected DH Group ID 0x0, received 0xff")
+ msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e020001ff00" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ # Unexpected Public Key ID
+ hapd.note("EAP-PAX: Expected Public Key ID 0x0, received 0xff")
+ msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e02000100ff" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ # Unsupported Flags - MF
+ hapd.note("EAP-PAX: fragmentation not supported")
+ msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e0201010000" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ # Unsupported Flags - CE
+ hapd.note("EAP-PAX: Unexpected CE flag")
+ msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e0202010000" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ # Too short Payload in PAX_STD-2
+ hapd.note("EAP-PAX: Too short PAX_STD-2 (B)")
+ msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e0200010000" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_pax_assoc(dev[0], hapd)
+
+ start_pax_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short Payload in PAX_STD-2
+ hapd.note("EAP-PAX: Too short PAX_STD-2 (CID)")
+ msg = resp[0:4] + "002c" + resp[8:12] + "002c" + "2e0200010000" + "0020" + 32*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_pax_assoc(dev[0], hapd)
+
+ start_pax_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short Payload in PAX_STD-2
+ hapd.note("EAP-PAX: Too short PAX_STD-2 (CID)")
+ msg = resp[0:4] + "002e" + resp[8:12] + "002e" + "2e0200010000" + "0020" + 32*"00" + "ffff"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_pax_assoc(dev[0], hapd)
+
+ start_pax_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too long CID in PAX_STD-2
+ hapd.note("EAP-PAX: Too long CID")
+ msg = resp[0:4] + "062e" + resp[8:12] + "062e" + "2e0200010000" + "0020" + 32*"00" + "0600" + 1536*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_pax_assoc(dev[0], hapd)
+
+ start_pax_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short Payload in PAX_STD-2
+ hapd.note("EAP-PAX: Too short PAX_STD-2 (MAC_CK)")
+ msg = resp[0:4] + "003c" + resp[8:12] + "003c" + "2e0200010000" + "0020" + 32*"00" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_pax_assoc(dev[0], hapd)
+
+ start_pax_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Unknown CID for PAX
+ hapd.note("EAP-PAX: EAP-PAX not enabled for CID")
+ msg = resp[0:4] + "0041" + resp[8:12] + "0041" + "2e0200010000" + "0020" + 32*"00" + "0001" + "00" + "0010" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_pax_assoc(dev[0], hapd)
+
+ start_pax_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short ICV
+ hapd.note("EAP-PAX: Too short ICV (15) in PAX_STD-2")
+ msg = resp[0:4] + "0063" + resp[8:12] + "0063" + resp[16:206]
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_pax_assoc(dev[0], hapd)
+
+ start_pax_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # PAX_STD-2
+ proxy_msg(hapd, dev[0]) # PAX_STD-3
+ resp = rx_msg(dev[0])
+ # Unexpected PAX_STD-2
+ hapd.note("EAP-PAX: Expected PAX-ACK - ignore op 1")
+ msg = resp[0:4] + "001a" + resp[8:12] + "001a" + "2e0100000000" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ stop_pax_assoc(dev[0], hapd)
+
+def test_eap_proto_psk(dev, apdev):
+ """EAP-PSK protocol tests"""
+ def psk_handler(ctx, req):
+ logger.info("psk_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_PSK)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Non-zero T in first message")
+ return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16,
+ EAP_TYPE_PSK, 0xc0, 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid first message")
+ return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16,
+ EAP_TYPE_PSK, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short third message")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_PSK)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid first message")
+ return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16,
+ EAP_TYPE_PSK, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Incorrect T in third message")
+ return struct.pack(">BBHBB4L4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16 + 16,
+ EAP_TYPE_PSK, 0, 0, 0, 0, 0, 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid first message")
+ return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16,
+ EAP_TYPE_PSK, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing PCHANNEL in third message")
+ return struct.pack(">BBHBB4L4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16 + 16,
+ EAP_TYPE_PSK, 0x80, 0, 0, 0, 0, 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid first message")
+ return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16,
+ EAP_TYPE_PSK, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalic MAC_S in third message")
+ return struct.pack(">BBHBB4L4L5LB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16 + 16 + 21,
+ EAP_TYPE_PSK, 0x80, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid first message")
+ return struct.pack(">BBHBB4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 16,
+ EAP_TYPE_PSK, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ return None
+
+ srv = start_radius_server(psk_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 6):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PSK", identity="user",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+
+ logger.info("Test: Invalid PSK length")
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PSK", identity="user",
+ password_hex="0123456789abcdef0123456789abcd",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_psk_errors(dev, apdev):
+ """EAP-PSK local error cases"""
+ check_eap_capa(dev[0], "PSK")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(1, 3):
+ with alloc_fail(dev[0], i, "eap_psk_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ for i in range(1, 4):
+ with fail_test(dev[0], i, "eap_psk_key_setup;eap_psk_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "=eap_psk_process_1"),
+ (2, "=eap_psk_process_1"),
+ (1, "eap_msg_alloc;eap_psk_process_1"),
+ (1, "=eap_psk_process_3"),
+ (2, "=eap_psk_process_3"),
+ (1, "eap_msg_alloc;eap_psk_process_3"),
+ (1, "eap_psk_getKey"),
+ (1, "eap_psk_get_session_id"),
+ (1, "eap_psk_get_emsk")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL",
+ note="No allocation failure seen for %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "os_get_random;eap_psk_process_1"),
+ (1, "omac1_aes_128;eap_psk_process_3"),
+ (1, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_encrypt"),
+ (2, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_encrypt"),
+ (3, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_encrypt"),
+ (1, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_decrypt"),
+ (2, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_decrypt"),
+ (3, "=omac1_aes_vector;omac1_aes_128;aes_128_eax_decrypt"),
+ (1, "aes_128_eax_decrypt;eap_psk_process_3"),
+ (2, "aes_128_eax_decrypt;eap_psk_process_3"),
+ (3, "aes_128_eax_decrypt;eap_psk_process_3"),
+ (1, "aes_128_eax_encrypt;eap_psk_process_3"),
+ (2, "aes_128_eax_encrypt;eap_psk_process_3"),
+ (3, "aes_128_eax_encrypt;eap_psk_process_3"),
+ (1, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
+ (2, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
+ (3, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
+ (4, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
+ (5, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
+ (6, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
+ (7, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
+ (8, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
+ (9, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
+ (10, "aes_128_encrypt_block;eap_psk_derive_keys;eap_psk_process_3"),
+ (1, "aes_ctr_encrypt;aes_128_eax_decrypt;eap_psk_process_3"),
+ (1, "aes_ctr_encrypt;aes_128_eax_encrypt;eap_psk_process_3")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_FAIL",
+ note="No failure seen for %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def run_eap_psk_connect(dev):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-DISCONNECTED"],
+ timeout=1)
+ dev.request("REMOVE_NETWORK all")
+ if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_eap_proto_psk_errors_server(dev, apdev):
+ """EAP-PSK local error cases on server"""
+ check_eap_capa(dev[0], "PSK")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ tests = [(1, "eap_psk_init"),
+ (1, "eap_msg_alloc;eap_psk_build_1"),
+ (1, "eap_msg_alloc;eap_psk_build_3"),
+ (1, "=eap_psk_build_3"),
+ (1, "=eap_psk_process_2"),
+ (2, "=eap_psk_process_2"),
+ (1, "=eap_psk_process_4"),
+ (1, "aes_128_eax_decrypt;eap_psk_process_4"),
+ (1, "eap_psk_getKey"),
+ (1, "eap_psk_get_emsk"),
+ (1, "eap_psk_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ run_eap_psk_connect(dev[0])
+
+ tests = [(1, "os_get_random;eap_psk_build_1"),
+ (1, "omac1_aes_128;eap_psk_build_3"),
+ (1, "eap_psk_derive_keys;eap_psk_build_3"),
+ (1, "aes_128_eax_encrypt;eap_psk_build_3"),
+ (1, "eap_psk_key_setup;eap_psk_process_2"),
+ (1, "omac1_aes_128;eap_psk_process_2"),
+ (1, "aes_128_eax_decrypt;eap_psk_process_4")]
+ for count, func in tests:
+ with fail_test(hapd, count, func):
+ run_eap_psk_connect(dev[0])
+
+def start_psk_assoc(dev, hapd):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ proxy_msg(hapd, dev) # EAP-Identity/Request
+ proxy_msg(dev, hapd) # EAP-Identity/Response
+ proxy_msg(hapd, dev) # PSK-1
+
+def stop_psk_assoc(dev, hapd):
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+ hapd.dump_monitor()
+
+def test_eap_proto_psk_server(dev, apdev):
+ """EAP-PSK protocol testing for the server"""
+ check_eap_capa(dev[0], "PSK")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+
+ # Successful exchange to verify proxying mechanism
+ start_psk_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # PSK-2
+ proxy_msg(hapd, dev[0]) # PSK-3
+ proxy_msg(dev[0], hapd) # PSK-4
+ proxy_msg(hapd, dev[0]) # EAP-Success
+ proxy_msg(hapd, dev[0]) # EAPOL-Key msg 1/4
+ proxy_msg(dev[0], hapd) # EAPOL-Key msg 2/4
+ proxy_msg(hapd, dev[0]) # EAPOL-Key msg 3/4
+ proxy_msg(dev[0], hapd) # EAPOL-Key msg 4/4
+ dev[0].wait_connected()
+ stop_psk_assoc(dev[0], hapd)
+
+ start_psk_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short EAP-PSK header (no Flags)
+ hapd.note("EAP-PSK: Invalid frame")
+ msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "2f"
+ tx_msg(dev[0], hapd, msg)
+ # Unexpected PSK-1
+ hapd.note("EAP-PSK: Expected PSK-2 - ignore T=0")
+ msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "2f00"
+ tx_msg(dev[0], hapd, msg)
+ # Too short PSK-2
+ hapd.note("EAP-PSK: Too short frame")
+ msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "2f40"
+ tx_msg(dev[0], hapd, msg)
+ # PSK-2 with unknown ID_P
+ hapd.note("EAP-PSK: EAP-PSK not enabled for ID_P")
+ msg = resp[0:4] + "004a" + resp[8:12] + "004a" + "2f40" + 3*16*"00" + 20*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd) # EAP-Failure
+ stop_psk_assoc(dev[0], hapd)
+
+ start_psk_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # PSK-2
+ proxy_msg(hapd, dev[0]) # PSK-3
+ resp = rx_msg(dev[0])
+ # Unexpected PSK-2
+ hapd.note("EAP-PSK: Expected PSK-4 - ignore T=1")
+ msg = resp[0:4] + "0016" + resp[8:12] + "0016" + "2f40" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ # Too short PSK-4 (no PCHANNEL)
+ hapd.note("EAP-PSK: Too short PCHANNEL data in PSK-4 (len=0, expected 21)")
+ msg = resp[0:4] + "0016" + resp[8:12] + "0016" + "2fc0" + 16*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd) # PSK-3 retry
+ stop_psk_assoc(dev[0], hapd)
+
+ start_psk_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # PSK-2
+ proxy_msg(hapd, dev[0]) # PSK-3
+ resp = rx_msg(dev[0])
+ # PCHANNEL Nonce did not increase
+ hapd.note("EAP-PSK: Nonce did not increase")
+ msg = resp[0:4] + "002b" + resp[8:12] + "002b" + "2fc0" + 16*"00" + 21*"00"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd) # PSK-3 retry
+ stop_psk_assoc(dev[0], hapd)
+
+ start_psk_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # PSK-2
+ proxy_msg(hapd, dev[0]) # PSK-3
+ resp = rx_msg(dev[0])
+ # Invalid PCHANNEL encryption
+ hapd.note("EAP-PSK: PCHANNEL decryption failed")
+ msg = resp[0:4] + "002b" + resp[8:12] + "002b" + "2fc0" + 16*"00" + 21*"11"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd) # PSK-3 retry
+ stop_psk_assoc(dev[0], hapd)
+
+EAP_SIM_SUBTYPE_START = 10
+EAP_SIM_SUBTYPE_CHALLENGE = 11
+EAP_SIM_SUBTYPE_NOTIFICATION = 12
+EAP_SIM_SUBTYPE_REAUTHENTICATION = 13
+EAP_SIM_SUBTYPE_CLIENT_ERROR = 14
+
+EAP_AKA_SUBTYPE_CHALLENGE = 1
+EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT = 2
+EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE = 4
+EAP_AKA_SUBTYPE_IDENTITY = 5
+EAP_AKA_SUBTYPE_NOTIFICATION = 12
+EAP_AKA_SUBTYPE_REAUTHENTICATION = 13
+EAP_AKA_SUBTYPE_CLIENT_ERROR = 14
+
+EAP_SIM_AT_RAND = 1
+EAP_SIM_AT_AUTN = 2
+EAP_SIM_AT_RES = 3
+EAP_SIM_AT_AUTS = 4
+EAP_SIM_AT_PADDING = 6
+EAP_SIM_AT_NONCE_MT = 7
+EAP_SIM_AT_PERMANENT_ID_REQ = 10
+EAP_SIM_AT_MAC = 11
+EAP_SIM_AT_NOTIFICATION = 12
+EAP_SIM_AT_ANY_ID_REQ = 13
+EAP_SIM_AT_IDENTITY = 14
+EAP_SIM_AT_VERSION_LIST = 15
+EAP_SIM_AT_SELECTED_VERSION = 16
+EAP_SIM_AT_FULLAUTH_ID_REQ = 17
+EAP_SIM_AT_COUNTER = 19
+EAP_SIM_AT_COUNTER_TOO_SMALL = 20
+EAP_SIM_AT_NONCE_S = 21
+EAP_SIM_AT_CLIENT_ERROR_CODE = 22
+EAP_SIM_AT_KDF_INPUT = 23
+EAP_SIM_AT_KDF = 24
+EAP_SIM_AT_IV = 129
+EAP_SIM_AT_ENCR_DATA = 130
+EAP_SIM_AT_NEXT_PSEUDONYM = 132
+EAP_SIM_AT_NEXT_REAUTH_ID = 133
+EAP_SIM_AT_CHECKCODE = 134
+EAP_SIM_AT_RESULT_IND = 135
+EAP_SIM_AT_BIDDING = 136
+
+def test_eap_proto_aka(dev, apdev):
+ """EAP-AKA protocol tests"""
+ def aka_handler(ctx, req):
+ logger.info("aka_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_AKA)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unknown subtype")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_AKA, 255, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Client Error")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CLIENT_ERROR, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short attribute header")
+ return struct.pack(">BBHBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 3,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0, 255)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated attribute")
+ return struct.pack(">BBHBBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0, 255,
+ 255)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short attribute data")
+ return struct.pack(">BBHBBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0, 255,
+ 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Skippable/non-skippable unrecognzized attribute")
+ return struct.pack(">BBHBBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 10,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ 255, 1, 0, 127, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request without ID type")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request ANY_ID")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request ANY_ID (duplicate)")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request ANY_ID")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request FULLAUTH_ID")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request FULLAUTH_ID (duplicate)")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request ANY_ID")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request FULLAUTH_ID")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request PERMANENT_ID")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request PERMANENT_ID (duplicate)")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with no attributes")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: AKA Challenge with BIDDING")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_BIDDING, 1, 0x8000)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification with no attributes")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification indicating success, but no MAC")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 32768)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification indicating success, but invalid MAC value")
+ return struct.pack(">BBHBBHBBHBBH4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 20,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 32768,
+ EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification indicating success with zero-key MAC")
+ return struct.pack(">BBHBBHBBHBBH16B", EAP_CODE_REQUEST,
+ ctx['id'] - 2,
+ 4 + 1 + 3 + 4 + 20,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 32768,
+ EAP_SIM_AT_MAC, 5, 0,
+ 0xbe, 0x2e, 0xbb, 0xa9, 0xfa, 0x2e, 0x82, 0x36,
+ 0x37, 0x8c, 0x32, 0x41, 0xb7, 0xc7, 0x58, 0xa3)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification before auth")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 16384)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification before auth")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 16385)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification with unrecognized non-failure")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification before auth (duplicate)")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Re-authentication (unexpected) with no attributes")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_REAUTHENTICATION,
+ 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: AKA Challenge with Checkcode claiming identity round was used")
+ return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_CHECKCODE, 6, 0, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request ANY_ID")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: AKA Challenge with Checkcode claiming no identity round was used")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_CHECKCODE, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request ANY_ID")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: AKA Challenge with mismatching Checkcode value")
+ return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_CHECKCODE, 6, 0, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Re-authentication (unexpected) with Checkcode claimin identity round was used")
+ return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_REAUTHENTICATION,
+ 0,
+ EAP_SIM_AT_CHECKCODE, 6, 0, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_RAND length")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_RAND, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_AUTN length")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_AUTN, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unencrypted AT_PADDING")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_PADDING, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_NONCE_MT length")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_NONCE_MT, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_MAC length")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_MAC, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_NOTIFICATION length")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_NOTIFICATION, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: AT_IDENTITY overflow")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_IDENTITY, 1, 0xffff)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected AT_VERSION_LIST")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_VERSION_LIST, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_SELECTED_VERSION length")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_SELECTED_VERSION, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unencrypted AT_COUNTER")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_COUNTER, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unencrypted AT_COUNTER_TOO_SMALL")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_COUNTER_TOO_SMALL, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unencrypted AT_NONCE_S")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_NONCE_S, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_CLIENT_ERROR_CODE length")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_CLIENT_ERROR_CODE, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_IV length")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_IV, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_ENCR_DATA length")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_ENCR_DATA, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unencrypted AT_NEXT_PSEUDONYM")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_NEXT_PSEUDONYM, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unencrypted AT_NEXT_REAUTH_ID")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_NEXT_REAUTH_ID, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_RES length")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_RES, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_RES length")
+ return struct.pack(">BBHBBHBBH5L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 24,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_RES, 6, 0xffff, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_AUTS length")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_AUTS, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_CHECKCODE length")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_CHECKCODE, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_RESULT_IND length")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_RESULT_IND, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected AT_KDF_INPUT")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected AT_KDF")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_KDF, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_BIDDING length")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_BIDDING, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ return None
+
+ srv = start_radius_server(aka_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 49):
+ eap = "AKA AKA'" if i == 11 else "AKA"
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap=eap, identity="0232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ if i in [0, 15]:
+ time.sleep(0.1)
+ else:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_aka_prime(dev, apdev):
+ """EAP-AKA' protocol tests"""
+ def aka_prime_handler(ctx, req):
+ logger.info("aka_prime_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ dev[0].note("Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_AKA_PRIME)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with no attributes")
+ dev[0].note("Challenge with no attributes")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with empty AT_KDF_INPUT")
+ dev[0].note("Challenge with empty AT_KDF_INPUT")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with AT_KDF_INPUT")
+ dev[0].note("Test: Challenge with AT_KDF_INPUT")
+ return struct.pack(">BBHBBHBBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'))
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with duplicated KDF")
+ dev[0].note("Challenge with duplicated KDF")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 3 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_KDF, 1, 2,
+ EAP_SIM_AT_KDF, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with multiple KDF proposals")
+ dev[0].note("Challenge with multiple KDF proposals (preparation)")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 3 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 254,
+ EAP_SIM_AT_KDF, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with incorrect KDF selected")
+ dev[0].note("Challenge with incorrect KDF selected")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 254,
+ EAP_SIM_AT_KDF, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with multiple KDF proposals")
+ dev[0].note("Challenge with multiple KDF proposals (preparation)")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 3 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 254,
+ EAP_SIM_AT_KDF, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with selected KDF not duplicated")
+ dev[0].note("Challenge with selected KDF not duplicated")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 3 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 254)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with multiple KDF proposals")
+ dev[0].note("Challenge with multiple KDF proposals (preparation)")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 3 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 254,
+ EAP_SIM_AT_KDF, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with selected KDF duplicated (missing MAC, RAND, AUTN)")
+ dev[0].note("Challenge with selected KDF duplicated (missing MAC, RAND, AUTN)")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 254,
+ EAP_SIM_AT_KDF, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with multiple unsupported KDF proposals")
+ dev[0].note("Challenge with multiple unsupported KDF proposals")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 2 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 254)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with multiple KDF proposals")
+ dev[0].note("Challenge with multiple KDF proposals (preparation)")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 3 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 254,
+ EAP_SIM_AT_KDF, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with invalid MAC, RAND, AUTN values)")
+ dev[0].note("Challenge with invalid MAC, RAND, AUTN values)")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBHBBH4LBBH4LBBH4L",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4 * 4 + 20 + 20 + 20,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 254,
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0,
+ EAP_SIM_AT_RAND, 5, 0, 0, 0, 0, 0,
+ EAP_SIM_AT_AUTN, 5, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge - AMF separation bit not set)")
+ dev[0].note("Challenge - AMF separation bit not set)")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBH4LBBH4LBBH4L",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4 + 20 + 20 + 20,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_MAC, 5, 0, 1, 2, 3, 4,
+ EAP_SIM_AT_RAND, 5, 0, 5, 6, 7, 8,
+ EAP_SIM_AT_AUTN, 5, 0, 9, 10,
+ 0x2fda8ef7, 0xbba518cc)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge - Invalid MAC")
+ dev[0].note("Challenge - Invalid MAC")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBH4LBBH4LBBH4L",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4 + 20 + 20 + 20,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_MAC, 5, 0, 1, 2, 3, 4,
+ EAP_SIM_AT_RAND, 5, 0, 5, 6, 7, 8,
+ EAP_SIM_AT_AUTN, 5, 0, 0xffffffff, 0xffffffff,
+ 0xd1f90322, 0x40514cb4)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge - Valid MAC")
+ dev[0].note("Challenge - Valid MAC")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBH4LBBH4LBBH4L",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4 + 20 + 20 + 20,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_MAC, 5, 0,
+ 0xf4a3c1d3, 0x7c901401, 0x34bd8b01, 0x6f7fa32f,
+ EAP_SIM_AT_RAND, 5, 0, 5, 6, 7, 8,
+ EAP_SIM_AT_AUTN, 5, 0, 0xffffffff, 0xffffffff,
+ 0xd1f90322, 0x40514cb4)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_KDF_INPUT length")
+ dev[0].note("Invalid AT_KDF_INPUT length")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 0xffff, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid AT_KDF length")
+ dev[0].note("Invalid AT_KDF length")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_IDENTITY, 0,
+ EAP_SIM_AT_KDF, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with large number of KDF proposals")
+ dev[0].note("Challenge with large number of KDF proposals")
+ return struct.pack(">BBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 12 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF, 1, 255,
+ EAP_SIM_AT_KDF, 1, 254,
+ EAP_SIM_AT_KDF, 1, 253,
+ EAP_SIM_AT_KDF, 1, 252,
+ EAP_SIM_AT_KDF, 1, 251,
+ EAP_SIM_AT_KDF, 1, 250,
+ EAP_SIM_AT_KDF, 1, 249,
+ EAP_SIM_AT_KDF, 1, 248,
+ EAP_SIM_AT_KDF, 1, 247,
+ EAP_SIM_AT_KDF, 1, 246,
+ EAP_SIM_AT_KDF, 1, 245,
+ EAP_SIM_AT_KDF, 1, 244)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with multiple KDF proposals")
+ dev[0].note("Challenge with multiple KDF proposals (preparation)")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 2 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 2,
+ EAP_SIM_AT_KDF, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with an extra KDF appended")
+ dev[0].note("Challenge with an extra KDF appended")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_KDF, 1, 2,
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_KDF, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with multiple KDF proposals")
+ dev[0].note("Challenge with multiple KDF proposals (preparation)")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 2 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 2,
+ EAP_SIM_AT_KDF, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge with a modified KDF")
+ dev[0].note("Challenge with a modified KDF")
+ return struct.pack(">BBHBBHBBHBBBBBBHBBHBBH",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 3 * 4,
+ EAP_TYPE_AKA_PRIME, EAP_AKA_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_KDF_INPUT, 2, 1, ord('a'), ord('b'),
+ ord('c'), ord('d'),
+ EAP_SIM_AT_KDF, 1, 1,
+ EAP_SIM_AT_KDF, 1, 0,
+ EAP_SIM_AT_KDF, 1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ return None
+
+ srv = start_radius_server(aka_prime_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 18):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="AKA'", identity="6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ if i in [0]:
+ time.sleep(0.1)
+ else:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_sim(dev, apdev):
+ """EAP-SIM protocol tests"""
+ def sim_handler(ctx, req):
+ logger.info("sim_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_SIM)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected AT_AUTN")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_AUTN, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short AT_VERSION_LIST")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: AT_VERSION_LIST overflow")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 1, 0xffff)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected AT_AUTS")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_AUTS, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected AT_CHECKCODE")
+ return struct.pack(">BBHBBHBBHL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_CHECKCODE, 2, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No AT_VERSION_LIST in Start")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No support version in AT_VERSION_LIST")
+ return struct.pack(">BBHBBHBBH4B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 3, 2, 3, 4, 5)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request without ID type")
+ return struct.pack(">BBHBBHBBH2H", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request ANY_ID")
+ return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+ EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request ANY_ID (duplicate)")
+ return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+ EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request ANY_ID")
+ return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+ EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request FULLAUTH_ID")
+ return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+ EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request FULLAUTH_ID (duplicate)")
+ return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+ EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request ANY_ID")
+ return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+ EAP_SIM_AT_ANY_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request FULLAUTH_ID")
+ return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+ EAP_SIM_AT_FULLAUTH_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request PERMANENT_ID")
+ return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+ EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Identity request PERMANENT_ID (duplicate)")
+ return struct.pack(">BBHBBHBBH2HBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 8 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START, 0,
+ EAP_SIM_AT_VERSION_LIST, 2, 2, 1, 0,
+ EAP_SIM_AT_PERMANENT_ID_REQ, 1, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No AT_MAC and AT_RAND in Challenge")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No AT_RAND in Challenge")
+ return struct.pack(">BBHBBHBBH4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 20,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Insufficient number of challenges in Challenge")
+ return struct.pack(">BBHBBHBBH4LBBH4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 20 + 20,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_RAND, 5, 0, 0, 0, 0, 0,
+ EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too many challenges in Challenge")
+ return struct.pack(">BBHBBHBBH4L4L4L4LBBH4L", EAP_CODE_REQUEST,
+ ctx['id'],
+ 4 + 1 + 3 + 4 + 4 * 16 + 20,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_RAND, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Same RAND multiple times in Challenge")
+ return struct.pack(">BBHBBHBBH4L4L4LBBH4L", EAP_CODE_REQUEST,
+ ctx['id'],
+ 4 + 1 + 3 + 4 + 3 * 16 + 20,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE, 0,
+ EAP_SIM_AT_RAND, 13, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0,
+ EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification with no attributes")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification indicating success, but no MAC")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 32768)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification indicating success, but invalid MAC value")
+ return struct.pack(">BBHBBHBBHBBH4L", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 20,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 32768,
+ EAP_SIM_AT_MAC, 5, 0, 0, 0, 0, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification before auth")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 16384)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification before auth")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 16385)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification with unrecognized non-failure")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Notification before auth (duplicate)")
+ return struct.pack(">BBHBBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_NOTIFICATION, 0,
+ EAP_SIM_AT_NOTIFICATION, 1, 0xc000)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Re-authentication (unexpected) with no attributes")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_REAUTHENTICATION,
+ 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Client Error")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CLIENT_ERROR, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unknown subtype")
+ return struct.pack(">BBHBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3,
+ EAP_TYPE_SIM, 255, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ return None
+
+ srv = start_radius_server(sim_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 25):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ if i in [0]:
+ time.sleep(0.1)
+ else:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_sim_errors(dev, apdev):
+ """EAP-SIM protocol tests (error paths)"""
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1, "eap_sim_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1, "os_get_random;eap_sim_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+
+ with fail_test(dev[0], 1, "aes_128_cbc_encrypt;eap_sim_response_reauth"):
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP re-authentication did not start")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+
+ with fail_test(dev[0], 1, "os_get_random;eap_sim_msg_add_encr_start"):
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP re-authentication did not start")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+
+ with fail_test(dev[0], 1, "os_get_random;eap_sim_init_for_reauth"):
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP re-authentication did not start")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+
+ with alloc_fail(dev[0], 1, "eap_sim_parse_encr;eap_sim_process_reauthentication"):
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP re-authentication did not start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = [(1, "eap_sim_verify_mac;eap_sim_process_challenge"),
+ (1, "eap_sim_parse_encr;eap_sim_process_challenge"),
+ (1, "eap_sim_msg_init;eap_sim_response_start"),
+ (1, "wpabuf_alloc;eap_sim_msg_init;eap_sim_response_start"),
+ (1, "=eap_sim_learn_ids"),
+ (2, "=eap_sim_learn_ids"),
+ (2, "eap_sim_learn_ids"),
+ (3, "eap_sim_learn_ids"),
+ (1, "eap_sim_process_start"),
+ (1, "eap_sim_getKey"),
+ (1, "eap_sim_get_emsk"),
+ (1, "eap_sim_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SIM", identity="1232010000000000@domain",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ erp="1", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = [(1, "aes_128_cbc_decrypt;eap_sim_parse_encr")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ params = int_eap_server_params()
+ params['eap_sim_db'] = "unix:/tmp/hlr_auc_gw.sock"
+ params['eap_sim_aka_result_ind'] = "1"
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ dev[0].scan_for_bss(hapd2.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1,
+ "eap_sim_msg_init;eap_sim_response_notification"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ phase1="result_ind=1",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = ["eap_sim_msg_add_encr_start;eap_sim_response_notification",
+ "aes_128_cbc_encrypt;eap_sim_response_notification"]
+ for func in tests:
+ with fail_test(dev[0], 1, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ phase1="result_ind=1",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("EAP method not started on reauthentication")
+ time.sleep(0.1)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = ["eap_sim_parse_encr;eap_sim_process_notification_reauth"]
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="SIM", identity="1232010000000000",
+ phase1="result_ind=1",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("EAP method not started on reauthentication")
+ time.sleep(0.1)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def test_eap_proto_aka_errors(dev, apdev):
+ """EAP-AKA protocol tests (error paths)"""
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1, "eap_aka_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="AKA", identity="0232010000000000",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "=eap_aka_learn_ids"),
+ (2, "=eap_aka_learn_ids"),
+ (1, "eap_sim_parse_encr;eap_aka_process_challenge"),
+ (1, "wpabuf_alloc;eap_aka_add_id_msg"),
+ (1, "eap_aka_getKey"),
+ (1, "eap_aka_get_emsk"),
+ (1, "eap_aka_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="AKA", identity="0232010000000000@domain",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ erp="1", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ params = int_eap_server_params()
+ params['eap_sim_db'] = "unix:/tmp/hlr_auc_gw.sock"
+ params['eap_sim_aka_result_ind'] = "1"
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ dev[0].scan_for_bss(hapd2.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1,
+ "eap_sim_msg_init;eap_aka_response_notification"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="AKA", identity="0232010000000000",
+ phase1="result_ind=1",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = [(1, "aes_128_encrypt_block;milenage_f1;milenage_check", None),
+ (2, "aes_128_encrypt_block;milenage_f1;milenage_check", None),
+ (1, "milenage_f2345;milenage_check", None),
+ (7, "aes_128_encrypt_block;milenage_f2345;milenage_check",
+ "ff0000000123"),
+ (1, "aes_128_encrypt_block;milenage_f1;milenage_check",
+ "fff000000123")]
+ for count, func, seq in tests:
+ if not seq:
+ seq = "000000000123"
+ with fail_test(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="AKA", identity="0232010000000000",
+ phase1="result_ind=1",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:" + seq,
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ tests = ["eap_sim_msg_add_encr_start;eap_aka_response_notification",
+ "aes_128_cbc_encrypt;eap_aka_response_notification"]
+ for func in tests:
+ with fail_test(dev[0], 1, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="AKA", identity="0232010000000000",
+ phase1="result_ind=1",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("EAP method not started on reauthentication")
+ time.sleep(0.1)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = ["eap_sim_parse_encr;eap_aka_process_notification_reauth"]
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="AKA", identity="0232010000000000",
+ phase1="result_ind=1",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("EAP method not started on reauthentication")
+ time.sleep(0.1)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def test_eap_proto_aka_prime_errors(dev, apdev):
+ """EAP-AKA' protocol tests (error paths)"""
+ check_hlr_auc_gw_support()
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1, "eap_aka_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="AKA'", identity="6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="AKA'", identity="6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+
+ with fail_test(dev[0], 1, "aes_128_cbc_encrypt;eap_aka_response_reauth"):
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP re-authentication did not start")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="AKA'", identity="6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+
+ with alloc_fail(dev[0], 1, "eap_sim_parse_encr;eap_aka_process_reauthentication"):
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP re-authentication did not start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = [(1, "eap_sim_verify_mac_sha256"),
+ (1, "=eap_aka_process_challenge")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="AKA'", identity="6555444333222111",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123",
+ erp="1", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def test_eap_proto_ikev2(dev, apdev):
+ """EAP-IKEv2 protocol tests"""
+ check_eap_capa(dev[0], "IKEV2")
+
+ global eap_proto_ikev2_test_done
+ eap_proto_ikev2_test_done = False
+
+ def ikev2_handler(ctx, req):
+ logger.info("ikev2_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_IKEV2)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated Message Length field")
+ return struct.pack(">BBHBB3B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 3,
+ EAP_TYPE_IKEV2, 0x80, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short Message Length value")
+ return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4 + 1,
+ EAP_TYPE_IKEV2, 0x80, 0, 1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated message")
+ return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4,
+ EAP_TYPE_IKEV2, 0x80, 1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated message(2)")
+ return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4,
+ EAP_TYPE_IKEV2, 0x80, 0xffffffff)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated message(3)")
+ return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4,
+ EAP_TYPE_IKEV2, 0xc0, 0xffffffff)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated message(4)")
+ return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4,
+ EAP_TYPE_IKEV2, 0xc0, 10000000)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too long fragments (first fragment)")
+ return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4 + 1,
+ EAP_TYPE_IKEV2, 0xc0, 2, 1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too long fragments (second fragment)")
+ return struct.pack(">BBHBB2B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2,
+ EAP_TYPE_IKEV2, 0x00, 2, 3)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No Message Length field in first fragment")
+ return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 1,
+ EAP_TYPE_IKEV2, 0x40, 1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: ICV before keys")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_IKEV2, 0x20)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unsupported IKEv2 header version")
+ return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 28,
+ EAP_TYPE_IKEV2, 0x00,
+ 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Incorrect IKEv2 header Length")
+ return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 28,
+ EAP_TYPE_IKEV2, 0x00,
+ 0, 0, 0, 0,
+ 0, 0x20, 0, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected IKEv2 Exchange Type in SA_INIT state")
+ return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 28,
+ EAP_TYPE_IKEV2, 0x00,
+ 0, 0, 0, 0,
+ 0, 0x20, 0, 0, 0, 28)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected IKEv2 Message ID in SA_INIT state")
+ return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 28,
+ EAP_TYPE_IKEV2, 0x00,
+ 0, 0, 0, 0,
+ 0, 0x20, 34, 0, 1, 28)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected IKEv2 Flags value")
+ return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 28,
+ EAP_TYPE_IKEV2, 0x00,
+ 0, 0, 0, 0,
+ 0, 0x20, 34, 0, 0, 28)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected IKEv2 Flags value(2)")
+ return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 28,
+ EAP_TYPE_IKEV2, 0x00,
+ 0, 0, 0, 0,
+ 0, 0x20, 34, 0x20, 0, 28)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No SAi1 in SA_INIT")
+ return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 28,
+ EAP_TYPE_IKEV2, 0x00,
+ 0, 0, 0, 0,
+ 0, 0x20, 34, 0x08, 0, 28)
+
+ def build_ike(id, next=0, exch_type=34, flags=0x00, ike=b''):
+ return struct.pack(">BBHBB2L2LBBBBLL", EAP_CODE_REQUEST, id,
+ 4 + 1 + 1 + 28 + len(ike),
+ EAP_TYPE_IKEV2, flags,
+ 0, 0, 0, 0,
+ next, 0x20, exch_type, 0x08, 0,
+ 28 + len(ike)) + ike
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected extra data after payloads")
+ return build_ike(ctx['id'], ike=struct.pack(">B", 1))
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated payload header")
+ return build_ike(ctx['id'], next=128, ike=struct.pack(">B", 1))
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too small payload header length")
+ ike = struct.pack(">BBH", 0, 0, 3)
+ return build_ike(ctx['id'], next=128, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too large payload header length")
+ ike = struct.pack(">BBH", 0, 0, 5)
+ return build_ike(ctx['id'], next=128, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unsupported payload (non-critical and critical)")
+ ike = struct.pack(">BBHBBH", 129, 0, 4, 0, 0x01, 4)
+ return build_ike(ctx['id'], next=128, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Certificate and empty SAi1")
+ ike = struct.pack(">BBHBBH", 33, 0, 4, 0, 0, 4)
+ return build_ike(ctx['id'], next=37, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short proposal")
+ ike = struct.pack(">BBHBBHBBB", 0, 0, 4 + 7,
+ 0, 0, 7, 0, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too small proposal length in SAi1")
+ ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+ 0, 0, 7, 0, 0, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too large proposal length in SAi1")
+ ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+ 0, 0, 9, 0, 0, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected proposal type in SAi1")
+ ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+ 1, 0, 8, 0, 0, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Protocol ID in SAi1")
+ ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+ 0, 0, 8, 0, 0, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected proposal number in SAi1")
+ ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+ 0, 0, 8, 0, 1, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Not enough room for SPI in SAi1")
+ ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+ 0, 0, 8, 1, 1, 1, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected SPI in SAi1")
+ ike = struct.pack(">BBHBBHBBBBB", 0, 0, 4 + 9,
+ 0, 0, 9, 1, 1, 1, 0, 1)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No transforms in SAi1")
+ ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+ 0, 0, 8, 1, 1, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short transform in SAi1")
+ ike = struct.pack(">BBHBBHBBBB", 0, 0, 4 + 8,
+ 0, 0, 8, 1, 1, 0, 1)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too small transform length in SAi1")
+ ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
+ 0, 0, 8 + 8, 1, 1, 0, 1,
+ 0, 0, 7, 0, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too large transform length in SAi1")
+ ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
+ 0, 0, 8 + 8, 1, 1, 0, 1,
+ 0, 0, 9, 0, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Transform type in SAi1")
+ ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
+ 0, 0, 8 + 8, 1, 1, 0, 1,
+ 1, 0, 8, 0, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No transform attributes in SAi1")
+ ike = struct.pack(">BBHBBHBBBBBBHBBH", 0, 0, 4 + 8 + 8,
+ 0, 0, 8 + 8, 1, 1, 0, 1,
+ 0, 0, 8, 0, 0, 0)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No transform attr for AES and unexpected data after transforms in SAi1")
+ tlen1 = 8 + 3
+ tlen2 = 8 + 4
+ tlen3 = 8 + 4
+ tlen = tlen1 + tlen2 + tlen3
+ ike = struct.pack(">BBHBBHBBBBBBHBBH3BBBHBBHHHBBHBBHHHB",
+ 0, 0, 4 + 8 + tlen + 1,
+ 0, 0, 8 + tlen + 1, 1, 1, 0, 3,
+ 3, 0, tlen1, 1, 0, 12, 1, 2, 3,
+ 3, 0, tlen2, 1, 0, 12, 0, 128,
+ 0, 0, tlen3, 1, 0, 12, 0x8000 | 14, 127,
+ 1)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ def build_sa(next=0):
+ tlen = 5 * 8
+ return struct.pack(">BBHBBHBBBBBBHBBHBBHBBHBBHBBHBBHBBHBBHBBH",
+ next, 0, 4 + 8 + tlen,
+ 0, 0, 8 + tlen, 1, 1, 0, 5,
+ 3, 0, 8, 1, 0, 3,
+ 3, 0, 8, 2, 0, 1,
+ 3, 0, 8, 3, 0, 1,
+ 3, 0, 8, 4, 0, 5,
+ 0, 0, 8, 241, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid proposal, but no KEi in SAi1")
+ ike = build_sa()
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Empty KEi in SAi1")
+ ike = build_sa(next=34) + struct.pack(">BBH", 0, 0, 4)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Mismatch in DH Group in SAi1")
+ ike = build_sa(next=34)
+ ike += struct.pack(">BBHHH", 0, 0, 4 + 4 + 96, 12345, 0)
+ ike += 96*b'\x00'
+ return build_ike(ctx['id'], next=33, ike=ike)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid DH public value length in SAi1")
+ ike = build_sa(next=34)
+ ike += struct.pack(">BBHHH", 0, 0, 4 + 4 + 96, 5, 0)
+ ike += 96*b'\x00'
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ def build_ke(next=0):
+ ke = struct.pack(">BBHHH", next, 0, 4 + 4 + 192, 5, 0)
+ ke += 191*b'\x00'+b'\x02'
+ return ke
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid proposal and KEi, but no Ni in SAi1")
+ ike = build_sa(next=34)
+ ike += build_ke()
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short Ni in SAi1")
+ ike = build_sa(next=34)
+ ike += build_ke(next=40)
+ ike += struct.pack(">BBH", 0, 0, 4)
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too long Ni in SAi1")
+ ike = build_sa(next=34)
+ ike += build_ke(next=40)
+ ike += struct.pack(">BBH", 0, 0, 4 + 257) + 257*b'\x00'
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ def build_ni(next=0):
+ return struct.pack(">BBH", next, 0, 4 + 256) + 256*b'\x00'
+
+ def build_sai1(id):
+ ike = build_sa(next=34)
+ ike += build_ke(next=40)
+ ike += build_ni()
+ return build_ike(ctx['id'], next=33, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
+ return build_sai1(ctx['id'])
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
+ return build_sai1(ctx['id'])
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No integrity checksum")
+ ike = b''
+ return build_ike(ctx['id'], next=37, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
+ return build_sai1(ctx['id'])
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated integrity checksum")
+ return struct.pack(">BBHBB",
+ EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_IKEV2, 0x20)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid proposal, KEi, and Ni in SAi1")
+ return build_sai1(ctx['id'])
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid integrity checksum")
+ ike = b''
+ return build_ike(ctx['id'], next=37, flags=0x20, ike=ike)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("No more test responses available - test case completed")
+ global eap_proto_ikev2_test_done
+ eap_proto_ikev2_test_done = True
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_IKEV2)
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(ikev2_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ i = 0
+ while not eap_proto_ikev2_test_done:
+ i += 1
+ logger.info("Running connection iteration %d" % i)
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="IKEV2", identity="user",
+ password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ if i in [41, 46]:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ else:
+ time.sleep(0.05)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def NtPasswordHash(password):
+ pw = password.encode('utf_16_le')
+ return hashlib.new('md4', pw).digest()
+
+def HashNtPasswordHash(password_hash):
+ return hashlib.new('md4', password_hash).digest()
+
+def ChallengeHash(peer_challenge, auth_challenge, username):
+ data = peer_challenge + auth_challenge + username
+ return hashlib.sha1(data).digest()[0:8]
+
+def GenerateAuthenticatorResponse(password, nt_response, peer_challenge,
+ auth_challenge, username):
+ magic1 = binascii.unhexlify("4D616769632073657276657220746F20636C69656E74207369676E696E6720636F6E7374616E74")
+ magic2 = binascii.unhexlify("50616420746F206D616B6520697420646F206D6F7265207468616E206F6E6520697465726174696F6E")
+
+ password_hash = NtPasswordHash(password)
+ password_hash_hash = HashNtPasswordHash(password_hash)
+ data = password_hash_hash + nt_response + magic1
+ digest = hashlib.sha1(data).digest()
+
+ challenge = ChallengeHash(peer_challenge, auth_challenge, username.encode())
+
+ data = digest + challenge + magic2
+ resp = hashlib.sha1(data).digest()
+ return resp
+
+def test_eap_proto_ikev2_errors(dev, apdev):
+ """EAP-IKEv2 local error cases"""
+ check_eap_capa(dev[0], "IKEV2")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(1, 5):
+ with alloc_fail(dev[0], i, "eap_ikev2_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="IKEV2", identity="ikev2 user",
+ password="ike password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "ikev2_encr_encrypt"),
+ (1, "ikev2_encr_decrypt"),
+ (1, "ikev2_derive_auth_data"),
+ (2, "ikev2_derive_auth_data"),
+ (1, "=ikev2_decrypt_payload"),
+ (1, "ikev2_encr_decrypt;ikev2_decrypt_payload"),
+ (1, "ikev2_encr_encrypt;ikev2_build_encrypted"),
+ (1, "ikev2_derive_sk_keys"),
+ (2, "ikev2_derive_sk_keys"),
+ (3, "ikev2_derive_sk_keys"),
+ (4, "ikev2_derive_sk_keys"),
+ (5, "ikev2_derive_sk_keys"),
+ (6, "ikev2_derive_sk_keys"),
+ (7, "ikev2_derive_sk_keys"),
+ (8, "ikev2_derive_sk_keys"),
+ (1, "eap_ikev2_derive_keymat;eap_ikev2_peer_keymat"),
+ (1, "eap_msg_alloc;eap_ikev2_build_msg"),
+ (1, "eap_ikev2_getKey"),
+ (1, "eap_ikev2_get_emsk"),
+ (1, "eap_ikev2_get_session_id"),
+ (1, "=ikev2_derive_keys"),
+ (2, "=ikev2_derive_keys"),
+ (1, "wpabuf_alloc;ikev2_process_kei"),
+ (1, "=ikev2_process_idi"),
+ (1, "ikev2_derive_auth_data;ikev2_build_auth"),
+ (1, "wpabuf_alloc;ikev2_build_sa_init"),
+ (2, "wpabuf_alloc;ikev2_build_sa_init"),
+ (3, "wpabuf_alloc;ikev2_build_sa_init"),
+ (4, "wpabuf_alloc;ikev2_build_sa_init"),
+ (5, "wpabuf_alloc;ikev2_build_sa_init"),
+ (6, "wpabuf_alloc;ikev2_build_sa_init"),
+ (1, "wpabuf_alloc;ikev2_build_sa_auth"),
+ (2, "wpabuf_alloc;ikev2_build_sa_auth"),
+ (1, "ikev2_build_auth;ikev2_build_sa_auth")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="IKEV2", identity="ikev2 user@domain",
+ password="ike password", erp="1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ok = False
+ for j in range(10):
+ state = dev[0].request('GET_ALLOC_FAIL')
+ if state.startswith('0:'):
+ ok = True
+ break
+ time.sleep(0.1)
+ if not ok:
+ raise Exception("No allocation failure seen for %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "wpabuf_alloc;ikev2_build_notify"),
+ (2, "wpabuf_alloc;ikev2_build_notify"),
+ (1, "ikev2_build_encrypted;ikev2_build_notify")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="IKEV2", identity="ikev2 user",
+ password="wrong password", erp="1",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ok = False
+ for j in range(10):
+ state = dev[0].request('GET_ALLOC_FAIL')
+ if state.startswith('0:'):
+ ok = True
+ break
+ time.sleep(0.1)
+ if not ok:
+ raise Exception("No allocation failure seen for %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "ikev2_integ_hash"),
+ (1, "ikev2_integ_hash;ikev2_decrypt_payload"),
+ (1, "os_get_random;ikev2_build_encrypted"),
+ (1, "ikev2_prf_plus;ikev2_derive_sk_keys"),
+ (1, "eap_ikev2_derive_keymat;eap_ikev2_peer_keymat"),
+ (1, "os_get_random;ikev2_build_sa_init"),
+ (2, "os_get_random;ikev2_build_sa_init"),
+ (1, "ikev2_integ_hash;eap_ikev2_validate_icv"),
+ (1, "hmac_sha1_vector;?ikev2_prf_hash;ikev2_derive_keys"),
+ (1, "hmac_sha1_vector;?ikev2_prf_hash;ikev2_derive_auth_data"),
+ (2, "hmac_sha1_vector;?ikev2_prf_hash;ikev2_derive_auth_data"),
+ (3, "hmac_sha1_vector;?ikev2_prf_hash;ikev2_derive_auth_data")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="IKEV2", identity="ikev2 user",
+ password="ike password", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ok = False
+ for j in range(10):
+ state = dev[0].request('GET_FAIL')
+ if state.startswith('0:'):
+ ok = True
+ break
+ time.sleep(0.1)
+ if not ok:
+ raise Exception("No failure seen for %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ params = {"ssid": "eap-test2", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP", "ieee8021x": "1",
+ "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
+ "fragment_size": "50"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ dev[0].scan_for_bss(hapd2.own_addr(), freq=2412)
+
+ tests = [(1, "eap_ikev2_build_frag_ack"),
+ (1, "wpabuf_alloc;eap_ikev2_process_fragment")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test2", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="IKEV2", identity="ikev2 user",
+ password="ike password", erp="1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ok = False
+ for j in range(10):
+ state = dev[0].request('GET_ALLOC_FAIL')
+ if state.startswith('0:'):
+ ok = True
+ break
+ time.sleep(0.1)
+ if not ok:
+ raise Exception("No allocation failure seen for %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def run_eap_ikev2_connect(dev):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="IKEV2", identity="ikev2 user",
+ password="ike password",
+ fragment_size="30", wait_connect=False)
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-DISCONNECTED"],
+ timeout=1)
+ dev.request("REMOVE_NETWORK all")
+ if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_eap_proto_ikev2_errors_server(dev, apdev):
+ """EAP-IKEV2 local error cases on server"""
+ check_eap_capa(dev[0], "IKEV2")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ tests = [(1, "eap_ikev2_init"),
+ (2, "=eap_ikev2_init"),
+ (3, "=eap_ikev2_init"),
+ (1, "eap_msg_alloc;eap_ikev2_build_msg"),
+ (1, "ikev2_initiator_build;eap_ikev2_buildReq"),
+ (1, "eap_ikev2_process_fragment"),
+ (1, "wpabuf_alloc_copy;ikev2_process_ker"),
+ (1, "ikev2_process_idr"),
+ (1, "ikev2_derive_auth_data;ikev2_process_auth_secret"),
+ (1, "ikev2_decrypt_payload;ikev2_process_sa_auth"),
+ (1, "ikev2_process_sa_auth_decrypted;ikev2_process_sa_auth"),
+ (1, "dh_init;ikev2_build_kei"),
+ (1, "ikev2_build_auth"),
+ (1, "wpabuf_alloc;ikev2_build_sa_init"),
+ (1, "ikev2_build_sa_auth"),
+ (1, "=ikev2_build_sa_auth"),
+ (2, "=ikev2_derive_auth_data"),
+ (1, "wpabuf_alloc;ikev2_build_sa_auth"),
+ (2, "wpabuf_alloc;=ikev2_build_sa_auth"),
+ (1, "ikev2_decrypt_payload;ikev2_process_sa_init_encr"),
+ (1, "dh_derive_shared;ikev2_derive_keys"),
+ (1, "=ikev2_derive_keys"),
+ (2, "=ikev2_derive_keys"),
+ (1, "eap_ikev2_getKey"),
+ (1, "eap_ikev2_get_emsk"),
+ (1, "eap_ikev2_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ run_eap_ikev2_connect(dev[0])
+
+ tests = [(1, "eap_ikev2_validate_icv;eap_ikev2_process_icv"),
+ (1, "eap_ikev2_server_keymat"),
+ (1, "ikev2_build_auth"),
+ (1, "os_get_random;ikev2_build_sa_init"),
+ (2, "os_get_random;ikev2_build_sa_init"),
+ (1, "ikev2_derive_keys"),
+ (2, "ikev2_derive_keys"),
+ (3, "ikev2_derive_keys"),
+ (4, "ikev2_derive_keys"),
+ (5, "ikev2_derive_keys"),
+ (6, "ikev2_derive_keys"),
+ (7, "ikev2_derive_keys"),
+ (8, "ikev2_derive_keys"),
+ (1, "ikev2_decrypt_payload;ikev2_process_sa_auth"),
+ (1, "eap_ikev2_process_icv;eap_ikev2_process")]
+ for count, func in tests:
+ with fail_test(hapd, count, func):
+ run_eap_ikev2_connect(dev[0])
+
+def start_ikev2_assoc(dev, hapd):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="IKEV2", identity="ikev2 user",
+ password="ike password", wait_connect=False)
+ proxy_msg(hapd, dev) # EAP-Identity/Request
+ proxy_msg(dev, hapd) # EAP-Identity/Response
+ proxy_msg(hapd, dev) # IKEV2 1
+
+def stop_ikev2_assoc(dev, hapd):
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+ hapd.dump_monitor()
+
+def test_eap_proto_ikev2_server(dev, apdev):
+ """EAP-IKEV2 protocol testing for the server"""
+ check_eap_capa(dev[0], "IKEV2")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+
+ # Successful exchange to verify proxying mechanism
+ start_ikev2_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # IKEV2 2
+ proxy_msg(hapd, dev[0]) # IKEV2 3
+ proxy_msg(dev[0], hapd) # IKEV2 4
+ proxy_msg(hapd, dev[0]) # EAP-Success
+ proxy_msg(hapd, dev[0]) # EAPOL-Key msg 1/4
+ proxy_msg(dev[0], hapd) # EAPOL-Key msg 2/4
+ proxy_msg(hapd, dev[0]) # EAPOL-Key msg 3/4
+ proxy_msg(dev[0], hapd) # EAPOL-Key msg 4/4
+ dev[0].wait_connected()
+ stop_ikev2_assoc(dev[0], hapd)
+
+ start_ikev2_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short EAP-IKEV2 header
+ hapd.note("IKEV2: Too short frame to include HDR")
+ msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "31"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_ikev2_assoc(dev[0], hapd)
+
+ start_ikev2_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short EAP-IKEV2 header - missing Message Length field
+ hapd.note("EAP-IKEV2: Message underflow")
+ msg = resp[0:4] + "0006" + resp[8:12] + "0006" + "3180"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_ikev2_assoc(dev[0], hapd)
+
+ start_ikev2_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short EAP-IKEV2 header - too small Message Length
+ hapd.note("EAP-IKEV2: Invalid Message Length (0; 1 remaining in this msg)")
+ msg = resp[0:4] + "000b" + resp[8:12] + "000b" + "318000000000ff"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_ikev2_assoc(dev[0], hapd)
+
+ start_ikev2_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short EAP-IKEV2 header - too large Message Length
+ hapd.note("EAP-IKEV2: Ignore too long message")
+ msg = resp[0:4] + "000b" + resp[8:12] + "000b" + "31c0bbccddeeff"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_ikev2_assoc(dev[0], hapd)
+
+ start_ikev2_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # No Message Length in first fragment
+ hapd.note("EAP-IKEV2: No Message Length field in a fragmented packet")
+ msg = resp[0:4] + "0007" + resp[8:12] + "0007" + "3140ff"
+ tx_msg(dev[0], hapd, msg)
+ rx_msg(hapd)
+ stop_ikev2_assoc(dev[0], hapd)
+
+ start_ikev2_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # First fragment (valid)
+ hapd.note("EAP-IKEV2: Received 1 bytes in first fragment, waiting for 255 bytes more")
+ msg = resp[0:4] + "000b" + resp[8:12] + "000b" + "31c000000100ff"
+ tx_msg(dev[0], hapd, msg)
+ req = rx_msg(hapd)
+ id, = struct.unpack('B', binascii.unhexlify(req)[5:6])
+ hapd.note("EAP-IKEV2: Received 1 bytes in first fragment, waiting for 254 bytes more")
+ payload = struct.pack('BBB', 49, 0x40, 0)
+ msg = struct.pack('>BBHBBH', 1, 0, 4 + len(payload), 2, id, 4 + len(payload)) + payload
+ tx_msg(dev[0], hapd, binascii.hexlify(msg).decode())
+ req = rx_msg(hapd)
+ id, = struct.unpack('B', binascii.unhexlify(req)[5:6])
+ hapd.note("EAP-IKEV2: Fragment overflow")
+ payload = struct.pack('BB', 49, 0x40) + 255*b'\x00'
+ msg = struct.pack('>BBHBBH', 1, 0, 4 + len(payload), 2, id, 4 + len(payload)) + payload
+ tx_msg(dev[0], hapd, binascii.hexlify(msg).decode())
+ rx_msg(hapd)
+ stop_ikev2_assoc(dev[0], hapd)
+
+ start_ikev2_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # IKEV2 2
+ req = proxy_msg(hapd, dev[0]) # IKEV2 3
+ id, = struct.unpack('B', binascii.unhexlify(req)[5:6])
+ # Missing ICV
+ hapd.note("EAP-IKEV2: The message should have included integrity checksum")
+ payload = struct.pack('BB', 49, 0) + b'\x00'
+ msg = struct.pack('>BBHBBH', 1, 0, 4 + len(payload), 2, id, 4 + len(payload)) + payload
+ tx_msg(dev[0], hapd, binascii.hexlify(msg).decode())
+ rx_msg(hapd)
+ stop_ikev2_assoc(dev[0], hapd)
+
+ tests = [("Unsupported HDR version 0x0 (expected 0x20)",
+ struct.pack('BB', 49, 0) + 16*b'\x00' +
+ struct.pack('>BBBBLL', 0, 0, 0, 0, 0, 0)),
+ ("IKEV2: Invalid length (HDR: 0 != RX: 28)",
+ struct.pack('BB', 49, 0) + 16*b'\x00' +
+ struct.pack('>BBBBLL', 0, 0x20, 0, 0, 0, 0)),
+ ("IKEV2: Unexpected Exchange Type 0 in SA_INIT state",
+ struct.pack('BB', 49, 0) + 16*b'\x00' +
+ struct.pack('>BBBBLL', 0, 0x20, 0, 0, 0, 28)),
+ ("IKEV2: Unexpected Flags value 0x0",
+ struct.pack('BB', 49, 0) + 16*b'\x00' +
+ struct.pack('>BBBBLL', 0, 0x20, 34, 0, 0, 28)),
+ ("IKEV2: SAr1 not received",
+ struct.pack('BB', 49, 0) + 16*b'\x00' +
+ struct.pack('>BBBBLL', 0, 0x20, 34, 0x20, 0, 28))]
+ for txt, payload in tests:
+ start_ikev2_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ id, = struct.unpack('B', binascii.unhexlify(resp)[5:6])
+ hapd.note(txt)
+ msg = struct.pack('>BBHBBH', 1, 0, 4 + len(payload), 2, id, 4 + len(payload)) + payload
+ tx_msg(dev[0], hapd, binascii.hexlify(msg).decode())
+ rx_msg(hapd)
+ stop_ikev2_assoc(dev[0], hapd)
+
+def test_eap_proto_mschapv2(dev, apdev):
+ """EAP-MSCHAPv2 protocol tests"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+
+ def mschapv2_handler(ctx, req):
+ logger.info("mschapv2_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_MSCHAPV2)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unknown MSCHAPv2 op_code")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1,
+ EAP_TYPE_MSCHAPV2,
+ 0, 0, 5, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid ms_len and unknown MSCHAPv2 op_code")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1,
+ EAP_TYPE_MSCHAPV2,
+ 255, 0, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Success before challenge")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1,
+ EAP_TYPE_MSCHAPV2,
+ 3, 0, 5, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure before challenge - required challenge field not present")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1,
+ EAP_TYPE_MSCHAPV2,
+ 4, 0, 5, 0)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure before challenge - invalid failure challenge len")
+ payload = b'C=12'
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 4, 0, 4 + len(payload)) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure before challenge - invalid failure challenge len")
+ payload = b'C=12 V=3'
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 4, 0, 4 + len(payload)) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure before challenge - invalid failure challenge")
+ payload = b'C=00112233445566778899aabbccddeefQ '
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 4, 0, 4 + len(payload)) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure before challenge - password expired")
+ payload = b'E=648 R=1 C=00112233445566778899aabbccddeeff V=3 M=Password expired'
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 4, 0, 4 + len(payload)) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Success after password change")
+ payload = b"S=1122334455667788990011223344556677889900"
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 3, 0, 4 + len(payload)) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid challenge length")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1,
+ EAP_TYPE_MSCHAPV2,
+ 1, 0, 4 + 1, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short challenge packet")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1,
+ EAP_TYPE_MSCHAPV2,
+ 1, 0, 4 + 1, 16)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1 + 16 + 6,
+ EAP_TYPE_MSCHAPV2,
+ 1, 0, 4 + 1 + 16 + 6, 16) + 16*b'A' + b'foobar'
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure - password expired")
+ payload = b'E=648 R=1 C=00112233445566778899aabbccddeeff V=3 M=Password expired'
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 4, 0, 4 + len(payload)) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Success after password change")
+ if len(req) != 591:
+ logger.info("Unexpected Change-Password packet length: %s" % len(req))
+ return None
+ data = req[9:]
+ enc_pw = data[0:516]
+ data = data[516:]
+ enc_hash = data[0:16]
+ data = data[16:]
+ peer_challenge = data[0:16]
+ data = data[16:]
+ # Reserved
+ data = data[8:]
+ nt_response = data[0:24]
+ data = data[24:]
+ flags = data
+ logger.info("enc_hash: " + binascii.hexlify(enc_hash).decode())
+ logger.info("peer_challenge: " + binascii.hexlify(peer_challenge).decode())
+ logger.info("nt_response: " + binascii.hexlify(nt_response).decode())
+ logger.info("flags: " + binascii.hexlify(flags).decode())
+
+ auth_challenge = binascii.unhexlify("00112233445566778899aabbccddeeff")
+ logger.info("auth_challenge: " + binascii.hexlify(auth_challenge).decode())
+
+ auth_resp = GenerateAuthenticatorResponse("new-pw", nt_response,
+ peer_challenge,
+ auth_challenge, "user")
+ payload = b"S=" + binascii.hexlify(auth_resp).decode().upper().encode()
+ logger.info("Success message payload: " + payload.decode())
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 3, 0, 4 + len(payload)) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure - password expired")
+ payload = b'E=648 R=1 C=00112233445566778899aabbccddeeff V=3 M=Password expired'
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 4, 0, 4 + len(payload)) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Success after password change")
+ if len(req) != 591:
+ logger.info("Unexpected Change-Password packet length: %s" % len(req))
+ return None
+ data = req[9:]
+ enc_pw = data[0:516]
+ data = data[516:]
+ enc_hash = data[0:16]
+ data = data[16:]
+ peer_challenge = data[0:16]
+ data = data[16:]
+ # Reserved
+ data = data[8:]
+ nt_response = data[0:24]
+ data = data[24:]
+ flags = data
+ logger.info("enc_hash: " + binascii.hexlify(enc_hash).decode())
+ logger.info("peer_challenge: " + binascii.hexlify(peer_challenge).decode())
+ logger.info("nt_response: " + binascii.hexlify(nt_response).decode())
+ logger.info("flags: " + binascii.hexlify(flags).decode())
+
+ auth_challenge = binascii.unhexlify("00112233445566778899aabbccddeeff")
+ logger.info("auth_challenge: " + binascii.hexlify(auth_challenge).decode())
+
+ auth_resp = GenerateAuthenticatorResponse("new-pw", nt_response,
+ peer_challenge,
+ auth_challenge, "user")
+ payload = b"S=" + binascii.hexlify(auth_resp).decode().upper().encode()
+ logger.info("Success message payload: " + payload.decode())
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 3, 0, 4 + len(payload)) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1 + 16 + 6,
+ EAP_TYPE_MSCHAPV2,
+ 1, 0, 4 + 1 + 16 + 6, 16) + 16*b'A' + b'foobar'
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure - authentication failure")
+ payload = b'E=691 R=1 C=00112233445566778899aabbccddeeff V=3 M=Authentication failed'
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 4, 0, 4 + len(payload)) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1 + 16 + 6,
+ EAP_TYPE_MSCHAPV2,
+ 1, 0, 4 + 1 + 16 + 6, 16) + 16*b'A' + b'foobar'
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure - authentication failure")
+ payload = b'E=691 R=1 C=00112233445566778899aabbccddeeff V=3 M=Authentication failed (2)'
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 4, 0, 4 + len(payload)) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Challenge - invalid ms_len and workaround disabled")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1 + 16 + 6,
+ EAP_TYPE_MSCHAPV2,
+ 1, 0, 4 + 1 + 16 + 6 + 1, 16) + 16*b'A' + b'foobar'
+
+ return None
+
+ srv = start_radius_server(mschapv2_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(0, 16):
+ logger.info("RUN: %d" % i)
+ if i == 12:
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MSCHAPV2", identity="user",
+ password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+ wait_connect=False)
+ elif i == 14:
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MSCHAPV2", identity="user",
+ phase2="mschapv2_retry=0",
+ password="password", wait_connect=False)
+ elif i == 15:
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MSCHAPV2", identity="user",
+ eap_workaround="0",
+ password="password", wait_connect=False)
+ else:
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MSCHAPV2", identity="user",
+ password="password", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+
+ if i in [8, 11, 12]:
+ ev = dev[0].wait_event(["CTRL-REQ-NEW_PASSWORD"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on new password request")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-NEW_PASSWORD-" + id + ":new-pw")
+ if i in [11, 12]:
+ ev = dev[0].wait_event(["CTRL-EVENT-PASSWORD-CHANGED"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on password change")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP success")
+ else:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+
+ if i in [13]:
+ ev = dev[0].wait_event(["CTRL-REQ-IDENTITY"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on identity request")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-IDENTITY-" + id + ":user")
+
+ ev = dev[0].wait_event(["CTRL-REQ-PASSWORD"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on password request")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-PASSWORD-" + id + ":password")
+
+ # TODO: Does this work correctly?
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+
+ if i in [4, 5, 6, 7, 14]:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ else:
+ time.sleep(0.05)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_mschapv2_errors(dev, apdev):
+ """EAP-MSCHAPv2 protocol tests (error paths)"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+
+ def mschapv2_fail_password_expired(ctx):
+ logger.info("Test: Failure before challenge - password expired")
+ payload = b'E=648 R=1 C=00112233445566778899aabbccddeeff V=3 M=Password expired'
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 4, 0, 4 + len(payload)) + payload
+
+ def mschapv2_success_after_password_change(ctx, req=None):
+ logger.info("Test: Success after password change")
+ if req is None or len(req) != 591:
+ payload = b"S=1122334455667788990011223344556677889900"
+ else:
+ data = req[9:]
+ enc_pw = data[0:516]
+ data = data[516:]
+ enc_hash = data[0:16]
+ data = data[16:]
+ peer_challenge = data[0:16]
+ data = data[16:]
+ # Reserved
+ data = data[8:]
+ nt_response = data[0:24]
+ data = data[24:]
+ flags = data
+ logger.info("enc_hash: " + binascii.hexlify(enc_hash).decode())
+ logger.info("peer_challenge: " + binascii.hexlify(peer_challenge).decode())
+ logger.info("nt_response: " + binascii.hexlify(nt_response).decode())
+ logger.info("flags: " + binascii.hexlify(flags).decode())
+
+ auth_challenge = binascii.unhexlify("00112233445566778899aabbccddeeff")
+ logger.info("auth_challenge: " + binascii.hexlify(auth_challenge).decode())
+
+ auth_resp = GenerateAuthenticatorResponse("new-pw", nt_response,
+ peer_challenge,
+ auth_challenge, "user")
+ payload = b"S=" + binascii.hexlify(auth_resp).decode().upper().encode()
+ return struct.pack(">BBHBBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + len(payload),
+ EAP_TYPE_MSCHAPV2,
+ 3, 0, 4 + len(payload)) + payload
+
+ def mschapv2_handler(ctx, req):
+ logger.info("mschapv2_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_fail_password_expired(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_success_after_password_change(ctx, req)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_fail_password_expired(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_success_after_password_change(ctx, req)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_fail_password_expired(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_success_after_password_change(ctx, req)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_fail_password_expired(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_success_after_password_change(ctx, req)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_fail_password_expired(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_success_after_password_change(ctx, req)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_fail_password_expired(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_success_after_password_change(ctx, req)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_fail_password_expired(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_success_after_password_change(ctx, req)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_fail_password_expired(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_success_after_password_change(ctx, req)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_fail_password_expired(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ return mschapv2_success_after_password_change(ctx, req)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ return None
+
+ srv = start_radius_server(mschapv2_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ tests = ["os_get_random;eap_mschapv2_change_password",
+ "generate_nt_response;eap_mschapv2_change_password",
+ "get_master_key;eap_mschapv2_change_password",
+ "nt_password_hash;eap_mschapv2_change_password",
+ "old_nt_password_hash_encrypted_with_new_nt_password_hash"]
+ for func in tests:
+ with fail_test(dev[0], 1, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MSCHAPV2", identity="user",
+ password="password", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-REQ-NEW_PASSWORD"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on new password request")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-NEW_PASSWORD-" + id + ":new-pw")
+ time.sleep(0.1)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+
+ tests = ["encrypt_pw_block_with_password_hash;eap_mschapv2_change_password",
+ "nt_password_hash;eap_mschapv2_change_password",
+ "nt_password_hash;eap_mschapv2_success"]
+ for func in tests:
+ with fail_test(dev[0], 1, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MSCHAPV2", identity="user",
+ password_hex="hash:8846f7eaee8fb117ad06bdd830b7586c",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-REQ-NEW_PASSWORD"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on new password request")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-NEW_PASSWORD-" + id + ":new-pw")
+ time.sleep(0.1)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+
+ tests = ["eap_msg_alloc;eap_mschapv2_change_password"]
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MSCHAPV2", identity="user",
+ password="password", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-REQ-NEW_PASSWORD"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on new password request")
+ id = ev.split(':')[0].split('-')[-1]
+ dev[0].request("CTRL-RSP-NEW_PASSWORD-" + id + ":new-pw")
+ time.sleep(0.1)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_pwd(dev, apdev):
+ """EAP-pwd protocol tests"""
+ check_eap_capa(dev[0], "PWD")
+
+ global eap_proto_pwd_test_done, eap_proto_pwd_test_wait
+ eap_proto_pwd_test_done = False
+ eap_proto_pwd_test_wait = False
+
+ def pwd_handler(ctx, req):
+ logger.info("pwd_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ global eap_proto_pwd_test_wait
+ eap_proto_pwd_test_wait = False
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing payload")
+ # EAP-pwd: Got a frame but pos is not NULL and len is 0
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'], 4 + 1,
+ EAP_TYPE_PWD)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing Total-Length field")
+ # EAP-pwd: Frame too short to contain Total-Length field
+ payload = struct.pack("B", 0x80)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too large Total-Length")
+ # EAP-pwd: Incoming fragments whose total length = 65535
+ payload = struct.pack(">BH", 0x80, 65535)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: First fragment")
+ # EAP-pwd: Incoming fragments whose total length = 10
+ # EAP-pwd: ACKing a 0 byte fragment
+ payload = struct.pack(">BH", 0xc0, 10)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Total-Length value in the second fragment")
+ # EAP-pwd: Incoming fragments whose total length = 0
+ # EAP-pwd: Unexpected new fragment start when previous fragment is still in use
+ payload = struct.pack(">BH", 0x80, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: First and only fragment")
+ # EAP-pwd: Incoming fragments whose total length = 0
+ # EAP-pwd: processing frame: exch 0, len 0
+ # EAP-pwd: Ignoring message with unknown opcode 128
+ payload = struct.pack(">BH", 0x80, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: First and only fragment with extra data")
+ # EAP-pwd: Incoming fragments whose total length = 0
+ # EAP-pwd: processing frame: exch 0, len 1
+ # EAP-pwd: Ignoring message with unknown opcode 128
+ payload = struct.pack(">BHB", 0x80, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: First fragment")
+ # EAP-pwd: Incoming fragments whose total length = 2
+ # EAP-pwd: ACKing a 1 byte fragment
+ payload = struct.pack(">BHB", 0xc0, 2, 1)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Extra data in the second fragment")
+ # EAP-pwd: Buffer overflow attack detected (3 vs. 1)!
+ payload = struct.pack(">BBB", 0x0, 2, 3)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short id exchange")
+ # EAP-pwd: processing frame: exch 1, len 0
+ # EAP-PWD: PWD-ID-Req -> FAILURE
+ payload = struct.pack(">B", 0x01)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unsupported rand func in id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=0 random=0 prf=0 prep=0
+ # EAP-PWD: PWD-ID-Req -> FAILURE
+ payload = struct.pack(">BHBBLB", 0x01, 0, 0, 0, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unsupported prf in id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=0 prep=0
+ # EAP-PWD: PWD-ID-Req -> FAILURE
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 0, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unsupported password pre-processing technique in id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=255
+ # EAP-PWD: Unsupported password pre-processing technique (Prep=255)
+ # EAP-PWD: PWD-ID-Req -> FAILURE
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 255)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=0
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected id exchange")
+ # EAP-pwd: processing frame: exch 1, len 9
+ # EAP-PWD: PWD-Commit-Req -> FAILURE
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected commit exchange")
+ # EAP-pwd: processing frame: exch 2, len 0
+ # EAP-PWD: PWD-ID-Req -> FAILURE
+ payload = struct.pack(">B", 0x02)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=0
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=None)")
+ # EAP-pwd commit request, password prep is NONE
+ # EAP-pwd: Unexpected Commit payload length 0 (expected 96)
+ payload = struct.pack(">B", 0x02)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=0
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Commit payload with all zeros values --> Shared key at infinity")
+ # EAP-pwd: Invalid coordinate in element
+ payload = struct.pack(">B", 0x02) + 96*b'\0'
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=0
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Commit payload with valid values")
+ # EAP-pwd commit request, password prep is NONE
+ element = binascii.unhexlify("8dcab2862c5396839a6bac0c689ff03d962863108e7c275bbf1d6eedf634ee832a214db99f0d0a1a6317733eecdd97f0fc4cda19f57e1bb9bb9c8dcf8c60ba6f")
+ scalar = binascii.unhexlify("450f31e058cf2ac2636a5d6e2b3c70b1fcc301957f0716e77f13aa69f9a2e5bd")
+ payload = struct.pack(">B", 0x02) + element + scalar
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Confirm payload length 0")
+ # EAP-pwd: Unexpected Confirm payload length 0 (expected 32)
+ payload = struct.pack(">B", 0x03)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=0
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Commit payload with valid values")
+ # EAP-pwd commit request, password prep is NONE
+ element = binascii.unhexlify("8dcab2862c5396839a6bac0c689ff03d962863108e7c275bbf1d6eedf634ee832a214db99f0d0a1a6317733eecdd97f0fc4cda19f57e1bb9bb9c8dcf8c60ba6f")
+ scalar = binascii.unhexlify("450f31e058cf2ac2636a5d6e2b3c70b1fcc301957f0716e77f13aa69f9a2e5bd")
+ payload = struct.pack(">B", 0x02) + element + scalar
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Confirm payload with incorrect value")
+ # EAP-PWD (peer): confirm did not verify
+ payload = struct.pack(">B", 0x03) + 32*b'\0'
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected confirm exchange")
+ # EAP-pwd: processing frame: exch 3, len 0
+ # EAP-PWD: PWD-ID-Req -> FAILURE
+ payload = struct.pack(">B", 0x03)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unsupported password pre-processing technique SASLprep in id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=2
+ # EAP-PWD: Unsupported password pre-processing technique (Prep=2)
+ # EAP-PWD: PWD-ID-Req -> FAILURE
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 2)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=1
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 1)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=MS)")
+ # EAP-pwd commit request, password prep is MS
+ # EAP-pwd: Unexpected Commit payload length 0 (expected 96)
+ payload = struct.pack(">B", 0x02)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=3
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 3)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=ssha1)")
+ # EAP-pwd commit request, password prep is salted sha1
+ # EAP-pwd: Invalid Salt-len
+ payload = struct.pack(">B", 0x02)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=3
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 3)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=ssha1)")
+ # EAP-pwd commit request, password prep is salted sha1
+ # EAP-pwd: Invalid Salt-len
+ payload = struct.pack(">BB", 0x02, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=3
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 3)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=ssha1)")
+ # EAP-pwd commit request, password prep is salted sha1
+ # EAP-pwd: Unexpected Commit payload length 1 (expected 98)
+ payload = struct.pack(">BB", 0x02, 1)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=4
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 4)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=ssha256)")
+ # EAP-pwd commit request, password prep is salted sha256
+ # EAP-pwd: Invalid Salt-len
+ payload = struct.pack(">B", 0x02)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=4
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 4)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=ssha256)")
+ # EAP-pwd commit request, password prep is salted sha256
+ # EAP-pwd: Invalid Salt-len
+ payload = struct.pack(">BB", 0x02, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=4
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 4)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=ssha256)")
+ # EAP-pwd commit request, password prep is salted sha256
+ # EAP-pwd: Unexpected Commit payload length 1 (expected 98)
+ payload = struct.pack(">BB", 0x02, 1)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=5
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 5)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=ssha512)")
+ # EAP-pwd commit request, password prep is salted sha512
+ # EAP-pwd: Invalid Salt-len
+ payload = struct.pack(">B", 0x02)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=5
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 5)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=ssha512)")
+ # EAP-pwd commit request, password prep is salted sha512
+ # EAP-pwd: Invalid Salt-len
+ payload = struct.pack(">BB", 0x02, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ eap_proto_pwd_test_wait = True
+ logger.info("Test: Valid id exchange")
+ # EAP-PWD: Server EAP-pwd-ID proposal: group=19 random=1 prf=1 prep=5
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 5)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Commit payload length (prep=ssha512)")
+ # EAP-pwd commit request, password prep is salted sha512
+ # EAP-pwd: Unexpected Commit payload length 1 (expected 98)
+ payload = struct.pack(">BB", 0x02, 1)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ logger.info("No more test responses available - test case completed")
+ global eap_proto_pwd_test_done
+ eap_proto_pwd_test_done = True
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(pwd_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ i = 0
+ while not eap_proto_pwd_test_done:
+ i += 1
+ logger.info("Running connection iteration %d" % i)
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ password="secret password",
+ wait_connect=False)
+ ok = False
+ for j in range(5):
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS",
+ "CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ if "CTRL-EVENT-EAP-PROPOSED-METHOD" in ev:
+ ok = True
+ break
+ if "CTRL-EVENT-EAP-STATUS" in ev and "status='completion' parameter='failure'" in ev:
+ ok = True
+ break
+ if not ok:
+ raise Exception("Expected EAP event not seen")
+ if eap_proto_pwd_test_wait:
+ for k in range(20):
+ time.sleep(0.1)
+ if not eap_proto_pwd_test_wait:
+ break
+ if eap_proto_pwd_test_wait:
+ raise Exception("eap_proto_pwd_test_wait not cleared")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_pwd_invalid_scalar(dev, apdev):
+ """EAP-pwd protocol tests - invalid server scalar"""
+ check_eap_capa(dev[0], "PWD")
+ run_eap_proto_pwd_invalid_scalar(dev, apdev, 32*b'\0')
+ run_eap_proto_pwd_invalid_scalar(dev, apdev, 31*b'\0' + b'\x01')
+ # Group Order
+ val = binascii.unhexlify("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551")
+ run_eap_proto_pwd_invalid_scalar(dev, apdev, val)
+ # Group Order - 1
+ val = binascii.unhexlify("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632550")
+ run_eap_proto_pwd_invalid_scalar(dev, apdev, val, valid_scalar=True)
+
+def run_eap_proto_pwd_invalid_scalar(dev, apdev, scalar, valid_scalar=False):
+ global eap_proto_pwd_invalid_scalar_fail
+ eap_proto_pwd_invalid_scalar_fail = False
+
+ def pwd_handler(ctx, req):
+ logger.info("pwd_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid id exchange")
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Commit payload with invalid scalar")
+ payload = struct.pack(">B", 0x02) + binascii.unhexlify("67feb2b46d59e6dd3af3a429ec9c04a949337564615d3a2c19bdf6826eb6f5efa303aed86af3a072ed819d518d620adb2659f0e84c4f8b739629db8c93088cfc") + scalar
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Confirm message next - should not get here")
+ global eap_proto_pwd_invalid_scalar_fail
+ eap_proto_pwd_invalid_scalar_fail = True
+ payload = struct.pack(">B", 0x03) + 32*b'\0'
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ logger.info("No more test responses available - test case completed")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(pwd_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ password="secret password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+ if valid_scalar and not eap_proto_pwd_invalid_scalar_fail:
+ raise Exception("Peer did not accept valid EAP-pwd-Commit scalar")
+ if not valid_scalar and eap_proto_pwd_invalid_scalar_fail:
+ raise Exception("Peer did not stop after invalid EAP-pwd-Commit scalar")
+
+def test_eap_proto_pwd_invalid_element(dev, apdev):
+ """EAP-pwd protocol tests - invalid server element"""
+ check_eap_capa(dev[0], "PWD")
+ # Invalid x,y coordinates
+ run_eap_proto_pwd_invalid_element(dev, apdev, 64*b'\x00')
+ run_eap_proto_pwd_invalid_element(dev, apdev, 32*b'\x00' + 32*b'\x01')
+ run_eap_proto_pwd_invalid_element(dev, apdev, 32*b'\x01' + 32*b'\x00')
+ run_eap_proto_pwd_invalid_element(dev, apdev, 32*b'\xff' + 32*b'\x01')
+ run_eap_proto_pwd_invalid_element(dev, apdev, 32*b'\x01' + 32*b'\xff')
+ run_eap_proto_pwd_invalid_element(dev, apdev, 64*b'\xff')
+ # Not on curve
+ run_eap_proto_pwd_invalid_element(dev, apdev, 64*b'\x01')
+
+def run_eap_proto_pwd_invalid_element(dev, apdev, element):
+ global eap_proto_pwd_invalid_element_fail
+ eap_proto_pwd_invalid_element_fail = False
+
+ def pwd_handler(ctx, req):
+ logger.info("pwd_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid id exchange")
+ payload = struct.pack(">BHBBLB", 0x01, 19, 1, 1, 0, 0)
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Commit payload with invalid element")
+ payload = struct.pack(">B", 0x02) + element + 31*b'\0' + b'\x02'
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Confirm message next - should not get here")
+ global eap_proto_pwd_invalid_element_fail
+ eap_proto_pwd_invalid_element_fail = True
+ payload = struct.pack(">B", 0x03) + 32*b'\0'
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + len(payload), EAP_TYPE_PWD) + payload
+
+ logger.info("No more test responses available - test case completed")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(pwd_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ password="secret password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+ if eap_proto_pwd_invalid_element_fail:
+ raise Exception("Peer did not stop after invalid EAP-pwd-Commit element")
+
+def rx_msg(src):
+ ev = src.wait_event(["EAPOL-TX"], timeout=5)
+ if ev is None:
+ raise Exception("No EAPOL-TX")
+ return ev.split(' ')[2]
+
+def tx_msg(src, dst, msg):
+ dst.request("EAPOL_RX " + src.own_addr() + " " + msg)
+
+def proxy_msg(src, dst):
+ msg = rx_msg(src)
+ tx_msg(src, dst, msg)
+ return msg
+
+def start_pwd_exchange(dev, ap):
+ check_eap_capa(dev, "PWD")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(ap, params)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev.request("SET ext_eapol_frame_io 1")
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PWD", identity="pwd user", password="secret password",
+ wait_connect=False, scan_freq="2412")
+ proxy_msg(hapd, dev) # EAP-Identity/Request
+ proxy_msg(dev, hapd) # EAP-Identity/Response
+ proxy_msg(hapd, dev) # EAP-pwd-ID/Request
+ proxy_msg(dev, hapd) # EAP-pwd-ID/Response
+ return hapd
+
+def test_eap_proto_pwd_unexpected_fragment(dev, apdev):
+ """EAP-pwd protocol tests - unexpected more-fragment frame"""
+ hapd = start_pwd_exchange(dev[0], apdev[0])
+
+ # EAP-pwd-Commit/Request
+ req = rx_msg(hapd)
+ if req[18:20] != "02":
+ raise Exception("Unexpected EAP-pwd-Commit/Request flag")
+ msg = req[0:18] + "42" + req[20:]
+ tx_msg(hapd, dev[0], msg)
+
+def test_eap_proto_pwd_reflection_attack(dev, apdev):
+ """EAP-pwd protocol tests - reflection attack on the server"""
+ hapd = start_pwd_exchange(dev[0], apdev[0])
+
+ # EAP-pwd-Commit/Request
+ req = proxy_msg(hapd, dev[0])
+ if len(req) != 212:
+ raise Exception("Unexpected EAP-pwd-Commit/Response length")
+
+ # EAP-pwd-Commit/Response
+ resp = rx_msg(dev[0])
+ # Reflect same Element/Scalar back to the server
+ msg = resp[0:20] + req[20:]
+ tx_msg(dev[0], hapd, msg)
+
+ # EAP-pwd-Commit/Response or EAP-Failure
+ req = rx_msg(hapd)
+ if req[8:10] != "04":
+ # reflect EAP-pwd-Confirm/Request
+ msg = req[0:8] + "02" + req[10:]
+ tx_msg(dev[0], hapd, msg)
+ req = rx_msg(hapd)
+ if req[8:10] == "03":
+ raise Exception("EAP-Success after reflected Element/Scalar")
+ raise Exception("No EAP-Failure to reject invalid EAP-pwd-Commit/Response")
+
+def test_eap_proto_pwd_invalid_scalar_peer(dev, apdev):
+ """EAP-pwd protocol tests - invalid peer scalar"""
+ run_eap_proto_pwd_invalid_scalar_peer(dev, apdev, 32*"00")
+ run_eap_proto_pwd_invalid_scalar_peer(dev, apdev, 31*"00" + "01")
+ # Group Order
+ run_eap_proto_pwd_invalid_scalar_peer(dev, apdev,
+ "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551")
+ # Group Order - 1
+ run_eap_proto_pwd_invalid_scalar_peer(dev, apdev,
+ "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632550",
+ valid_scalar=True)
+
+def run_eap_proto_pwd_invalid_scalar_peer(dev, apdev, scalar,
+ valid_scalar=False):
+ hapd = start_pwd_exchange(dev[0], apdev[0])
+ proxy_msg(hapd, dev[0]) # EAP-pwd-Commit/Request
+
+ # EAP-pwd-Commit/Response
+ resp = rx_msg(dev[0])
+ # Replace scalar with an invalid value
+ msg = resp[0:20] + resp[20:148] + scalar
+ tx_msg(dev[0], hapd, msg)
+
+ # EAP-pwd-Commit/Response or EAP-Failure
+ req = rx_msg(hapd)
+ if valid_scalar and req[8:10] == "04":
+ raise Exception("Unexpected EAP-Failure with valid scalar")
+ if not valid_scalar and req[8:10] != "04":
+ raise Exception("No EAP-Failure to reject invalid scalar")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ hapd.disable()
+
+def test_eap_proto_pwd_invalid_element_peer(dev, apdev):
+ """EAP-pwd protocol tests - invalid peer element"""
+ # Invalid x,y coordinates
+ run_eap_proto_pwd_invalid_element_peer(dev, apdev, 64*'00')
+ run_eap_proto_pwd_invalid_element_peer(dev, apdev, 32*'00' + 32*'01')
+ run_eap_proto_pwd_invalid_element_peer(dev, apdev, 32*'01' + 32*'00')
+ run_eap_proto_pwd_invalid_element_peer(dev, apdev, 32*'ff' + 32*'01')
+ run_eap_proto_pwd_invalid_element_peer(dev, apdev, 32*'01' + 32*'ff')
+ run_eap_proto_pwd_invalid_element_peer(dev, apdev, 64*'ff')
+ # Not on curve
+ run_eap_proto_pwd_invalid_element_peer(dev, apdev, 64*'01')
+
+def run_eap_proto_pwd_invalid_element_peer(dev, apdev, element):
+ hapd = start_pwd_exchange(dev[0], apdev[0])
+ proxy_msg(hapd, dev[0]) # EAP-pwd-Commit/Request
+
+ # EAP-pwd-Commit/Response
+ resp = rx_msg(dev[0])
+ # Replace element with an invalid value
+ msg = resp[0:20] + element + resp[148:]
+ tx_msg(dev[0], hapd, msg)
+
+ # EAP-pwd-Commit/Response or EAP-Failure
+ req = rx_msg(hapd)
+ if req[8:10] != "04":
+ raise Exception("No EAP-Failure to reject invalid element")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ hapd.disable()
+
+def test_eap_proto_pwd_errors(dev, apdev):
+ """EAP-pwd local error cases"""
+ check_eap_capa(dev[0], "PWD")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(1, 4):
+ with alloc_fail(dev[0], i, "eap_pwd_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ password="secret password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "eap_pwd_get_session_id"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ fragment_size="0",
+ password="secret password")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ funcs = ["eap_pwd_getkey", "eap_pwd_get_emsk",
+ "=wpabuf_alloc;eap_pwd_perform_commit_exchange",
+ "=wpabuf_alloc;eap_pwd_perform_confirm_exchange"]
+ for func in funcs:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user@domain",
+ password="secret password", erp="1",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ for i in range(1, 5):
+ with alloc_fail(dev[0], i, "eap_pwd_perform_id_exchange"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ password="secret password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ok = False
+ for j in range(10):
+ state = dev[0].request('GET_ALLOC_FAIL')
+ if state.startswith('0:'):
+ ok = True
+ break
+ time.sleep(0.1)
+ if not ok:
+ raise Exception("No allocation failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;eap_pwd_perform_id_exchange"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ password="secret password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ for i in range(1, 9):
+ with alloc_fail(dev[0], i, "eap_pwd_perform_commit_exchange"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ password="secret password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ok = False
+ for j in range(10):
+ state = dev[0].request('GET_ALLOC_FAIL')
+ if state.startswith('0:'):
+ ok = True
+ break
+ time.sleep(0.1)
+ if not ok:
+ raise Exception("No allocation failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ for i in range(1, 12):
+ with alloc_fail(dev[0], i, "eap_pwd_perform_confirm_exchange"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ password="secret password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ok = False
+ for j in range(10):
+ state = dev[0].request('GET_ALLOC_FAIL')
+ if state.startswith('0:'):
+ ok = True
+ break
+ time.sleep(0.1)
+ if not ok:
+ raise Exception("No allocation failure seen")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ for i in range(1, 5):
+ with alloc_fail(dev[0], i, "eap_msg_alloc;=eap_pwd_process"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ password="secret password", fragment_size="50",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ # No password configured
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD vendor=0 method=52"],
+ timeout=15)
+ if ev is None:
+ raise Exception("EAP-pwd not started")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ funcs = [(1, "hash_nt_password_hash;eap_pwd_perform_commit_exchange"),
+ (1, "=crypto_bignum_init;eap_pwd_perform_commit_exchange"),
+ (1, "=crypto_ec_point_init;eap_pwd_perform_commit_exchange"),
+ (2, "=crypto_ec_point_init;eap_pwd_perform_commit_exchange"),
+ (1, "=crypto_ec_point_mul;eap_pwd_perform_commit_exchange"),
+ (2, "=crypto_ec_point_mul;eap_pwd_perform_commit_exchange"),
+ (3, "=crypto_ec_point_mul;eap_pwd_perform_commit_exchange"),
+ (1, "=crypto_ec_point_add;eap_pwd_perform_commit_exchange"),
+ (1, "=crypto_ec_point_invert;eap_pwd_perform_commit_exchange"),
+ (1, "=crypto_ec_point_to_bin;eap_pwd_perform_commit_exchange"),
+ (1, "crypto_hash_finish;eap_pwd_kdf"),
+ (1, "crypto_ec_point_from_bin;eap_pwd_get_element"),
+ (3, "crypto_bignum_init;compute_password_element"),
+ (4, "crypto_bignum_init;compute_password_element"),
+ (1, "crypto_bignum_init_set;compute_password_element"),
+ (2, "crypto_bignum_init_set;compute_password_element"),
+ (3, "crypto_bignum_init_set;compute_password_element"),
+ (1, "crypto_bignum_to_bin;compute_password_element"),
+ (1, "crypto_ec_point_compute_y_sqr;compute_password_element"),
+ (1, "crypto_ec_point_solve_y_coord;compute_password_element"),
+ (1, "crypto_bignum_rand;compute_password_element"),
+ (1, "crypto_bignum_sub;compute_password_element")]
+ for count, func in funcs:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd-hash",
+ password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP-Failure reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ params = {"ssid": "eap-test2", "wpa": "2", "wpa_key_mgmt": "WPA-EAP",
+ "rsn_pairwise": "CCMP", "ieee8021x": "1",
+ "eap_server": "1", "eap_user_file": "auth_serv/eap_user.conf",
+ "pwd_group": "19", "fragment_size": "40"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ dev[0].scan_for_bss(hapd2.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;=eap_pwd_process"):
+ dev[0].connect("eap-test2", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd user",
+ password="secret password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ for i in range(1, 5):
+ with fail_test(dev[0], i,
+ "=crypto_ec_point_to_bin;eap_pwd_perform_confirm_exchange"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PWD", identity="pwd-hash",
+ password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP-Failure reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def run_eap_pwd_connect(dev, hash=True, fragment=2000):
+ if hash:
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ fragment_size=str(fragment),
+ eap="PWD", identity="pwd-hash",
+ password_hex="hash:e3718ece8ab74792cbbfffd316d2d19a",
+ scan_freq="2412", wait_connect=False)
+ else:
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ fragment_size=str(fragment),
+ eap="PWD", identity="pwd-hash-sha1",
+ password="secret password",
+ scan_freq="2412", wait_connect=False)
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS", "CTRL-EVENT-EAP-FAILURE",
+ "CTRL-EVENT-DISCONNECTED"],
+ timeout=1)
+ dev.request("REMOVE_NETWORK all")
+ if not ev or "CTRL-EVENT-DISCONNECTED" not in ev:
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_eap_proto_pwd_errors_server(dev, apdev):
+ """EAP-pwd local error cases on server"""
+ check_eap_capa(dev[0], "PWD")
+ params = int_eap_server_params()
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ tests = [(1, "eap_pwd_init"),
+ (2, "eap_pwd_init"),
+ (3, "eap_pwd_init"),
+ (1, "eap_pwd_build_id_req"),
+ (1, "eap_pwd_build_commit_req"),
+ (1, "eap_pwd_build_confirm_req"),
+ (1, "eap_pwd_h_init;eap_pwd_build_confirm_req"),
+ (1, "wpabuf_alloc;eap_pwd_build_confirm_req"),
+ (1, "eap_msg_alloc;eap_pwd_build_req"),
+ (1, "eap_pwd_process_id_resp"),
+ (1, "get_eap_pwd_group;eap_pwd_process_id_resp"),
+ (1, "eap_pwd_process_confirm_resp"),
+ (1, "eap_pwd_h_init;eap_pwd_process_confirm_resp"),
+ (1, "compute_keys;eap_pwd_process_confirm_resp"),
+ (1, "eap_pwd_getkey"),
+ (1, "eap_pwd_get_emsk"),
+ (1, "eap_pwd_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ run_eap_pwd_connect(dev[0], hash=True)
+
+ tests = [(1, "eap_msg_alloc;eap_pwd_build_req"),
+ (2, "eap_msg_alloc;eap_pwd_build_req"),
+ (1, "wpabuf_alloc;eap_pwd_process")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ run_eap_pwd_connect(dev[0], hash=True, fragment=13)
+
+ tests = [(4, "eap_pwd_init")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ run_eap_pwd_connect(dev[0], hash=False)
+
+ tests = [(1, "eap_pwd_build_id_req"),
+ (1, "eap_pwd_build_commit_req"),
+ (1, "crypto_ec_point_mul;eap_pwd_build_commit_req"),
+ (1, "crypto_ec_point_invert;eap_pwd_build_commit_req"),
+ (1, "crypto_ec_point_to_bin;eap_pwd_build_commit_req"),
+ (1, "crypto_ec_point_to_bin;eap_pwd_build_confirm_req"),
+ (2, "=crypto_ec_point_to_bin;eap_pwd_build_confirm_req"),
+ (1, "hash_nt_password_hash;eap_pwd_process_id_resp"),
+ (1, "compute_password_element;eap_pwd_process_id_resp"),
+ (1, "crypto_bignum_init;eap_pwd_process_commit_resp"),
+ (1, "crypto_ec_point_mul;eap_pwd_process_commit_resp"),
+ (2, "crypto_ec_point_mul;eap_pwd_process_commit_resp"),
+ (1, "crypto_ec_point_add;eap_pwd_process_commit_resp"),
+ (1, "crypto_ec_point_to_bin;eap_pwd_process_confirm_resp"),
+ (2, "=crypto_ec_point_to_bin;eap_pwd_process_confirm_resp")]
+ for count, func in tests:
+ with fail_test(hapd, count, func):
+ run_eap_pwd_connect(dev[0], hash=True)
+
+def start_pwd_assoc(dev, hapd):
+ dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PWD", identity="pwd user", password="secret password",
+ wait_connect=False, scan_freq="2412")
+ proxy_msg(hapd, dev) # EAP-Identity/Request
+ proxy_msg(dev, hapd) # EAP-Identity/Response
+ proxy_msg(hapd, dev) # EAP-pwd-Identity/Request
+
+def stop_pwd_assoc(dev, hapd):
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+ hapd.dump_monitor()
+
+def test_eap_proto_pwd_server(dev, apdev):
+ """EAP-pwd protocol testing for the server"""
+ check_eap_capa(dev[0], "PWD")
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+
+ start_pwd_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Replace exch field with unexpected value
+ # --> EAP-pwd: Unexpected opcode=4 in state=0
+ msg = resp[0:18] + "04" + resp[20:]
+ tx_msg(dev[0], hapd, msg)
+
+ # Too short EAP-pwd header (no flags/exch field)
+ # --> EAP-pwd: Invalid frame
+ msg = resp[0:4] + "0005" + resp[8:12] + "0005" + "34"
+ tx_msg(dev[0], hapd, msg)
+
+ # Too short EAP-pwd header (L=1 but only one octet of total length field)
+ # --> EAP-pwd: Frame too short to contain Total-Length field
+ msg = resp[0:4] + "0007" + resp[8:12] + "0007" + "34" + "81ff"
+ tx_msg(dev[0], hapd, msg)
+ # server continues exchange, so start from scratch for the next step
+ rx_msg(hapd)
+ stop_pwd_assoc(dev[0], hapd)
+
+ start_pwd_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too large total length
+ msg = resp[0:4] + "0008" + resp[8:12] + "0008" + "34" + "c1ffff"
+ tx_msg(dev[0], hapd, msg)
+ # server continues exchange, so start from scratch for the next step
+ rx_msg(hapd)
+ stop_pwd_assoc(dev[0], hapd)
+
+ start_pwd_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # First fragment
+ msg = resp[0:4] + "0009" + resp[8:12] + "0009" + "34" + "c100ff" + "aa"
+ tx_msg(dev[0], hapd, msg)
+ # Ack
+ req = rx_msg(hapd)
+ # Unexpected first fragment
+ # --> EAP-pwd: Unexpected new fragment start when previous fragment is still in use
+ msg = resp[0:4] + "0009" + resp[8:10] + req[10:12] + "0009" + "34" + "c100ee" + "bb"
+ tx_msg(dev[0], hapd, msg)
+ # server continues exchange, so start from scratch for the next step
+ rx_msg(hapd)
+ stop_pwd_assoc(dev[0], hapd)
+
+ start_pwd_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too much data in first fragment
+ # --> EAP-pwd: Buffer overflow attack detected! (0+2 > 1)
+ msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "34" + "c10001" + "aabb"
+ tx_msg(dev[0], hapd, msg)
+ # EAP-Failure
+ rx_msg(hapd)
+ stop_pwd_assoc(dev[0], hapd)
+
+ start_pwd_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Change parameters
+ # --> EAP-pwd: peer changed parameters
+ msg = resp[0:20] + "ff" + resp[22:]
+ tx_msg(dev[0], hapd, msg)
+ # EAP-Failure
+ rx_msg(hapd)
+ stop_pwd_assoc(dev[0], hapd)
+
+ start_pwd_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Too short ID response
+ # --> EAP-pwd: Invalid ID response
+ msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "34" + "01ffeeddcc"
+ tx_msg(dev[0], hapd, msg)
+ # server continues exchange, so start from scratch for the next step
+ rx_msg(hapd)
+ stop_pwd_assoc(dev[0], hapd)
+
+ start_pwd_assoc(dev[0], hapd)
+ # EAP-pwd-Identity/Response
+ resp = rx_msg(dev[0])
+ tx_msg(dev[0], hapd, resp)
+ # EAP-pwd-Commit/Request
+ req = rx_msg(hapd)
+ # Unexpected EAP-pwd-Identity/Response
+ # --> EAP-pwd: Unexpected opcode=1 in state=1
+ msg = resp[0:10] + req[10:12] + resp[12:]
+ tx_msg(dev[0], hapd, msg)
+ # server continues exchange, so start from scratch for the next step
+ rx_msg(hapd)
+ stop_pwd_assoc(dev[0], hapd)
+
+ start_pwd_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # EAP-pwd-Identity/Response
+ proxy_msg(hapd, dev[0]) # EAP-pwd-Commit/Request
+ # EAP-pwd-Commit/Response
+ resp = rx_msg(dev[0])
+ # Too short Commit response
+ # --> EAP-pwd: Unexpected Commit payload length 4 (expected 96)
+ msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "34" + "02ffeeddcc"
+ tx_msg(dev[0], hapd, msg)
+ # EAP-Failure
+ rx_msg(hapd)
+ stop_pwd_assoc(dev[0], hapd)
+
+ start_pwd_assoc(dev[0], hapd)
+ proxy_msg(dev[0], hapd) # EAP-pwd-Identity/Response
+ proxy_msg(hapd, dev[0]) # EAP-pwd-Commit/Request
+ proxy_msg(dev[0], hapd) # EAP-pwd-Commit/Response
+ proxy_msg(hapd, dev[0]) # EAP-pwd-Confirm/Request
+ # EAP-pwd-Confirm/Response
+ resp = rx_msg(dev[0])
+ # Too short Confirm response
+ # --> EAP-pwd: Unexpected Confirm payload length 4 (expected 32)
+ msg = resp[0:4] + "000a" + resp[8:12] + "000a" + "34" + "03ffeeddcc"
+ tx_msg(dev[0], hapd, msg)
+ # EAP-Failure
+ rx_msg(hapd)
+ stop_pwd_assoc(dev[0], hapd)
+
+ start_pwd_assoc(dev[0], hapd)
+ resp = rx_msg(dev[0])
+ # Set M=1
+ # --> EAP-pwd: No buffer for reassembly
+ msg = resp[0:18] + "41" + resp[20:]
+ tx_msg(dev[0], hapd, msg)
+ # EAP-Failure
+ rx_msg(hapd)
+ stop_pwd_assoc(dev[0], hapd)
+
+def test_eap_proto_erp(dev, apdev):
+ """ERP protocol tests"""
+ check_erp_capa(dev[0])
+
+ global eap_proto_erp_test_done
+ eap_proto_erp_test_done = False
+
+ def erp_handler(ctx, req):
+ logger.info("erp_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] += 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing type")
+ return struct.pack(">BBH", EAP_CODE_INITIATE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected type")
+ return struct.pack(">BBHB", EAP_CODE_INITIATE, ctx['id'], 4 + 1,
+ 255)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing Reserved field")
+ return struct.pack(">BBHB", EAP_CODE_INITIATE, ctx['id'], 4 + 1,
+ EAP_ERP_TYPE_REAUTH_START)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Zero-length TVs/TLVs")
+ payload = b""
+ return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
+ 4 + 1 + 1 + len(payload),
+ EAP_ERP_TYPE_REAUTH_START, 0) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short TLV")
+ payload = struct.pack("B", 191)
+ return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
+ 4 + 1 + 1 + len(payload),
+ EAP_ERP_TYPE_REAUTH_START, 0) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated TLV")
+ payload = struct.pack("BB", 191, 1)
+ return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
+ 4 + 1 + 1 + len(payload),
+ EAP_ERP_TYPE_REAUTH_START, 0) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Ignored unknown TLV and unknown TV/TLV terminating parsing")
+ payload = struct.pack("BBB", 191, 0, 192)
+ return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
+ 4 + 1 + 1 + len(payload),
+ EAP_ERP_TYPE_REAUTH_START, 0) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: More than one keyName-NAI")
+ payload = struct.pack("BBBB", EAP_ERP_TLV_KEYNAME_NAI, 0,
+ EAP_ERP_TLV_KEYNAME_NAI, 0)
+ return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
+ 4 + 1 + 1 + len(payload),
+ EAP_ERP_TYPE_REAUTH_START, 0) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too short TLV keyName-NAI")
+ payload = struct.pack("B", EAP_ERP_TLV_KEYNAME_NAI)
+ return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
+ 4 + 1 + 1 + len(payload),
+ EAP_ERP_TYPE_REAUTH_START, 0) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Truncated TLV keyName-NAI")
+ payload = struct.pack("BB", EAP_ERP_TLV_KEYNAME_NAI, 1)
+ return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
+ 4 + 1 + 1 + len(payload),
+ EAP_ERP_TYPE_REAUTH_START, 0) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid rRK lifetime TV followed by too short rMSK lifetime TV")
+ payload = struct.pack(">BLBH", EAP_ERP_TV_RRK_LIFETIME, 0,
+ EAP_ERP_TV_RMSK_LIFETIME, 0)
+ return struct.pack(">BBHBB", EAP_CODE_INITIATE, ctx['id'],
+ 4 + 1 + 1 + len(payload),
+ EAP_ERP_TYPE_REAUTH_START, 0) + payload
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing type (Finish)")
+ return struct.pack(">BBH", EAP_CODE_FINISH, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected type (Finish)")
+ return struct.pack(">BBHB", EAP_CODE_FINISH, ctx['id'], 4 + 1,
+ 255)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing fields (Finish)")
+ return struct.pack(">BBHB", EAP_CODE_FINISH, ctx['id'], 4 + 1,
+ EAP_ERP_TYPE_REAUTH)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected SEQ (Finish)")
+ return struct.pack(">BBHBBHB", EAP_CODE_FINISH, ctx['id'],
+ 4 + 1 + 4,
+ EAP_ERP_TYPE_REAUTH, 0, 0xffff, 0)
+
+ logger.info("No more test responses available - test case completed")
+ global eap_proto_erp_test_done
+ eap_proto_erp_test_done = True
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(erp_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ i = 0
+ while not eap_proto_erp_test_done:
+ i += 1
+ logger.info("Running connection iteration %d" % i)
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_fast_errors(dev, apdev):
+ """EAP-FAST local error cases"""
+ check_eap_capa(dev[0], "FAST")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(1, 5):
+ with alloc_fail(dev[0], i, "eap_fast_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="FAST", anonymous_identity="FAST",
+ identity="user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_auth",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "wpabuf_alloc;eap_fast_tlv_eap_payload"),
+ (1, "eap_fast_derive_key;eap_fast_derive_key_auth"),
+ (1, "eap_msg_alloc;eap_peer_tls_phase2_nak"),
+ (1, "wpabuf_alloc;eap_fast_tlv_result"),
+ (1, "wpabuf_alloc;eap_fast_tlv_pac_ack"),
+ (1, "=eap_peer_tls_derive_session_id;eap_fast_process_crypto_binding"),
+ (1, "eap_peer_tls_decrypt;eap_fast_decrypt"),
+ (1, "eap_fast_getKey"),
+ (1, "eap_fast_get_session_id"),
+ (1, "eap_fast_get_emsk")]
+ for count, func in tests:
+ dev[0].request("SET blob fast_pac_auth_errors ")
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="FAST", anonymous_identity="FAST",
+ identity="user@example.com", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_auth_errors",
+ erp="1",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "eap_fast_derive_key;eap_fast_derive_key_provisioning"),
+ (1, "eap_mschapv2_getKey;eap_fast_get_phase2_key"),
+ (1, "=eap_fast_use_pac_opaque"),
+ (1, "eap_fast_copy_buf"),
+ (1, "=eap_fast_add_pac"),
+ (1, "=eap_fast_init_pac_data"),
+ (1, "=eap_fast_write_pac"),
+ (2, "=eap_fast_write_pac")]
+ for count, func in tests:
+ dev[0].request("SET blob fast_pac_errors ")
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="FAST", anonymous_identity="FAST",
+ identity="user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac_errors",
+ erp="1",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "eap_fast_get_cmk;eap_fast_process_crypto_binding"),
+ (1, "eap_fast_derive_eap_msk;eap_fast_process_crypto_binding"),
+ (1, "eap_fast_derive_eap_emsk;eap_fast_process_crypto_binding")]
+ for count, func in tests:
+ dev[0].request("SET blob fast_pac_auth_errors ")
+ with fail_test(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="FAST", anonymous_identity="FAST",
+ identity="user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_auth_errors",
+ erp="1",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].request("SET blob fast_pac_errors ")
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="FAST", anonymous_identity="FAST",
+ identity="user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac_errors",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ # EAP-FAST: Only EAP-MSCHAPv2 is allowed during unauthenticated
+ # provisioning; reject phase2 type 6
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ logger.info("Wrong password in Phase 2")
+ dev[0].request("SET blob fast_pac_errors ")
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="FAST", anonymous_identity="FAST",
+ identity="user", password="wrong password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac_errors",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = ["FOOBAR\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nFOOBAR\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nSTART\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nEND\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nPAC-Type=12345\nEND\n"
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nPAC-Key=12\nEND\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nPAC-Key=1\nEND\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nPAC-Key=1q\nEND\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nPAC-Opaque=1\nEND\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nA-ID=1\nEND\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nI-ID=1\nEND\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nA-ID-Info=1\nEND\n"]
+ for pac in tests:
+ blob = binascii.hexlify(pac.encode()).decode()
+ dev[0].request("SET blob fast_pac_errors " + blob)
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="FAST", anonymous_identity="FAST",
+ identity="user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_errors",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = ["wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nEND\n",
+ "wpa_supplicant EAP-FAST PAC file - version 1\nSTART\nEND\nSTART\nEND\nSTART\nEND\n"]
+ for pac in tests:
+ blob = binascii.hexlify(pac.encode()).decode()
+ dev[0].request("SET blob fast_pac_errors " + blob)
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="FAST", anonymous_identity="FAST",
+ identity="user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=GTC",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_errors")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].request("SET blob fast_pac_errors ")
+
+def test_eap_proto_peap_errors_server(dev, apdev):
+ """EAP-PEAP local error cases on server"""
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ tests = [(1, "get_asymetric_start_key;eap_mschapv2_getKey"),
+ (1, "generate_authenticator_response_pwhash;eap_mschapv2_process_response"),
+ (1, "hash_nt_password_hash;eap_mschapv2_process_response"),
+ (1, "get_master_key;eap_mschapv2_process_response")]
+ for count, func in tests:
+ with fail_test(hapd, count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="PEAP", anonymous_identity="peap",
+ identity="user", password="password",
+ phase1="peapver=0 crypto_binding=2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ erp="1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP-Failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_eap_proto_peap_errors(dev, apdev):
+ """EAP-PEAP local error cases"""
+ check_eap_capa(dev[0], "PEAP")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(1, 5):
+ with alloc_fail(dev[0], i, "eap_peap_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PEAP", anonymous_identity="peap",
+ identity="user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "eap_mschapv2_getKey;eap_peap_get_isk;eap_peap_derive_cmk"),
+ (1, "eap_msg_alloc;eap_tlv_build_result"),
+ (1, "eap_mschapv2_init;eap_peap_phase2_request"),
+ (1, "eap_peer_tls_decrypt;eap_peap_decrypt"),
+ (1, "wpabuf_alloc;=eap_peap_decrypt"),
+ (1, "eap_peer_tls_encrypt;eap_peap_decrypt"),
+ (1, "eap_peer_tls_process_helper;eap_peap_process"),
+ (1, "eap_peer_tls_derive_key;eap_peap_process"),
+ (1, "eap_peer_tls_derive_session_id;eap_peap_process"),
+ (1, "eap_peap_getKey"),
+ (1, "eap_peap_get_session_id")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PEAP", anonymous_identity="peap",
+ identity="user", password="password",
+ phase1="peapver=0 crypto_binding=2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ erp="1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "peap_prfplus;eap_peap_derive_cmk"),
+ (1, "eap_tlv_add_cryptobinding;eap_tlv_build_result"),
+ (1, "peap_prfplus;eap_peap_getKey"),
+ (1, "get_asymetric_start_key;eap_mschapv2_getKey")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PEAP", anonymous_identity="peap",
+ identity="user", password="password",
+ phase1="peapver=0 crypto_binding=2",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ erp="1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with alloc_fail(dev[0], 1,
+ "eap_peer_tls_phase2_nak;eap_peap_phase2_request"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PEAP", anonymous_identity="peap",
+ identity="cert user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_eap_proto_ttls_errors(dev, apdev):
+ """EAP-TTLS local error cases"""
+ check_eap_capa(dev[0], "TTLS")
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ for i in range(1, 5):
+ with alloc_fail(dev[0], i, "eap_ttls_init"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="TTLS", anonymous_identity="ttls",
+ identity="user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase2="autheap=MSCHAPV2",
+ wait_connect=False)
+ ev = dev[0].wait_event(["EAP: Failed to initialize EAP method"],
+ timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "eap_peer_tls_derive_key;eap_ttls_v0_derive_key",
+ "DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
+ (1, "eap_peer_tls_derive_session_id;eap_ttls_v0_derive_key",
+ "DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
+ (1, "wpabuf_alloc;eap_ttls_phase2_request_mschapv2",
+ "DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
+ (1, "eap_peer_tls_derive_key;eap_ttls_phase2_request_mschapv2",
+ "DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
+ (1, "eap_peer_tls_encrypt;eap_ttls_encrypt_response;eap_ttls_implicit_identity_request",
+ "DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
+ (1, "eap_peer_tls_decrypt;eap_ttls_decrypt",
+ "DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
+ (1, "eap_ttls_getKey",
+ "DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
+ (1, "eap_ttls_get_session_id",
+ "DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
+ (1, "eap_ttls_get_emsk",
+ "mschapv2 user@domain", "auth=MSCHAPV2"),
+ (1, "wpabuf_alloc;eap_ttls_phase2_request_mschap",
+ "mschap user", "auth=MSCHAP"),
+ (1, "eap_peer_tls_derive_key;eap_ttls_phase2_request_mschap",
+ "mschap user", "auth=MSCHAP"),
+ (1, "wpabuf_alloc;eap_ttls_phase2_request_chap",
+ "chap user", "auth=CHAP"),
+ (1, "eap_peer_tls_derive_key;eap_ttls_phase2_request_chap",
+ "chap user", "auth=CHAP"),
+ (1, "wpabuf_alloc;eap_ttls_phase2_request_pap",
+ "pap user", "auth=PAP"),
+ (1, "wpabuf_alloc;eap_ttls_avp_encapsulate",
+ "user", "autheap=MSCHAPV2"),
+ (1, "eap_mschapv2_init;eap_ttls_phase2_request_eap_method",
+ "user", "autheap=MSCHAPV2"),
+ (1, "eap_sm_buildIdentity;eap_ttls_phase2_request_eap",
+ "user", "autheap=MSCHAPV2"),
+ (1, "eap_ttls_avp_encapsulate;eap_ttls_phase2_request_eap",
+ "user", "autheap=MSCHAPV2"),
+ (1, "eap_ttls_parse_attr_eap",
+ "user", "autheap=MSCHAPV2"),
+ (1, "eap_peer_tls_encrypt;eap_ttls_encrypt_response;eap_ttls_process_decrypted",
+ "user", "autheap=MSCHAPV2"),
+ (1, "eap_ttls_fake_identity_request",
+ "user", "autheap=MSCHAPV2"),
+ (1, "eap_msg_alloc;eap_tls_process_output",
+ "user", "autheap=MSCHAPV2"),
+ (1, "eap_msg_alloc;eap_peer_tls_build_ack",
+ "user", "autheap=MSCHAPV2"),
+ (1, "eap_peer_tls_phase2_nak;eap_ttls_phase2_request_eap_method",
+ "cert user", "autheap=MSCHAPV2")]
+ tls = dev[0].request("GET tls_library")
+ if tls.startswith("internal"):
+ tests += [(1, "tlsv1_client_decrypt;eap_peer_tls_decrypt",
+ "user", "autheap=MSCHAPV2")]
+ else:
+ tests += [(1, "tls_connection_decrypt;eap_peer_tls_decrypt",
+ "user", "autheap=MSCHAPV2")]
+ for count, func, identity, phase2 in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="TTLS", anonymous_identity="ttls",
+ identity=identity, password="password",
+ ca_cert="auth_serv/ca.pem", phase2=phase2,
+ erp="1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL",
+ note="Allocation failure not triggered for: %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "os_get_random;eap_ttls_phase2_request_mschapv2"),
+ (1, "mschapv2_derive_response;eap_ttls_phase2_request_mschapv2")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="TTLS", anonymous_identity="ttls",
+ identity="DOMAIN\mschapv2 user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ erp="1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_FAIL",
+ note="Test failure not triggered for: %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ tests = [(1, "nt_challenge_response;eap_ttls_phase2_request_mschap")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="TTLS", anonymous_identity="ttls",
+ identity="mschap user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAP",
+ erp="1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_FAIL",
+ note="Test failure not triggered for: %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_eap_proto_expanded(dev, apdev):
+ """EAP protocol tests with expanded header"""
+ global eap_proto_expanded_test_done
+ eap_proto_expanded_test_done = False
+
+ def expanded_handler(ctx, req):
+ logger.info("expanded_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] += 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: MD5 challenge in expanded header")
+ return struct.pack(">BBHB3BLBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 3,
+ EAP_TYPE_EXPANDED, 0, 0, 0, EAP_TYPE_MD5,
+ 1, 0xaa, ord('n'))
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid expanded EAP length")
+ return struct.pack(">BBHB3BH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 2,
+ EAP_TYPE_EXPANDED, 0, 0, 0, EAP_TYPE_MD5)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid expanded frame type")
+ return struct.pack(">BBHB3BL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_EXPANDED, 0, 0, 1, EAP_TYPE_MD5)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: MSCHAPv2 Challenge")
+ return struct.pack(">BBHBBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 4 + 1 + 16 + 6,
+ EAP_TYPE_MSCHAPV2,
+ 1, 0, 4 + 1 + 16 + 6, 16) + 16*b'A' + b'foobar'
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid expanded frame type")
+ return struct.pack(">BBHB3BL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4,
+ EAP_TYPE_EXPANDED, 0, 0, 1, EAP_TYPE_MSCHAPV2)
+
+ logger.info("No more test responses available - test case completed")
+ global eap_proto_expanded_test_done
+ eap_proto_expanded_test_done = True
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(expanded_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ i = 0
+ while not eap_proto_expanded_test_done:
+ i += 1
+ logger.info("Running connection iteration %d" % i)
+ if i == 4:
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MSCHAPV2", identity="user",
+ password="password",
+ wait_connect=False)
+ else:
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ if i in [1]:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ elif i in [2, 3]:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP proposed method")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ else:
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_tls(dev, apdev):
+ """EAP-TLS protocol tests"""
+ check_eap_capa(dev[0], "TLS")
+ global eap_proto_tls_test_done, eap_proto_tls_test_wait
+ eap_proto_tls_test_done = False
+ eap_proto_tls_test_wait = False
+
+ def tls_handler(ctx, req):
+ logger.info("tls_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] += 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ global eap_proto_tls_test_wait
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too much payload in TLS/Start: TLS Message Length (0 bytes) smaller than this fragment (1 bytes)")
+ return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4 + 1,
+ EAP_TYPE_TLS, 0xa0, 0, 1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Fragmented TLS/Start")
+ return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4 + 1,
+ EAP_TYPE_TLS, 0xe0, 2, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too long fragment of TLS/Start: Invalid reassembly state: tls_in_left=2 tls_in_len=0 in_len=0")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2,
+ EAP_TYPE_TLS, 0x00, 2, 3)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TLS/Start")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TLS, 0x20)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Fragmented TLS message")
+ return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4 + 1,
+ EAP_TYPE_TLS, 0xc0, 2, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid TLS message: no Flags octet included + workaround")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_TLS)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Too long fragment of TLS message: more data than TLS message length indicated")
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2,
+ EAP_TYPE_TLS, 0x00, 2, 3)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Fragmented TLS/Start and truncated Message Length field")
+ return struct.pack(">BBHBB3B", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 3,
+ EAP_TYPE_TLS, 0xe0, 1, 2, 3)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TLS/Start")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TLS, 0x20)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Fragmented TLS message")
+ return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4 + 1,
+ EAP_TYPE_TLS, 0xc0, 2, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid TLS message: no Flags octet included + workaround disabled")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_TLS)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TLS/Start")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TLS, 0x20)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Fragmented TLS message (long; first)")
+ payload = 1450*b'A'
+ return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4 + len(payload),
+ EAP_TYPE_TLS, 0xc0, 65536) + payload
+ # "Too long TLS fragment (size over 64 kB)" on the last one
+ for i in range(44):
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Fragmented TLS message (long; cont %d)" % i)
+ eap_proto_tls_test_wait = True
+ payload = 1470*b'A'
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(payload),
+ EAP_TYPE_TLS, 0x40) + payload
+ eap_proto_tls_test_wait = False
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TLS/Start")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TLS, 0x20)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Non-ACK to more-fragment message")
+ return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 1,
+ EAP_TYPE_TLS, 0x00, 255)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Failure")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ logger.info("No more test responses available - test case completed")
+ global eap_proto_tls_test_done
+ eap_proto_tls_test_done = True
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(tls_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ i = 0
+ while not eap_proto_tls_test_done:
+ i += 1
+ logger.info("Running connection iteration %d" % i)
+ workaround = "0" if i == 6 else "1"
+ fragment_size = "100" if i == 8 else "1400"
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ eap_workaround=workaround,
+ fragment_size=fragment_size,
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD",
+ "CTRL-EVENT-EAP-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ time.sleep(0.1)
+ start = os.times()[4]
+ while eap_proto_tls_test_wait:
+ now = os.times()[4]
+ if now - start > 10:
+ break
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_tnc(dev, apdev):
+ """EAP-TNC protocol tests"""
+ check_eap_capa(dev[0], "TNC")
+ global eap_proto_tnc_test_done
+ eap_proto_tnc_test_done = False
+
+ def tnc_handler(ctx, req):
+ logger.info("tnc_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] += 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TNC start with unsupported version")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x20)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TNC without Flags field")
+ return struct.pack(">BBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1,
+ EAP_TYPE_TNC)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Message underflow due to missing Message Length")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0xa1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid Message Length")
+ return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4 + 1,
+ EAP_TYPE_TNC, 0xa1, 0, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid Message Length")
+ return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4,
+ EAP_TYPE_TNC, 0xe1, 75001)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Start with Message Length")
+ return struct.pack(">BBHBBL", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4,
+ EAP_TYPE_TNC, 0xa1, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Server used start flag again")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Fragmentation and unexpected payload in ack")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x01)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 1,
+ EAP_TYPE_TNC, 0x01, 0)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Server fragmenting and fragment overflow")
+ return struct.pack(">BBHBBLB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4 + 1,
+ EAP_TYPE_TNC, 0xe1, 2, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBHBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 2,
+ EAP_TYPE_TNC, 0x01, 2, 3)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Server fragmenting and no message length in a fragment")
+ return struct.pack(">BBHBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 1,
+ EAP_TYPE_TNC, 0x61, 2)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TNC start followed by invalid TNCCS-Batch")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"FOO"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TNC start followed by invalid TNCCS-Batch (2)")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"</TNCCS-Batch><TNCCS-Batch>"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TNCCS-Batch missing BatchId attribute")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"<TNCCS-Batch foo=3></TNCCS-Batch>"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected IF-TNCCS BatchId")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"<TNCCS-Batch BatchId=123456789></TNCCS-Batch>"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing IMC-IMV-Message and TNCC-TNCS-Message end tags")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"<TNCCS-Batch BatchId=2><IMC-IMV-Message><TNCC-TNCS-Message></TNCCS-Batch>"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing IMC-IMV-Message and TNCC-TNCS-Message Type")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"<TNCCS-Batch BatchId=2><IMC-IMV-Message></IMC-IMV-Message><TNCC-TNCS-Message></TNCC-TNCS-Message></TNCCS-Batch>"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing TNCC-TNCS-Message XML end tag")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><XML></TNCC-TNCS-Message></TNCCS-Batch>"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing TNCC-TNCS-Message Base64 start tag")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type></TNCC-TNCS-Message></TNCCS-Batch>"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing TNCC-TNCS-Message Base64 end tag")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><Base64>abc</TNCC-TNCS-Message></TNCCS-Batch>"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TNCC-TNCS-Message Base64 message")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><Base64>aGVsbG8=</Base64></TNCC-TNCS-Message></TNCCS-Batch>"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid TNCC-TNCS-Message XML message")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b"<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><XML>hello</XML></TNCC-TNCS-Message></TNCCS-Batch>"
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing TNCCS-Recommendation type")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b'<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><XML><TNCCS-Recommendation foo=1></TNCCS-Recommendation></XML></TNCC-TNCS-Message></TNCCS-Batch>'
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TNCCS-Recommendation type=none")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b'<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><XML><TNCCS-Recommendation type="none"></TNCCS-Recommendation></XML></TNCC-TNCS-Message></TNCCS-Batch>'
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: TNCCS-Recommendation type=isolate")
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1,
+ EAP_TYPE_TNC, 0x21)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Received TNCCS-Batch: " + binascii.hexlify(req[6:]).decode())
+ resp = b'<TNCCS-Batch BatchId=2><TNCC-TNCS-Message><Type>00000001</Type><XML><TNCCS-Recommendation type="isolate"></TNCCS-Recommendation></XML></TNCC-TNCS-Message></TNCCS-Batch>'
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(resp),
+ EAP_TYPE_TNC, 0x01) + resp
+ idx += 1
+ if ctx['num'] == idx:
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ logger.info("No more test responses available - test case completed")
+ global eap_proto_tnc_test_done
+ eap_proto_tnc_test_done = True
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(tnc_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ i = 0
+ while not eap_proto_tnc_test_done:
+ i += 1
+ logger.info("Running connection iteration %d" % i)
+ frag = 1400
+ if i == 8:
+ frag = 150
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="TNC", identity="tnc", fragment_size=str(frag),
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD",
+ "CTRL-EVENT-EAP-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_canned_success_after_identity(dev, apdev):
+ """EAP protocol tests for canned EAP-Success after identity"""
+ check_eap_capa(dev[0], "MD5")
+ def eap_canned_success_handler(ctx, req):
+ logger.info("eap_canned_success_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: EAP-Success")
+ return struct.pack(">BBH", EAP_CODE_SUCCESS, ctx['id'], 4)
+
+ return None
+
+ srv = start_radius_server(eap_canned_success_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ phase1="allow_canned_success=1",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP success")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected EAP success")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_proto_wsc(dev, apdev):
+ """EAP-WSC protocol tests"""
+ global eap_proto_wsc_test_done, eap_proto_wsc_wait_failure
+ eap_proto_wsc_test_done = False
+
+ def wsc_handler(ctx, req):
+ logger.info("wsc_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] += 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ global eap_proto_wsc_wait_failure
+ eap_proto_wsc_wait_failure = False
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Missing Flags field")
+ return struct.pack(">BBHB3BLB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 1,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 1)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Message underflow (missing Message Length field)")
+ return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 2,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 1, 0x02)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid Message Length (> 50000)")
+ return struct.pack(">BBHB3BLBBH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 4,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 1, 0x02, 65535)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Invalid Message Length (< current payload)")
+ return struct.pack(">BBHB3BLBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 5,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 1, 0x02, 0, 0xff)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Op-Code 5 in WAIT_START state")
+ return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 2,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 5, 0x00)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid WSC Start to start the sequence")
+ return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 2,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 1, 0x00)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: No Message Length field in a fragmented packet")
+ return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 2,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 4, 0x01)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid WSC Start to start the sequence")
+ return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 2,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 1, 0x00)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid first fragmented packet")
+ return struct.pack(">BBHB3BLBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 5,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 4, 0x03, 10, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Op-Code 5 in fragment (expected 4)")
+ return struct.pack(">BBHB3BLBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 3,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 5, 0x01, 2)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid WSC Start to start the sequence")
+ return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 2,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 1, 0x00)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid first fragmented packet")
+ return struct.pack(">BBHB3BLBBHB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 5,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 4, 0x03, 2, 1)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Fragment overflow")
+ return struct.pack(">BBHB3BLBBBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 4,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 4, 0x01, 2, 3)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid WSC Start to start the sequence")
+ return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 2,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 1, 0x00)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Unexpected Op-Code 5 in WAIT_FRAG_ACK state")
+ return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 2,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 5, 0x00)
+
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("Test: Valid WSC Start")
+ return struct.pack(">BBHB3BLBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 3 + 4 + 2,
+ EAP_TYPE_EXPANDED, 0x00, 0x37, 0x2a, 1,
+ 1, 0x00)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("No more test responses available - test case completed")
+ global eap_proto_wsc_test_done
+ eap_proto_wsc_test_done = True
+ eap_proto_wsc_wait_failure = True
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(wsc_handler)
+
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ i = 0
+ while not eap_proto_wsc_test_done:
+ i += 1
+ logger.info("Running connection iteration %d" % i)
+ fragment_size = 1398 if i != 9 else 50
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", eap="WSC",
+ fragment_size=str(fragment_size),
+ identity="WFA-SimpleConfig-Enrollee-1-0",
+ phase1="pin=12345670",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP method start")
+ if eap_proto_wsc_wait_failure:
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ else:
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected(timeout=1)
+ dev[0].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_canned_success_before_method(dev, apdev):
+ """EAP protocol tests for canned EAP-Success before any method"""
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ bssid = apdev[0]['bssid']
+ hapd.request("SET ext_eapol_frame_io 1")
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ phase1="allow_canned_success=1",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+
+ res = dev[0].request("EAPOL_RX " + bssid + " 0200000403020004")
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP success")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_eap_canned_failure_before_method(dev, apdev):
+ """EAP protocol tests for canned EAP-Failure before any method"""
+ params = int_eap_server_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ bssid = apdev[0]['bssid']
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", scan_freq="2412",
+ phase1="allow_canned_success=1",
+ eap="MD5", identity="user", password="password",
+ wait_connect=False)
+
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+
+ res = dev[0].request("EAPOL_RX " + bssid + " 0200000404020004")
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on EAP failure")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_eap_nak_oom(dev, apdev):
+ """EAP-Nak OOM"""
+ check_eap_capa(dev[0], "MD5")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_sm_buildNak"):
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="MD5", identity="sake user", password="password",
+ wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_eap_nak_expanded(dev, apdev):
+ """EAP-Nak with expanded method"""
+ check_eap_capa(dev[0], "MD5")
+ check_eap_capa(dev[0], "VENDOR-TEST")
+ params = hostapd.wpa2_eap_params(ssid="eap-test")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="VENDOR-TEST WSC",
+ identity="sake user", password="password",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"], timeout=10)
+ if ev is None or "NAK" not in ev:
+ raise Exception("No NAK event seen")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No EAP-Failure seen")
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+EAP_TLV_RESULT_TLV = 3
+EAP_TLV_NAK_TLV = 4
+EAP_TLV_ERROR_CODE_TLV = 5
+EAP_TLV_CONNECTION_BINDING_TLV = 6
+EAP_TLV_VENDOR_SPECIFIC_TLV = 7
+EAP_TLV_URI_TLV = 8
+EAP_TLV_EAP_PAYLOAD_TLV = 9
+EAP_TLV_INTERMEDIATE_RESULT_TLV = 10
+EAP_TLV_PAC_TLV = 11
+EAP_TLV_CRYPTO_BINDING_TLV = 12
+EAP_TLV_CALLING_STATION_ID_TLV = 13
+EAP_TLV_CALLED_STATION_ID_TLV = 14
+EAP_TLV_NAS_PORT_TYPE_TLV = 15
+EAP_TLV_SERVER_IDENTIFIER_TLV = 16
+EAP_TLV_IDENTITY_TYPE_TLV = 17
+EAP_TLV_SERVER_TRUSTED_ROOT_TLV = 18
+EAP_TLV_REQUEST_ACTION_TLV = 19
+EAP_TLV_PKCS7_TLV = 20
+
+EAP_TLV_RESULT_SUCCESS = 1
+EAP_TLV_RESULT_FAILURE = 2
+
+EAP_TLV_TYPE_MANDATORY = 0x8000
+EAP_TLV_TYPE_MASK = 0x3fff
+
+PAC_TYPE_PAC_KEY = 1
+PAC_TYPE_PAC_OPAQUE = 2
+PAC_TYPE_CRED_LIFETIME = 3
+PAC_TYPE_A_ID = 4
+PAC_TYPE_I_ID = 5
+PAC_TYPE_A_ID_INFO = 7
+PAC_TYPE_PAC_ACKNOWLEDGEMENT = 8
+PAC_TYPE_PAC_INFO = 9
+PAC_TYPE_PAC_TYPE = 10
+
+def eap_fast_start(ctx):
+ logger.info("Send EAP-FAST/Start")
+ return struct.pack(">BBHBBHH", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + 4 + 16,
+ EAP_TYPE_FAST, 0x21, 4, 16) + 16*b'A'
+
+def test_eap_fast_proto(dev, apdev):
+ """EAP-FAST Phase protocol testing"""
+ check_eap_capa(dev[0], "FAST")
+ global eap_fast_proto_ctx
+ eap_fast_proto_ctx = None
+
+ def eap_handler(ctx, req):
+ logger.info("eap_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ global eap_fast_proto_ctx
+ eap_fast_proto_ctx = ctx
+ ctx['test_done'] = False
+
+ idx += 1
+ if ctx['num'] == idx:
+ return eap_fast_start(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ logger.info("EAP-FAST: TLS processing failed")
+ data = b'ABCDEFGHIK'
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(data),
+ EAP_TYPE_FAST, 0x01) + data
+ idx += 1
+ if ctx['num'] == idx:
+ ctx['test_done'] = True
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ logger.info("Past last test case")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(eap_handler)
+ try:
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="FAST", anonymous_identity="FAST",
+ identity="user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac_proto",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Could not start EAP-FAST")
+ ok = False
+ for i in range(100):
+ if eap_fast_proto_ctx:
+ if eap_fast_proto_ctx['test_done']:
+ ok = True
+ break
+ time.sleep(0.05)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ finally:
+ stop_radius_server(srv)
+
+def run_eap_fast_phase2(dev, test_payload, test_failure=True):
+ global eap_fast_proto_ctx
+ eap_fast_proto_ctx = None
+
+ def ssl_info_callback(conn, where, ret):
+ logger.debug("SSL: info where=%d ret=%d" % (where, ret))
+
+ def log_conn_state(conn):
+ try:
+ state = conn.state_string()
+ except AttributeError:
+ state = conn.get_state_string()
+ if state:
+ logger.info("State: " + str(state))
+
+ def process_clienthello(ctx, payload):
+ logger.info("Process ClientHello")
+ ctx['sslctx'] = OpenSSL.SSL.Context(OpenSSL.SSL.TLSv1_METHOD)
+ ctx['sslctx'].set_info_callback(ssl_info_callback)
+ ctx['sslctx'].load_tmp_dh("auth_serv/dh.conf")
+ if OpenSSL.SSL.OPENSSL_VERSION_NUMBER >= 0x10100000:
+ ctx['sslctx'].set_cipher_list("ADH-AES128-SHA:@SECLEVEL=0")
+ else:
+ ctx['sslctx'].set_cipher_list("ADH-AES128-SHA")
+ ctx['conn'] = OpenSSL.SSL.Connection(ctx['sslctx'], None)
+ ctx['conn'].set_accept_state()
+ log_conn_state(ctx['conn'])
+ ctx['conn'].bio_write(payload)
+ try:
+ ctx['conn'].do_handshake()
+ except OpenSSL.SSL.WantReadError:
+ pass
+ log_conn_state(ctx['conn'])
+ data = ctx['conn'].bio_read(4096)
+ log_conn_state(ctx['conn'])
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(data),
+ EAP_TYPE_FAST, 0x01) + data
+
+ def process_clientkeyexchange(ctx, payload, appl_data):
+ logger.info("Process ClientKeyExchange")
+ log_conn_state(ctx['conn'])
+ ctx['conn'].bio_write(payload)
+ try:
+ ctx['conn'].do_handshake()
+ except OpenSSL.SSL.WantReadError:
+ pass
+ ctx['conn'].send(appl_data)
+ log_conn_state(ctx['conn'])
+ data = ctx['conn'].bio_read(4096)
+ log_conn_state(ctx['conn'])
+ return struct.pack(">BBHBB", EAP_CODE_REQUEST, ctx['id'],
+ 4 + 1 + 1 + len(data),
+ EAP_TYPE_FAST, 0x01) + data
+
+ def eap_handler(ctx, req):
+ logger.info("eap_handler - RX " + binascii.hexlify(req).decode())
+ if 'num' not in ctx:
+ ctx['num'] = 0
+ ctx['num'] = ctx['num'] + 1
+ if 'id' not in ctx:
+ ctx['id'] = 1
+ ctx['id'] = (ctx['id'] + 1) % 256
+ idx = 0
+
+ global eap_fast_proto_ctx
+ eap_fast_proto_ctx = ctx
+ ctx['test_done'] = False
+ logger.debug("ctx['num']=%d" % ctx['num'])
+
+ idx += 1
+ if ctx['num'] == idx:
+ return eap_fast_start(ctx)
+ idx += 1
+ if ctx['num'] == idx:
+ return process_clienthello(ctx, req[6:])
+ idx += 1
+ if ctx['num'] == idx:
+ if not test_failure:
+ ctx['test_done'] = True
+ return process_clientkeyexchange(ctx, req[6:], test_payload)
+ idx += 1
+ if ctx['num'] == idx:
+ ctx['test_done'] = True
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ logger.info("Past last test case")
+ return struct.pack(">BBH", EAP_CODE_FAILURE, ctx['id'], 4)
+
+ srv = start_radius_server(eap_handler)
+ try:
+ dev[0].connect("eap-test", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="FAST", anonymous_identity="FAST",
+ identity="user", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ phase1="fast_provisioning=1",
+ pac_file="blob://fast_pac_proto",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-METHOD"], timeout=5)
+ if ev is None:
+ raise Exception("Could not start EAP-FAST")
+ dev[0].dump_monitor()
+ ok = False
+ for i in range(100):
+ if eap_fast_proto_ctx:
+ if eap_fast_proto_ctx['test_done']:
+ ok = True
+ break
+ time.sleep(0.05)
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ if not ok:
+ raise Exception("EAP-FAST TLS exchange did not complete")
+ for i in range(3):
+ dev[i].dump_monitor()
+ finally:
+ stop_radius_server(srv)
+
+def test_eap_fast_proto_phase2(dev, apdev):
+ """EAP-FAST Phase 2 protocol testing"""
+ if not openssl_imported:
+ raise HwsimSkip("OpenSSL python method not available")
+ check_eap_capa(dev[0], "FAST")
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ tests = [("Too short Phase 2 TLV frame (len=3)",
+ "ABC",
+ False),
+ ("EAP-FAST: TLV overflow",
+ struct.pack(">HHB", 0, 2, 0xff),
+ False),
+ ("EAP-FAST: Unknown TLV (optional and mandatory)",
+ struct.pack(">HHB", 0, 1, 0xff) +
+ struct.pack(">HHB", EAP_TLV_TYPE_MANDATORY, 1, 0xff),
+ True),
+ ("EAP-FAST: More than one EAP-Payload TLV in the message",
+ struct.pack(">HHBHHB",
+ EAP_TLV_EAP_PAYLOAD_TLV, 1, 0xff,
+ EAP_TLV_EAP_PAYLOAD_TLV, 1, 0xff),
+ True),
+ ("EAP-FAST: Unknown Result 255 and More than one Result TLV in the message",
+ struct.pack(">HHHHHH",
+ EAP_TLV_RESULT_TLV, 2, 0xff,
+ EAP_TLV_RESULT_TLV, 2, 0xff),
+ True),
+ ("EAP-FAST: Too short Result TLV",
+ struct.pack(">HHB", EAP_TLV_RESULT_TLV, 1, 0xff),
+ True),
+ ("EAP-FAST: Unknown Intermediate Result 255 and More than one Intermediate-Result TLV in the message",
+ struct.pack(">HHHHHH",
+ EAP_TLV_INTERMEDIATE_RESULT_TLV, 2, 0xff,
+ EAP_TLV_INTERMEDIATE_RESULT_TLV, 2, 0xff),
+ True),
+ ("EAP-FAST: Too short Intermediate-Result TLV",
+ struct.pack(">HHB", EAP_TLV_INTERMEDIATE_RESULT_TLV, 1, 0xff),
+ True),
+ ("EAP-FAST: More than one Crypto-Binding TLV in the message",
+ struct.pack(">HH", EAP_TLV_CRYPTO_BINDING_TLV, 60) + 60*b'A' +
+ struct.pack(">HH", EAP_TLV_CRYPTO_BINDING_TLV, 60) + 60*b'A',
+ True),
+ ("EAP-FAST: Too short Crypto-Binding TLV",
+ struct.pack(">HHB", EAP_TLV_CRYPTO_BINDING_TLV, 1, 0xff),
+ True),
+ ("EAP-FAST: More than one Request-Action TLV in the message",
+ struct.pack(">HHBBHHBB",
+ EAP_TLV_REQUEST_ACTION_TLV, 2, 0xff, 0xff,
+ EAP_TLV_REQUEST_ACTION_TLV, 2, 0xff, 0xff),
+ True),
+ ("EAP-FAST: Too short Request-Action TLV",
+ struct.pack(">HHB", EAP_TLV_REQUEST_ACTION_TLV, 1, 0xff),
+ True),
+ ("EAP-FAST: More than one PAC TLV in the message",
+ struct.pack(">HHBHHB",
+ EAP_TLV_PAC_TLV, 1, 0xff,
+ EAP_TLV_PAC_TLV, 1, 0xff),
+ True),
+ ("EAP-FAST: Too short EAP Payload TLV (Len=3)",
+ struct.pack(">HH3B",
+ EAP_TLV_EAP_PAYLOAD_TLV, 3, 0, 0, 0),
+ False),
+ ("EAP-FAST: Too short Phase 2 request (Len=0)",
+ struct.pack(">HHBBH",
+ EAP_TLV_EAP_PAYLOAD_TLV, 4,
+ EAP_CODE_REQUEST, 0, 0),
+ False),
+ ("EAP-FAST: EAP packet overflow in EAP Payload TLV",
+ struct.pack(">HHBBH",
+ EAP_TLV_EAP_PAYLOAD_TLV, 4,
+ EAP_CODE_REQUEST, 0, 4 + 1),
+ False),
+ ("EAP-FAST: Unexpected code=0 in Phase 2 EAP header",
+ struct.pack(">HHBBH",
+ EAP_TLV_EAP_PAYLOAD_TLV, 4,
+ 0, 0, 0),
+ False),
+ ("EAP-FAST: PAC TLV without Result TLV acknowledging success",
+ struct.pack(">HHB", EAP_TLV_PAC_TLV, 1, 0xff),
+ True),
+ ("EAP-FAST: PAC TLV does not include all the required fields",
+ struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
+ EAP_TLV_RESULT_SUCCESS) +
+ struct.pack(">HHB", EAP_TLV_PAC_TLV, 1, 0xff),
+ True),
+ ("EAP-FAST: Invalid PAC-Key length 0, Ignored unknown PAC type 0, and PAC TLV overrun (type=0 len=2 left=1)",
+ struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
+ EAP_TLV_RESULT_SUCCESS) +
+ struct.pack(">HHHHHHHHB", EAP_TLV_PAC_TLV, 4 + 4 + 5,
+ PAC_TYPE_PAC_KEY, 0, 0, 0, 0, 2, 0),
+ True),
+ ("EAP-FAST: PAC-Info does not include all the required fields",
+ struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
+ EAP_TLV_RESULT_SUCCESS) +
+ struct.pack(">HHHHHHHH", EAP_TLV_PAC_TLV, 4 + 4 + 4 + 32,
+ PAC_TYPE_PAC_OPAQUE, 0,
+ PAC_TYPE_PAC_INFO, 0,
+ PAC_TYPE_PAC_KEY, 32) + 32*b'A',
+ True),
+ ("EAP-FAST: Invalid CRED_LIFETIME length, Ignored unknown PAC-Info type 0, and Invalid PAC-Type length 1",
+ struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
+ EAP_TLV_RESULT_SUCCESS) +
+ struct.pack(">HHHHHHHHHHHHBHH", EAP_TLV_PAC_TLV, 4 + 4 + 13 + 4 + 32,
+ PAC_TYPE_PAC_OPAQUE, 0,
+ PAC_TYPE_PAC_INFO, 13, PAC_TYPE_CRED_LIFETIME, 0,
+ 0, 0, PAC_TYPE_PAC_TYPE, 1, 0,
+ PAC_TYPE_PAC_KEY, 32) + 32*b'A',
+ True),
+ ("EAP-FAST: Unsupported PAC-Type 0",
+ struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
+ EAP_TLV_RESULT_SUCCESS) +
+ struct.pack(">HHHHHHHHHHH", EAP_TLV_PAC_TLV, 4 + 4 + 6 + 4 + 32,
+ PAC_TYPE_PAC_OPAQUE, 0,
+ PAC_TYPE_PAC_INFO, 6, PAC_TYPE_PAC_TYPE, 2, 0,
+ PAC_TYPE_PAC_KEY, 32) + 32*b'A',
+ True),
+ ("EAP-FAST: PAC-Info overrun (type=0 len=2 left=1)",
+ struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
+ EAP_TLV_RESULT_SUCCESS) +
+ struct.pack(">HHHHHHHHBHH", EAP_TLV_PAC_TLV, 4 + 4 + 5 + 4 + 32,
+ PAC_TYPE_PAC_OPAQUE, 0,
+ PAC_TYPE_PAC_INFO, 5, 0, 2, 1,
+ PAC_TYPE_PAC_KEY, 32) + 32*b'A',
+ True),
+ ("EAP-FAST: Valid PAC",
+ struct.pack(">HHH", EAP_TLV_RESULT_TLV, 2,
+ EAP_TLV_RESULT_SUCCESS) +
+ struct.pack(">HHHHHHHHBHHBHH", EAP_TLV_PAC_TLV,
+ 4 + 4 + 10 + 4 + 32,
+ PAC_TYPE_PAC_OPAQUE, 0,
+ PAC_TYPE_PAC_INFO, 10, PAC_TYPE_A_ID, 1, 0x41,
+ PAC_TYPE_A_ID_INFO, 1, 0x42,
+ PAC_TYPE_PAC_KEY, 32) + 32*b'A',
+ True),
+ ("EAP-FAST: Invalid version/subtype in Crypto-Binding TLV",
+ struct.pack(">HH", EAP_TLV_CRYPTO_BINDING_TLV, 60) + 60*b'A',
+ True)]
+ for title, payload, failure in tests:
+ logger.info("Phase 2 test: " + title)
+ run_eap_fast_phase2(dev, payload, failure)
+
+def test_eap_fast_tlv_nak_oom(dev, apdev):
+ """EAP-FAST Phase 2 TLV NAK OOM"""
+ if not openssl_imported:
+ raise HwsimSkip("OpenSSL python method not available")
+ check_eap_capa(dev[0], "FAST")
+ hapd = start_ap(apdev[0])
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ with alloc_fail(dev[0], 1, "eap_fast_tlv_nak"):
+ run_eap_fast_phase2(dev, struct.pack(">HHB", EAP_TLV_TYPE_MANDATORY,
+ 1, 0xff), False)
diff --git a/contrib/wpa/tests/hwsim/test_erp.py b/contrib/wpa/tests/hwsim/test_erp.py
new file mode 100644
index 000000000000..6ca1259ab1a1
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_erp.py
@@ -0,0 +1,741 @@
+# EAP Re-authentication Protocol (ERP) tests
+# Copyright (c) 2014-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import logging
+logger = logging.getLogger()
+import os
+import time
+
+import hostapd
+from utils import *
+from test_ap_eap import int_eap_server_params
+from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+
+def test_erp_initiate_reauth_start(dev, apdev):
+ """Authenticator sending EAP-Initiate/Re-auth-Start, but ERP disabled on peer"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PAX", identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+def test_erp_enabled_on_server(dev, apdev):
+ """ERP enabled on internal EAP server, but disabled on peer"""
+ params = int_eap_server_params()
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PAX", identity="pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+def test_erp(dev, apdev):
+ """ERP enabled on server and peer"""
+ check_erp_capa(dev[0])
+ params = int_eap_server_params()
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ for i in range(3):
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ if "EAP re-authentication completed successfully" not in ev:
+ raise Exception("Did not use ERP")
+ dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def test_erp_server_no_match(dev, apdev):
+ """ERP enabled on server and peer, but server has no key match"""
+ check_erp_capa(dev[0])
+ params = int_eap_server_params()
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ hapd.request("ERP_FLUSH")
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-EAP-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("EAP result timed out")
+ if "CTRL-EVENT-EAP-SUCCESS" in ev:
+ raise Exception("Unexpected EAP success")
+ dev[0].request("DISCONNECT")
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ if "EAP re-authentication completed successfully" in ev:
+ raise Exception("Unexpected use of ERP")
+ dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def start_erp_as(erp_domain="example.com", msk_dump=None, tls13=False,
+ eap_user_file="auth_serv/eap_user.conf"):
+ params = {"driver": "none",
+ "interface": "as-erp",
+ "radius_server_clients": "auth_serv/radius_clients.conf",
+ "radius_server_auth_port": '18128',
+ "eap_server": "1",
+ "eap_user_file": eap_user_file,
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key",
+ "eap_sim_db": "unix:/tmp/hlr_auc_gw.sock",
+ "dh_file": "auth_serv/dh.conf",
+ "pac_opaque_encr_key": "000102030405060708090a0b0c0d0e0f",
+ "eap_fast_a_id": "101112131415161718191a1b1c1d1e1f",
+ "eap_fast_a_id_info": "test server",
+ "eap_server_erp": "1",
+ "erp_domain": erp_domain}
+ if msk_dump:
+ params["dump_msk_file"] = msk_dump
+ if tls13:
+ params["tls_flags"] = "[ENABLE-TLSv1.3]"
+ apdev = {'ifname': 'as-erp'}
+ return hostapd.add_ap(apdev, params, driver="none")
+
+def test_erp_radius(dev, apdev):
+ """ERP enabled on RADIUS server and peer"""
+ check_erp_capa(dev[0])
+ start_erp_as()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ for i in range(3):
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ if "EAP re-authentication completed successfully" not in ev:
+ raise Exception("Did not use ERP")
+ dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def test_erp_radius_no_wildcard_user(dev, apdev, params):
+ """ERP enabled on RADIUS server and peer and no wildcard user"""
+ check_erp_capa(dev[0])
+ user_file = os.path.join(params['logdir'],
+ 'erp_radius_no_wildcard_user.eap_users')
+ with open(user_file, 'w') as f:
+ f.write('"user@example.com" PSK 0123456789abcdef0123456789abcdef\n')
+ start_erp_as(eap_user_file=user_file)
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PSK", identity="user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ for i in range(3):
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ if "EAP re-authentication completed successfully" not in ev:
+ raise Exception("Did not use ERP")
+ dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def test_erp_radius_ext(dev, apdev):
+ """ERP enabled on a separate RADIUS server and peer"""
+ as_hapd = hostapd.Hostapd("as")
+ try:
+ as_hapd.disable()
+ as_hapd.set("eap_server_erp", "1")
+ as_hapd.set("erp_domain", "erp.example.com")
+ as_hapd.enable()
+ run_erp_radius_ext(dev, apdev)
+ finally:
+ as_hapd.disable()
+ as_hapd.set("eap_server_erp", "0")
+ as_hapd.set("erp_domain", "")
+ as_hapd.enable()
+
+def run_erp_radius_ext(dev, apdev):
+ check_erp_capa(dev[0])
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'erp.example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PSK", identity="psk@erp.example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ for i in range(3):
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ if "EAP re-authentication completed successfully" not in ev:
+ raise Exception("Did not use ERP")
+ dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def erp_test(dev, hapd, reauth=False, **kwargs):
+ res = dev.get_capability("eap")
+ if kwargs['eap'] not in res:
+ logger.info("Skip ERP test with %s due to missing support" % kwargs['eap'])
+ return
+ hapd.dump_monitor()
+ dev.dump_monitor()
+ dev.request("ERP_FLUSH")
+ id = dev.connect("test-wpa2-eap", key_mgmt="WPA-EAP", erp="1",
+ scan_freq="2412", **kwargs)
+ dev.request("DISCONNECT")
+ dev.wait_disconnected(timeout=15)
+ dev.dump_monitor()
+ hapd.dump_monitor()
+
+ if reauth:
+ dev.request("ERP_FLUSH")
+ dev.request("RECONNECT")
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ if "EAP re-authentication completed successfully" in ev:
+ raise Exception("Used ERP unexpectedly")
+ dev.wait_connected(timeout=15, error="Reconnection timed out")
+ dev.request("DISCONNECT")
+ dev.wait_disconnected(timeout=15)
+ dev.dump_monitor()
+ hapd.dump_monitor()
+
+ dev.request("RECONNECT")
+ ev = dev.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ if "EAP re-authentication completed successfully" not in ev:
+ raise Exception("Did not use ERP")
+ dev.wait_connected(timeout=15, error="Reconnection timed out")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ dev.request("DISCONNECT")
+
+def test_erp_radius_eap_methods(dev, apdev):
+ """ERP enabled on RADIUS server and peer"""
+ check_erp_capa(dev[0])
+ eap_methods = dev[0].get_capability("eap")
+ start_erp_as()
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ erp_test(dev[0], hapd, eap="AKA", identity="0232010000000000@example.com",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+ erp_test(dev[0], hapd, reauth=True,
+ eap="AKA", identity="0232010000000000@example.com",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581:000000000123")
+ erp_test(dev[0], hapd, eap="AKA'", identity="6555444333222111@example.com",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+ erp_test(dev[0], hapd, reauth=True,
+ eap="AKA'", identity="6555444333222111@example.com",
+ password="5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123")
+ erp_test(dev[0], hapd, eap="EKE", identity="erp-eke@example.com",
+ password="hello")
+ if "FAST" in eap_methods:
+ erp_test(dev[0], hapd, eap="FAST", identity="erp-fast@example.com",
+ password="password", ca_cert="auth_serv/ca.pem",
+ phase2="auth=GTC",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_auth_erp")
+ erp_test(dev[0], hapd, eap="GPSK", identity="erp-gpsk@example.com",
+ password="abcdefghijklmnop0123456789abcdef")
+ erp_test(dev[0], hapd, eap="IKEV2", identity="erp-ikev2@example.com",
+ password="password")
+ erp_test(dev[0], hapd, eap="PAX", identity="erp-pax@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+ if "MSCHAPV2" in eap_methods:
+ erp_test(dev[0], hapd, eap="PEAP", identity="erp-peap@example.com",
+ password="password", ca_cert="auth_serv/ca.pem",
+ phase2="auth=MSCHAPV2")
+ erp_test(dev[0], hapd, eap="TEAP", identity="erp-teap@example.com",
+ password="password", ca_cert="auth_serv/ca.pem",
+ phase2="auth=MSCHAPV2", pac_file="blob://teap_pac")
+ erp_test(dev[0], hapd, eap="PSK", identity="erp-psk@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+ if "PWD" in eap_methods:
+ erp_test(dev[0], hapd, eap="PWD", identity="erp-pwd@example.com",
+ password="secret password")
+ erp_test(dev[0], hapd, eap="SAKE", identity="erp-sake@example.com",
+ password_hex="0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")
+ erp_test(dev[0], hapd, eap="SIM", identity="1232010000000000@example.com",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+ erp_test(dev[0], hapd, reauth=True,
+ eap="SIM", identity="1232010000000000@example.com",
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581")
+ erp_test(dev[0], hapd, eap="TLS", identity="erp-tls@example.com",
+ ca_cert="auth_serv/ca.pem", client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key")
+ erp_test(dev[0], hapd, eap="TTLS", identity="erp-ttls@example.com",
+ password="password", ca_cert="auth_serv/ca.pem", phase2="auth=PAP")
+
+def test_erp_radius_eap_tls_v13(dev, apdev):
+ """ERP enabled on RADIUS server and peer using EAP-TLS v1.3"""
+ check_erp_capa(dev[0])
+ tls = dev[0].request("GET tls_library")
+ if "run=OpenSSL 1.1.1" not in tls:
+ raise HwsimSkip("No TLS v1.3 support in TLS library")
+
+ eap_methods = dev[0].get_capability("eap")
+ start_erp_as(tls13=True)
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ erp_test(dev[0], hapd, eap="TLS", identity="erp-tls@example.com",
+ ca_cert="auth_serv/ca.pem", client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0")
+
+def test_erp_key_lifetime_in_memory(dev, apdev, params):
+ """ERP and key lifetime in memory"""
+ check_erp_capa(dev[0])
+ p = int_eap_server_params()
+ p['erp_send_reauth_start'] = '1'
+ p['erp_domain'] = 'example.com'
+ p['eap_server_erp'] = '1'
+ p['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], p)
+ password = "63d2d21ac3c09ed567ee004a34490f1d16e7fa5835edf17ddba70a63f1a90a25"
+
+ pid = find_wpas_process(dev[0])
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="pap-secret@example.com", password=password,
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412")
+
+ # The decrypted copy of GTK is freed only after the CTRL-EVENT-CONNECTED
+ # event has been delivered, so verify that wpa_supplicant has returned to
+ # eloop before reading process memory.
+ time.sleep(1)
+ dev[0].ping()
+ password = password.encode()
+ buf = read_process_memory(pid, password)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+
+ dev[0].relog()
+ msk = None
+ emsk = None
+ rRK = None
+ rIK = None
+ pmk = None
+ ptk = None
+ gtk = None
+ with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+ for l in f.readlines():
+ if "EAP-TTLS: Derived key - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ msk = binascii.unhexlify(val)
+ if "EAP-TTLS: Derived EMSK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ emsk = binascii.unhexlify(val)
+ if "EAP: ERP rRK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ rRK = binascii.unhexlify(val)
+ if "EAP: ERP rIK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ rIK = binascii.unhexlify(val)
+ if "WPA: PMK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ pmk = binascii.unhexlify(val)
+ if "WPA: PTK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ ptk = binascii.unhexlify(val)
+ if "WPA: Group Key - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ gtk = binascii.unhexlify(val)
+ if not msk or not emsk or not rIK or not rRK or not pmk or not ptk or not gtk:
+ raise Exception("Could not find keys from debug log")
+ if len(gtk) != 16:
+ raise Exception("Unexpected GTK length")
+
+ kck = ptk[0:16]
+ kek = ptk[16:32]
+ tk = ptk[32:48]
+
+ fname = os.path.join(params['logdir'],
+ 'erp_key_lifetime_in_memory.memctx-')
+
+ logger.info("Checking keys in memory while associated")
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, msk, "MSK")
+ get_key_locations(buf, emsk, "EMSK")
+ get_key_locations(buf, rRK, "rRK")
+ get_key_locations(buf, rIK, "rIK")
+ if password not in buf:
+ raise HwsimSkip("Password not found while associated")
+ if pmk not in buf:
+ raise HwsimSkip("PMK not found while associated")
+ if kck not in buf:
+ raise Exception("KCK not found while associated")
+ if kek not in buf:
+ raise Exception("KEK not found while associated")
+ #if tk in buf:
+ # raise Exception("TK found from memory")
+
+ logger.info("Checking keys in memory after disassociation")
+ buf = read_process_memory(pid, password)
+
+ # Note: Password is still present in network configuration
+ # Note: PMK is in EAP fast re-auth data
+
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, msk, "MSK")
+ get_key_locations(buf, emsk, "EMSK")
+ get_key_locations(buf, rRK, "rRK")
+ get_key_locations(buf, rIK, "rIK")
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ if gtk in buf:
+ get_key_locations(buf, gtk, "GTK")
+ verify_not_present(buf, gtk, fname, "GTK")
+
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ if "EAP re-authentication completed successfully" not in ev:
+ raise Exception("Did not use ERP")
+ dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+
+ dev[0].relog()
+ pmk = None
+ ptk = None
+ gtk = None
+ with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+ for l in f.readlines():
+ if "WPA: PMK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ pmk = binascii.unhexlify(val)
+ if "WPA: PTK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ ptk = binascii.unhexlify(val)
+ if "WPA: GTK in EAPOL-Key - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ gtk = binascii.unhexlify(val)
+ if not pmk or not ptk or not gtk:
+ raise Exception("Could not find keys from debug log")
+
+ kck = ptk[0:16]
+ kek = ptk[16:32]
+ tk = ptk[32:48]
+
+ logger.info("Checking keys in memory after ERP and disassociation")
+ buf = read_process_memory(pid, password)
+
+ # Note: Password is still present in network configuration
+
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, msk, "MSK")
+ get_key_locations(buf, emsk, "EMSK")
+ get_key_locations(buf, rRK, "rRK")
+ get_key_locations(buf, rIK, "rIK")
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ verify_not_present(buf, gtk, fname, "GTK")
+
+ dev[0].request("REMOVE_NETWORK all")
+
+ logger.info("Checking keys in memory after network profile removal")
+ buf = read_process_memory(pid, password)
+
+ # Note: rRK and rIK are still in memory
+
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ get_key_locations(buf, msk, "MSK")
+ get_key_locations(buf, emsk, "EMSK")
+ get_key_locations(buf, rRK, "rRK")
+ get_key_locations(buf, rIK, "rIK")
+ verify_not_present(buf, password, fname, "password")
+ verify_not_present(buf, pmk, fname, "PMK")
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ verify_not_present(buf, gtk, fname, "GTK")
+ verify_not_present(buf, msk, fname, "MSK")
+ verify_not_present(buf, emsk, fname, "EMSK")
+
+ dev[0].request("ERP_FLUSH")
+ logger.info("Checking keys in memory after ERP_FLUSH")
+ buf = read_process_memory(pid, password)
+ get_key_locations(buf, rRK, "rRK")
+ get_key_locations(buf, rIK, "rIK")
+ verify_not_present(buf, rRK, fname, "rRK")
+ verify_not_present(buf, rIK, fname, "rIK")
+
+def test_erp_anonymous_identity(dev, apdev):
+ """ERP and anonymous identity"""
+ check_erp_capa(dev[0])
+ params = int_eap_server_params()
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412")
+ for i in range(3):
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ if "EAP re-authentication completed successfully" not in ev:
+ raise Exception("Did not use ERP")
+ dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def test_erp_home_realm_oom(dev, apdev):
+ """ERP and home realm OOM"""
+ check_erp_capa(dev[0])
+ params = int_eap_server_params()
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ for count in range(1, 3):
+ with alloc_fail(dev[0], count, "eap_get_realm"):
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls@example.com",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected(timeout=10)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ for count in range(1, 3):
+ with alloc_fail(dev[0], count, "eap_get_realm"):
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected(timeout=10)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ for count in range(1, 3):
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls@example.com",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected(timeout=10)
+ if count > 1:
+ continue
+ with alloc_fail(dev[0], count, "eap_get_realm"):
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_erp_local_errors(dev, apdev):
+ """ERP and local error cases"""
+ check_erp_capa(dev[0])
+ params = int_eap_server_params()
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['eap_server_erp'] = '1'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("ERP_FLUSH")
+ with alloc_fail(dev[0], 1, "eap_peer_erp_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls@example.com",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ for count in range(1, 6):
+ dev[0].request("ERP_FLUSH")
+ with fail_test(dev[0], count, "hmac_sha256_kdf;eap_peer_erp_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls@example.com",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].request("ERP_FLUSH")
+ with alloc_fail(dev[0], 1, "eap_msg_alloc;eap_peer_erp_reauth_start"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls@example.com",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].request("ERP_FLUSH")
+ with fail_test(dev[0], 1, "hmac_sha256;eap_peer_erp_reauth_start"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls@example.com",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].request("ERP_FLUSH")
+ with fail_test(dev[0], 1, "hmac_sha256;eap_peer_finish"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls@example.com",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].request("ERP_FLUSH")
+ with alloc_fail(dev[0], 1, "eap_peer_erp_init"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls@example.com",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+
+ dev[0].request("ERP_FLUSH")
+ with alloc_fail(dev[0], 1, "eap_peer_finish"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls@example.com",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].request("ERP_FLUSH")
+ with fail_test(dev[0], 1, "hmac_sha256_kdf;eap_peer_finish"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="erp-ttls@example.com",
+ anonymous_identity="anonymous@example.com",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=PAP",
+ erp="1", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=15)
+ dev[0].request("RECONNECT")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
diff --git a/contrib/wpa/tests/hwsim/test_ext_password.py b/contrib/wpa/tests/hwsim/test_ext_password.py
new file mode 100644
index 000000000000..789b673d9625
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ext_password.py
@@ -0,0 +1,112 @@
+# External password storage
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import os
+import tempfile
+
+import hostapd
+from utils import skip_with_fips
+from wpasupplicant import WpaSupplicant
+from test_ap_hs20 import hs20_ap_params
+from test_ap_hs20 import interworking_select
+from test_ap_hs20 import interworking_connect
+
+@remote_compatible
+def test_ext_password_psk(dev, apdev):
+ """External password storage for PSK"""
+ params = hostapd.wpa2_params(ssid="ext-pw-psk", passphrase="12345678")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET ext_password_backend test:psk1=12345678")
+ dev[0].connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412")
+
+def test_ext_password_psk_not_found(dev, apdev):
+ """External password storage for PSK and PSK not found"""
+ params = hostapd.wpa2_params(ssid="ext-pw-psk", passphrase="12345678")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET ext_password_backend test:psk1=12345678")
+ dev[0].connect("ext-pw-psk", raw_psk="ext:psk2", scan_freq="2412",
+ wait_connect=False)
+ dev[1].request("SET ext_password_backend test:psk1=1234567")
+ dev[1].connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412",
+ wait_connect=False)
+ dev[2].request("SET ext_password_backend test:psk1=1234567890123456789012345678901234567890123456789012345678901234567890")
+ dev[2].connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412",
+ wait_connect=False)
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.request("SET ext_password_backend test:psk1=123456789012345678901234567890123456789012345678901234567890123q")
+ wpas.connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412",
+ wait_connect=False)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected association")
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected association")
+ ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected association")
+ ev = wpas.wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected association")
+
+def test_ext_password_eap(dev, apdev):
+ """External password storage for EAP password"""
+ params = hostapd.wpa2_eap_params(ssid="ext-pw-eap")
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET ext_password_backend test:pw0=hello|pw1=password|pw2=secret")
+ dev[0].connect("ext-pw-eap", key_mgmt="WPA-EAP", eap="PEAP",
+ identity="user", password_hex="ext:pw1",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ scan_freq="2412")
+
+def test_ext_password_interworking(dev, apdev):
+ """External password storage for Interworking network selection"""
+ skip_with_fips(dev[0])
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ dev[0].request("SET ext_password_backend test:pw1=password")
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test"})
+ dev[0].set_cred(id, "password", "ext:pw1")
+ interworking_select(dev[0], bssid, freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+
+def test_ext_password_file_psk(dev, apdev):
+ """External password (file) storage for PSK"""
+ params = hostapd.wpa2_params(ssid="ext-pw-psk", passphrase="12345678")
+ hostapd.add_ap(apdev[0], params)
+ fd, fn = tempfile.mkstemp()
+ with open(fn, "w") as f:
+ f.write("psk1=12345678\n")
+ os.close(fd)
+ dev[0].request("SET ext_password_backend file:%s" % fn)
+ dev[0].connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412")
+ for i in range(2):
+ dev[0].request("REMOVE_NETWORK all")
+ if i == 0:
+ dev[0].wait_disconnected()
+ dev[0].connect("ext-pw-psk", raw_psk="ext:psk2", scan_freq="2412",
+ wait_connect=False)
+ else:
+ dev[0].connect("ext-pw-psk", raw_psk="ext:psk1", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "EXT PW: No PSK found from external storage"],
+ timeout=10)
+ if i == 0:
+ os.unlink(fn)
+ if ev is None:
+ raise Exception("No connection result reported")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
diff --git a/contrib/wpa/tests/hwsim/test_fils.py b/contrib/wpa/tests/hwsim/test_fils.py
new file mode 100644
index 000000000000..9998299d81a8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_fils.py
@@ -0,0 +1,2411 @@
+# Test cases for FILS
+# Copyright (c) 2015-2017, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import hashlib
+import logging
+logger = logging.getLogger()
+import os
+import socket
+import struct
+import time
+
+import hostapd
+from tshark import run_tshark
+from wpasupplicant import WpaSupplicant
+import hwsim_utils
+from utils import *
+from test_erp import start_erp_as
+from test_ap_hs20 import ip_checksum
+
+def test_fils_sk_full_auth(dev, apdev, params):
+ """FILS SK full authentication"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['wpa_group_rekey'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ bss = dev[0].get_bss(bssid)
+ logger.debug("BSS: " + str(bss))
+ if "[FILS]" not in bss['flags']:
+ raise Exception("[FILS] flag not indicated")
+ if "[WPA2-FILS-SHA256-CCMP]" not in bss['flags']:
+ raise Exception("[WPA2-FILS-SHA256-CCMP] flag not indicated")
+
+ res = dev[0].request("SCAN_RESULTS")
+ logger.debug("SCAN_RESULTS: " + res)
+ if "[FILS]" not in res:
+ raise Exception("[FILS] flag not indicated")
+ if "[WPA2-FILS-SHA256-CCMP]" not in res:
+ raise Exception("[WPA2-FILS-SHA256-CCMP] flag not indicated")
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ conf = hapd.get_config()
+ if conf['key_mgmt'] != 'FILS-SHA256':
+ raise Exception("Unexpected config key_mgmt: " + conf['key_mgmt'])
+
+def test_fils_sk_sha384_full_auth(dev, apdev, params):
+ """FILS SK full authentication (SHA384)"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA384"
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['wpa_group_rekey'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ bss = dev[0].get_bss(bssid)
+ logger.debug("BSS: " + str(bss))
+ if "[FILS]" not in bss['flags']:
+ raise Exception("[FILS] flag not indicated")
+ if "[WPA2-FILS-SHA384-CCMP]" not in bss['flags']:
+ raise Exception("[WPA2-FILS-SHA384-CCMP] flag not indicated")
+
+ res = dev[0].request("SCAN_RESULTS")
+ logger.debug("SCAN_RESULTS: " + res)
+ if "[FILS]" not in res:
+ raise Exception("[FILS] flag not indicated")
+ if "[WPA2-FILS-SHA384-CCMP]" not in res:
+ raise Exception("[WPA2-FILS-SHA384-CCMP] flag not indicated")
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("fils", key_mgmt="FILS-SHA384",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ conf = hapd.get_config()
+ if conf['key_mgmt'] != 'FILS-SHA384':
+ raise Exception("Unexpected config key_mgmt: " + conf['key_mgmt'])
+
+def test_fils_sk_pmksa_caching(dev, apdev, params):
+ """FILS SK and PMKSA caching"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using PMKSA caching timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ pmksa2 = dev[0].get_pmksa(bssid)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa['pmkid'] != pmksa2['pmkid']:
+ raise Exception("Unexpected PMKID change")
+
+ # Verify EAPOL reauthentication after FILS authentication
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not succeed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_fils_sk_pmksa_caching_ocv(dev, apdev, params):
+ """FILS SK and PMKSA caching with OCV"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['ieee80211w'] = '1'
+ params['ocv'] = '1'
+ try:
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412", ieee80211w="1", ocv="1")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using PMKSA caching timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ pmksa2 = dev[0].get_pmksa(bssid)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa['pmkid'] != pmksa2['pmkid']:
+ raise Exception("Unexpected PMKID change")
+
+ # Verify EAPOL reauthentication after FILS authentication
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not succeed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_fils_sk_pmksa_caching_and_cache_id(dev, apdev):
+ """FILS SK and PMKSA caching with Cache Identifier"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['fils_cache_id'] = "abcd"
+ params["radius_server_clients"] = "auth_serv/radius_clients.conf"
+ params["radius_server_auth_port"] = '18128'
+ params["eap_server"] = "1"
+ params["eap_user_file"] = "auth_serv/eap_user.conf"
+ params["ca_cert"] = "auth_serv/ca.pem"
+ params["server_cert"] = "auth_serv/server.pem"
+ params["private_key"] = "auth_serv/server.key"
+ params["eap_sim_db"] = "unix:/tmp/hlr_auc_gw.sock"
+ params["dh_file"] = "auth_serv/dh.conf"
+ params["pac_opaque_encr_key"] = "000102030405060708090a0b0c0d0e0f"
+ params["eap_fast_a_id"] = "101112131415161718191a1b1c1d1e1f"
+ params["eap_fast_a_id_info"] = "test server"
+ params["eap_server_erp"] = "1"
+ params["erp_domain"] = "example.com"
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ res = dev[0].request("PMKSA")
+ if "FILS Cache Identifier" not in res:
+ raise Exception("PMKSA list does not include FILS Cache Identifier")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+ if "cache_id" not in pmksa:
+ raise Exception("No FILS Cache Identifier listed")
+ if pmksa["cache_id"] != "abcd":
+ raise Exception("The configured FILS Cache Identifier not seen in PMKSA")
+
+ bssid2 = apdev[1]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['fils_cache_id'] = "abcd"
+ hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid2, freq=2412)
+
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("ROAM " + bssid2):
+ raise Exception("ROAM failed")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using PMKSA caching timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if bssid2 not in ev:
+ raise Exception("Failed to connect to the second AP")
+
+ hwsim_utils.test_connectivity(dev[0], hapd2)
+ pmksa2 = dev[0].get_pmksa(bssid2)
+ if pmksa2:
+ raise Exception("Unexpected extra PMKSA cache added")
+ pmksa2 = dev[0].get_pmksa(bssid)
+ if not pmksa2:
+ raise Exception("Original PMKSA cache entry removed")
+ if pmksa['pmkid'] != pmksa2['pmkid']:
+ raise Exception("Unexpected PMKID change")
+
+def test_fils_sk_pmksa_caching_ctrl_ext(dev, apdev, params):
+ """FILS SK and PMKSA caching with Cache Identifier and external management"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ hapd_as = start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA384"
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['fils_cache_id'] = "ffee"
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA384",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ res1 = dev[0].request("PMKSA_GET %d" % id)
+ logger.info("PMKSA_GET: " + res1)
+ if "UNKNOWN COMMAND" in res1:
+ raise HwsimSkip("PMKSA_GET not supported in the build")
+ if bssid not in res1:
+ raise Exception("PMKSA cache entry missing")
+ if "ffee" not in res1:
+ raise Exception("FILS Cache Identifier not seen in PMKSA cache entry")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd_as.disable()
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("PMKSA_FLUSH")
+ dev[0].request("ERP_FLUSH")
+ for entry in res1.splitlines():
+ if "OK" not in dev[0].request("PMKSA_ADD %d %s" % (id, entry)):
+ raise Exception("Failed to add PMKSA entry")
+
+ bssid2 = apdev[1]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA384"
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['fils_cache_id'] = "ffee"
+ hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].set_network(id, "bssid", bssid2)
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_connected()
+ if bssid2 not in ev:
+ raise Exception("Unexpected BSS selected")
+
+def test_fils_sk_erp(dev, apdev, params):
+ """FILS SK using ERP"""
+ run_fils_sk_erp(dev, apdev, "FILS-SHA256", params)
+
+def test_fils_sk_erp_sha384(dev, apdev, params):
+ """FILS SK using ERP and SHA384"""
+ run_fils_sk_erp(dev, apdev, "FILS-SHA384", params)
+
+def run_fils_sk_erp(dev, apdev, key_mgmt, params):
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = key_mgmt
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt=key_mgmt,
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_fils_sk_erp_followed_by_pmksa_caching(dev, apdev, params):
+ """FILS SK ERP following by PMKSA caching"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # Force the second connection to use ERP by deleting the PMKSA entry.
+ dev[0].request("PMKSA_FLUSH")
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # The third connection is expected to use PMKSA caching for FILS
+ # authentication.
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using PMKSA caching timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ pmksa2 = dev[0].get_pmksa(bssid)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa['pmkid'] != pmksa2['pmkid']:
+ raise Exception("Unexpected PMKID change")
+
+def test_fils_sk_erp_another_ssid(dev, apdev, params):
+ """FILS SK using ERP and roam to another SSID"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.disable()
+ dev[0].flush_scan_cache()
+ if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+
+ params = hostapd.wpa2_eap_params(ssid="fils2")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].dump_monitor()
+ id = dev[0].connect("fils2", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412", wait_connect=False)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_fils_sk_multiple_realms(dev, apdev, params):
+ """FILS SK and multiple realms"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ fils_realms = ['r1.example.org', 'r2.EXAMPLE.org', 'r3.example.org',
+ 'r4.example.org', 'r5.example.org', 'r6.example.org',
+ 'r7.example.org', 'r8.example.org',
+ 'example.com',
+ 'r9.example.org', 'r10.example.org', 'r11.example.org',
+ 'r12.example.org', 'r13.example.org', 'r14.example.org',
+ 'r15.example.org', 'r16.example.org']
+ params['fils_realm'] = fils_realms
+ params['fils_cache_id'] = "1234"
+ params['hessid'] = bssid
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 275"):
+ raise Exception("ANQP_GET command failed")
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+ bss = dev[0].get_bss(bssid)
+
+ if 'fils_info' not in bss:
+ raise Exception("FILS Indication element information missing")
+ if bss['fils_info'] != '02b8':
+ raise Exception("Unexpected FILS Information: " + bss['fils_info'])
+
+ if 'fils_cache_id' not in bss:
+ raise Exception("FILS Cache Identifier missing")
+ if bss['fils_cache_id'] != '1234':
+ raise Exception("Unexpected FILS Cache Identifier: " + bss['fils_cache_id'])
+
+ if 'fils_realms' not in bss:
+ raise Exception("FILS Realm Identifiers missing")
+ expected = ''
+ count = 0
+ for realm in fils_realms:
+ hash = hashlib.sha256(realm.lower().encode()).digest()
+ expected += binascii.hexlify(hash[0:2]).decode()
+ count += 1
+ if count == 7:
+ break
+ if bss['fils_realms'] != expected:
+ raise Exception("Unexpected FILS Realm Identifiers: " + bss['fils_realms'])
+
+ if 'anqp_fils_realm_info' not in bss:
+ raise Exception("FILS Realm Information ANQP-element not seen")
+ info = bss['anqp_fils_realm_info']
+ expected = ''
+ for realm in fils_realms:
+ hash = hashlib.sha256(realm.lower().encode()).digest()
+ expected += binascii.hexlify(hash[0:2]).decode()
+ if info != expected:
+ raise Exception("Unexpected FILS Realm Info ANQP-element: " + info)
+
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+# DHCP message op codes
+BOOTREQUEST = 1
+BOOTREPLY = 2
+
+OPT_PAD = 0
+OPT_DHCP_MESSAGE_TYPE = 53
+OPT_RAPID_COMMIT = 80
+OPT_END = 255
+
+DHCPDISCOVER = 1
+DHCPOFFER = 2
+DHCPREQUEST = 3
+DHCPDECLINE = 4
+DHCPACK = 5
+DHCPNAK = 6
+DHCPRELEASE = 7
+DHCPINFORM = 8
+
+def build_dhcp(req, dhcp_msg, chaddr, giaddr="0.0.0.0",
+ ip_src="0.0.0.0", ip_dst="255.255.255.255",
+ rapid_commit=True, override_op=None, magic_override=None,
+ opt_end=True, extra_op=None):
+ proto = b'\x08\x00' # IPv4
+ _ip_src = socket.inet_pton(socket.AF_INET, ip_src)
+ _ip_dst = socket.inet_pton(socket.AF_INET, ip_dst)
+
+ _ciaddr = b'\x00\x00\x00\x00'
+ _yiaddr = b'\x00\x00\x00\x00'
+ _siaddr = b'\x00\x00\x00\x00'
+ _giaddr = socket.inet_pton(socket.AF_INET, giaddr)
+ _chaddr = binascii.unhexlify(chaddr.replace(':', '')) + 10 * b'\x00'
+ htype = 1 # Hardware address type; 1 = Ethernet
+ hlen = 6 # Hardware address length
+ hops = 0
+ xid = 123456
+ secs = 0
+ flags = 0
+ if req:
+ op = BOOTREQUEST
+ src_port = 68
+ dst_port = 67
+ else:
+ op = BOOTREPLY
+ src_port = 67
+ dst_port = 68
+ if override_op is not None:
+ op = override_op
+ payload = struct.pack('>BBBBLHH', op, htype, hlen, hops, xid, secs, flags)
+ sname = 64*b'\x00'
+ file = 128*b'\x00'
+ payload += _ciaddr + _yiaddr + _siaddr + _giaddr + _chaddr + sname + file
+ # magic - DHCP
+ if magic_override is not None:
+ payload += magic_override
+ else:
+ payload += b'\x63\x82\x53\x63'
+ # Option: DHCP Message Type
+ if dhcp_msg is not None:
+ payload += struct.pack('BBB', OPT_DHCP_MESSAGE_TYPE, 1, dhcp_msg)
+ if rapid_commit:
+ # Option: Rapid Commit
+ payload += struct.pack('BB', OPT_RAPID_COMMIT, 0)
+ if extra_op:
+ payload += extra_op
+ # End Option
+ if opt_end:
+ payload += struct.pack('B', OPT_END)
+
+ udp = struct.pack('>HHHH', src_port, dst_port,
+ 8 + len(payload), 0) + payload
+
+ tot_len = 20 + len(udp)
+ start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
+ ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
+ csum = ip_checksum(ipv4)
+ ipv4 = start + csum + _ip_src + _ip_dst
+
+ return proto + ipv4 + udp
+
+def fils_hlp_config(fils_hlp_wait_time=10000):
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ params['own_ip_addr'] = '127.0.0.3'
+ params['dhcp_server'] = '127.0.0.2'
+ params['fils_hlp_wait_time'] = str(fils_hlp_wait_time)
+ return params
+
+def test_fils_sk_hlp(dev, apdev, params):
+ """FILS SK HLP (rapid commit server)"""
+ run_fils_sk_hlp(dev, apdev, True, params)
+
+def test_fils_sk_hlp_no_rapid_commit(dev, apdev, params):
+ """FILS SK HLP (no rapid commit server)"""
+ run_fils_sk_hlp(dev, apdev, False, params)
+
+def run_fils_sk_hlp(dev, apdev, rapid_commit_server, params):
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.settimeout(5)
+ sock.bind(("127.0.0.2", 67))
+
+ bssid = apdev[0]['bssid']
+ params = fils_hlp_config()
+ params['fils_hlp_wait_time'] = '10000'
+ if not rapid_commit_server:
+ params['dhcp_rapid_commit_proxy'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ if "OK" not in dev[0].request("FILS_HLP_REQ_FLUSH"):
+ raise Exception("Failed to flush pending FILS HLP requests")
+ tests = ["",
+ "q",
+ "ff:ff:ff:ff:ff:ff",
+ "ff:ff:ff:ff:ff:ff q"]
+ for t in tests:
+ if "FAIL" not in dev[0].request("FILS_HLP_REQ_ADD " + t):
+ raise Exception("Invalid FILS_HLP_REQ_ADD accepted: " + t)
+ dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
+ chaddr=dev[0].own_addr())
+ tests = ["ff:ff:ff:ff:ff:ff aabb",
+ "ff:ff:ff:ff:ff:ff " + 255*'cc',
+ hapd.own_addr() + " ddee010203040506070809",
+ "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()]
+ for t in tests:
+ if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + t):
+ raise Exception("FILS_HLP_REQ_ADD failed: " + t)
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ if rapid_commit_server:
+ # TODO: Proper rapid commit response
+ dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPACK,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
+ sock.sendto(dhcpdisc[2+20+8:], addr)
+ else:
+ dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
+ sock.sendto(dhcpdisc[2+20+8:], addr)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPACK, rapid_commit=False,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
+ sock.sendto(dhcpdisc[2+20+8:], addr)
+ ev = dev[0].wait_event(["FILS-HLP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("FILS HLP response not reported")
+ vals = ev.split(' ')
+ frame = binascii.unhexlify(vals[3].split('=')[1])
+ proto, = struct.unpack('>H', frame[0:2])
+ if proto != 0x0800:
+ raise Exception("Unexpected ethertype in HLP response: %d" % proto)
+ frame = frame[2:]
+ ip = frame[0:20]
+ if ip_checksum(ip) != b'\x00\x00':
+ raise Exception("IP header checksum mismatch in HLP response")
+ frame = frame[20:]
+ udp = frame[0:8]
+ frame = frame[8:]
+ sport, dport, ulen, ucheck = struct.unpack('>HHHH', udp)
+ if sport != 67 or dport != 68:
+ raise Exception("Unexpected UDP port in HLP response")
+ dhcp = frame[0:28]
+ frame = frame[28:]
+ op, htype, hlen, hops, xid, secs, flags, ciaddr, yiaddr, siaddr, giaddr = struct.unpack('>4BL2H4L', dhcp)
+ chaddr = frame[0:16]
+ frame = frame[16:]
+ sname = frame[0:64]
+ frame = frame[64:]
+ file = frame[0:128]
+ frame = frame[128:]
+ options = frame
+ if options[0:4] != b'\x63\x82\x53\x63':
+ raise Exception("No DHCP magic seen in HLP response")
+ options = options[4:]
+ # TODO: fully parse and validate DHCPACK options
+ if struct.pack('BBB', OPT_DHCP_MESSAGE_TYPE, 1, DHCPACK) not in options:
+ raise Exception("DHCPACK not in HLP response")
+
+ dev[0].wait_connected()
+
+ dev[0].request("FILS_HLP_REQ_FLUSH")
+
+def test_fils_sk_hlp_timeout(dev, apdev, params):
+ """FILS SK HLP (rapid commit server timeout)"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.settimeout(5)
+ sock.bind(("127.0.0.2", 67))
+
+ bssid = apdev[0]['bssid']
+ params = fils_hlp_config(fils_hlp_wait_time=30)
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ if "OK" not in dev[0].request("FILS_HLP_REQ_FLUSH"):
+ raise Exception("Failed to flush pending FILS HLP requests")
+ dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
+ chaddr=dev[0].own_addr())
+ if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()):
+ raise Exception("FILS_HLP_REQ_ADD failed")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ # Wait for HLP wait timeout to hit
+ # FILS: HLP response timeout - continue with association response
+ dev[0].wait_connected()
+
+ dev[0].request("FILS_HLP_REQ_FLUSH")
+
+def test_fils_sk_hlp_oom(dev, apdev, params):
+ """FILS SK HLP and hostapd OOM"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.settimeout(5)
+ sock.bind(("127.0.0.2", 67))
+
+ bssid = apdev[0]['bssid']
+ params = fils_hlp_config(fils_hlp_wait_time=500)
+ params['dhcp_rapid_commit_proxy'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ if "OK" not in dev[0].request("FILS_HLP_REQ_FLUSH"):
+ raise Exception("Failed to flush pending FILS HLP requests")
+ dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
+ chaddr=dev[0].own_addr())
+ if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()):
+ raise Exception("FILS_HLP_REQ_ADD failed")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ with alloc_fail(hapd, 1, "fils_process_hlp"):
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ with alloc_fail(hapd, 1, "fils_process_hlp_dhcp"):
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ with alloc_fail(hapd, 1, "wpabuf_alloc;fils_process_hlp_dhcp"):
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ with alloc_fail(hapd, 1, "wpabuf_alloc;fils_dhcp_handler"):
+ dev[0].select_network(id, freq=2412)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPACK,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
+ sock.sendto(dhcpdisc[2+20+8:], addr)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ with alloc_fail(hapd, 1, "wpabuf_resize;fils_dhcp_handler"):
+ dev[0].select_network(id, freq=2412)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPACK,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
+ sock.sendto(dhcpdisc[2+20+8:], addr)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
+ with alloc_fail(hapd, 1, "wpabuf_resize;fils_dhcp_request"):
+ sock.sendto(dhcpoffer[2+20+8:], addr)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].request("FILS_HLP_REQ_FLUSH")
+
+def test_fils_sk_hlp_req_parsing(dev, apdev, params):
+ """FILS SK HLP request parsing"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = fils_hlp_config(fils_hlp_wait_time=30)
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ if "OK" not in dev[0].request("FILS_HLP_REQ_FLUSH"):
+ raise Exception("Failed to flush pending FILS HLP requests")
+
+ tot_len = 20 + 1
+ start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
+ _ip_src = b'\x00\x00\x00\x00'
+ _ip_dst = b'\x00\x00\x00\x00'
+ ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
+ csum = ip_checksum(ipv4)
+ ipv4_overflow = start + csum + _ip_src + _ip_dst
+
+ tot_len = 20
+ start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 123)
+ ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
+ csum = ip_checksum(ipv4)
+ ipv4_unknown_proto = start + csum + _ip_src + _ip_dst
+
+ tot_len = 20
+ start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
+ ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
+ csum = ip_checksum(ipv4)
+ ipv4_missing_udp_hdr = start + csum + _ip_src + _ip_dst
+
+ src_port = 68
+ dst_port = 67
+ udp = struct.pack('>HHHH', src_port, dst_port, 8 + 1, 0)
+ tot_len = 20 + len(udp)
+ start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
+ ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
+ csum = ip_checksum(ipv4)
+ udp_overflow = start + csum + _ip_src + _ip_dst + udp
+
+ udp = struct.pack('>HHHH', src_port, dst_port, 7, 0)
+ tot_len = 20 + len(udp)
+ start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
+ ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
+ csum = ip_checksum(ipv4)
+ udp_underflow = start + csum + _ip_src + _ip_dst + udp
+
+ src_port = 123
+ dst_port = 456
+ udp = struct.pack('>HHHH', src_port, dst_port, 8, 0)
+ tot_len = 20 + len(udp)
+ start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
+ ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
+ csum = ip_checksum(ipv4)
+ udp_unknown_port = start + csum + _ip_src + _ip_dst + udp
+
+ src_port = 68
+ dst_port = 67
+ udp = struct.pack('>HHHH', src_port, dst_port, 8, 0)
+ tot_len = 20 + len(udp)
+ start = struct.pack('>BBHHBBBB', 0x45, 0, tot_len, 0, 0, 0, 128, 17)
+ ipv4 = start + b'\x00\x00' + _ip_src + _ip_dst
+ csum = ip_checksum(ipv4)
+ dhcp_missing_data = start + csum + _ip_src + _ip_dst + udp
+
+ dhcp_not_req = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
+ chaddr=dev[0].own_addr(), override_op=BOOTREPLY)
+ dhcp_no_magic = build_dhcp(req=True, dhcp_msg=None,
+ chaddr=dev[0].own_addr(), magic_override=b'',
+ rapid_commit=False, opt_end=False)
+ dhcp_unknown_magic = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
+ chaddr=dev[0].own_addr(),
+ magic_override=b'\x00\x00\x00\x00')
+ dhcp_opts = build_dhcp(req=True, dhcp_msg=DHCPNAK,
+ chaddr=dev[0].own_addr(),
+ extra_op=b'\x00\x11', opt_end=False)
+ dhcp_opts2 = build_dhcp(req=True, dhcp_msg=DHCPNAK,
+ chaddr=dev[0].own_addr(),
+ extra_op=b'\x11\x01', opt_end=False)
+ dhcp_valid = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
+ chaddr=dev[0].own_addr())
+
+ tests = ["ff",
+ "0800",
+ "0800" + 20*"00",
+ "0800" + binascii.hexlify(ipv4_overflow).decode(),
+ "0800" + binascii.hexlify(ipv4_unknown_proto).decode(),
+ "0800" + binascii.hexlify(ipv4_missing_udp_hdr).decode(),
+ "0800" + binascii.hexlify(udp_overflow).decode(),
+ "0800" + binascii.hexlify(udp_underflow).decode(),
+ "0800" + binascii.hexlify(udp_unknown_port).decode(),
+ "0800" + binascii.hexlify(dhcp_missing_data).decode(),
+ binascii.hexlify(dhcp_not_req).decode(),
+ binascii.hexlify(dhcp_no_magic).decode(),
+ binascii.hexlify(dhcp_unknown_magic).decode()]
+ for t in tests:
+ if "OK" not in dev[0].request("FILS_HLP_REQ_ADD ff:ff:ff:ff:ff:ff " + t):
+ raise Exception("FILS_HLP_REQ_ADD failed: " + t)
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].request("FILS_HLP_REQ_FLUSH")
+ tests = [binascii.hexlify(dhcp_opts).decode(),
+ binascii.hexlify(dhcp_opts2).decode()]
+ for t in tests:
+ if "OK" not in dev[0].request("FILS_HLP_REQ_ADD ff:ff:ff:ff:ff:ff " + t):
+ raise Exception("FILS_HLP_REQ_ADD failed: " + t)
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].request("FILS_HLP_REQ_FLUSH")
+ if "OK" not in dev[0].request("FILS_HLP_REQ_ADD ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcp_valid).decode()):
+ raise Exception("FILS_HLP_REQ_ADD failed")
+ hapd.set("own_ip_addr", "0.0.0.0")
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ hapd.set("dhcp_server", "0.0.0.0")
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # FILS: Failed to bind DHCP socket: Address already in use
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.settimeout(5)
+ sock.bind(("127.0.0.2", 67))
+ hapd.set("own_ip_addr", "127.0.0.2")
+ hapd.set("dhcp_server", "127.0.0.2")
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # FILS: DHCP sendto failed: Invalid argument
+ hapd.set("own_ip_addr", "127.0.0.3")
+ hapd.set("dhcp_server", "127.0.0.2")
+ hapd.set("dhcp_relay_port", "0")
+ hapd.set("dhcp_server_port", "0")
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].request("FILS_HLP_REQ_FLUSH")
+
+def test_fils_sk_hlp_dhcp_parsing(dev, apdev, params):
+ """FILS SK HLP and DHCP response parsing"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+ sock.settimeout(5)
+ sock.bind(("127.0.0.2", 67))
+
+ bssid = apdev[0]['bssid']
+ params = fils_hlp_config(fils_hlp_wait_time=30)
+ params['dhcp_rapid_commit_proxy'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ if "OK" not in dev[0].request("FILS_HLP_REQ_FLUSH"):
+ raise Exception("Failed to flush pending FILS HLP requests")
+ dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
+ chaddr=dev[0].own_addr())
+ if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()):
+ raise Exception("FILS_HLP_REQ_ADD failed")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ with alloc_fail(hapd, 1, "fils_process_hlp"):
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dhcpdisc = build_dhcp(req=False, dhcp_msg=DHCPACK,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
+ #sock.sendto(dhcpdisc[2+20+8:], addr)
+ chaddr = binascii.unhexlify(dev[0].own_addr().replace(':', '')) + 10*b'\x00'
+ tests = [b"\x00",
+ b"\x02" + 500 * b"\x00",
+ b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + 500*b"\x00",
+ b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + 16*b"\x00" + 64*b"\x00" + 128*b"\x00" + b"\x63\x82\x53\x63",
+ b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + 16*b"\x00" + 64*b"\x00" + 128*b"\x00" + b"\x63\x82\x53\x63" + b"\x00\x11",
+ b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + 16*b"\x00" + 64*b"\x00" + 128*b"\x00" + b"\x63\x82\x53\x63" + b"\x11\x01",
+ b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + chaddr + 64*b"\x00" + 128*b"\x00" + b"\x63\x82\x53\x63" + b"\x35\x00\xff",
+ b"\x02\x00\x00\x00" + 20*b"\x00" + b"\x7f\x00\x00\x03" + chaddr + 64*b"\x00" + 128*b"\x00" + b"\x63\x82\x53\x63" + b"\x35\x01\x00\xff",
+ 1501 * b"\x00"]
+ for t in tests:
+ sock.sendto(t, addr)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # FILS: DHCP sendto failed: Invalid argument for second DHCP TX in proxy
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ hapd.set("dhcp_server_port", "0")
+ dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3")
+ sock.sendto(dhcpoffer[2+20+8:], addr)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.set("dhcp_server_port", "67")
+
+ # Options in DHCPOFFER
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3",
+ extra_op=b"\x00\x11", opt_end=False)
+ sock.sendto(dhcpoffer[2+20+8:], addr)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # Options in DHCPOFFER (2)
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3",
+ extra_op=b"\x11\x01", opt_end=False)
+ sock.sendto(dhcpoffer[2+20+8:], addr)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # Server ID in DHCPOFFER
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3",
+ extra_op=b"\x36\x01\x30")
+ sock.sendto(dhcpoffer[2+20+8:], addr)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # FILS: Could not update DHCPDISCOVER
+ dev[0].request("FILS_HLP_REQ_FLUSH")
+ dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
+ chaddr=dev[0].own_addr(),
+ extra_op=b"\x00\x11", opt_end=False)
+ if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()):
+ raise Exception("FILS_HLP_REQ_ADD failed")
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3",
+ extra_op=b"\x36\x01\x30")
+ sock.sendto(dhcpoffer[2+20+8:], addr)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # FILS: Could not update DHCPDISCOVER (2)
+ dev[0].request("FILS_HLP_REQ_FLUSH")
+ dhcpdisc = build_dhcp(req=True, dhcp_msg=DHCPDISCOVER,
+ chaddr=dev[0].own_addr(),
+ extra_op=b"\x11\x01", opt_end=False)
+ if "OK" not in dev[0].request("FILS_HLP_REQ_ADD " + "ff:ff:ff:ff:ff:ff " + binascii.hexlify(dhcpdisc).decode()):
+ raise Exception("FILS_HLP_REQ_ADD failed")
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ (msg, addr) = sock.recvfrom(1000)
+ logger.debug("Received DHCP message from %s" % str(addr))
+ dhcpoffer = build_dhcp(req=False, dhcp_msg=DHCPOFFER, rapid_commit=False,
+ chaddr=dev[0].own_addr(), giaddr="127.0.0.3",
+ extra_op=b"\x36\x01\x30")
+ sock.sendto(dhcpoffer[2+20+8:], addr)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].request("FILS_HLP_REQ_FLUSH")
+
+def test_fils_sk_erp_and_reauth(dev, apdev, params):
+ """FILS SK using ERP and AP going away"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ params['broadcast_deauth'] = '0'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ hapd.disable()
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ hapd.enable()
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Reconnection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+
+def test_fils_sk_erp_sim(dev, apdev, params):
+ """FILS SK using ERP with SIM"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ realm = 'wlan.mnc001.mcc232.3gppnetwork.org'
+ start_erp_as(erp_domain=realm,
+ msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['fils_realm'] = realm
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="SIM", identity="1232010000000000@" + realm,
+ password="90dca4eda45b53cf0f12d7c9c3bc6a89:cb9cccc4b9258e6dca4760379fb82581",
+ erp="1", scan_freq="2412")
+
+ hapd.disable()
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ hapd.enable()
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Reconnection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+
+def test_fils_sk_pfs_19(dev, apdev, params):
+ """FILS SK with PFS (DH group 19)"""
+ run_fils_sk_pfs(dev, apdev, "19", params)
+
+def test_fils_sk_pfs_20(dev, apdev, params):
+ """FILS SK with PFS (DH group 20)"""
+ run_fils_sk_pfs(dev, apdev, "20", params)
+
+def test_fils_sk_pfs_21(dev, apdev, params):
+ """FILS SK with PFS (DH group 21)"""
+ run_fils_sk_pfs(dev, apdev, "21", params)
+
+def test_fils_sk_pfs_25(dev, apdev, params):
+ """FILS SK with PFS (DH group 25)"""
+ run_fils_sk_pfs(dev, apdev, "25", params)
+
+def test_fils_sk_pfs_26(dev, apdev, params):
+ """FILS SK with PFS (DH group 26)"""
+ run_fils_sk_pfs(dev, apdev, "26", params)
+
+def test_fils_sk_pfs_27(dev, apdev, params):
+ """FILS SK with PFS (DH group 27)"""
+ run_fils_sk_pfs(dev, apdev, "27", params)
+
+def test_fils_sk_pfs_28(dev, apdev, params):
+ """FILS SK with PFS (DH group 28)"""
+ run_fils_sk_pfs(dev, apdev, "28", params)
+
+def test_fils_sk_pfs_29(dev, apdev, params):
+ """FILS SK with PFS (DH group 29)"""
+ run_fils_sk_pfs(dev, apdev, "29", params)
+
+def test_fils_sk_pfs_30(dev, apdev, params):
+ """FILS SK with PFS (DH group 30)"""
+ run_fils_sk_pfs(dev, apdev, "30", params)
+
+def run_fils_sk_pfs(dev, apdev, group, params):
+ check_fils_sk_pfs_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ tls = dev[0].request("GET tls_library")
+ if int(group) in [25]:
+ if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls)):
+ raise HwsimSkip("EC group not supported")
+ if int(group) in [27, 28, 29, 30]:
+ if not (tls.startswith("OpenSSL") and ("build=OpenSSL 1.0.2" in tls or "build=OpenSSL 1.1" in tls) and ("run=OpenSSL 1.0.2" in tls or "run=OpenSSL 1.1" in tls)):
+ raise HwsimSkip("Brainpool EC group not supported")
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ params['fils_dh_group'] = group
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", fils_dh_group=group, scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_fils_sk_pfs_group_mismatch(dev, apdev, params):
+ """FILS SK PFS DH group mismatch"""
+ check_fils_sk_pfs_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ params['fils_dh_group'] = "20"
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", fils_dh_group="19", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Authentication rejection not seen")
+ if "auth_type=5 auth_transaction=2 status_code=77" not in ev:
+ raise Exception("Unexpected auth reject value: " + ev)
+
+def test_fils_sk_pfs_pmksa_caching(dev, apdev, params):
+ """FILS SK with PFS and PMKSA caching"""
+ check_fils_sk_pfs_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['fils_dh_group'] = "19"
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", fils_dh_group="19", scan_freq="2412")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # FILS authentication with PMKSA caching and PFS
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using PMKSA caching timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ pmksa2 = dev[0].get_pmksa(bssid)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa['pmkid'] != pmksa2['pmkid']:
+ raise Exception("Unexpected PMKID change")
+
+ # Verify EAPOL reauthentication after FILS authentication
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not succeed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # FILS authentication with ERP and PFS
+ dev[0].request("PMKSA_FLUSH")
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-EAP-SUCCESS",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using ERP and PFS timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "CTRL-EVENT-EAP-SUCCESS" not in ev:
+ raise Exception("ERP success not reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "SME: Trying to authenticate",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using ERP and PFS timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "SME: Trying to authenticate" in ev:
+ raise Exception("Unexpected extra authentication round with ERP and PFS")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ pmksa3 = dev[0].get_pmksa(bssid)
+ if pmksa3 is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa2['pmkid'] == pmksa3['pmkid']:
+ raise Exception("PMKID did not change")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # FILS authentication with PMKSA caching and PFS
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using PMKSA caching timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ pmksa4 = dev[0].get_pmksa(bssid)
+ if pmksa4 is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa3['pmkid'] != pmksa4['pmkid']:
+ raise Exception("Unexpected PMKID change (2)")
+
+def test_fils_sk_auth_mismatch(dev, apdev, params):
+ """FILS SK authentication type mismatch (PFS not supported)"""
+ check_fils_sk_pfs_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", fils_dh_group="19", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" not in ev:
+ raise Exception("No EAP exchange seen")
+ dev[0].wait_connected()
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def setup_fils_rekey(dev, apdev, params, wpa_ptk_rekey=0, wpa_group_rekey=0,
+ pmksa_caching=True, ext_key_id=False):
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ if wpa_ptk_rekey:
+ params['wpa_ptk_rekey'] = str(wpa_ptk_rekey)
+ if wpa_group_rekey:
+ params['wpa_group_rekey'] = str(wpa_group_rekey)
+ if not pmksa_caching:
+ params['disable_pmksa_caching'] = '1'
+ if ext_key_id:
+ params['extended_key_id'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using ERP or PMKSA caching timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ dev[0].dump_monitor()
+
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ return hapd
+
+def test_fils_auth_gtk_rekey(dev, apdev, params):
+ """GTK rekeying after FILS authentication"""
+ hapd = setup_fils_rekey(dev, apdev, params, wpa_group_rekey=1)
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is not None:
+ raise Exception("Rekeying failed - disconnected")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_fils_auth_ptk_rekey_ap(dev, apdev, params):
+ """PTK rekeying after FILS authentication triggered by AP"""
+ hapd = setup_fils_rekey(dev, apdev, params, wpa_ptk_rekey=2)
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=3)
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Rekeying failed - disconnected")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_fils_auth_ptk_rekey_ap_erp(dev, apdev, params):
+ """PTK rekeying after FILS authentication triggered by AP (ERP)"""
+ hapd = setup_fils_rekey(dev, apdev, params, wpa_ptk_rekey=2,
+ pmksa_caching=False)
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=3)
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Rekeying failed - disconnected")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_fils_and_ft(dev, apdev, params):
+ """FILS SK using ERP and FT initial mobility domain association"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ er = start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.disable()
+ dev[0].flush_scan_cache()
+ if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+
+ params = hostapd.wpa2_eap_params(ssid="fils-ft")
+ params['wpa_key_mgmt'] = "FILS-SHA256 FT-FILS-SHA256 FT-EAP"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ params["mobility_domain"] = "a1b2"
+ params["r0_key_lifetime"] = "10000"
+ params["pmk_r1_push"] = "1"
+ params["reassociation_deadline"] = "1000"
+ params['nas_identifier'] = "nas1.w1.fi"
+ params['r1_key_holder'] = "000102030405"
+ params['r0kh'] = ["02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f"]
+ params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
+ params['ieee80211w'] = "1"
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].dump_monitor()
+ id = dev[0].connect("fils-ft", key_mgmt="FILS-SHA256 FT-FILS-SHA256 FT-EAP",
+ ieee80211w="1",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412", wait_connect=False)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-AUTH-REJECT",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "CTRL-EVENT-AUTH-REJECT" in ev:
+ raise Exception("Authentication failed")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ er.disable()
+
+ # FIX: FT-FILS-SHA256 does not currently work for FT protocol due to not
+ # fully defined FT Reassociation Request/Response frame MIC use in FTE.
+ # FT-EAP can be used to work around that in this test case to confirm the
+ # FT key hierarchy was properly formed in the previous step.
+ #params['wpa_key_mgmt'] = "FILS-SHA256 FT-FILS-SHA256"
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params['nas_identifier'] = "nas2.w1.fi"
+ params['r1_key_holder'] = "000102030406"
+ params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f"]
+ params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
+ hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412", force_scan=True)
+ # FIX: Cannot use FT-over-DS without the FTE MIC issue addressed
+ #dev[0].roam_over_ds(apdev[1]['bssid'])
+ dev[0].roam(apdev[1]['bssid'])
+
+def test_fils_and_ft_over_air(dev, apdev, params):
+ """FILS SK using ERP and FT-over-air (SHA256)"""
+ run_fils_and_ft_over_air(dev, apdev, params, "FT-FILS-SHA256")
+
+def test_fils_and_ft_over_air_sha384(dev, apdev, params):
+ """FILS SK using ERP and FT-over-air (SHA384)"""
+ run_fils_and_ft_over_air(dev, apdev, params, "FT-FILS-SHA384")
+
+def run_fils_and_ft_over_air(dev, apdev, params, key_mgmt):
+ hapd, hapd2 = run_fils_and_ft_setup(dev, apdev, params, key_mgmt)
+ conf = hapd.request("GET_CONFIG")
+ if "key_mgmt=" + key_mgmt not in conf.splitlines():
+ logger.info("GET_CONFIG:\n" + conf)
+ raise Exception("GET_CONFIG did not report correct key_mgmt")
+
+ logger.info("FT protocol using FT key hierarchy established during FILS authentication")
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412", force_scan=True)
+ hapd.request("NOTE FT protocol to AP2 using FT keys established during FILS FILS authentication")
+ dev[0].roam(apdev[1]['bssid'])
+ hwsim_utils.test_connectivity(dev[0], hapd2)
+
+ logger.info("FT protocol using the previously established FT key hierarchy from FILS authentication")
+ hapd.request("NOTE FT protocol back to AP1 using FT keys established during FILS FILS authentication")
+ dev[0].roam(apdev[0]['bssid'])
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("NOTE FT protocol back to AP2 using FT keys established during FILS FILS authentication")
+ dev[0].roam(apdev[1]['bssid'])
+ hwsim_utils.test_connectivity(dev[0], hapd2)
+
+ hapd.request("NOTE FT protocol back to AP1 using FT keys established during FILS FILS authentication (2)")
+ dev[0].roam(apdev[0]['bssid'])
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_fils_and_ft_over_ds(dev, apdev, params):
+ """FILS SK using ERP and FT-over-DS (SHA256)"""
+ run_fils_and_ft_over_ds(dev, apdev, params, "FT-FILS-SHA256")
+
+def test_fils_and_ft_over_ds_sha384(dev, apdev, params):
+ """FILS SK using ERP and FT-over-DS (SHA384)"""
+ run_fils_and_ft_over_ds(dev, apdev, params, "FT-FILS-SHA384")
+
+def run_fils_and_ft_over_ds(dev, apdev, params, key_mgmt):
+ hapd, hapd2 = run_fils_and_ft_setup(dev, apdev, params, key_mgmt)
+
+ logger.info("FT protocol using FT key hierarchy established during FILS authentication")
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412", force_scan=True)
+ hapd.request("NOTE FT protocol to AP2 using FT keys established during FILS FILS authentication")
+ dev[0].roam_over_ds(apdev[1]['bssid'])
+
+ logger.info("FT protocol using the previously established FT key hierarchy from FILS authentication")
+ hapd.request("NOTE FT protocol back to AP1 using FT keys established during FILS FILS authentication")
+ dev[0].roam_over_ds(apdev[0]['bssid'])
+
+ hapd.request("NOTE FT protocol back to AP2 using FT keys established during FILS FILS authentication")
+ dev[0].roam_over_ds(apdev[1]['bssid'])
+
+ hapd.request("NOTE FT protocol back to AP1 using FT keys established during FILS FILS authentication (2)")
+ dev[0].roam_over_ds(apdev[0]['bssid'])
+
+def run_fils_and_ft_setup(dev, apdev, params, key_mgmt):
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ er = start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ logger.info("Set up ERP key hierarchy without FILS/FT authentication")
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = key_mgmt
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ params['ieee80211w'] = "2"
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ hapd.request("NOTE Initial association to establish ERP keys")
+ id = dev[0].connect("fils", key_mgmt=key_mgmt, ieee80211w="2",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.disable()
+ dev[0].flush_scan_cache()
+ if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+
+ logger.info("Initial mobility domain association using FILS authentication")
+ params = hostapd.wpa2_eap_params(ssid="fils-ft")
+ params['wpa_key_mgmt'] = key_mgmt
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ params["mobility_domain"] = "a1b2"
+ params["r0_key_lifetime"] = "10000"
+ params["pmk_r1_push"] = "1"
+ params["reassociation_deadline"] = "1000"
+ params['nas_identifier'] = "nas1.w1.fi"
+ params['r1_key_holder'] = "000102030405"
+ params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
+ "02:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f"]
+ params['r1kh'] = "02:00:00:00:04:00 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
+ params['ieee80211w'] = "2"
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].dump_monitor()
+ hapd.request("NOTE Initial FT mobility domain association using FILS authentication")
+ dev[0].set_network_quoted(id, "ssid", "fils-ft")
+ dev[0].select_network(id, freq=2412)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-AUTH-REJECT",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "CTRL-EVENT-AUTH-REJECT" in ev:
+ raise Exception("Authentication failed")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ er.disable()
+
+ params['wpa_key_mgmt'] = key_mgmt
+ params['nas_identifier'] = "nas2.w1.fi"
+ params['r1_key_holder'] = "000102030406"
+ params['r0kh'] = ["02:00:00:00:03:00 nas1.w1.fi 200102030405060708090a0b0c0d0e0f",
+ "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f"]
+ params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
+ hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ return hapd, hapd2
+
+def test_fils_assoc_replay(dev, apdev, params):
+ """FILS AP and replayed Association Request frame"""
+ capfile = os.path.join(params['logdir'], "hwsim0.pcapng")
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as()
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+
+ assocreq = None
+ count = 0
+ while count < 100:
+ req = hapd.mgmt_rx()
+ count += 1
+ hapd.dump_monitor()
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ if req['subtype'] == 0:
+ assocreq = req
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("No TX status seen")
+ cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
+ if "OK" not in hapd.request(cmd):
+ raise Exception("MGMT_TX_STATUS_PROCESS failed")
+ break
+ hapd.set("ext_mgmt_frame_handling", "0")
+ if assocreq is None:
+ raise Exception("No Association Request frame seen")
+ dev[0].wait_connected()
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ logger.info("Replay the last Association Request frame")
+ hapd.dump_monitor()
+ hapd.set("ext_mgmt_frame_handling", "1")
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("No TX status seen")
+ cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
+ if "OK" not in hapd.request(cmd):
+ raise Exception("MGMT_TX_STATUS_PROCESS failed")
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+ try:
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ ok = True
+ except:
+ ok = False
+
+ ap = hapd.own_addr()
+ sta = dev[0].own_addr()
+ filt = "wlan.fc.type == 2 && " + \
+ "wlan.da == " + sta + " && " + \
+ "wlan.sa == " + ap + " && wlan.ccmp.extiv"
+ fields = ["wlan.ccmp.extiv"]
+ res = run_tshark(capfile, filt, fields)
+ vals = res.splitlines()
+ logger.info("CCMP PN: " + str(vals))
+ if len(vals) < 2:
+ raise Exception("Could not find all CCMP protected frames from capture")
+ if len(set(vals)) < len(vals):
+ raise Exception("Duplicate CCMP PN used")
+
+ if not ok:
+ raise Exception("The second hwsim connectivity test failed")
+
+def test_fils_sk_erp_server_flush(dev, apdev, params):
+ """FILS SK ERP and ERP flush on server, but not on peer"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ hapd_as = start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ hapd_as.request("ERP_FLUSH")
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("No authentication rejection seen after ERP flush on server")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-AUTH-REJECT",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection attempt using FILS/ERP timed out")
+ if "CTRL-EVENT-AUTH-REJECT" in ev:
+ raise Exception("Failed to recover from ERP flush on server")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+ if "CTRL-EVENT-EAP-STARTED" not in ev:
+ raise Exception("New EAP exchange not seen")
+ dev[0].wait_connected(error="Connection timeout after ERP flush")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-AUTH-REJECT",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection attempt using FILS with new ERP keys timed out")
+ if "CTRL-EVENT-AUTH-REJECT" in ev:
+ raise Exception("Authentication failed with new ERP keys")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed with new ERP keys")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+
+def test_fils_sk_erp_radius_ext(dev, apdev, params):
+ """FILS SK using ERP and external RADIUS server"""
+ as_hapd = hostapd.Hostapd("as")
+ try:
+ as_hapd.disable()
+ as_hapd.set("eap_server_erp", "1")
+ as_hapd.set("erp_domain", "erp.example.com")
+ as_hapd.enable()
+ run_fils_sk_erp_radius_ext(dev, apdev, params)
+ finally:
+ as_hapd.disable()
+ as_hapd.set("eap_server_erp", "0")
+ as_hapd.set("erp_domain", "")
+ as_hapd.enable()
+
+def run_fils_sk_erp_radius_ext(dev, apdev, params):
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['erp_domain'] = 'erp.example.com'
+ params['fils_realm'] = 'erp.example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PWD", identity="pwd@erp.example.com",
+ password="secret password",
+ erp="1", scan_freq="2412")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_fils_sk_erp_radius_roam(dev, apdev):
+ """FILS SK/ERP and roaming with different AKM"""
+ as_hapd = hostapd.Hostapd("as")
+ try:
+ as_hapd.disable()
+ as_hapd.set("eap_server_erp", "1")
+ as_hapd.set("erp_domain", "example.com")
+ as_hapd.enable()
+ run_fils_sk_erp_radius_roam(dev, apdev)
+ finally:
+ as_hapd.disable()
+ as_hapd.set("eap_server_erp", "0")
+ as_hapd.set("erp_domain", "")
+ as_hapd.enable()
+
+def run_fils_sk_erp_radius_roam(dev, apdev):
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256 FILS-SHA384",
+ eap="PWD", identity="erp-pwd@example.com",
+ password="secret password",
+ erp="1", scan_freq="2412")
+
+ bssid2 = apdev[1]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA384"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid2, freq=2412)
+
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("ROAM " + bssid2):
+ raise Exception("ROAM failed")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using PMKSA caching timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if bssid2 not in ev:
+ raise Exception("Failed to connect to the second AP")
+
+ hapd2.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd2)
+
+def test_fils_sk_erp_roam_diff_akm(dev, apdev, params):
+ """FILS SK using ERP and SHA256/SHA384 change in roam"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as()
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect("fils", key_mgmt="FILS-SHA256 FILS-SHA384",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+
+ bssid2 = apdev[1]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256 FILS-SHA384"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ dev[0].scan_for_bss(bssid2, freq=2412)
+
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("ROAM " + bssid2):
+ raise Exception("ROAM failed")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming using FILS timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if bssid2 not in ev:
+ raise Exception("Failed to connect to the second AP")
+
+ hwsim_utils.test_connectivity(dev[0], hapd2)
+
+def test_fils_auth_ptk_rekey_ap_ext_key_id(dev, apdev, params):
+ """PTK rekeying after FILS authentication triggered by AP (Ext Key ID)"""
+ check_ext_key_id_capa(dev[0])
+ try:
+ dev[0].set("extended_key_id", "1")
+ hapd = setup_fils_rekey(dev, apdev, params, wpa_ptk_rekey=2,
+ ext_key_id=True)
+ check_ext_key_id_capa(hapd)
+ idx = int(dev[0].request("GET last_tk_key_idx"))
+ if idx != 0:
+ raise Exception("Unexpected Key ID before TK rekey: %d" % idx)
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=3)
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+ idx = int(dev[0].request("GET last_tk_key_idx"))
+ if idx != 1:
+ raise Exception("Unexpected Key ID after TK rekey: %d" % idx)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Rekeying failed - disconnected")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ dev[0].set("extended_key_id", "0")
+
+def test_fils_discovery_frame(dev, apdev, params):
+ """FILS Discovery frame generation"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['wpa_group_rekey'] = '1'
+ params['fils_discovery_min_interval'] = '20'
+ params['fils_discovery_max_interval'] = '20'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params, no_enable=True)
+
+ if "OK" not in hapd.request("ENABLE"):
+ raise HwsimSkip("FILS Discovery frame transmission not supported")
+
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ if "AP-ENABLED" not in ev:
+ raise Exception("AP startup failed")
+
+ dev[0].request("ERP_FLUSH")
+ dev[0].connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+def test_fils_offload_to_driver(dev, apdev, params):
+ """FILS offload to driver"""
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+ run_fils_offload_to_driver(dev[0], apdev, params)
+
+def test_fils_offload_to_driver2(dev, apdev, params):
+ """FILS offload to driver"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ run_fils_offload_to_driver(wpas, apdev, params)
+
+def run_fils_offload_to_driver(dev, apdev, params):
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev.request("ERP_FLUSH")
+ id = dev.connect("fils", key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ p = "freq=2412 authorized=1 fils_erp_next_seq_num=4"
+ if "OK" not in dev.request("DRIVER_EVENT ASSOC " + p):
+ raise Exception("DRIVER_EVENT ASSOC did not succeed")
+ dev.wait_connected()
+
+ dev.request("DISCONNECT")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+ dev.select_network(id, freq=2412)
+ dev.wait_connected()
+ dev.dump_monitor()
+
+ # This does not really work properly with SME-in-wpa_supplicant case
+ p = "freq=2412 authorized=1 fils_erp_next_seq_num=4"
+ if "OK" not in dev.request("DRIVER_EVENT ASSOC " + p):
+ raise Exception("DRIVER_EVENT ASSOC did not succeed")
+
+ dev.wait_connected()
diff --git a/contrib/wpa/tests/hwsim/test_fst_config.py b/contrib/wpa/tests/hwsim/test_fst_config.py
new file mode 100644
index 000000000000..98134014150f
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_fst_config.py
@@ -0,0 +1,553 @@
+# FST configuration tests
+# Copyright (c) 2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+import os
+import signal
+import hostapd
+import wpasupplicant
+import utils
+
+import fst_test_common
+
+class FstLauncherConfig:
+ """FstLauncherConfig class represents configuration to be used for
+ FST config tests related hostapd/wpa_supplicant instances"""
+ def __init__(self, iface, fst_group, fst_pri, fst_llt=None):
+ self.iface = iface
+ self.fst_group = fst_group
+ self.fst_pri = fst_pri
+ self.fst_llt = fst_llt # None llt means no llt parameter will be set
+
+ def ifname(self):
+ return self.iface
+
+ def is_ap(self):
+ """Returns True if the configuration is for AP, otherwise - False"""
+ raise Exception("Virtual is_ap() called!")
+
+ def to_file(self, pathname):
+ """Creates configuration file to be used by FST config tests related
+ hostapd/wpa_supplicant instances"""
+ raise Exception("Virtual to_file() called!")
+
+class FstLauncherConfigAP(FstLauncherConfig):
+ """FstLauncherConfigAP class represents configuration to be used for
+ FST config tests related hostapd instance"""
+ def __init__(self, iface, ssid, mode, chan, fst_group, fst_pri,
+ fst_llt=None):
+ self.ssid = ssid
+ self.mode = mode
+ self.chan = chan
+ FstLauncherConfig.__init__(self, iface, fst_group, fst_pri, fst_llt)
+
+ def is_ap(self):
+ return True
+
+ def get_channel(self):
+ return self.chan
+
+ def to_file(self, pathname):
+ """Creates configuration file to be used by FST config tests related
+ hostapd instance"""
+ with open(pathname, "w") as f:
+ f.write("country_code=US\n"
+ "interface=%s\n"
+ "ctrl_interface=/var/run/hostapd\n"
+ "ssid=%s\n"
+ "channel=%s\n"
+ "hw_mode=%s\n"
+ "ieee80211n=1\n" % (self.iface, self.ssid, self.chan,
+ self.mode))
+ if len(self.fst_group) != 0:
+ f.write("fst_group_id=%s\n"
+ "fst_priority=%s\n" % (self.fst_group, self.fst_pri))
+ if self.fst_llt is not None:
+ f.write("fst_llt=%s\n" % self.fst_llt)
+ with open(pathname, "r") as f:
+ logger.debug("wrote hostapd config file %s:\n%s" % (pathname,
+ f.read()))
+
+class FstLauncherConfigSTA(FstLauncherConfig):
+ """FstLauncherConfig class represents configuration to be used for
+ FST config tests related wpa_supplicant instance"""
+ def __init__(self, iface, fst_group, fst_pri, fst_llt=None):
+ FstLauncherConfig.__init__(self, iface, fst_group, fst_pri, fst_llt)
+
+ def is_ap(self):
+ return False
+
+ def to_file(self, pathname):
+ """Creates configuration file to be used by FST config tests related
+ wpa_supplicant instance"""
+ with open(pathname, "w") as f:
+ f.write("ctrl_interface=DIR=/var/run/wpa_supplicant\n"
+ "p2p_no_group_iface=1\n")
+ if len(self.fst_group) != 0:
+ f.write("fst_group_id=%s\n"
+ "fst_priority=%s\n" % (self.fst_group, self.fst_pri))
+ if self.fst_llt is not None:
+ f.write("fst_llt=%s\n" % self.fst_llt)
+ with open(pathname, "r") as f:
+ logger.debug("wrote wpa_supplicant config file %s:\n%s" % (pathname, f.read()))
+
+class FstLauncher:
+ """FstLauncher class is responsible for launching and cleaning up of FST
+ config tests related hostapd/wpa_supplicant instances"""
+ def __init__(self, logpath):
+ self.logger = logging.getLogger()
+ self.fst_logpath = logpath
+ self.cfgs_to_run = []
+ self.hapd_fst_global = '/var/run/hostapd-fst-global'
+ self.wsup_fst_global = '/tmp/fststa'
+ self.nof_aps = 0
+ self.nof_stas = 0
+ self.reg_ctrl = fst_test_common.HapdRegCtrl()
+ self.test_is_supported()
+
+ def __del__(self):
+ self.cleanup()
+
+ @staticmethod
+ def test_is_supported():
+ h = hostapd.HostapdGlobal()
+ resp = h.request("FST-MANAGER TEST_REQUEST IS_SUPPORTED")
+ if not resp.startswith("OK"):
+ raise utils.HwsimSkip("FST not supported")
+ w = wpasupplicant.WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ resp = w.global_request("FST-MANAGER TEST_REQUEST IS_SUPPORTED")
+ if not resp.startswith("OK"):
+ raise utils.HwsimSkip("FST not supported")
+
+ def get_cfg_pathname(self, cfg):
+ """Returns pathname of ifname based configuration file"""
+ return self.fst_logpath +'/'+ cfg.ifname() + '.conf'
+
+ def add_cfg(self, cfg):
+ """Adds configuration to be used for launching hostapd/wpa_supplicant
+ instances"""
+ if cfg not in self.cfgs_to_run:
+ self.cfgs_to_run.append(cfg)
+ if cfg.is_ap() == True:
+ self.nof_aps += 1
+ else:
+ self.nof_stas += 1
+
+ def remove_cfg(self, cfg):
+ """Removes configuration previously added with add_cfg"""
+ if cfg in self.cfgs_to_run:
+ self.cfgs_to_run.remove(cfg)
+ if cfg.is_ap() == True:
+ self.nof_aps -= 1
+ else:
+ self.nof_stas -= 1
+ config_file = self.get_cfg_pathname(cfg)
+ if os.path.exists(config_file):
+ os.remove(config_file)
+
+ def run_hostapd(self):
+ """Lauches hostapd with interfaces configured according to
+ FstLauncherConfigAP configurations added"""
+ if self.nof_aps == 0:
+ raise Exception("No FST APs to start")
+ pidfile = self.fst_logpath + '/' + 'myhostapd.pid'
+ mylogfile = self.fst_logpath + '/' + 'fst-hostapd'
+ prg = os.path.join(self.fst_logpath,
+ 'alt-hostapd/hostapd/hostapd')
+ if not os.path.exists(prg):
+ prg = '../../hostapd/hostapd'
+ cmd = [prg, '-B', '-dddt',
+ '-P', pidfile, '-f', mylogfile, '-g', self.hapd_fst_global]
+ for i in range(0, len(self.cfgs_to_run)):
+ cfg = self.cfgs_to_run[i]
+ if cfg.is_ap() == True:
+ cfgfile = self.get_cfg_pathname(cfg)
+ cfg.to_file(cfgfile)
+ cmd.append(cfgfile)
+ self.reg_ctrl.add_ap(cfg.ifname(), cfg.get_channel())
+ self.logger.debug("Starting fst hostapd: " + ' '.join(cmd))
+ res = subprocess.call(cmd)
+ self.logger.debug("fst hostapd start result: %d" % res)
+ if res == 0:
+ self.reg_ctrl.start()
+ return res
+
+ def run_wpa_supplicant(self):
+ """Lauches wpa_supplicant with interfaces configured according to
+ FstLauncherConfigSTA configurations added"""
+ if self.nof_stas == 0:
+ raise Exception("No FST STAs to start")
+ pidfile = self.fst_logpath + '/' + 'mywpa_supplicant.pid'
+ mylogfile = self.fst_logpath + '/' + 'fst-wpa_supplicant'
+ prg = os.path.join(self.fst_logpath,
+ 'alt-wpa_supplicant/wpa_supplicant/wpa_supplicant')
+ if not os.path.exists(prg):
+ prg = '../../wpa_supplicant/wpa_supplicant'
+ cmd = [prg, '-B', '-dddt',
+ '-P' + pidfile, '-f', mylogfile, '-g', self.wsup_fst_global]
+ sta_no = 0
+ for i in range(0, len(self.cfgs_to_run)):
+ cfg = self.cfgs_to_run[i]
+ if cfg.is_ap() == False:
+ cfgfile = self.get_cfg_pathname(cfg)
+ cfg.to_file(cfgfile)
+ cmd.append('-c' + cfgfile)
+ cmd.append('-i' + cfg.ifname())
+ cmd.append('-Dnl80211')
+ if sta_no != self.nof_stas -1:
+ cmd.append('-N') # Next station configuration
+ sta_no += 1
+ self.logger.debug("Starting fst supplicant: " + ' '.join(cmd))
+ res = subprocess.call(cmd)
+ self.logger.debug("fst supplicant start result: %d" % res)
+ return res
+
+ def cleanup(self):
+ """Terminates hostapd/wpa_supplicant processes previously launched with
+ run_hostapd/run_wpa_supplicant"""
+ pidfile = self.fst_logpath + '/' + 'myhostapd.pid'
+ self.kill_pid(pidfile, self.nof_aps > 0)
+ pidfile = self.fst_logpath + '/' + 'mywpa_supplicant.pid'
+ self.kill_pid(pidfile, self.nof_stas > 0)
+ self.reg_ctrl.stop()
+ while len(self.cfgs_to_run) != 0:
+ cfg = self.cfgs_to_run[0]
+ self.remove_cfg(cfg)
+ fst_test_common.fst_clear_regdom()
+
+ def kill_pid(self, pidfile, try_again=False):
+ """Kills process by PID file"""
+ if not os.path.exists(pidfile):
+ if not try_again:
+ return
+ # It might take some time for the process to write the PID file,
+ # so wait a bit longer before giving up.
+ self.logger.info("kill_pid: pidfile %s does not exist - try again after a second" % pidfile)
+ time.sleep(1)
+ if not os.path.exists(pidfile):
+ self.logger.info("kill_pid: pidfile %s does not exist - could not kill the process" % pidfile)
+ return
+ pid = -1
+ try:
+ for i in range(3):
+ pf = open(pidfile, 'r')
+ pidtxt = pf.read().strip()
+ self.logger.debug("kill_pid: %s: '%s'" % (pidfile, pidtxt))
+ pf.close()
+ try:
+ pid = int(pidtxt)
+ break
+ except Exception as e:
+ self.logger.debug("kill_pid: No valid PID found: %s" % str(e))
+ time.sleep(1)
+ self.logger.debug("kill_pid %s --> pid %d" % (pidfile, pid))
+ os.kill(pid, signal.SIGTERM)
+ for i in range(10):
+ try:
+ # Poll the pid (Is the process still existing?)
+ os.kill(pid, 0)
+ except OSError:
+ # No, already done
+ break
+ # Wait and check again
+ time.sleep(1)
+ except Exception as e:
+ self.logger.debug("Didn't stop the pid=%d. Was it stopped already? (%s)" % (pid, str(e)))
+
+
+def parse_ies(iehex, el=-1):
+ """Parses the information elements hex string 'iehex' in format
+ "0a0b0c0d0e0f". If no 'el' defined just checks the IE string for integrity.
+ If 'el' is defined returns the list of hex values of the specific IE (or
+ empty list if the element is not in the string."""
+ iel = [iehex[i:i + 2] for i in range(0, len(iehex), 2)]
+ for i in range(0, len(iel)):
+ iel[i] = int(iel[i], 16)
+ # Sanity check
+ i = 0
+ res = []
+ while i < len(iel):
+ logger.debug("IE found: %x" % iel[i])
+ if el != -1 and el == iel[i]:
+ res = iel[i + 2:i + 2 + iel[i + 1]]
+ i += 2 + iel[i + 1]
+ if i != len(iel):
+ logger.error("Bad IE string: " + iehex)
+ res = []
+ return res
+
+def scan_and_get_bss(dev, frq):
+ """Issues a scan on given device on given frequency, returns the bss info
+ dictionary ('ssid','ie','flags', etc.) or None. Note, the function
+ implies there is only one AP on the given channel. If not a case,
+ the function must be changed to call dev.get_bss() till the AP with the
+ [b]ssid that we need is found"""
+ dev.scan(freq=frq)
+ return dev.get_bss('0')
+
+
+# AP configuration tests
+
+def run_test_ap_configuration(apdev, test_params,
+ fst_group=fst_test_common.fst_test_def_group,
+ fst_pri=fst_test_common.fst_test_def_prio_high,
+ fst_llt=fst_test_common.fst_test_def_llt):
+ """Runs FST hostapd where the 1st AP configuration is fixed, the 2nd fst
+ configuration is provided by the parameters. Returns the result of the run:
+ 0 - no errors discovered, an error otherwise. The function is used for
+ simplek "bad configuration" tests."""
+ logdir = test_params['logdir']
+ fst_launcher = FstLauncher(logdir)
+ ap1 = FstLauncherConfigAP(apdev[0]['ifname'], 'fst_goodconf', 'a',
+ fst_test_common.fst_test_def_chan_a,
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_low,
+ fst_test_common.fst_test_def_llt)
+ ap2 = FstLauncherConfigAP(apdev[1]['ifname'], 'fst_badconf', 'b',
+ fst_test_common.fst_test_def_chan_g, fst_group,
+ fst_pri, fst_llt)
+ fst_launcher.add_cfg(ap1)
+ fst_launcher.add_cfg(ap2)
+ res = fst_launcher.run_hostapd()
+ return res
+
+def run_test_sta_configuration(test_params,
+ fst_group=fst_test_common.fst_test_def_group,
+ fst_pri=fst_test_common.fst_test_def_prio_high,
+ fst_llt=fst_test_common.fst_test_def_llt):
+ """Runs FST wpa_supplicant where the 1st STA configuration is fixed, the
+ 2nd fst configuration is provided by the parameters. Returns the result of
+ the run: 0 - no errors discovered, an error otherwise. The function is used
+ for simple "bad configuration" tests."""
+ logdir = test_params['logdir']
+ fst_launcher = FstLauncher(logdir)
+ sta1 = FstLauncherConfigSTA('wlan5',
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_low,
+ fst_test_common.fst_test_def_llt)
+ sta2 = FstLauncherConfigSTA('wlan6', fst_group, fst_pri, fst_llt)
+ fst_launcher.add_cfg(sta1)
+ fst_launcher.add_cfg(sta2)
+ res = fst_launcher.run_wpa_supplicant()
+ return res
+
+def test_fst_ap_config_llt_neg(dev, apdev, test_params):
+ """FST AP configuration negative LLT"""
+ res = run_test_ap_configuration(apdev, test_params, fst_llt='-1')
+ if res == 0:
+ raise Exception("hostapd started with a negative llt")
+
+def test_fst_ap_config_llt_zero(dev, apdev, test_params):
+ """FST AP configuration zero LLT"""
+ res = run_test_ap_configuration(apdev, test_params, fst_llt='0')
+ if res == 0:
+ raise Exception("hostapd started with a zero llt")
+
+def test_fst_ap_config_llt_too_big(dev, apdev, test_params):
+ """FST AP configuration LLT is too big"""
+ res = run_test_ap_configuration(apdev, test_params,
+ fst_llt='4294967296') #0x100000000
+ if res == 0:
+ raise Exception("hostapd started with llt that is too big")
+
+def test_fst_ap_config_llt_nan(dev, apdev, test_params):
+ """FST AP configuration LLT is not a number"""
+ res = run_test_ap_configuration(apdev, test_params, fst_llt='nan')
+ if res == 0:
+ raise Exception("hostapd started with llt not a number")
+
+def test_fst_ap_config_pri_neg(dev, apdev, test_params):
+ """FST AP configuration Priority negative"""
+ res = run_test_ap_configuration(apdev, test_params, fst_pri='-1')
+ if res == 0:
+ raise Exception("hostapd started with a negative fst priority")
+
+def test_fst_ap_config_pri_zero(dev, apdev, test_params):
+ """FST AP configuration Priority zero"""
+ res = run_test_ap_configuration(apdev, test_params, fst_pri='0')
+ if res == 0:
+ raise Exception("hostapd started with a zero fst priority")
+
+def test_fst_ap_config_pri_large(dev, apdev, test_params):
+ """FST AP configuration Priority too large"""
+ res = run_test_ap_configuration(apdev, test_params, fst_pri='256')
+ if res == 0:
+ raise Exception("hostapd started with too large fst priority")
+
+def test_fst_ap_config_pri_nan(dev, apdev, test_params):
+ """FST AP configuration Priority not a number"""
+ res = run_test_ap_configuration(apdev, test_params, fst_pri='nan')
+ if res == 0:
+ raise Exception("hostapd started with fst priority not a number")
+
+def test_fst_ap_config_group_len(dev, apdev, test_params):
+ """FST AP configuration Group max length"""
+ res = run_test_ap_configuration(apdev, test_params,
+ fst_group='fstg5678abcd34567')
+ if res == 0:
+ raise Exception("hostapd started with fst_group length too big")
+
+def test_fst_ap_config_good(dev, apdev, test_params):
+ """FST AP configuration good parameters"""
+ res = run_test_ap_configuration(apdev, test_params)
+ if res != 0:
+ raise Exception("hostapd didn't start with valid config parameters")
+
+def test_fst_ap_config_default(dev, apdev, test_params):
+ """FST AP configuration default parameters"""
+ res = run_test_ap_configuration(apdev, test_params, fst_llt=None)
+ if res != 0:
+ raise Exception("hostapd didn't start with valid config parameters")
+
+
+# STA configuration tests
+
+def test_fst_sta_config_llt_neg(dev, apdev, test_params):
+ """FST STA configuration negative LLT"""
+ res = run_test_sta_configuration(test_params, fst_llt='-1')
+ if res == 0:
+ raise Exception("wpa_supplicant started with a negative llt")
+
+def test_fst_sta_config_llt_zero(dev, apdev, test_params):
+ """FST STA configuration zero LLT"""
+ res = run_test_sta_configuration(test_params, fst_llt='0')
+ if res == 0:
+ raise Exception("wpa_supplicant started with a zero llt")
+
+def test_fst_sta_config_llt_large(dev, apdev, test_params):
+ """FST STA configuration LLT is too large"""
+ res = run_test_sta_configuration(test_params,
+ fst_llt='4294967296') #0x100000000
+ if res == 0:
+ raise Exception("wpa_supplicant started with llt that is too large")
+
+def test_fst_sta_config_llt_nan(dev, apdev, test_params):
+ """FST STA configuration LLT is not a number"""
+ res = run_test_sta_configuration(test_params, fst_llt='nan')
+ if res == 0:
+ raise Exception("wpa_supplicant started with llt not a number")
+
+def test_fst_sta_config_pri_neg(dev, apdev, test_params):
+ """FST STA configuration Priority negative"""
+ res = run_test_sta_configuration(test_params, fst_pri='-1')
+ if res == 0:
+ raise Exception("wpa_supplicant started with a negative fst priority")
+
+def test_fst_sta_config_pri_zero(dev, apdev, test_params):
+ """FST STA configuration Priority zero"""
+ res = run_test_sta_configuration(test_params, fst_pri='0')
+ if res == 0:
+ raise Exception("wpa_supplicant started with a zero fst priority")
+
+def test_fst_sta_config_pri_big(dev, apdev, test_params):
+ """FST STA configuration Priority too large"""
+ res = run_test_sta_configuration(test_params, fst_pri='256')
+ if res == 0:
+ raise Exception("wpa_supplicant started with too large fst priority")
+
+def test_fst_sta_config_pri_nan(dev, apdev, test_params):
+ """FST STA configuration Priority not a number"""
+ res = run_test_sta_configuration(test_params, fst_pri='nan')
+ if res == 0:
+ raise Exception("wpa_supplicant started with fst priority not a number")
+
+def test_fst_sta_config_group_len(dev, apdev, test_params):
+ """FST STA configuration Group max length"""
+ res = run_test_sta_configuration(test_params,
+ fst_group='fstg5678abcd34567')
+ if res == 0:
+ raise Exception("wpa_supplicant started with fst_group length too big")
+
+def test_fst_sta_config_good(dev, apdev, test_params):
+ """FST STA configuration good parameters"""
+ res = run_test_sta_configuration(test_params)
+ if res != 0:
+ raise Exception("wpa_supplicant didn't start with valid config parameters")
+
+def test_fst_sta_config_default(dev, apdev, test_params):
+ """FST STA configuration default parameters"""
+ res = run_test_sta_configuration(test_params, fst_llt=None)
+ if res != 0:
+ raise Exception("wpa_supplicant didn't start with valid config parameters")
+
+def test_fst_scan_mb(dev, apdev, test_params):
+ """FST scan valid MB IE presence with normal start"""
+ logdir = test_params['logdir']
+
+ # Test valid MB IE in scan results
+ fst_launcher = FstLauncher(logdir)
+ ap1 = FstLauncherConfigAP(apdev[0]['ifname'], 'fst_11a', 'a',
+ fst_test_common.fst_test_def_chan_a,
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_high)
+ ap2 = FstLauncherConfigAP(apdev[1]['ifname'], 'fst_11g', 'b',
+ fst_test_common.fst_test_def_chan_g,
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_low)
+ fst_launcher.add_cfg(ap1)
+ fst_launcher.add_cfg(ap2)
+ res = fst_launcher.run_hostapd()
+ if res != 0:
+ raise Exception("hostapd didn't start properly")
+ try:
+ mbie1 = []
+ flags1 = ''
+ mbie2 = []
+ flags2 = ''
+ # Scan 1st AP
+ vals1 = scan_and_get_bss(dev[0], fst_test_common.fst_test_def_freq_a)
+ if vals1 != None:
+ if 'ie' in vals1:
+ mbie1 = parse_ies(vals1['ie'], 0x9e)
+ if 'flags' in vals1:
+ flags1 = vals1['flags']
+ # Scan 2nd AP
+ vals2 = scan_and_get_bss(dev[2], fst_test_common.fst_test_def_freq_g)
+ if vals2 != None:
+ if 'ie' in vals2:
+ mbie2 = parse_ies(vals2['ie'], 0x9e)
+ if 'flags' in vals2:
+ flags2 = vals2['flags']
+ finally:
+ fst_launcher.cleanup()
+
+ if len(mbie1) == 0:
+ raise Exception("No MB IE created by 1st AP")
+ if len(mbie2) == 0:
+ raise Exception("No MB IE created by 2nd AP")
+
+def test_fst_scan_nomb(dev, apdev, test_params):
+ """FST scan no MB IE presence with 1 AP start"""
+ logdir = test_params['logdir']
+
+ # Test valid MB IE in scan results
+ fst_launcher = FstLauncher(logdir)
+ ap1 = FstLauncherConfigAP(apdev[0]['ifname'], 'fst_11a', 'a',
+ fst_test_common.fst_test_def_chan_a,
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_high)
+ fst_launcher.add_cfg(ap1)
+ res = fst_launcher.run_hostapd()
+ if res != 0:
+ raise Exception("Hostapd didn't start properly")
+ try:
+ time.sleep(2)
+ mbie1 = []
+ flags1 = ''
+ vals1 = scan_and_get_bss(dev[0], fst_test_common.fst_test_def_freq_a)
+ if vals1 != None:
+ if 'ie' in vals1:
+ mbie1 = parse_ies(vals1['ie'], 0x9e)
+ if 'flags' in vals1:
+ flags1 = vals1['flags']
+ finally:
+ fst_launcher.cleanup()
+
+ if len(mbie1) != 0:
+ raise Exception("MB IE exists with 1 AP")
diff --git a/contrib/wpa/tests/hwsim/test_fst_module.py b/contrib/wpa/tests/hwsim/test_fst_module.py
new file mode 100644
index 000000000000..bb3b44ca3c57
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_fst_module.py
@@ -0,0 +1,2825 @@
+# FST functionality tests
+# Copyright (c) 2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import struct
+import subprocess
+import time
+import os
+import re
+
+import hwsim_utils
+from hwsim import HWSimRadio
+import hostapd
+from wpasupplicant import WpaSupplicant
+import fst_test_common
+import fst_module_aux
+from utils import alloc_fail, HwsimSkip
+
+#enum - bad parameter types
+bad_param_none = 0
+bad_param_session_add_no_params = 1
+bad_param_group_id = 2
+bad_param_session_set_no_params = 3
+bad_param_session_set_unknown_param = 4
+bad_param_session_id = 5
+bad_param_old_iface = 6
+bad_param_new_iface = 7
+bad_param_negative_llt = 8
+bad_param_zero_llt = 9
+bad_param_llt_too_big = 10
+bad_param_llt_nan = 11
+bad_param_peer_addr = 12
+bad_param_session_initiate_no_params = 13
+bad_param_session_initiate_bad_session_id = 14
+bad_param_session_initiate_with_no_new_iface_set = 15
+bad_param_session_initiate_with_bad_peer_addr_set = 16
+bad_param_session_initiate_request_with_bad_stie = 17
+bad_param_session_initiate_response_with_reject = 18
+bad_param_session_initiate_response_with_bad_stie = 19
+bad_param_session_initiate_response_with_zero_llt = 20
+bad_param_session_initiate_stt_no_response = 21
+bad_param_session_initiate_concurrent_setup_request = 22
+bad_param_session_transfer_no_params = 23
+bad_param_session_transfer_bad_session_id = 24
+bad_param_session_transfer_setup_skipped = 25
+bad_param_session_teardown_no_params = 26
+bad_param_session_teardown_bad_session_id = 27
+bad_param_session_teardown_setup_skipped = 28
+bad_param_session_teardown_bad_fsts_id = 29
+
+bad_param_names = ("None",
+ "No params passed to session add",
+ "Group ID",
+ "No params passed to session set",
+ "Unknown param passed to session set",
+ "Session ID",
+ "Old interface name",
+ "New interface name",
+ "Negative LLT",
+ "Zero LLT",
+ "LLT too big",
+ "LLT is not a number",
+ "Peer address",
+ "No params passed to session initiate",
+ "Session ID",
+ "No new_iface was set",
+ "Peer address",
+ "Request with bad st ie",
+ "Response with reject",
+ "Response with bad st ie",
+ "Response with zero llt",
+ "No response, STT",
+ "Concurrent setup request",
+ "No params passed to session transfer",
+ "Session ID",
+ "Session setup skipped",
+ "No params passed to session teardown",
+ "Bad session",
+ "Session setup skipped",
+ "Bad fsts_id")
+
+def fst_start_session(apdev, test_params, bad_param_type, start_on_ap,
+ peer_addr=None):
+ """This function makes the necessary preparations and the adds and sets a
+ session using either correct or incorrect parameters depending on the value
+ of bad_param_type. If the call ends as expected (with session being
+ successfully added and set in case of correct parameters or with the
+ expected exception in case of incorrect parameters), the function silently
+ exits. Otherwise, it throws an exception thus failing the test."""
+
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ bad_parameter_detected = False
+ exception_already_raised = False
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ if start_on_ap:
+ initiator = ap1
+ responder = sta1
+ new_iface = ap2.ifname()
+ new_peer_addr = ap2.get_actual_peer_addr()
+ else:
+ initiator = sta1
+ responder = ap1
+ new_iface = sta2.ifname()
+ new_peer_addr = sta2.get_actual_peer_addr()
+ initiator.add_peer(responder, peer_addr, new_peer_addr)
+ group_id = None
+ if bad_param_type == bad_param_group_id:
+ group_id = '-1'
+ elif bad_param_type == bad_param_session_add_no_params:
+ group_id = ''
+ initiator.set_fst_parameters(group_id=group_id)
+ sid = initiator.add_session()
+ if bad_param_type == bad_param_session_set_no_params:
+ res = initiator.set_session_param(None)
+ if not res.startswith("OK"):
+ raise Exception("Session set operation failed")
+ elif bad_param_type == bad_param_session_set_unknown_param:
+ res = initiator.set_session_param("bad_param=1")
+ if not res.startswith("OK"):
+ raise Exception("Session set operation failed")
+ else:
+ if bad_param_type == bad_param_session_initiate_with_no_new_iface_set:
+ new_iface = None
+ elif bad_param_type == bad_param_new_iface:
+ new_iface = 'wlan12'
+ old_iface = None if bad_param_type != bad_param_old_iface else 'wlan12'
+ llt = None
+ if bad_param_type == bad_param_negative_llt:
+ llt = '-1'
+ elif bad_param_type == bad_param_zero_llt:
+ llt = '0'
+ elif bad_param_type == bad_param_llt_too_big:
+ llt = '4294967296' #0x100000000
+ elif bad_param_type == bad_param_llt_nan:
+ llt = 'nan'
+ elif bad_param_type == bad_param_session_id:
+ sid = '-1'
+ initiator.set_fst_parameters(llt=llt)
+ initiator.configure_session(sid, new_iface, old_iface)
+ except Exception as e:
+ if e.args[0].startswith("Cannot add FST session with groupid"):
+ if bad_param_type == bad_param_group_id or bad_param_type == bad_param_session_add_no_params:
+ bad_parameter_detected = True
+ elif e.args[0].startswith("Cannot set FST session new_ifname:"):
+ if bad_param_type == bad_param_new_iface:
+ bad_parameter_detected = True
+ elif e.args[0].startswith("Session set operation failed"):
+ if (bad_param_type == bad_param_session_set_no_params or
+ bad_param_type == bad_param_session_set_unknown_param):
+ bad_parameter_detected = True
+ elif e.args[0].startswith("Cannot set FST session old_ifname:"):
+ if (bad_param_type == bad_param_old_iface or
+ bad_param_type == bad_param_session_id or
+ bad_param_type == bad_param_session_set_no_params):
+ bad_parameter_detected = True
+ elif e.args[0].startswith("Cannot set FST session llt:"):
+ if (bad_param_type == bad_param_negative_llt or
+ bad_param_type == bad_param_llt_too_big or
+ bad_param_type == bad_param_llt_nan):
+ bad_parameter_detected = True
+ elif e.args[0].startswith("Cannot set FST session peer address:"):
+ if bad_param_type == bad_param_peer_addr:
+ bad_parameter_detected = True
+ if not bad_parameter_detected:
+ # The exception was unexpected
+ logger.info(e)
+ exception_already_raised = True
+ raise
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ if not exception_already_raised:
+ if bad_parameter_detected:
+ logger.info("Success. Bad parameter was detected (%s)" % bad_param_names[bad_param_type])
+ else:
+ if bad_param_type == bad_param_none or bad_param_type == bad_param_zero_llt:
+ logger.info("Success. Session added and set")
+ else:
+ exception_text = ""
+ if bad_param_type == bad_param_peer_addr:
+ exception_text = "Failure. Bad parameter was not detected (Peer address == %s)" % ap1.get_new_peer_addr()
+ else:
+ exception_text = "Failure. Bad parameter was not detected (%s)" % bad_param_names[bad_param_type]
+ raise Exception(exception_text)
+ else:
+ logger.info("Failure. Unexpected exception")
+
+def fst_initiate_session(apdev, test_params, bad_param_type, init_on_ap):
+ """This function makes the necessary preparations and then adds, sets and
+ initiates a session using either correct or incorrect parameters at each
+ stage depending on the value of bad_param_type. If the call ends as expected
+ (with session being successfully added, set and initiated in case of correct
+ parameters or with the expected exception in case of incorrect parameters),
+ the function silently exits. Otherwise it throws an exception thus failing
+ the test."""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ bad_parameter_detected = False
+ exception_already_raised = False
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ # This call makes sure FstHostapd singleton object is created and, as a
+ # result, the global control interface is registered (this is done from
+ # the constructor).
+ ap1.get_global_instance()
+ if init_on_ap:
+ initiator = ap1
+ responder = sta1
+ new_iface = ap2.ifname() if bad_param_type != bad_param_session_initiate_with_no_new_iface_set else None
+ new_peer_addr = ap2.get_actual_peer_addr()
+ resp_newif = sta2.ifname()
+ else:
+ initiator = sta1
+ responder = ap1
+ new_iface = sta2.ifname() if bad_param_type != bad_param_session_initiate_with_no_new_iface_set else None
+ new_peer_addr = sta2.get_actual_peer_addr()
+ resp_newif = ap2.ifname()
+ peeraddr = None if bad_param_type != bad_param_session_initiate_with_bad_peer_addr_set else '10:DE:AD:DE:AD:11'
+ initiator.add_peer(responder, peeraddr, new_peer_addr)
+ if bad_param_type == bad_param_session_initiate_response_with_zero_llt:
+ initiator.set_fst_parameters(llt='0')
+ sid = initiator.add_session()
+ initiator.configure_session(sid, new_iface)
+ if bad_param_type == bad_param_session_initiate_no_params:
+ sid = ''
+ elif bad_param_type == bad_param_session_initiate_bad_session_id:
+ sid = '-1'
+ if bad_param_type == bad_param_session_initiate_request_with_bad_stie:
+ actual_fsts_id = initiator.get_fsts_id_by_sid(sid)
+ initiator.send_test_session_setup_request(str(actual_fsts_id), "bad_new_band")
+ responder.wait_for_session_event(5)
+ elif bad_param_type == bad_param_session_initiate_response_with_reject:
+ initiator.send_session_setup_request(sid)
+ initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ setup_event = responder.wait_for_session_event(5, [],
+ ['EVENT_FST_SETUP'])
+ if 'id' not in setup_event:
+ raise Exception("No session id in FST setup event")
+ responder.send_session_setup_response(str(setup_event['id']),
+ "reject")
+ event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ if event['new_state'] != "INITIAL" or event['reason'] != "REASON_REJECT":
+ raise Exception("Response with reject not handled as expected")
+ bad_parameter_detected = True
+ elif bad_param_type == bad_param_session_initiate_response_with_bad_stie:
+ initiator.send_session_setup_request(sid)
+ initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ responder.wait_for_session_event(5, [], ['EVENT_FST_SETUP'])
+ actual_fsts_id = initiator.get_fsts_id_by_sid(sid)
+ responder.send_test_session_setup_response(str(actual_fsts_id),
+ "accept", "bad_new_band")
+ event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ if event['new_state'] != "INITIAL" or event['reason'] != "REASON_ERROR_PARAMS":
+ raise Exception("Response with bad STIE not handled as expected")
+ bad_parameter_detected = True
+ elif bad_param_type == bad_param_session_initiate_response_with_zero_llt:
+ initiator.initiate_session(sid, "accept")
+ event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ if event['new_state'] != "TRANSITION_DONE":
+ raise Exception("Response reception for a session with llt=0 not handled as expected")
+ bad_parameter_detected = True
+ elif bad_param_type == bad_param_session_initiate_stt_no_response:
+ initiator.send_session_setup_request(sid)
+ initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ responder.wait_for_session_event(5, [], ['EVENT_FST_SETUP'])
+ event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ if event['new_state'] != "INITIAL" or event['reason'] != "REASON_STT":
+ raise Exception("No response scenario not handled as expected")
+ bad_parameter_detected = True
+ elif bad_param_type == bad_param_session_initiate_concurrent_setup_request:
+ responder.add_peer(initiator)
+ resp_sid = responder.add_session()
+ responder.configure_session(resp_sid, resp_newif)
+ initiator.send_session_setup_request(sid)
+ actual_fsts_id = initiator.get_fsts_id_by_sid(sid)
+ responder.send_test_session_setup_request(str(actual_fsts_id))
+ event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ initiator_addr = initiator.get_own_mac_address()
+ responder_addr = responder.get_own_mac_address()
+ if initiator_addr < responder_addr:
+ event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ if event['new_state'] != "INITIAL" or event['reason'] != "REASON_SETUP":
+ raise Exception("Concurrent setup scenario not handled as expected")
+ event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SETUP"])
+ # The incoming setup request received by the initiator has
+ # priority over the one sent previously by the initiator itself
+ # because the initiator's MAC address is numerically lower than
+ # the one of the responder. Thus, the initiator should generate
+ # an FST_SETUP event.
+ else:
+ event = initiator.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ if event['new_state'] != "INITIAL" or event['reason'] != "REASON_STT":
+ raise Exception("Concurrent setup scenario not handled as expected")
+ # The incoming setup request was dropped at the initiator
+ # because its MAC address is numerically bigger than the one of
+ # the responder. Thus, the initiator continue to wait for a
+ # setup response until the STT event fires.
+ bad_parameter_detected = True
+ else:
+ initiator.initiate_session(sid, "accept")
+ except Exception as e:
+ if e.args[0].startswith("Cannot initiate fst session"):
+ if bad_param_type != bad_param_none:
+ bad_parameter_detected = True
+ elif e.args[0].startswith("No FST-EVENT-SESSION received"):
+ if bad_param_type == bad_param_session_initiate_request_with_bad_stie:
+ bad_parameter_detected = True
+ if not bad_parameter_detected:
+ #The exception was unexpected
+ logger.info(e)
+ exception_already_raised = True
+ raise
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ if not exception_already_raised:
+ if bad_parameter_detected:
+ logger.info("Success. Bad parameter was detected (%s)" % bad_param_names[bad_param_type])
+ else:
+ if bad_param_type == bad_param_none:
+ logger.info("Success. Session initiated")
+ else:
+ raise Exception("Failure. Bad parameter was not detected (%s)" % bad_param_names[bad_param_type])
+ else:
+ logger.info("Failure. Unexpected exception")
+
+def fst_transfer_session(apdev, test_params, bad_param_type, init_on_ap,
+ rsn=False):
+ """This function makes the necessary preparations and then adds, sets,
+ initiates and attempts to transfer a session using either correct or
+ incorrect parameters at each stage depending on the value of bad_param_type.
+ If the call ends as expected the function silently exits. Otherwise, it
+ throws an exception thus failing the test."""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev, rsn=rsn)
+ bad_parameter_detected = False
+ exception_already_raised = False
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2, rsn=rsn)
+ # This call makes sure FstHostapd singleton object is created and, as a
+ # result, the global control interface is registered (this is done from
+ # the constructor).
+ ap1.get_global_instance()
+ if init_on_ap:
+ initiator = ap1
+ responder = sta1
+ new_iface = ap2.ifname()
+ new_peer_addr = ap2.get_actual_peer_addr()
+ else:
+ initiator = sta1
+ responder = ap1
+ new_iface = sta2.ifname()
+ new_peer_addr = sta2.get_actual_peer_addr()
+ initiator.add_peer(responder, new_peer_addr=new_peer_addr)
+ sid = initiator.add_session()
+ initiator.configure_session(sid, new_iface)
+ if bad_param_type != bad_param_session_transfer_setup_skipped:
+ initiator.initiate_session(sid, "accept")
+ if bad_param_type == bad_param_session_transfer_no_params:
+ sid = ''
+ elif bad_param_type == bad_param_session_transfer_bad_session_id:
+ sid = '-1'
+ initiator.transfer_session(sid)
+ except Exception as e:
+ if e.args[0].startswith("Cannot transfer fst session"):
+ if bad_param_type != bad_param_none:
+ bad_parameter_detected = True
+ if not bad_parameter_detected:
+ # The exception was unexpected
+ logger.info(e)
+ exception_already_raised = True
+ raise
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ if not exception_already_raised:
+ if bad_parameter_detected:
+ logger.info("Success. Bad parameter was detected (%s)" % bad_param_names[bad_param_type])
+ else:
+ if bad_param_type == bad_param_none:
+ logger.info("Success. Session transferred")
+ else:
+ raise Exception("Failure. Bad parameter was not detected (%s)" % bad_param_names[bad_param_type])
+ else:
+ logger.info("Failure. Unexpected exception")
+
+
+def fst_tear_down_session(apdev, test_params, bad_param_type, init_on_ap):
+ """This function makes the necessary preparations and then adds, sets, and
+ initiates a session. It then issues a tear down command using either
+ correct or incorrect parameters at each stage. If the call ends as expected,
+ the function silently exits. Otherwise, it throws an exception thus failing
+ the test."""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ bad_parameter_detected = False
+ exception_already_raised = False
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ # This call makes sure FstHostapd singleton object is created and, as a
+ # result, the global control interface is registered (this is done from
+ # the constructor).
+ ap1.get_global_instance()
+ if init_on_ap:
+ initiator = ap1
+ responder = sta1
+ new_iface = ap2.ifname()
+ new_peer_addr = ap2.get_actual_peer_addr()
+ else:
+ initiator = sta1
+ responder = ap1
+ new_iface = sta2.ifname()
+ new_peer_addr = sta2.get_actual_peer_addr()
+ initiator.add_peer(responder, new_peer_addr=new_peer_addr)
+ sid = initiator.add_session()
+ initiator.configure_session(sid, new_iface)
+ if bad_param_type != bad_param_session_teardown_setup_skipped:
+ initiator.initiate_session(sid, "accept")
+ if bad_param_type == bad_param_session_teardown_bad_fsts_id:
+ initiator.send_test_tear_down('-1')
+ responder.wait_for_session_event(5)
+ else:
+ if bad_param_type == bad_param_session_teardown_no_params:
+ sid = ''
+ elif bad_param_type == bad_param_session_teardown_bad_session_id:
+ sid = '-1'
+ initiator.teardown_session(sid)
+ except Exception as e:
+ if e.args[0].startswith("Cannot tear down fst session"):
+ if (bad_param_type == bad_param_session_teardown_no_params or
+ bad_param_type == bad_param_session_teardown_bad_session_id or
+ bad_param_type == bad_param_session_teardown_setup_skipped):
+ bad_parameter_detected = True
+ elif e.args[0].startswith("No FST-EVENT-SESSION received"):
+ if bad_param_type == bad_param_session_teardown_bad_fsts_id:
+ bad_parameter_detected = True
+ if not bad_parameter_detected:
+ # The exception was unexpected
+ logger.info(e)
+ exception_already_raised = True
+ raise
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ if not exception_already_raised:
+ if bad_parameter_detected:
+ logger.info("Success. Bad parameter was detected (%s)" % bad_param_names[bad_param_type])
+ else:
+ if bad_param_type == bad_param_none:
+ logger.info("Success. Session torn down")
+ else:
+ raise Exception("Failure. Bad parameter was not detected (%s)" % bad_param_names[bad_param_type])
+ else:
+ logger.info("Failure. Unexpected exception")
+
+
+#enum - remove session scenarios
+remove_scenario_no_params = 0
+remove_scenario_bad_session_id = 1
+remove_scenario_non_established_session = 2
+remove_scenario_established_session = 3
+
+remove_scenario_names = ("No params",
+ "Bad session id",
+ "Remove non-established session",
+ "Remove established session")
+
+
+def fst_remove_session(apdev, test_params, remove_session_scenario, init_on_ap):
+ """This function attempts to remove a session at various stages of its
+ formation, depending on the value of remove_session_scenario. If the call
+ ends as expected, the function silently exits. Otherwise, it throws an
+ exception thus failing the test."""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ bad_parameter_detected = False
+ exception_already_raised = False
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ # This call makes sure FstHostapd singleton object is created and, as a
+ # result, the global control interface is registered (this is done from
+ # the constructor).
+ ap1.get_global_instance()
+ if init_on_ap:
+ initiator = ap1
+ responder = sta1
+ new_iface = ap2.ifname()
+ new_peer_addr = ap2.get_actual_peer_addr()
+ else:
+ initiator = sta1
+ responder = ap1
+ new_iface = sta2.ifname()
+ new_peer_addr = sta2.get_actual_peer_addr()
+ initiator.add_peer(responder, new_peer_addr=new_peer_addr)
+ sid = initiator.add_session()
+ initiator.configure_session(sid, new_iface)
+ if remove_session_scenario != remove_scenario_no_params:
+ if remove_session_scenario != remove_scenario_non_established_session:
+ initiator.initiate_session(sid, "accept")
+ if remove_session_scenario == remove_scenario_no_params:
+ sid = ''
+ elif remove_session_scenario == remove_scenario_bad_session_id:
+ sid = '-1'
+ initiator.remove_session(sid)
+ except Exception as e:
+ if e.args[0].startswith("Cannot remove fst session"):
+ if (remove_session_scenario == remove_scenario_no_params or
+ remove_session_scenario == remove_scenario_bad_session_id):
+ bad_parameter_detected = True
+ elif e.args[0].startswith("No FST-EVENT-SESSION received"):
+ if remove_session_scenario == remove_scenario_non_established_session:
+ bad_parameter_detected = True
+ if not bad_parameter_detected:
+ #The exception was unexpected
+ logger.info(e)
+ exception_already_raised = True
+ raise
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ if not exception_already_raised:
+ if bad_parameter_detected:
+ logger.info("Success. Remove scenario ended as expected (%s)" % remove_scenario_names[remove_session_scenario])
+ else:
+ if remove_session_scenario == remove_scenario_established_session:
+ logger.info("Success. Session removed")
+ else:
+ raise Exception("Failure. Remove scenario ended in an unexpected way (%s)" % remove_scenario_names[remove_session_scenario])
+ else:
+ logger.info("Failure. Unexpected exception")
+
+
+#enum - frame types
+frame_type_session_request = 0
+frame_type_session_response = 1
+frame_type_ack_request = 2
+frame_type_ack_response = 3
+frame_type_tear_down = 4
+
+frame_type_names = ("Session request",
+ "Session Response",
+ "Ack request",
+ "Ack response",
+ "Tear down")
+
+def fst_send_unexpected_frame(apdev, test_params, frame_type, send_from_ap, additional_param=''):
+ """This function creates two pairs of APs and stations, makes them connect
+ and then causes one side to send an unexpected FST frame of the specified
+ type to the other. The other side should then identify and ignore the
+ frame."""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ exception_already_raised = False
+ frame_receive_timeout = False
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ # This call makes sure FstHostapd singleton object is created and, as a
+ # result, the global control interface is registered (this is done from
+ # the constructor).
+ ap1.get_global_instance()
+ if send_from_ap:
+ sender = ap1
+ receiver = sta1
+ new_iface = ap2.ifname()
+ new_peer_addr = ap2.get_actual_peer_addr()
+ else:
+ sender = sta1
+ receiver = ap1
+ new_iface = sta2.ifname()
+ new_peer_addr = sta2.get_actual_peer_addr()
+ sender.add_peer(receiver, new_peer_addr=new_peer_addr)
+ sid = sender.add_session()
+ sender.configure_session(sid, new_iface)
+ if frame_type == frame_type_session_request:
+ sender.send_session_setup_request(sid)
+ event = receiver.wait_for_session_event(5)
+ if event['type'] != 'EVENT_FST_SETUP':
+ raise Exception("Unexpected indication: " + event['type'])
+ elif frame_type == frame_type_session_response:
+ #fsts_id doesn't matter, no actual session exists
+ sender.send_test_session_setup_response('0', additional_param)
+ receiver.wait_for_session_event(5)
+ elif frame_type == frame_type_ack_request:
+ #fsts_id doesn't matter, no actual session exists
+ sender.send_test_ack_request('0')
+ receiver.wait_for_session_event(5)
+ elif frame_type == frame_type_ack_response:
+ #fsts_id doesn't matter, no actual session exists
+ sender.send_test_ack_response('0')
+ receiver.wait_for_session_event(5)
+ elif frame_type == frame_type_tear_down:
+ #fsts_id doesn't matter, no actual session exists
+ sender.send_test_tear_down('0')
+ receiver.wait_for_session_event(5)
+ except Exception as e:
+ if e.args[0].startswith("No FST-EVENT-SESSION received"):
+ if frame_type != frame_type_session_request:
+ frame_receive_timeout = True
+ else:
+ logger.info(e)
+ exception_already_raised = True
+ raise
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ if not exception_already_raised:
+ if frame_receive_timeout:
+ logger.info("Success. Frame was ignored (%s)" % frame_type_names[frame_type])
+ else:
+ if frame_type == frame_type_session_request:
+ logger.info("Success. Frame received, session created")
+ else:
+ raise Exception("Failure. Frame was not ignored (%s)" % frame_type_names[frame_type])
+ else:
+ logger.info("Failure. Unexpected exception")
+
+
+#enum - bad session transfer scenarios
+bad_scenario_none = 0
+bad_scenario_ack_req_session_not_set_up = 1
+bad_scenario_ack_req_session_not_established_init_side = 2
+bad_scenario_ack_req_session_not_established_resp_side = 3
+bad_scenario_ack_req_bad_fsts_id = 4
+bad_scenario_ack_resp_session_not_set_up = 5
+bad_scenario_ack_resp_session_not_established_init_side = 6
+bad_scenario_ack_resp_session_not_established_resp_side = 7
+bad_scenario_ack_resp_no_ack_req = 8
+bad_scenario_ack_resp_bad_fsts_id = 9
+
+bad_scenario_names = ("None",
+ "Ack request received before the session was set up",
+ "Ack request received on the initiator side before session was established",
+ "Ack request received on the responder side before session was established",
+ "Ack request received with bad fsts_id",
+ "Ack response received before the session was set up",
+ "Ack response received on the initiator side before session was established",
+ "Ack response received on the responder side before session was established",
+ "Ack response received before ack request was sent",
+ "Ack response received with bad fsts_id")
+
+def fst_bad_transfer(apdev, test_params, bad_scenario_type, init_on_ap):
+ """This function makes the necessary preparations and then adds and sets a
+ session. It then initiates and it unless instructed otherwise) and attempts
+ to send one of the frames involved in the session transfer protocol,
+ skipping or distorting one of the stages according to the value of
+ bad_scenario_type parameter."""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ bad_parameter_detected = False
+ exception_already_raised = False
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ # This call makes sure FstHostapd singleton object is created and, as a
+ # result, the global control interface is registered (this is done from
+ # the constructor).
+ ap1.get_global_instance()
+ if init_on_ap:
+ initiator = ap1
+ responder = sta1
+ new_iface = ap2.ifname()
+ new_peer_addr = ap2.get_actual_peer_addr()
+ else:
+ initiator = sta1
+ responder = ap1
+ new_iface = sta2.ifname()
+ new_peer_addr = sta2.get_actual_peer_addr()
+ initiator.add_peer(responder, new_peer_addr=new_peer_addr)
+ sid = initiator.add_session()
+ initiator.configure_session(sid, new_iface)
+ if (bad_scenario_type != bad_scenario_ack_req_session_not_set_up and
+ bad_scenario_type != bad_scenario_ack_resp_session_not_set_up):
+ if (bad_scenario_type != bad_scenario_ack_req_session_not_established_init_side and
+ bad_scenario_type != bad_scenario_ack_resp_session_not_established_init_side and
+ bad_scenario_type != bad_scenario_ack_req_session_not_established_resp_side and
+ bad_scenario_type != bad_scenario_ack_resp_session_not_established_resp_side):
+ response = "accept"
+ else:
+ response = ''
+ initiator.initiate_session(sid, response)
+ if bad_scenario_type == bad_scenario_ack_req_session_not_set_up:
+ #fsts_id doesn't matter, no actual session exists
+ responder.send_test_ack_request('0')
+ initiator.wait_for_session_event(5)
+ # We want to send the unexpected frame to the side that already has
+ # a session created
+ elif bad_scenario_type == bad_scenario_ack_resp_session_not_set_up:
+ #fsts_id doesn't matter, no actual session exists
+ responder.send_test_ack_response('0')
+ initiator.wait_for_session_event(5)
+ # We want to send the unexpected frame to the side that already has
+ # a session created
+ elif bad_scenario_type == bad_scenario_ack_req_session_not_established_init_side:
+ #fsts_id doesn't matter, no actual session exists
+ initiator.send_test_ack_request('0')
+ responder.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+ elif bad_scenario_type == bad_scenario_ack_req_session_not_established_resp_side:
+ #fsts_id doesn't matter, no actual session exists
+ responder.send_test_ack_request('0')
+ initiator.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+ elif bad_scenario_type == bad_scenario_ack_resp_session_not_established_init_side:
+ #fsts_id doesn't matter, no actual session exists
+ initiator.send_test_ack_response('0')
+ responder.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+ elif bad_scenario_type == bad_scenario_ack_resp_session_not_established_resp_side:
+ #fsts_id doesn't matter, no actual session exists
+ responder.send_test_ack_response('0')
+ initiator.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+ elif bad_scenario_type == bad_scenario_ack_req_bad_fsts_id:
+ initiator.send_test_ack_request('-1')
+ responder.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+ elif bad_scenario_type == bad_scenario_ack_resp_bad_fsts_id:
+ initiator.send_test_ack_response('-1')
+ responder.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+ elif bad_scenario_type == bad_scenario_ack_resp_no_ack_req:
+ actual_fsts_id = initiator.get_fsts_id_by_sid(sid)
+ initiator.send_test_ack_response(str(actual_fsts_id))
+ responder.wait_for_session_event(5, ["EVENT_FST_SESSION_STATE"])
+ else:
+ raise Exception("Unknown bad scenario identifier")
+ except Exception as e:
+ if e.args[0].startswith("No FST-EVENT-SESSION received"):
+ bad_parameter_detected = True
+ if not bad_parameter_detected:
+ # The exception was unexpected
+ logger.info(e)
+ exception_already_raised = True
+ raise
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ if not exception_already_raised:
+ if bad_parameter_detected:
+ logger.info("Success. Bad scenario was handled correctly (%s)" % bad_scenario_names[bad_scenario_type])
+ else:
+ raise Exception("Failure. Bad scenario was handled incorrectly (%s)" % bad_scenario_names[bad_scenario_type])
+ else:
+ logger.info("Failure. Unexpected exception")
+
+def test_fst_sta_connect_to_non_fst_ap(dev, apdev, test_params):
+ """FST STA connecting to non-FST AP"""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ with HWSimRadio() as (radio, iface):
+ non_fst_ap = hostapd.add_ap(iface, {"ssid": "non_fst_11g"})
+ try:
+ orig_sta1_mbies = sta1.get_local_mbies()
+ orig_sta2_mbies = sta2.get_local_mbies()
+ sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g",
+ key_mgmt="NONE", scan_freq='2412')
+ time.sleep(2)
+ res_sta1_mbies = sta1.get_local_mbies()
+ res_sta2_mbies = sta2.get_local_mbies()
+ if (orig_sta1_mbies.startswith("FAIL") or
+ orig_sta2_mbies.startswith("FAIL") or
+ res_sta1_mbies.startswith("FAIL") or
+ res_sta2_mbies.startswith("FAIL")):
+ raise Exception("Failure. MB IEs must be present on the stations")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ sta2.disconnect_from_external_ap()
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_sta_connect_to_fst_ap(dev, apdev, test_params):
+ """FST STA connecting to FST AP"""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ try:
+ orig_sta2_mbies = sta2.get_local_mbies()
+ vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ sta1.connect(ap1, key_mgmt="NONE",
+ scan_freq=fst_test_common.fst_test_def_freq_a)
+ time.sleep(2)
+ res_sta2_mbies = sta2.get_local_mbies()
+ if res_sta2_mbies == orig_sta2_mbies:
+ raise Exception("Failure. MB IEs have not been updated")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ sta1.disconnect()
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_ap_connect_to_fst_sta(dev, apdev, test_params):
+ """FST AP connecting to FST STA"""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ try:
+ orig_ap_mbies = ap1.get_local_mbies()
+ vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ sta1.connect(ap1, key_mgmt="NONE",
+ scan_freq=fst_test_common.fst_test_def_freq_a)
+ time.sleep(2)
+ res_ap_mbies = ap1.get_local_mbies()
+ if res_ap_mbies != orig_ap_mbies:
+ raise Exception("Failure. MB IEs have been unexpectedly updated on the AP")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ sta1.disconnect()
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_ap_connect_to_non_fst_sta(dev, apdev, test_params):
+ """FST AP connecting to non-FST STA"""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ try:
+ orig_ap_mbies = ap2.get_local_mbies()
+ vals = dev[0].scan(None, fst_test_common.fst_test_def_freq_g)
+ fst_module_aux.external_sta_connect(dev[0], ap2, key_mgmt="NONE",
+ scan_freq=fst_test_common.fst_test_def_freq_g)
+ time.sleep(2)
+ res_ap_mbies = ap2.get_local_mbies()
+ if res_ap_mbies != orig_ap_mbies:
+ raise Exception("Failure. MB IEs have been unexpectedly updated on the AP")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ fst_module_aux.disconnect_external_sta(dev[0], ap2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_second_sta_connect_to_non_fst_ap(dev, apdev, test_params):
+ """FST STA 2nd connecting to non-FST AP"""
+ fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ with HWSimRadio() as (radio, iface):
+ non_fst_ap = hostapd.add_ap(iface, {"ssid": "non_fst_11g"})
+ try:
+ vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+ time.sleep(2)
+ orig_sta1_mbies = sta1.get_local_mbies()
+ orig_sta2_mbies = sta2.get_local_mbies()
+ sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g",
+ key_mgmt="NONE", scan_freq='2412')
+ time.sleep(2)
+ res_sta1_mbies = sta1.get_local_mbies()
+ res_sta2_mbies = sta2.get_local_mbies()
+ if (orig_sta1_mbies.startswith("FAIL") or
+ orig_sta2_mbies.startswith("FAIL") or
+ res_sta1_mbies.startswith("FAIL") or
+ res_sta2_mbies.startswith("FAIL")):
+ raise Exception("Failure. MB IEs must be present on the stations")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ sta1.disconnect()
+ sta2.disconnect_from_external_ap()
+ fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+ hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_second_sta_connect_to_fst_ap(dev, apdev, test_params):
+ """FST STA 2nd connecting to FST AP"""
+ fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ with HWSimRadio() as (radio, iface):
+ non_fst_ap = hostapd.add_ap(iface, {"ssid": "non_fst_11g"})
+ try:
+ sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g",
+ key_mgmt="NONE", scan_freq='2412')
+ time.sleep(2)
+ orig_sta1_mbies = sta1.get_local_mbies()
+ orig_sta2_mbies = sta2.get_local_mbies()
+ vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+ time.sleep(2)
+ res_sta1_mbies = sta1.get_local_mbies()
+ res_sta2_mbies = sta2.get_local_mbies()
+ if (orig_sta1_mbies.startswith("FAIL") or
+ orig_sta2_mbies.startswith("FAIL") or
+ res_sta1_mbies.startswith("FAIL") or
+ res_sta2_mbies.startswith("FAIL")):
+ raise Exception("Failure. MB IEs must be present on the stations")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ sta1.disconnect()
+ sta2.disconnect_from_external_ap()
+ fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+ hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_disconnect_1_of_2_stas_from_non_fst_ap(dev, apdev, test_params):
+ """FST disconnect 1 of 2 STAs from non-FST AP"""
+ fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ with HWSimRadio() as (radio, iface):
+ non_fst_ap = hostapd.add_ap(iface, {"ssid": "non_fst_11g"})
+ try:
+ vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+ sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g",
+ key_mgmt="NONE", scan_freq='2412')
+ time.sleep(2)
+ orig_sta1_mbies = sta1.get_local_mbies()
+ orig_sta2_mbies = sta2.get_local_mbies()
+ sta2.disconnect_from_external_ap()
+ time.sleep(2)
+ res_sta1_mbies = sta1.get_local_mbies()
+ res_sta2_mbies = sta2.get_local_mbies()
+ if (orig_sta1_mbies.startswith("FAIL") or
+ orig_sta2_mbies.startswith("FAIL") or
+ res_sta1_mbies.startswith("FAIL") or
+ res_sta2_mbies.startswith("FAIL")):
+ raise Exception("Failure. MB IEs must be present on the stations")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ sta1.disconnect()
+ sta2.disconnect_from_external_ap()
+ fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+ hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_disconnect_1_of_2_stas_from_fst_ap(dev, apdev, test_params):
+ """FST disconnect 1 of 2 STAs from FST AP"""
+ fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ with HWSimRadio() as (radio, iface):
+ non_fst_ap = hostapd.add_ap(iface, {"ssid": "non_fst_11g"})
+ try:
+ vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+ sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g",
+ key_mgmt="NONE", scan_freq='2412')
+ time.sleep(2)
+ orig_sta1_mbies = sta1.get_local_mbies()
+ orig_sta2_mbies = sta2.get_local_mbies()
+ sta1.disconnect()
+ time.sleep(2)
+ res_sta1_mbies = sta1.get_local_mbies()
+ res_sta2_mbies = sta2.get_local_mbies()
+ if (orig_sta1_mbies.startswith("FAIL") or
+ orig_sta2_mbies.startswith("FAIL") or
+ res_sta1_mbies.startswith("FAIL") or
+ res_sta2_mbies.startswith("FAIL")):
+ raise Exception("Failure. MB IEs must be present on the stations")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ sta1.disconnect()
+ sta2.disconnect_from_external_ap()
+ fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+ hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_disconnect_2_of_2_stas_from_non_fst_ap(dev, apdev, test_params):
+ """FST disconnect 2 of 2 STAs from non-FST AP"""
+ fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ with HWSimRadio() as (radio, iface):
+ non_fst_ap = hostapd.add_ap(iface, {"ssid": "non_fst_11g"})
+ try:
+ vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+ sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g",
+ key_mgmt="NONE", scan_freq='2412')
+ time.sleep(2)
+ sta1.disconnect()
+ time.sleep(2)
+ orig_sta1_mbies = sta1.get_local_mbies()
+ orig_sta2_mbies = sta2.get_local_mbies()
+ sta2.disconnect_from_external_ap()
+ time.sleep(2)
+ res_sta1_mbies = sta1.get_local_mbies()
+ res_sta2_mbies = sta2.get_local_mbies()
+ if (orig_sta1_mbies.startswith("FAIL") or
+ orig_sta2_mbies.startswith("FAIL") or
+ res_sta1_mbies.startswith("FAIL") or
+ res_sta2_mbies.startswith("FAIL")):
+ raise Exception("Failure. MB IEs must be present on the stations")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ sta1.disconnect()
+ sta2.disconnect_from_external_ap()
+ fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+ hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_disconnect_2_of_2_stas_from_fst_ap(dev, apdev, test_params):
+ """FST disconnect 2 of 2 STAs from FST AP"""
+ fst_ap1, fst_ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ with HWSimRadio() as (radio, iface):
+ non_fst_ap = hostapd.add_ap(iface, {"ssid": "non_fst_11g"})
+ try:
+ vals = sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ sta1.connect(fst_ap1, key_mgmt="NONE", scan_freq=fst_test_common.fst_test_def_freq_a)
+ sta2.connect_to_external_ap(non_fst_ap, ssid="non_fst_11g",
+ key_mgmt="NONE", scan_freq='2412')
+ time.sleep(2)
+ sta2.disconnect_from_external_ap()
+ time.sleep(2)
+ orig_sta1_mbies = sta1.get_local_mbies()
+ orig_sta2_mbies = sta2.get_local_mbies()
+ sta1.disconnect()
+ time.sleep(2)
+ res_sta1_mbies = sta1.get_local_mbies()
+ res_sta2_mbies = sta2.get_local_mbies()
+ if (orig_sta1_mbies.startswith("FAIL") or
+ orig_sta2_mbies.startswith("FAIL") or
+ res_sta1_mbies.startswith("FAIL") or
+ res_sta2_mbies.startswith("FAIL")):
+ raise Exception("Failure. MB IEs should have stayed present on both stations")
+ # Mandatory part of 8.4.2.140 Multi-band element is 24 bytes = 48 hex chars
+ basic_sta1_mbies = res_sta1_mbies[0:48] + res_sta1_mbies[60:108]
+ basic_sta2_mbies = res_sta2_mbies[0:48] + res_sta2_mbies[60:108]
+ if (basic_sta1_mbies != basic_sta2_mbies):
+ raise Exception("Failure. Basic MB IEs should have become identical on both stations")
+ addr_sta1_str = sta1.get_own_mac_address().replace(":", "")
+ addr_sta2_str = sta2.get_own_mac_address().replace(":", "")
+ # Mandatory part of 8.4.2.140 Multi-band element is followed by STA MAC Address field (6 bytes = 12 hex chars)
+ addr_sta1_mbie1 = res_sta1_mbies[48:60]
+ addr_sta1_mbie2 = res_sta1_mbies[108:120]
+ addr_sta2_mbie1 = res_sta2_mbies[48:60]
+ addr_sta2_mbie2 = res_sta2_mbies[108:120]
+ if (addr_sta1_mbie1 != addr_sta1_mbie2 or
+ addr_sta1_mbie1 != addr_sta2_str or
+ addr_sta2_mbie1 != addr_sta2_mbie2 or
+ addr_sta2_mbie1 != addr_sta1_str):
+ raise Exception("Failure. STA Address in MB IEs should have been same as the other STA's")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ sta1.disconnect()
+ sta2.disconnect_from_external_ap()
+ fst_module_aux.stop_two_ap_sta_pairs(fst_ap1, fst_ap2, sta1, sta2)
+ hostapd.HostapdGlobal().remove(iface)
+
+def test_fst_disconnect_non_fst_sta(dev, apdev, test_params):
+ """FST disconnect non-FST STA"""
+ ap1, ap2, fst_sta1, fst_sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ external_sta_connected = False
+ try:
+ vals = fst_sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ fst_sta1.connect(ap1, key_mgmt="NONE",
+ scan_freq=fst_test_common.fst_test_def_freq_a)
+ vals = dev[0].scan(None, fst_test_common.fst_test_def_freq_g)
+ fst_module_aux.external_sta_connect(dev[0], ap2, key_mgmt="NONE",
+ scan_freq=fst_test_common.fst_test_def_freq_g)
+ external_sta_connected = True
+ time.sleep(2)
+ fst_sta1.disconnect()
+ time.sleep(2)
+ orig_ap_mbies = ap2.get_local_mbies()
+ fst_module_aux.disconnect_external_sta(dev[0], ap2)
+ external_sta_connected = False
+ time.sleep(2)
+ res_ap_mbies = ap2.get_local_mbies()
+ if res_ap_mbies != orig_ap_mbies:
+ raise Exception("Failure. MB IEs have been unexpectedly updated on the AP")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ fst_sta1.disconnect()
+ if external_sta_connected:
+ fst_module_aux.disconnect_external_sta(dev[0], ap2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, fst_sta1, fst_sta2)
+
+def test_fst_disconnect_fst_sta(dev, apdev, test_params):
+ """FST disconnect FST STA"""
+ ap1, ap2, fst_sta1, fst_sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ external_sta_connected = False
+ try:
+ vals = fst_sta1.scan(freq=fst_test_common.fst_test_def_freq_a)
+ fst_sta1.connect(ap1, key_mgmt="NONE",
+ scan_freq=fst_test_common.fst_test_def_freq_a)
+ vals = dev[0].scan(None, fst_test_common.fst_test_def_freq_g)
+ fst_module_aux.external_sta_connect(dev[0], ap2, key_mgmt="NONE",
+ scan_freq=fst_test_common.fst_test_def_freq_g)
+ external_sta_connected = True
+ time.sleep(2)
+ fst_module_aux.disconnect_external_sta(dev[0], ap2)
+ external_sta_connected = False
+ time.sleep(2)
+ orig_ap_mbies = ap2.get_local_mbies()
+ fst_sta1.disconnect()
+ time.sleep(2)
+ res_ap_mbies = ap2.get_local_mbies()
+ if res_ap_mbies != orig_ap_mbies:
+ raise Exception("Failure. MB IEs have been unexpectedly updated on the AP")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ fst_sta1.disconnect()
+ if external_sta_connected:
+ fst_module_aux.disconnect_external_sta(dev[0], ap2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, fst_sta1, fst_sta2)
+
+def test_fst_dynamic_iface_attach(dev, apdev, test_params):
+ """FST dynamic interface attach"""
+ ap1 = fst_module_aux.FstAP(apdev[0]['ifname'], 'fst_11a', 'a',
+ fst_test_common.fst_test_def_chan_a,
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_low,
+ fst_test_common.fst_test_def_llt)
+ ap1.start()
+ ap2 = fst_module_aux.FstAP(apdev[1]['ifname'], 'fst_11g', 'b',
+ fst_test_common.fst_test_def_chan_g,
+ '', '', '')
+ ap2.start()
+
+ sta1 = fst_module_aux.FstSTA('wlan5',
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_low,
+ fst_test_common.fst_test_def_llt)
+ sta1.start()
+ sta2 = fst_module_aux.FstSTA('wlan6', '', '', '')
+ sta2.start()
+
+ try:
+ orig_sta2_mbies = sta2.get_local_mbies()
+ orig_ap2_mbies = ap2.get_local_mbies()
+ sta2.send_iface_attach_request(sta2.ifname(),
+ fst_test_common.fst_test_def_group,
+ '52', '27')
+ event = sta2.wait_for_iface_event(5)
+ if event['event_type'] != 'attached':
+ raise Exception("Failure. Iface was not properly attached")
+ ap2.send_iface_attach_request(ap2.ifname(),
+ fst_test_common.fst_test_def_group,
+ '102', '77')
+ event = ap2.wait_for_iface_event(5)
+ if event['event_type'] != 'attached':
+ raise Exception("Failure. Iface was not properly attached")
+ time.sleep(2)
+ res_sta2_mbies = sta2.get_local_mbies()
+ res_ap2_mbies = ap2.get_local_mbies()
+ sta2.send_iface_detach_request(sta2.ifname())
+ event = sta2.wait_for_iface_event(5)
+ if event['event_type'] != 'detached':
+ raise Exception("Failure. Iface was not properly detached")
+ ap2.send_iface_detach_request(ap2.ifname())
+ event = ap2.wait_for_iface_event(5)
+ if event['event_type'] != 'detached':
+ raise Exception("Failure. Iface was not properly detached")
+ if (not orig_sta2_mbies.startswith("FAIL") or
+ not orig_ap2_mbies.startswith("FAIL") or
+ res_sta2_mbies.startswith("FAIL") or
+ res_ap2_mbies.startswith("FAIL")):
+ raise Exception("Failure. MB IEs should have appeared on the station and on the AP")
+ except Exception as e:
+ logger.info(e)
+ raise
+ finally:
+ ap1.stop()
+ ap2.stop()
+ sta1.stop()
+ sta2.stop()
+
+# AP side FST module tests
+
+def test_fst_ap_start_session(dev, apdev, test_params):
+ """FST AP start session"""
+ fst_start_session(apdev, test_params, bad_param_none, True)
+
+def test_fst_ap_start_session_no_add_params(dev, apdev, test_params):
+ """FST AP start session - no add params"""
+ fst_start_session(apdev, test_params, bad_param_session_add_no_params, True)
+
+def test_fst_ap_start_session_bad_group_id(dev, apdev, test_params):
+ """FST AP start session - bad group id"""
+ fst_start_session(apdev, test_params, bad_param_group_id, True)
+
+def test_fst_ap_start_session_no_set_params(dev, apdev, test_params):
+ """FST AP start session - no set params"""
+ fst_start_session(apdev, test_params, bad_param_session_set_no_params, True)
+
+def test_fst_ap_start_session_set_unknown_param(dev, apdev, test_params):
+ """FST AP start session - set unknown param"""
+ fst_start_session(apdev, test_params, bad_param_session_set_unknown_param,
+ True)
+
+def test_fst_ap_start_session_bad_session_id(dev, apdev, test_params):
+ """FST AP start session - bad session id"""
+ fst_start_session(apdev, test_params, bad_param_session_id, True)
+
+def test_fst_ap_start_session_bad_new_iface(dev, apdev, test_params):
+ """FST AP start session - bad new iface"""
+ fst_start_session(apdev, test_params, bad_param_new_iface, True)
+
+def test_fst_ap_start_session_bad_old_iface(dev, apdev, test_params):
+ """FST AP start session - bad old iface"""
+ fst_start_session(apdev, test_params, bad_param_old_iface, True)
+
+def test_fst_ap_start_session_negative_llt(dev, apdev, test_params):
+ """FST AP start session - negative llt"""
+ fst_start_session(apdev, test_params, bad_param_negative_llt, True)
+
+def test_fst_ap_start_session_zero_llt(dev, apdev, test_params):
+ """FST AP start session - zero llt"""
+ fst_start_session(apdev, test_params, bad_param_zero_llt, True)
+
+def test_fst_ap_start_session_llt_too_big(dev, apdev, test_params):
+ """FST AP start session - llt too large"""
+ fst_start_session(apdev, test_params, bad_param_llt_too_big, True)
+
+def test_fst_ap_start_session_invalid_peer_addr(dev, apdev, test_params):
+ """FST AP start session - invalid peer address"""
+ fst_start_session(apdev, test_params, bad_param_peer_addr, True,
+ 'GG:GG:GG:GG:GG:GG')
+
+def test_fst_ap_start_session_multicast_peer_addr(dev, apdev, test_params):
+ """FST AP start session - multicast peer address"""
+ fst_start_session(apdev, test_params, bad_param_peer_addr, True,
+ '01:00:11:22:33:44')
+
+def test_fst_ap_start_session_broadcast_peer_addr(dev, apdev, test_params):
+ """FST AP start session - broadcast peer address"""
+ fst_start_session(apdev, test_params, bad_param_peer_addr, True,
+ 'FF:FF:FF:FF:FF:FF')
+
+def test_fst_ap_initiate_session(dev, apdev, test_params):
+ """FST AP initiate session"""
+ fst_initiate_session(apdev, test_params, bad_param_none, True)
+
+def test_fst_ap_initiate_session_no_params(dev, apdev, test_params):
+ """FST AP initiate session - no params"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_no_params, True)
+
+def test_fst_ap_initiate_session_invalid_session_id(dev, apdev, test_params):
+ """FST AP initiate session - invalid session id"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_bad_session_id, True)
+
+def test_fst_ap_initiate_session_no_new_iface(dev, apdev, test_params):
+ """FST AP initiate session - no new iface"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_with_no_new_iface_set, True)
+
+def test_fst_ap_initiate_session_bad_peer_addr(dev, apdev, test_params):
+ """FST AP initiate session - bad peer address"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_with_bad_peer_addr_set,
+ True)
+
+def test_fst_ap_initiate_session_request_with_bad_stie(dev, apdev, test_params):
+ """FST AP initiate session - request with bad stie"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_request_with_bad_stie, True)
+
+def test_fst_ap_initiate_session_response_with_reject(dev, apdev, test_params):
+ """FST AP initiate session - response with reject"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_response_with_reject, True)
+
+def test_fst_ap_initiate_session_response_with_bad_stie(dev, apdev,
+ test_params):
+ """FST AP initiate session - response with bad stie"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_response_with_bad_stie,
+ True)
+
+def test_fst_ap_initiate_session_response_with_zero_llt(dev, apdev,
+ test_params):
+ """FST AP initiate session - zero llt"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_response_with_zero_llt,
+ True)
+
+def test_fst_ap_initiate_session_stt_no_response(dev, apdev, test_params):
+ """FST AP initiate session - stt no response"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_stt_no_response, True)
+
+def test_fst_ap_initiate_session_concurrent_setup_request(dev, apdev,
+ test_params):
+ """FST AP initiate session - concurrent setup request"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_concurrent_setup_request,
+ True)
+
+def test_fst_ap_session_request_with_no_session(dev, apdev, test_params):
+ """FST AP session request with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_session_request,
+ True)
+
+def test_fst_ap_session_response_accept_with_no_session(dev, apdev,
+ test_params):
+ """FST AP session response accept with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_session_response,
+ True, "accept")
+
+def test_fst_ap_session_response_reject_with_no_session(dev, apdev,
+ test_params):
+ """FST AP session response reject with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_session_response,
+ True, "reject")
+
+def test_fst_ap_ack_request_with_no_session(dev, apdev, test_params):
+ """FST AP ack request with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_ack_request, True)
+
+def test_fst_ap_ack_response_with_no_session(dev, apdev, test_params):
+ """FST AP ack response with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_ack_response, True)
+
+def test_fst_ap_tear_down_response_with_no_session(dev, apdev, test_params):
+ """FST AP tear down response with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_tear_down, True)
+
+def test_fst_ap_transfer_session(dev, apdev, test_params):
+ """FST AP transfer session"""
+ fst_transfer_session(apdev, test_params, bad_param_none, True)
+
+def test_fst_ap_transfer_session_no_params(dev, apdev, test_params):
+ """FST AP transfer session - no params"""
+ fst_transfer_session(apdev, test_params,
+ bad_param_session_transfer_no_params, True)
+
+def test_fst_ap_transfer_session_bad_session_id(dev, apdev, test_params):
+ """FST AP transfer session - bad session id"""
+ fst_transfer_session(apdev, test_params,
+ bad_param_session_transfer_bad_session_id, True)
+
+def test_fst_ap_transfer_session_setup_skipped(dev, apdev, test_params):
+ """FST AP transfer session - setup skipped"""
+ fst_transfer_session(apdev, test_params,
+ bad_param_session_transfer_setup_skipped, True)
+
+def test_fst_ap_ack_request_with_session_not_set_up(dev, apdev, test_params):
+ """FST AP ack request with session not set up"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_req_session_not_set_up, True)
+
+def test_fst_ap_ack_request_with_session_not_established_init_side(dev, apdev,
+ test_params):
+ """FST AP ack request with session not established init side"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_req_session_not_established_init_side,
+ True)
+
+def test_fst_ap_ack_request_with_session_not_established_resp_side(dev, apdev,
+ test_params):
+ """FST AP ack request with session not established resp side"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_req_session_not_established_resp_side,
+ True)
+
+def test_fst_ap_ack_request_with_bad_fsts_id(dev, apdev, test_params):
+ """FST AP ack request with bad fsts id"""
+ fst_bad_transfer(apdev, test_params, bad_scenario_ack_req_bad_fsts_id, True)
+
+def test_fst_ap_ack_response_with_session_not_set_up(dev, apdev, test_params):
+ """FST AP ack response with session not set up"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_resp_session_not_set_up, True)
+
+def test_fst_ap_ack_response_with_session_not_established_init_side(dev, apdev, test_params):
+ """FST AP ack response with session not established init side"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_resp_session_not_established_init_side,
+ True)
+
+def test_fst_ap_ack_response_with_session_not_established_resp_side(dev, apdev, test_params):
+ """FST AP ack response with session not established resp side"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_resp_session_not_established_resp_side,
+ True)
+
+def test_fst_ap_ack_response_with_no_ack_request(dev, apdev, test_params):
+ """FST AP ack response with no ack request"""
+ fst_bad_transfer(apdev, test_params, bad_scenario_ack_resp_no_ack_req, True)
+
+def test_fst_ap_tear_down_session(dev, apdev, test_params):
+ """FST AP tear down session"""
+ fst_tear_down_session(apdev, test_params, bad_param_none, True)
+
+def test_fst_ap_tear_down_session_no_params(dev, apdev, test_params):
+ """FST AP tear down session - no params"""
+ fst_tear_down_session(apdev, test_params,
+ bad_param_session_teardown_no_params, True)
+
+def test_fst_ap_tear_down_session_bad_session_id(dev, apdev, test_params):
+ """FST AP tear down session - bad session id"""
+ fst_tear_down_session(apdev, test_params,
+ bad_param_session_teardown_bad_session_id, True)
+
+def test_fst_ap_tear_down_session_setup_skipped(dev, apdev, test_params):
+ """FST AP tear down session - setup skipped"""
+ fst_tear_down_session(apdev, test_params,
+ bad_param_session_teardown_setup_skipped, True)
+
+def test_fst_ap_tear_down_session_bad_fsts_id(dev, apdev, test_params):
+ """FST AP tear down session - bad fsts id"""
+ fst_tear_down_session(apdev, test_params,
+ bad_param_session_teardown_bad_fsts_id, True)
+
+def test_fst_ap_remove_session_not_established(dev, apdev, test_params):
+ """FST AP remove session - not established"""
+ fst_remove_session(apdev, test_params,
+ remove_scenario_non_established_session, True)
+
+def test_fst_ap_remove_session_established(dev, apdev, test_params):
+ """FST AP remove session - established"""
+ fst_remove_session(apdev, test_params,
+ remove_scenario_established_session, True)
+
+def test_fst_ap_remove_session_no_params(dev, apdev, test_params):
+ """FST AP remove session - no params"""
+ fst_remove_session(apdev, test_params, remove_scenario_no_params, True)
+
+def test_fst_ap_remove_session_bad_session_id(dev, apdev, test_params):
+ """FST AP remove session - bad session id"""
+ fst_remove_session(apdev, test_params, remove_scenario_bad_session_id, True)
+
+def test_fst_ap_ctrl_iface(dev, apdev, test_params):
+ """FST control interface behavior"""
+ hglobal = hostapd.HostapdGlobal()
+ start_num_groups = 0
+ res = hglobal.request("FST-MANAGER LIST_GROUPS")
+ del hglobal
+ if "FAIL" not in res:
+ start_num_groups = len(res.splitlines())
+
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ initiator = ap1
+ responder = sta1
+ initiator.add_peer(responder, None)
+ initiator.set_fst_parameters(group_id=None)
+ sid = initiator.add_session()
+ res = initiator.get_session_params(sid)
+ logger.info("Initial session params:\n" + str(res))
+ if res['state'] != 'INITIAL':
+ raise Exception("Unexpected state: " + res['state'])
+ initiator.set_fst_parameters(llt=None)
+ initiator.configure_session(sid, ap2.ifname(), None)
+ res = initiator.get_session_params(sid)
+ logger.info("Session params after configuration:\n" + str(res))
+ res = initiator.iface_peers(initiator.ifname())
+ logger.info("Interface peers: " + str(res))
+ if len(res) != 1:
+ raise Exception("Unexpected number of peers")
+ res = initiator.get_peer_mbies(initiator.ifname(),
+ initiator.get_new_peer_addr())
+ logger.info("Peer MB IEs: " + str(res))
+ res = initiator.list_ifaces()
+ logger.info("Interfaces: " + str(res))
+ if len(res) != 2:
+ raise Exception("Unexpected number of interfaces")
+ res = initiator.list_groups()
+ logger.info("Groups: " + str(res))
+ if len(res) != 1 + start_num_groups:
+ raise Exception("Unexpected number of groups")
+
+ tests = ["LIST_IFACES unknown",
+ "LIST_IFACES unknown2",
+ "SESSION_GET 12345678",
+ "SESSION_SET " + sid + " unknown=foo",
+ "SESSION_RESPOND 12345678 foo",
+ "SESSION_RESPOND " + sid,
+ "SESSION_RESPOND " + sid + " foo",
+ "TEST_REQUEST foo",
+ "TEST_REQUEST SEND_SETUP_REQUEST",
+ "TEST_REQUEST SEND_SETUP_REQUEST foo",
+ "TEST_REQUEST SEND_SETUP_RESPONSE",
+ "TEST_REQUEST SEND_SETUP_RESPONSE foo",
+ "TEST_REQUEST SEND_ACK_REQUEST",
+ "TEST_REQUEST SEND_ACK_REQUEST foo",
+ "TEST_REQUEST SEND_ACK_RESPONSE",
+ "TEST_REQUEST SEND_ACK_RESPONSE foo",
+ "TEST_REQUEST SEND_TEAR_DOWN",
+ "TEST_REQUEST SEND_TEAR_DOWN foo",
+ "TEST_REQUEST GET_FSTS_ID",
+ "TEST_REQUEST GET_FSTS_ID foo",
+ "TEST_REQUEST GET_LOCAL_MBIES",
+ "TEST_REQUEST GET_LOCAL_MBIES foo",
+ "GET_PEER_MBIES",
+ "GET_PEER_MBIES ",
+ "GET_PEER_MBIES unknown",
+ "GET_PEER_MBIES unknown unknown",
+ "GET_PEER_MBIES unknown " + initiator.get_new_peer_addr(),
+ "GET_PEER_MBIES " + initiator.ifname() + " 01:ff:ff:ff:ff:ff",
+ "GET_PEER_MBIES " + initiator.ifname() + " 00:ff:ff:ff:ff:ff",
+ "GET_PEER_MBIES " + initiator.ifname() + " 00:00:00:00:00:00",
+ "IFACE_PEERS",
+ "IFACE_PEERS ",
+ "IFACE_PEERS unknown",
+ "IFACE_PEERS unknown unknown",
+ "IFACE_PEERS " + initiator.fst_group,
+ "IFACE_PEERS " + initiator.fst_group + " unknown"]
+ for t in tests:
+ if "FAIL" not in initiator.grequest("FST-MANAGER " + t):
+ raise Exception("Unexpected response for invalid FST-MANAGER command " + t)
+ if "UNKNOWN FST COMMAND" not in initiator.grequest("FST-MANAGER unknown"):
+ raise Exception("Unexpected response for unknown FST-MANAGER command")
+
+ tests = ["FST-DETACH", "FST-DETACH ", "FST-DETACH unknown",
+ "FST-ATTACH", "FST-ATTACH ", "FST-ATTACH unknown",
+ "FST-ATTACH unknown unknown"]
+ for t in tests:
+ if "FAIL" not in initiator.grequest(t):
+ raise Exception("Unexpected response for invalid command " + t)
+
+ try:
+ # Trying to add same interface again needs to fail.
+ ap1.send_iface_attach_request(ap1.iface, ap1.fst_group,
+ ap1.fst_llt, ap1.fst_pri)
+ raise Exception("Duplicate FST-ATTACH succeeded")
+ except Exception as e:
+ if not str(e).startswith("Cannot attach"):
+ raise
+
+ try:
+ ap1.get_fsts_id_by_sid("123")
+ except Exception as e:
+ if not str(e).startswith("Cannot get fsts_id for sid"):
+ raise
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_ap_start_session_oom(dev, apdev, test_params):
+ """FST AP setup failing due to OOM"""
+ ap1 = fst_module_aux.FstAP(apdev[0]['ifname'], 'fst_11a', 'a',
+ fst_test_common.fst_test_def_chan_a,
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_low,
+ fst_test_common.fst_test_def_llt)
+ ap1.start()
+ try:
+ run_fst_ap_start_session_oom(apdev, ap1)
+ finally:
+ ap1.stop()
+ fst_test_common.fst_clear_regdom()
+
+def run_fst_ap_start_session_oom(apdev, ap1):
+ with alloc_fail(ap1, 1, "fst_iface_create"):
+ ap2_started = False
+ try:
+ ap2 = fst_module_aux.FstAP(apdev[1]['ifname'], 'fst_11g', 'b',
+ fst_test_common.fst_test_def_chan_g,
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_high,
+ fst_test_common.fst_test_def_llt)
+ try:
+ # This will fail in fst_iface_create() OOM
+ ap2.start()
+ except:
+ pass
+ finally:
+ try:
+ ap2.stop()
+ except:
+ pass
+
+# STA side FST module tests
+
+def test_fst_sta_start_session(dev, apdev, test_params):
+ """FST STA start session"""
+ fst_start_session(apdev, test_params, bad_param_none, False)
+
+def test_fst_sta_start_session_no_add_params(dev, apdev, test_params):
+ """FST STA start session - no add params"""
+ fst_start_session(apdev, test_params, bad_param_session_add_no_params,
+ False)
+
+def test_fst_sta_start_session_bad_group_id(dev, apdev, test_params):
+ """FST STA start session - bad group id"""
+ fst_start_session(apdev, test_params, bad_param_group_id, False)
+
+def test_fst_sta_start_session_no_set_params(dev, apdev, test_params):
+ """FST STA start session - no set params"""
+ fst_start_session(apdev, test_params, bad_param_session_set_no_params,
+ False)
+
+def test_fst_sta_start_session_set_unknown_param(dev, apdev, test_params):
+ """FST STA start session - set unknown param"""
+ fst_start_session(apdev, test_params, bad_param_session_set_unknown_param,
+ False)
+
+def test_fst_sta_start_session_bad_session_id(dev, apdev, test_params):
+ """FST STA start session - bad session id"""
+ fst_start_session(apdev, test_params, bad_param_session_id, False)
+
+def test_fst_sta_start_session_bad_new_iface(dev, apdev, test_params):
+ """FST STA start session - bad new iface"""
+ fst_start_session(apdev, test_params, bad_param_new_iface, False)
+
+def test_fst_sta_start_session_bad_old_iface(dev, apdev, test_params):
+ """FST STA start session - bad old iface"""
+ fst_start_session(apdev, test_params, bad_param_old_iface, False)
+
+def test_fst_sta_start_session_negative_llt(dev, apdev, test_params):
+ """FST STA start session - negative llt"""
+ fst_start_session(apdev, test_params, bad_param_negative_llt, False)
+
+def test_fst_sta_start_session_zero_llt(dev, apdev, test_params):
+ """FST STA start session - zero llt"""
+ fst_start_session(apdev, test_params, bad_param_zero_llt, False)
+
+def test_fst_sta_start_session_llt_too_big(dev, apdev, test_params):
+ """FST STA start session - llt too large"""
+ fst_start_session(apdev, test_params, bad_param_llt_too_big, False)
+
+def test_fst_sta_start_session_invalid_peer_addr(dev, apdev, test_params):
+ """FST STA start session - invalid peer address"""
+ fst_start_session(apdev, test_params, bad_param_peer_addr, False,
+ 'GG:GG:GG:GG:GG:GG')
+
+def test_fst_sta_start_session_multicast_peer_addr(dev, apdev, test_params):
+ """FST STA start session - multicast peer address"""
+ fst_start_session(apdev, test_params, bad_param_peer_addr, False,
+ '11:00:11:22:33:44')
+
+def test_fst_sta_start_session_broadcast_peer_addr(dev, apdev, test_params):
+ """FST STA start session - broadcast peer addr"""
+ fst_start_session(apdev, test_params, bad_param_peer_addr, False,
+ 'FF:FF:FF:FF:FF:FF')
+
+def test_fst_sta_initiate_session(dev, apdev, test_params):
+ """FST STA initiate session"""
+ fst_initiate_session(apdev, test_params, bad_param_none, False)
+
+def test_fst_sta_initiate_session_no_params(dev, apdev, test_params):
+ """FST STA initiate session - no params"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_no_params, False)
+
+def test_fst_sta_initiate_session_invalid_session_id(dev, apdev, test_params):
+ """FST STA initiate session - invalid session id"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_bad_session_id, False)
+
+def test_fst_sta_initiate_session_no_new_iface(dev, apdev, test_params):
+ """FST STA initiate session - no new iface"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_with_no_new_iface_set,
+ False)
+
+def test_fst_sta_initiate_session_bad_peer_addr(dev, apdev, test_params):
+ """FST STA initiate session - bad peer address"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_with_bad_peer_addr_set,
+ False)
+
+def test_fst_sta_initiate_session_request_with_bad_stie(dev, apdev,
+ test_params):
+ """FST STA initiate session - request with bad stie"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_request_with_bad_stie,
+ False)
+
+def test_fst_sta_initiate_session_response_with_reject(dev, apdev, test_params):
+ """FST STA initiate session - response with reject"""
+ fst_initiate_session(apdev, test_params, bad_param_session_initiate_response_with_reject, False)
+
+def test_fst_sta_initiate_session_response_with_bad_stie(dev, apdev, test_params):
+ """FST STA initiate session - response with bad stie"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_response_with_bad_stie,
+ False)
+
+def test_fst_sta_initiate_session_response_with_zero_llt(dev, apdev,
+ test_params):
+ """FST STA initiate session - response with zero llt"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_response_with_zero_llt,
+ False)
+
+def test_fst_sta_initiate_session_stt_no_response(dev, apdev, test_params):
+ """FST STA initiate session - stt no response"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_stt_no_response, False)
+
+def test_fst_sta_initiate_session_concurrent_setup_request(dev, apdev,
+ test_params):
+ """FST STA initiate session - concurrent setup request"""
+ fst_initiate_session(apdev, test_params,
+ bad_param_session_initiate_concurrent_setup_request,
+ False)
+
+def test_fst_sta_session_request_with_no_session(dev, apdev, test_params):
+ """FST STA session request with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_session_request,
+ False)
+
+def test_fst_sta_session_response_accept_with_no_session(dev, apdev,
+ test_params):
+ """FST STA session response accept with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_session_response,
+ False, "accept")
+
+def test_fst_sta_session_response_reject_with_no_session(dev, apdev,
+ test_params):
+ """FST STA session response reject with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_session_response,
+ False, "reject")
+
+def test_fst_sta_ack_request_with_no_session(dev, apdev, test_params):
+ """FST STA ack request with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_ack_request, False)
+
+def test_fst_sta_ack_response_with_no_session(dev, apdev, test_params):
+ """FST STA ack response with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_ack_response,
+ False)
+
+def test_fst_sta_tear_down_response_with_no_session(dev, apdev, test_params):
+ """FST STA tear down response with no session"""
+ fst_send_unexpected_frame(apdev, test_params, frame_type_tear_down, False)
+
+def test_fst_sta_transfer_session(dev, apdev, test_params):
+ """FST STA transfer session"""
+ fst_transfer_session(apdev, test_params, bad_param_none, False)
+
+def test_fst_sta_transfer_session_no_params(dev, apdev, test_params):
+ """FST STA transfer session - no params"""
+ fst_transfer_session(apdev, test_params,
+ bad_param_session_transfer_no_params, False)
+
+def test_fst_sta_transfer_session_bad_session_id(dev, apdev, test_params):
+ """FST STA transfer session - bad session id"""
+ fst_transfer_session(apdev, test_params,
+ bad_param_session_transfer_bad_session_id, False)
+
+def test_fst_sta_transfer_session_setup_skipped(dev, apdev, test_params):
+ """FST STA transfer session - setup skipped"""
+ fst_transfer_session(apdev, test_params,
+ bad_param_session_transfer_setup_skipped, False)
+
+def test_fst_sta_ack_request_with_session_not_set_up(dev, apdev, test_params):
+ """FST STA ack request with session not set up"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_req_session_not_set_up, False)
+
+def test_fst_sta_ack_request_with_session_not_established_init_side(dev, apdev, test_params):
+ """FST STA ack request with session not established init side"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_req_session_not_established_init_side,
+ False)
+
+def test_fst_sta_ack_request_with_session_not_established_resp_side(dev, apdev, test_params):
+ """FST STA ack request with session not established resp side"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_req_session_not_established_resp_side,
+ False)
+
+def test_fst_sta_ack_request_with_bad_fsts_id(dev, apdev, test_params):
+ """FST STA ack request with bad fsts id"""
+ fst_bad_transfer(apdev, test_params, bad_scenario_ack_req_bad_fsts_id,
+ False)
+
+def test_fst_sta_ack_response_with_session_not_set_up(dev, apdev, test_params):
+ """FST STA ack response with session not set up"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_resp_session_not_set_up, False)
+
+def test_fst_sta_ack_response_with_session_not_established_init_side(dev, apdev, test_params):
+ """FST STA ack response with session not established init side"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_resp_session_not_established_init_side,
+ False)
+
+def test_fst_sta_ack_response_with_session_not_established_resp_side(dev, apdev, test_params):
+ """FST STA ack response with session not established resp side"""
+ fst_bad_transfer(apdev, test_params,
+ bad_scenario_ack_resp_session_not_established_resp_side,
+ False)
+
+def test_fst_sta_ack_response_with_no_ack_request(dev, apdev, test_params):
+ """FST STA ack response with no ack request"""
+ fst_bad_transfer(apdev, test_params, bad_scenario_ack_resp_no_ack_req,
+ False)
+
+def test_fst_sta_tear_down_session(dev, apdev, test_params):
+ """FST STA tear down session"""
+ fst_tear_down_session(apdev, test_params, bad_param_none, False)
+
+def test_fst_sta_tear_down_session_no_params(dev, apdev, test_params):
+ """FST STA tear down session - no params"""
+ fst_tear_down_session(apdev, test_params,
+ bad_param_session_teardown_no_params, False)
+
+def test_fst_sta_tear_down_session_bad_session_id(dev, apdev, test_params):
+ """FST STA tear down session - bad session id"""
+ fst_tear_down_session(apdev, test_params,
+ bad_param_session_teardown_bad_session_id, False)
+
+def test_fst_sta_tear_down_session_setup_skipped(dev, apdev, test_params):
+ """FST STA tear down session - setup skipped"""
+ fst_tear_down_session(apdev, test_params,
+ bad_param_session_teardown_setup_skipped, False)
+
+def test_fst_sta_tear_down_session_bad_fsts_id(dev, apdev, test_params):
+ """FST STA tear down session - bad fsts id"""
+ fst_tear_down_session(apdev, test_params,
+ bad_param_session_teardown_bad_fsts_id, False)
+
+def test_fst_sta_remove_session_not_established(dev, apdev, test_params):
+ """FST STA tear down session - not established"""
+ fst_remove_session(apdev, test_params,
+ remove_scenario_non_established_session, False)
+
+def test_fst_sta_remove_session_established(dev, apdev, test_params):
+ """FST STA remove session - established"""
+ fst_remove_session(apdev, test_params,
+ remove_scenario_established_session, False)
+
+def test_fst_sta_remove_session_no_params(dev, apdev, test_params):
+ """FST STA remove session - no params"""
+ fst_remove_session(apdev, test_params, remove_scenario_no_params, False)
+
+def test_fst_sta_remove_session_bad_session_id(dev, apdev, test_params):
+ """FST STA remove session - bad session id"""
+ fst_remove_session(apdev, test_params, remove_scenario_bad_session_id,
+ False)
+
+def test_fst_rsn_ap_transfer_session(dev, apdev, test_params):
+ """FST RSN AP transfer session"""
+ fst_transfer_session(apdev, test_params, bad_param_none, True, rsn=True)
+
+MGMT_SUBTYPE_ACTION = 13
+ACTION_CATEG_FST = 18
+FST_ACTION_SETUP_REQUEST = 0
+FST_ACTION_SETUP_RESPONSE = 1
+FST_ACTION_TEAR_DOWN = 2
+FST_ACTION_ACK_REQUEST = 3
+FST_ACTION_ACK_RESPONSE = 4
+FST_ACTION_ON_CHANNEL_TUNNEL = 5
+
+def hostapd_tx_and_status(hapd, msg):
+ hapd.set("ext_mgmt_frame_handling", "1")
+ hapd.mgmt_tx(msg)
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=1)
+ if ev is None or "ok=1" not in ev:
+ raise Exception("No ACK")
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+def test_fst_proto(dev, apdev, test_params):
+ """FST protocol testing"""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ hapd = ap1.get_instance()
+ sta = sta1.get_instance()
+ dst = sta.own_addr()
+ src = apdev[0]['bssid']
+
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = dst
+ msg['sa'] = src
+ msg['bssid'] = src
+
+ # unknown FST Action (255) received!
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST, 255)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Request dropped: too short
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_REQUEST)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Request dropped: invalid STIE (EID)
+ msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_REQUEST, 0, 0,
+ 163, 11, 0, 0, 0, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Request dropped: invalid STIE (Len)
+ msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_REQUEST, 0, 0,
+ 164, 10, 0, 0, 0, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Request dropped: new and old band IDs are the same
+ msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_REQUEST, 0, 0,
+ 164, 11, 0, 0, 0, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+
+ ifaces = sta1.list_ifaces()
+ id = int(ifaces[0]['name'].split('|')[1])
+ # FST Request dropped: new iface not found (new_band_id mismatch)
+ msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_REQUEST, 0, 0,
+ 164, 11, 0, 0, id + 1, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Action 'Setup Response' dropped: no session in progress found
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_RESPONSE)
+ hostapd_tx_and_status(hapd, msg)
+
+ # Create session
+ initiator = ap1
+ responder = sta1
+ new_iface = ap2.ifname()
+ new_peer_addr = ap2.get_actual_peer_addr()
+ resp_newif = sta2.ifname()
+ peeraddr = None
+ initiator.add_peer(responder, peeraddr, new_peer_addr)
+ sid = initiator.add_session()
+ initiator.configure_session(sid, new_iface)
+ initiator.initiate_session(sid, "accept")
+
+ # FST Response dropped due to wrong state: SETUP_COMPLETION
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_RESPONSE)
+ hostapd_tx_and_status(hapd, msg)
+
+ # Too short FST Tear Down dropped
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+ FST_ACTION_TEAR_DOWN)
+ hostapd_tx_and_status(hapd, msg)
+
+ # tear down for wrong FST Setup ID (0)
+ msg['payload'] = struct.pack("<BBL", ACTION_CATEG_FST,
+ FST_ACTION_TEAR_DOWN, 0)
+ hostapd_tx_and_status(hapd, msg)
+
+ # Ack received on wrong interface
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+ FST_ACTION_ACK_REQUEST)
+ hostapd_tx_and_status(hapd, msg)
+
+ # Ack Response in inappropriate session state (SETUP_COMPLETION)
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+ FST_ACTION_ACK_RESPONSE)
+ hostapd_tx_and_status(hapd, msg)
+
+ # Unsupported FST Action frame (On channel tunnel)
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+ FST_ACTION_ON_CHANNEL_TUNNEL)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Request dropped: new iface not found (new_band_id match)
+ # FST Request dropped due to MAC comparison
+ msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_REQUEST, 0, 0,
+ 164, 11, 0, 0, id, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+
+ hapd2 = ap2.get_instance()
+ dst2 = sta2.get_instance().own_addr()
+ src2 = apdev[1]['bssid']
+
+ msg2 = {}
+ msg2['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg2['da'] = dst2
+ msg2['sa'] = src2
+ msg2['bssid'] = src2
+ # FST Response dropped: wlan6 is not the old iface
+ msg2['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_RESPONSE)
+ hostapd_tx_and_status(hapd2, msg2)
+
+ sta.dump_monitor()
+
+ group = ap1.fst_group
+ ap1.send_iface_detach_request(ap1.iface)
+
+ sta.flush_scan_cache()
+ sta.request("REASSOCIATE")
+ sta.wait_connected()
+
+ # FST Request dropped due to no interface connection
+ msg['payload'] = struct.pack("<BBBLBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_REQUEST, 0, 0,
+ 164, 11, 0, 0, id, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ try:
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ except:
+ pass
+
+def test_fst_setup_response_proto(dev, apdev, test_params):
+ """FST protocol testing for Setup Response"""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ hapd = ap1.get_instance()
+ sta = sta1.get_instance()
+ dst = sta.own_addr()
+ src = apdev[0]['bssid']
+
+ sta1.add_peer(ap1, None, sta2.get_actual_peer_addr())
+ sta1.set_fst_parameters(llt='0')
+ sid = sta1.add_session()
+ sta1.configure_session(sid, sta2.ifname())
+ sta1.initiate_session(sid, "")
+
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = dst
+ msg['sa'] = src
+ msg['bssid'] = src
+
+ # Too short FST Response dropped
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_RESPONSE)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Response dropped: invalid STIE (EID)
+ dialog_token = 1
+ status_code = 0
+ id = 0
+ msg['payload'] = struct.pack("<BBBBBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_RESPONSE, dialog_token,
+ status_code,
+ 163, 11, 0, 0, id, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Response dropped: invalid STIE (Len)
+ dialog_token = 1
+ status_code = 0
+ id = 0
+ msg['payload'] = struct.pack("<BBBBBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_RESPONSE, dialog_token,
+ status_code,
+ 164, 10, 0, 0, id, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Response dropped due to wrong dialog token
+ dialog_token = 123
+ status_code = 0
+ id = 0
+ msg['payload'] = struct.pack("<BBBBBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_RESPONSE, dialog_token,
+ status_code,
+ 164, 11, 0, 0, id, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Response dropped due to wrong FST Session ID
+ dialog_token = 1
+ status_code = 0
+ id = 1
+ msg['payload'] = struct.pack("<BBBBBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_RESPONSE, dialog_token,
+ status_code,
+ 164, 11, int(sid) + 123456,
+ 0, id, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+
+ # FST Response with non-zero status code
+ dialog_token = 1
+ status_code = 1
+ id = 1
+ msg['payload'] = struct.pack("<BBBBBBLBBBBBBB", ACTION_CATEG_FST,
+ FST_ACTION_SETUP_RESPONSE, dialog_token,
+ status_code,
+ 164, 11, int(sid), 0, id, 0, 0, 0, 0, 0)
+ hostapd_tx_and_status(hapd, msg)
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_ack_response_proto(dev, apdev, test_params):
+ """FST protocol testing for Ack Response"""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ hapd = ap2.get_instance()
+ sta = sta2.get_instance()
+ dst = sta.own_addr()
+ src = apdev[1]['bssid']
+
+ sta1.add_peer(ap1, None, sta2.get_actual_peer_addr())
+ sta1.set_fst_parameters(llt='0')
+ sid = sta1.add_session()
+ sta1.configure_session(sid, sta2.ifname())
+
+ s = sta1.grequest("FST-MANAGER SESSION_INITIATE "+ sid)
+ if not s.startswith('OK'):
+ raise Exception("Cannot initiate fst session: %s" % s)
+ ev = sta1.peer_obj.wait_gevent(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION received")
+ event = fst_module_aux.parse_fst_session_event(ev)
+ if event == None:
+ raise Exception("Unrecognized FST event: " % ev)
+ if event['type'] != 'EVENT_FST_SETUP':
+ raise Exception("Expected FST_SETUP event, got: " + event['type'])
+ ev = sta1.peer_obj.wait_gevent(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION received")
+ event = fst_module_aux.parse_fst_session_event(ev)
+ if event == None:
+ raise Exception("Unrecognized FST event: " % ev)
+ if event['type'] != 'EVENT_FST_SESSION_STATE':
+ raise Exception("Expected EVENT_FST_SESSION_STATE event, got: " + event['type'])
+ if event['new_state'] != "SETUP_COMPLETION":
+ raise Exception("Expected new state SETUP_COMPLETION, got: " + event['new_state'])
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ s = sta1.peer_obj.grequest("FST-MANAGER SESSION_RESPOND "+ event['id'] + " accept")
+ if not s.startswith('OK'):
+ raise Exception("Error session_respond: %s" % s)
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("No Ack Request seen")
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = dst
+ msg['sa'] = src
+ msg['bssid'] = src
+
+ # Too short FST Ack Response dropped
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_FST,
+ FST_ACTION_ACK_RESPONSE)
+ hapd.mgmt_tx(msg)
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=1)
+ if ev is None or "ok=1" not in ev:
+ raise Exception("No ACK")
+
+ # Ack Response for wrong FSt Setup ID
+ msg['payload'] = struct.pack("<BBBL", ACTION_CATEG_FST,
+ FST_ACTION_ACK_RESPONSE,
+ 0, int(sid) + 123456)
+ hostapd_tx_and_status(hapd, msg)
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_ap_config_oom(dev, apdev, test_params):
+ """FST AP configuration and OOM"""
+ ap1 = fst_module_aux.FstAP(apdev[0]['ifname'], 'fst_11a', 'a',
+ fst_test_common.fst_test_def_chan_a,
+ fst_test_common.fst_test_def_group,
+ fst_test_common.fst_test_def_prio_low)
+ hapd = ap1.start(return_early=True)
+ with alloc_fail(hapd, 1, "fst_group_create"):
+ res = ap1.grequest("FST-ATTACH %s %s" % (ap1.iface, ap1.fst_group))
+ if not res.startswith("FAIL"):
+ raise Exception("FST-ATTACH succeeded unexpectedly")
+
+ with alloc_fail(hapd, 1, "fst_iface_create"):
+ res = ap1.grequest("FST-ATTACH %s %s" % (ap1.iface, ap1.fst_group))
+ if not res.startswith("FAIL"):
+ raise Exception("FST-ATTACH succeeded unexpectedly")
+
+ with alloc_fail(hapd, 1, "fst_group_create_mb_ie"):
+ res = ap1.grequest("FST-ATTACH %s %s" % (ap1.iface, ap1.fst_group))
+ # This is allowed to complete currently
+
+ ap1.stop()
+ fst_test_common.fst_clear_regdom()
+
+def test_fst_send_oom(dev, apdev, test_params):
+ """FST send action OOM"""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ hapd = ap1.get_instance()
+ sta = sta1.get_instance()
+ dst = sta.own_addr()
+ src = apdev[0]['bssid']
+
+ # Create session
+ initiator = ap1
+ responder = sta1
+ new_iface = ap2.ifname()
+ new_peer_addr = ap2.get_actual_peer_addr()
+ resp_newif = sta2.ifname()
+ peeraddr = None
+ initiator.add_peer(responder, peeraddr, new_peer_addr)
+ sid = initiator.add_session()
+ initiator.configure_session(sid, new_iface)
+ with alloc_fail(hapd, 1, "fst_session_send_action"):
+ res = initiator.grequest("FST-MANAGER SESSION_INITIATE " + sid)
+ if not res.startswith("FAIL"):
+ raise Exception("Unexpected SESSION_INITIATE result")
+
+ res = initiator.grequest("FST-MANAGER SESSION_INITIATE " + sid)
+ if not res.startswith("OK"):
+ raise Exception("SESSION_INITIATE failed")
+
+ tests = ["", "foo", sid, sid + " foo", sid + " foo=bar"]
+ for t in tests:
+ res = initiator.grequest("FST-MANAGER SESSION_SET " + t)
+ if not res.startswith("FAIL"):
+ raise Exception("Invalid SESSION_SET accepted")
+
+ with alloc_fail(hapd, 1, "fst_session_send_action"):
+ res = initiator.grequest("FST-MANAGER SESSION_TEARDOWN " + sid)
+ if not res.startswith("FAIL"):
+ raise Exception("Unexpected SESSION_TEARDOWN result")
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_session_oom(dev, apdev, test_params):
+ """FST session create OOM"""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ hapd = ap1.get_instance()
+ sta = sta1.get_instance()
+ dst = sta.own_addr()
+ src = apdev[0]['bssid']
+
+ # Create session
+ initiator = ap1
+ responder = sta1
+ new_iface = ap2.ifname()
+ new_peer_addr = ap2.get_actual_peer_addr()
+ resp_newif = sta2.ifname()
+ peeraddr = None
+ initiator.add_peer(responder, peeraddr, new_peer_addr)
+ with alloc_fail(hapd, 1, "fst_session_create"):
+ sid = initiator.grequest("FST-MANAGER SESSION_ADD " + initiator.fst_group)
+ if not sid.startswith("FAIL"):
+ raise Exception("Unexpected SESSION_ADD success")
+ sid = initiator.add_session()
+ initiator.configure_session(sid, new_iface)
+ with alloc_fail(sta, 1, "fst_session_create"):
+ res = initiator.grequest("FST-MANAGER SESSION_INITIATE " + sid)
+ if not res.startswith("OK"):
+ raise Exception("Unexpected SESSION_INITIATE result")
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def test_fst_attach_zero_llt(dev, apdev):
+ """FST attach with llt=0"""
+ sta1 = fst_module_aux.FstSTA('wlan5', fst_test_common.fst_test_def_group,
+ "100", "0")
+ sta1.start()
+ sta1.stop()
+
+def test_fst_session_respond_fail(dev, apdev, test_params):
+ """FST-MANAGER SESSION_RESPOND failure"""
+ ap1, ap2, sta1, sta2 = fst_module_aux.start_two_ap_sta_pairs(apdev)
+ try:
+ fst_module_aux.connect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ sta1.add_peer(ap1, None, sta2.get_actual_peer_addr())
+ sid = sta1.add_session()
+ sta1.configure_session(sid, sta2.ifname())
+ sta1.send_session_setup_request(sid)
+ sta1.wait_for_session_event(5, [], ["EVENT_FST_SESSION_STATE"])
+ ev = ap1.wait_for_session_event(5, [], ['EVENT_FST_SETUP'])
+ if 'id' not in ev:
+ raise Exception("No session id in FST setup event")
+ # Disconnect STA to make SESSION_RESPOND fail due to no peer found
+ sta = sta1.get_instance()
+ sta.request("DISCONNECT")
+ sta.wait_disconnected()
+ req = "FST-MANAGER SESSION_RESPOND %s reject" % ev['id']
+ s = ap1.grequest(req)
+ if not s.startswith("FAIL"):
+ raise Exception("SESSION_RESPOND succeeded unexpectedly")
+ finally:
+ fst_module_aux.disconnect_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+ fst_module_aux.stop_two_ap_sta_pairs(ap1, ap2, sta1, sta2)
+
+def fst_session_set(dev, sid, param, value):
+ cmd = "FST-MANAGER SESSION_SET %s %s=%s" % (sid, param, value)
+ if "OK" not in dev.global_request(cmd):
+ raise Exception(cmd + " failed")
+
+def fst_session_set_ap(dev, sid, param, value):
+ cmd = "FST-MANAGER SESSION_SET %s %s=%s" % (sid, param, value)
+ if "OK" not in dev.request(cmd):
+ raise Exception(cmd + " failed")
+
+def fst_attach_ap(dev, ifname, group):
+ cmd = "FST-ATTACH %s %s" % (ifname, group)
+ if "OK" not in dev.request(cmd):
+ raise Exception("FST-ATTACH (AP) failed")
+ ev = dev.wait_event(['FST-EVENT-IFACE'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-IFACE attached (AP)")
+ for t in ["attached", "ifname=" + ifname, "group=" + group]:
+ if t not in ev:
+ raise Exception("Unexpected FST-EVENT-IFACE data (AP): " + ev)
+
+def fst_attach_sta(dev, ifname, group):
+ if "OK" not in dev.global_request("FST-ATTACH %s %s" % (ifname, group)):
+ raise Exception("FST-ATTACH (STA) failed")
+ ev = dev.wait_global_event(['FST-EVENT-IFACE'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-IFACE attached (STA)")
+ for t in ["attached", "ifname=" + ifname, "group=" + group]:
+ if t not in ev:
+ raise Exception("Unexpected FST-EVENT-IFACE data (STA): " + ev)
+
+def fst_detach_ap(dev, ifname, group):
+ if "OK" not in dev.request("FST-DETACH " + ifname):
+ raise Exception("FST-DETACH (AP) failed for " + ifname)
+ ev = dev.wait_event(['FST-EVENT-IFACE'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-IFACE detached (AP) for " + ifname)
+ for t in ["detached", "ifname=" + ifname, "group=" + group]:
+ if t not in ev:
+ raise Exception("Unexpected FST-EVENT-IFACE data (AP): " + ev)
+
+def fst_detach_sta(dev, ifname, group):
+ dev.dump_monitor()
+ if "OK" not in dev.global_request("FST-DETACH " + ifname):
+ raise Exception("FST-DETACH (STA) failed for " + ifname)
+ ev = dev.wait_global_event(['FST-EVENT-IFACE'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-IFACE detached (STA) for " + ifname)
+ for t in ["detached", "ifname=" + ifname, "group=" + group]:
+ if t not in ev:
+ raise Exception("Unexpected FST-EVENT-IFACE data (STA): " + ev)
+
+def fst_wait_event_peer_ap(dev, event, ifname, addr):
+ ev = dev.wait_event(['FST-EVENT-PEER'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-PEER connected (AP)")
+ for t in [" " + event + " ", "ifname=" + ifname, "peer_addr=" + addr]:
+ if t not in ev:
+ raise Exception("Unexpected FST-EVENT-PEER data (AP): " + ev)
+
+def fst_wait_event_peer_sta(dev, event, ifname, addr):
+ ev = dev.wait_global_event(['FST-EVENT-PEER'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-PEER connected (STA)")
+ for t in [" " + event + " ", "ifname=" + ifname, "peer_addr=" + addr]:
+ if t not in ev:
+ raise Exception("Unexpected FST-EVENT-PEER data (STA): " + ev)
+
+def fst_setup_req(dev, hglobal, freq, dst, req, stie, mbie="", no_wait=False):
+ act = req + stie + mbie
+ dev.request("MGMT_TX %s %s freq=%d action=%s" % (dst, dst, freq, act))
+ ev = dev.wait_event(['MGMT-TX-STATUS'], timeout=5)
+ if ev is None or "result=SUCCESS" not in ev:
+ raise Exception("FST Action frame not ACKed")
+
+ if no_wait:
+ return
+ while True:
+ ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION (AP)")
+ if "new_state=SETUP_COMPLETION" in ev:
+ break
+
+def fst_start_and_connect(apdev, group, sgroup):
+ hglobal = hostapd.HostapdGlobal()
+ if "OK" not in hglobal.request("FST-MANAGER TEST_REQUEST IS_SUPPORTED"):
+ raise HwsimSkip("No FST testing support")
+
+ params = {"ssid": "fst_11a", "hw_mode": "a", "channel": "36",
+ "country_code": "US"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ fst_attach_ap(hglobal, apdev[0]['ifname'], group)
+
+ cmd = "FST-ATTACH %s %s" % (apdev[0]['ifname'], group)
+ if "FAIL" not in hglobal.request(cmd):
+ raise Exception("Duplicated FST-ATTACH (AP) accepted")
+
+ params = {"ssid": "fst_11g", "hw_mode": "g", "channel": "1",
+ "country_code": "US"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ fst_attach_ap(hglobal, apdev[1]['ifname'], group)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ fst_attach_sta(wpas, wpas.ifname, sgroup)
+
+ wpas.interface_add("wlan6", set_ifname=False)
+ wpas2 = WpaSupplicant(ifname="wlan6")
+ fst_attach_sta(wpas, wpas2.ifname, sgroup)
+
+ wpas.connect("fst_11a", key_mgmt="NONE", scan_freq="5180",
+ wait_connect=False)
+ wpas.wait_connected()
+
+ fst_wait_event_peer_sta(wpas, "connected", wpas.ifname, apdev[0]['bssid'])
+ fst_wait_event_peer_ap(hglobal, "connected", apdev[0]['ifname'],
+ wpas.own_addr())
+
+ wpas2.connect("fst_11g", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ wpas2.wait_connected()
+
+ fst_wait_event_peer_sta(wpas, "connected", wpas2.ifname, apdev[1]['bssid'])
+ fst_wait_event_peer_ap(hglobal, "connected", apdev[1]['ifname'],
+ wpas2.own_addr())
+ return hglobal, wpas, wpas2, hapd, hapd2
+
+def test_fst_test_setup(dev, apdev, test_params):
+ """FST setup using separate commands"""
+ try:
+ _test_fst_test_setup(dev, apdev, test_params)
+ finally:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def _test_fst_test_setup(dev, apdev, test_params):
+ group = "fstg0b"
+ sgroup = "fstg1b"
+ hglobal, wpas, wpas2, hapd, hapd2 = fst_start_and_connect(apdev, group, sgroup)
+
+ sid = wpas.global_request("FST-MANAGER SESSION_ADD " + sgroup).strip()
+ if "FAIL" in sid:
+ raise Exception("FST-MANAGER SESSION_ADD (STA) failed")
+
+ fst_session_set(wpas, sid, "old_ifname", wpas.ifname)
+ fst_session_set(wpas, sid, "old_peer_addr", apdev[0]['bssid'])
+ fst_session_set(wpas, sid, "new_ifname", wpas2.ifname)
+ fst_session_set(wpas, sid, "new_peer_addr", apdev[1]['bssid'])
+
+ if "OK" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("FST-MANAGER SESSION_INITIATE failed")
+
+ while True:
+ ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION (AP)")
+ if "new_state=SETUP_COMPLETION" in ev:
+ f = re.search("session_id=(\d+)", ev)
+ if f is None:
+ raise Exception("No session_id in FST-EVENT-SESSION")
+ sid_ap = f.group(1)
+ cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+ if "OK" not in hglobal.request(cmd):
+ raise Exception("FST-MANAGER SESSION_RESPOND failed on AP")
+ break
+
+ ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION")
+ if "new_state=SETUP_COMPLETION" not in ev:
+ raise Exception("Unexpected FST-EVENT-SESSION data: " + ev)
+
+ ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION")
+ if "event_type=EVENT_FST_ESTABLISHED" not in ev:
+ raise Exception("Unexpected FST-EVENT-SESSION data: " + ev)
+
+ cmd = "FST-MANAGER SESSION_REMOVE " + sid
+ if "OK" not in wpas.global_request(cmd):
+ raise Exception("FST-MANAGER SESSION_REMOVE failed")
+ ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION")
+ if "new_state=INITIAL" not in ev:
+ raise Exception("Unexpected FST-EVENT-SESSION data (STA): " + ev)
+
+ ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION (AP)")
+ if "new_state=INITIAL" not in ev:
+ raise Exception("Unexpected FST-EVENT-SESSION data (AP): " + ev)
+
+ if "FAIL" not in wpas.global_request(cmd):
+ raise Exception("Duplicated FST-MANAGER SESSION_REMOVE accepted")
+
+ hglobal.request("FST-MANAGER SESSION_REMOVE " + sid_ap)
+
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ fst_wait_event_peer_sta(wpas, "disconnected", wpas.ifname,
+ apdev[0]['bssid'])
+ fst_wait_event_peer_ap(hglobal, "disconnected", apdev[0]['ifname'],
+ wpas.own_addr())
+
+ wpas2.request("DISCONNECT")
+ wpas2.wait_disconnected()
+ fst_wait_event_peer_sta(wpas, "disconnected", wpas2.ifname,
+ apdev[1]['bssid'])
+ fst_wait_event_peer_ap(hglobal, "disconnected", apdev[1]['ifname'],
+ wpas2.own_addr())
+
+ fst_detach_ap(hglobal, apdev[0]['ifname'], group)
+ if "FAIL" not in hglobal.request("FST-DETACH " + apdev[0]['ifname']):
+ raise Exception("Duplicated FST-DETACH (AP) accepted")
+ hapd.disable()
+
+ fst_detach_ap(hglobal, apdev[1]['ifname'], group)
+ hapd2.disable()
+
+ fst_detach_sta(wpas, wpas.ifname, sgroup)
+ fst_detach_sta(wpas, wpas2.ifname, sgroup)
+
+def test_fst_setup_mbie_diff(dev, apdev, test_params):
+ """FST setup and different MBIE in FST Setup Request"""
+ try:
+ _test_fst_setup_mbie_diff(dev, apdev, test_params)
+ finally:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def _test_fst_setup_mbie_diff(dev, apdev, test_params):
+ group = "fstg0c"
+ sgroup = "fstg1c"
+ hglobal, wpas, wpas2, hapd, hapd2 = fst_start_and_connect(apdev, group, sgroup)
+
+ # FST Setup Request: Category, FST Action, Dialog Token (non-zero),
+ # LLT (32 bits, see 10.32), Session Transition (see 8.4.2.147),
+ # Multi-band element (optional, see 8.4.2.140)
+
+ # Session Transition: EID, Len, FSTS ID(4), Session Control,
+ # New Band (Band ID, Setup, Operation), Old Band (Band ID, Setup, Operation)
+
+ # Multi-band element: EID, Len, Multi-band Control, Band ID,
+ # Operating Class, Channel Number, BSSID (6), Beacon Interval (2),
+ # TSF Offset (8), Multi-band Connection Capability, FSTSessionTimeOut,
+ # STA MAC Address (6, optional), Pairwise Cipher Suite Count (2, optional),
+ # Pairwise Cipher Suite List (4xm, optional)
+
+ # MBIE with the non-matching STA MAC Address:
+ req = "1200011a060000"
+ stie = "a40b0100000000020001040001"
+ mbie = "9e1c0c0200010200000004000000000000000000000000ff0200000006ff"
+ fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie, mbie)
+
+ # MBIE without the STA MAC Address:
+ req = "1200011a060000"
+ stie = "a40b0100000000020001040001"
+ mbie = "9e16040200010200000004000000000000000000000000ff"
+ fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie, mbie)
+
+ # MBIE with unsupported STA Role:
+ req = "1200011a060000"
+ stie = "a40b0100000000020001040001"
+ mbie = "9e16070200010200000004000000000000000000000000ff"
+ fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie, mbie)
+
+ # MBIE with unsupported Band ID:
+ req = "1200011a060000"
+ stie = "a40b0100000000020001040001"
+ mbie = "9e1604ff00010200000004000000000000000000000000ff"
+ fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie, mbie)
+
+ # FST Setup Request without MBIE (different FSTS ID):
+ req = "1200011a060000"
+ stie = "a40b0200000000020001040001"
+ fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie)
+
+ # MBIE update OOM on AP
+ req = "1200011a060000"
+ stie = "a40b0100000000020001040001"
+ mbie = "9e16040200010200000004000000000000000000000000ff"
+ try:
+ with alloc_fail(hapd, 1, "mb_ies_by_info"):
+ fst_setup_req(wpas, hglobal, 5180, apdev[0]['bssid'], req, stie,
+ mbie, no_wait=True)
+ except HwsimSkip as e:
+ # Skip exception to allow proper cleanup
+ pass
+
+ # Remove sessions to avoid causing issues to following test ases
+ s = hglobal.request("FST-MANAGER LIST_SESSIONS " + group)
+ if not s.startswith("FAIL"):
+ for sid in s.split(' '):
+ if len(sid):
+ hglobal.request("FST-MANAGER SESSION_REMOVE " + sid)
+
+def test_fst_many_setup(dev, apdev, test_params):
+ """FST setup multiple times"""
+ try:
+ _test_fst_many_setup(dev, apdev, test_params)
+ finally:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def _test_fst_many_setup(dev, apdev, test_params):
+ group = "fstg0d"
+ sgroup = "fstg1d"
+ hglobal, wpas, wpas2, hapd, hapd2 = fst_start_and_connect(apdev, group, sgroup)
+
+ sid = wpas.global_request("FST-MANAGER SESSION_ADD " + sgroup).strip()
+ if "FAIL" in sid:
+ raise Exception("FST-MANAGER SESSION_ADD (STA) failed")
+
+ fst_session_set(wpas, sid, "old_ifname", wpas.ifname)
+ fst_session_set(wpas, sid, "old_peer_addr", apdev[0]['bssid'])
+ fst_session_set(wpas, sid, "new_ifname", wpas2.ifname)
+ fst_session_set(wpas, sid, "new_peer_addr", apdev[1]['bssid'])
+
+ for i in range(257):
+ if "OK" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("FST-MANAGER SESSION_INITIATE failed")
+
+ while True:
+ ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION (AP)")
+ if "new_state=SETUP_COMPLETION" in ev:
+ f = re.search("session_id=(\d+)", ev)
+ if f is None:
+ raise Exception("No session_id in FST-EVENT-SESSION")
+ sid_ap = f.group(1)
+ cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+ if "OK" not in hglobal.request(cmd):
+ raise Exception("FST-MANAGER SESSION_RESPOND failed on AP")
+ break
+
+ ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION (STA)")
+ if "new_state=SETUP_COMPLETION" not in ev:
+ raise Exception("Unexpected FST-EVENT-SESSION data: " + ev)
+
+ ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION (STA)")
+ if "event_type=EVENT_FST_ESTABLISHED" not in ev:
+ raise Exception("Unexpected FST-EVENT-SESSION data: " + ev)
+
+ if "OK" not in wpas.global_request("FST-MANAGER SESSION_TEARDOWN " + sid):
+ raise Exception("FST-MANAGER SESSION_INITIATE failed")
+
+ if i == 0:
+ if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_TEARDOWN " + sid):
+ raise Exception("Duplicate FST-MANAGER SESSION_TEARDOWN accepted")
+
+ ev = wpas.wait_global_event(["FST-EVENT-SESSION"], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION (STA teardown -->initial)")
+ if "new_state=INITIAL" not in ev:
+ raise Exception("Unexpected FST-EVENT-SESSION data (STA): " + ev)
+
+ ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION (AP teardown -->initial)")
+ if "new_state=INITIAL" not in ev:
+ raise Exception("Unexpected FST-EVENT-SESSION data (AP): " + ev)
+
+ if "OK" not in hglobal.request("FST-MANAGER SESSION_REMOVE " + sid_ap):
+ raise Exception("FST-MANAGER SESSION_REMOVE (AP) failed")
+
+ if "OK" not in wpas.global_request("FST-MANAGER SESSION_REMOVE " + sid):
+ raise Exception("FST-MANAGER SESSION_REMOVE failed")
+
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ fst_wait_event_peer_sta(wpas, "disconnected", wpas.ifname,
+ apdev[0]['bssid'])
+ fst_wait_event_peer_ap(hglobal, "disconnected", apdev[0]['ifname'],
+ wpas.own_addr())
+
+ wpas2.request("DISCONNECT")
+ wpas2.wait_disconnected()
+ fst_wait_event_peer_sta(wpas, "disconnected", wpas2.ifname,
+ apdev[1]['bssid'])
+ fst_wait_event_peer_ap(hglobal, "disconnected", apdev[1]['ifname'],
+ wpas2.own_addr())
+
+ fst_detach_ap(hglobal, apdev[0]['ifname'], group)
+ fst_detach_ap(hglobal, apdev[1]['ifname'], group)
+ hapd.disable()
+ hapd2.disable()
+
+ fst_detach_sta(wpas, wpas.ifname, sgroup)
+ fst_detach_sta(wpas, wpas2.ifname, sgroup)
+
+def test_fst_attach_wpas_error(dev, apdev, test_params):
+ """FST attach errors in wpa_supplicant"""
+ if "OK" not in dev[0].global_request("FST-MANAGER TEST_REQUEST IS_SUPPORTED"):
+ raise HwsimSkip("No FST testing support")
+ group = "fstg0"
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ fst_attach_sta(wpas, wpas.ifname, group)
+ if "FAIL" not in wpas.global_request("FST-ATTACH %s %s" % (wpas.ifname,
+ group)):
+ raise Exception("Duplicated FST-ATTACH accepted")
+ if "FAIL" not in wpas.global_request("FST-ATTACH %s %s" % ("foofoo",
+ group)):
+ raise Exception("FST-ATTACH for unknown interface accepted")
+
+def test_fst_session_initiate_errors(dev, apdev, test_params):
+ """FST SESSION_INITIATE error cases"""
+ try:
+ _test_fst_session_initiate_errors(dev, apdev, test_params)
+ finally:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def _test_fst_session_initiate_errors(dev, apdev, test_params):
+ group = "fstg0"
+ sgroup = "fstg1"
+ hglobal, wpas, wpas2, hapd, hapd2 = fst_start_and_connect(apdev, group, sgroup)
+
+ sid = wpas.global_request("FST-MANAGER SESSION_ADD " + sgroup).strip()
+ if "FAIL" in sid:
+ raise Exception("FST-MANAGER SESSION_ADD (STA) failed")
+
+ # No old peer MAC address
+ if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+ fst_session_set(wpas, sid, "old_peer_addr", "00:ff:ff:ff:ff:ff")
+ # No new peer MAC address
+ if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+ fst_session_set(wpas, sid, "new_peer_addr", "00:ff:ff:ff:ff:fe")
+ # No old interface defined
+ if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+ fst_session_set(wpas, sid, "old_ifname", wpas.ifname)
+ # No new interface defined
+ if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+ fst_session_set(wpas, sid, "new_ifname", wpas.ifname)
+ # Same interface set as old and new
+ if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+ fst_session_set(wpas, sid, "new_ifname", wpas2.ifname)
+ # The preset old peer address is not connected
+ if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+ fst_session_set(wpas, sid, "old_peer_addr", apdev[0]['bssid'])
+ # The preset new peer address is not connected
+ if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("Invalid FST-MANAGER SESSION_INITIATE accepted")
+
+ fst_session_set(wpas, sid, "new_peer_addr", apdev[1]['bssid'])
+ # Initiate session setup
+ if "OK" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("FST-MANAGER SESSION_INITIATE failed")
+
+ # Session in progress
+ if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("Duplicated FST-MANAGER SESSION_INITIATE accepted")
+
+ sid2 = wpas.global_request("FST-MANAGER SESSION_ADD " + sgroup).strip()
+ if "FAIL" in sid:
+ raise Exception("FST-MANAGER SESSION_ADD (STA) failed")
+ fst_session_set(wpas, sid2, "old_ifname", wpas.ifname)
+ fst_session_set(wpas, sid2, "old_peer_addr", apdev[0]['bssid'])
+ fst_session_set(wpas, sid2, "new_ifname", wpas2.ifname)
+ fst_session_set(wpas, sid2, "new_peer_addr", apdev[1]['bssid'])
+
+ # There is another session in progress (old)
+ if "FAIL" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid2):
+ raise Exception("Duplicated FST-MANAGER SESSION_INITIATE accepted")
+
+ if "OK" not in wpas.global_request("FST-MANAGER SESSION_REMOVE " + sid):
+ raise Exception("FST-MANAGER SESSION_REMOVE failed")
+
+ while True:
+ ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION (AP)")
+ if "new_state=SETUP_COMPLETION" in ev:
+ f = re.search("session_id=(\d+)", ev)
+ if f is None:
+ raise Exception("No session_id in FST-EVENT-SESSION")
+ sid_ap = f.group(1)
+ break
+ if "OK" not in hglobal.request("FST-MANAGER SESSION_REMOVE " + sid_ap):
+ raise Exception("FST-MANAGER SESSION_REMOVE (AP) failed")
+
+ if "OK" not in wpas.global_request("FST-MANAGER SESSION_REMOVE " + sid2):
+ raise Exception("FST-MANAGER SESSION_REMOVE failed")
+
+def test_fst_session_respond_errors(dev, apdev, test_params):
+ """FST SESSION_RESPOND error cases"""
+ try:
+ _test_fst_session_respond_errors(dev, apdev, test_params)
+ finally:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def _test_fst_session_respond_errors(dev, apdev, test_params):
+ group = "fstg0b"
+ sgroup = "fstg1b"
+ hglobal, wpas, wpas2, hapd, hapd2 = fst_start_and_connect(apdev, group, sgroup)
+
+ sid = wpas.global_request("FST-MANAGER SESSION_ADD " + sgroup).strip()
+ if "FAIL" in sid:
+ raise Exception("FST-MANAGER SESSION_ADD (STA) failed")
+
+ fst_session_set(wpas, sid, "old_ifname", wpas.ifname)
+ fst_session_set(wpas, sid, "old_peer_addr", apdev[0]['bssid'])
+ fst_session_set(wpas, sid, "new_ifname", wpas2.ifname)
+ fst_session_set(wpas, sid, "new_peer_addr", apdev[1]['bssid'])
+
+ if "OK" not in wpas.global_request("FST-MANAGER SESSION_INITIATE " + sid):
+ raise Exception("FST-MANAGER SESSION_INITIATE failed")
+
+ while True:
+ ev = hglobal.wait_event(['FST-EVENT-SESSION'], timeout=5)
+ if ev is None:
+ raise Exception("No FST-EVENT-SESSION (AP)")
+ if "new_state=SETUP_COMPLETION" in ev:
+ f = re.search("session_id=(\d+)", ev)
+ if f is None:
+ raise Exception("No session_id in FST-EVENT-SESSION")
+ sid_ap = f.group(1)
+ break
+
+ # The preset peer address is not in the peer list
+ fst_session_set_ap(hglobal, sid_ap, "old_peer_addr", "00:00:00:00:00:01")
+ cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+ if "FAIL" not in hglobal.request(cmd):
+ raise Exception("Invalid FST-MANAGER SESSION_RESPOND accepted")
+
+ # Same interface set as old and new
+ fst_session_set_ap(hglobal, sid_ap, "old_peer_addr", wpas.own_addr())
+ fst_session_set_ap(hglobal, sid_ap, "old_ifname", apdev[1]['ifname'])
+ cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+ if "FAIL" not in hglobal.request(cmd):
+ raise Exception("Invalid FST-MANAGER SESSION_RESPOND accepted")
+
+ # valid command
+ fst_session_set_ap(hglobal, sid_ap, "old_ifname", apdev[0]['ifname'])
+ cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+ if "OK" not in hglobal.request(cmd):
+ raise Exception("FST-MANAGER SESSION_RESPOND failed")
+
+ # incorrect state
+ cmd = "FST-MANAGER SESSION_RESPOND %s accept" % sid_ap
+ if "FAIL" not in hglobal.request(cmd):
+ raise Exception("Invalid FST-MANAGER SESSION_RESPOND accepted")
+
+ cmd = "FST-MANAGER SESSION_REMOVE " + sid
+ if "OK" not in wpas.global_request(cmd):
+ raise Exception("FST-MANAGER SESSION_REMOVE (STA) failed")
+
+ cmd = "FST-MANAGER SESSION_REMOVE %s" % sid_ap
+ if "OK" not in hglobal.request(cmd):
+ raise Exception("FST-MANAGER SESSION_REMOVE (AP) failed")
diff --git a/contrib/wpa/tests/hwsim/test_gas.py b/contrib/wpa/tests/hwsim/test_gas.py
new file mode 100644
index 000000000000..cb4a1a8d6656
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_gas.py
@@ -0,0 +1,2053 @@
+# GAS tests
+# Copyright (c) 2013, Qualcomm Atheros, Inc.
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import binascii
+import logging
+logger = logging.getLogger()
+import os
+import re
+import struct
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from tshark import run_tshark
+from utils import alloc_fail, wait_fail_trigger, skip_with_fips, HwsimSkip
+from hwsim import HWSimRadio
+
+def hs20_ap_params():
+ params = hostapd.wpa2_params(ssid="test-gas")
+ params['wpa_key_mgmt'] = "WPA-EAP"
+ params['ieee80211w'] = "1"
+ params['ieee8021x'] = "1"
+ params['auth_server_addr'] = "127.0.0.1"
+ params['auth_server_port'] = "1812"
+ params['auth_server_shared_secret'] = "radius"
+ params['interworking'] = "1"
+ params['access_network_type'] = "14"
+ params['internet'] = "1"
+ params['asra'] = "0"
+ params['esr'] = "0"
+ params['uesa'] = "0"
+ params['venue_group'] = "7"
+ params['venue_type'] = "1"
+ params['venue_name'] = ["eng:Example venue", "fin:Esimerkkipaikka"]
+ params['roaming_consortium'] = ["112233", "1020304050", "010203040506",
+ "fedcba"]
+ params['domain_name'] = "example.com,another.example.com"
+ params['nai_realm'] = ["0,example.com,13[5:6],21[2:4][5:7]",
+ "0,another.example.com"]
+ params['anqp_3gpp_cell_net'] = "244,91"
+ params['network_auth_type'] = "02http://www.example.com/redirect/me/here/"
+ params['ipaddr_type_availability'] = "14"
+ params['hs20'] = "1"
+ params['hs20_oper_friendly_name'] = ["eng:Example operator", "fin:Esimerkkioperaattori"]
+ params['hs20_wan_metrics'] = "01:8000:1000:80:240:3000"
+ params['hs20_conn_capab'] = ["1:0:2", "6:22:1", "17:5060:0"]
+ params['hs20_operating_class'] = "5173"
+ return params
+
+def start_ap(ap):
+ params = hs20_ap_params()
+ params['hessid'] = ap['bssid']
+ return hostapd.add_ap(ap, params)
+
+def get_gas_response(dev, bssid, info, allow_fetch_failure=False,
+ extra_test=False):
+ exp = r'<.>(GAS-RESPONSE-INFO) addr=([0-9a-f:]*) dialog_token=([0-9]*) status_code=([0-9]*) resp_len=([\-0-9]*)'
+ res = re.split(exp, info)
+ if len(res) < 6:
+ raise Exception("Could not parse GAS-RESPONSE-INFO")
+ if res[2] != bssid:
+ raise Exception("Unexpected BSSID in response")
+ token = res[3]
+ status = res[4]
+ if status != "0":
+ raise Exception("GAS query failed")
+ resp_len = res[5]
+ if resp_len == "-1":
+ raise Exception("GAS query reported invalid response length")
+ if int(resp_len) > 2000:
+ raise Exception("Unexpected long GAS response")
+
+ if extra_test:
+ if "FAIL" not in dev.request("GAS_RESPONSE_GET " + bssid + " 123456"):
+ raise Exception("Invalid dialog token accepted")
+ if "FAIL-Invalid range" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 10000,10001"):
+ raise Exception("Invalid range accepted")
+ if "FAIL-Invalid range" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 0,10000"):
+ raise Exception("Invalid range accepted")
+ if "FAIL" not in dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 0"):
+ raise Exception("Invalid GAS_RESPONSE_GET accepted")
+
+ res1_2 = dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 1,2")
+ res5_3 = dev.request("GAS_RESPONSE_GET " + bssid + " " + token + " 5,3")
+
+ resp = dev.request("GAS_RESPONSE_GET " + bssid + " " + token)
+ if "FAIL" in resp:
+ if allow_fetch_failure:
+ logger.debug("GAS response was not available anymore")
+ return
+ raise Exception("Could not fetch GAS response")
+ if len(resp) != int(resp_len) * 2:
+ raise Exception("Unexpected GAS response length")
+ logger.debug("GAS response: " + resp)
+ if extra_test:
+ if resp[2:6] != res1_2:
+ raise Exception("Unexpected response substring res1_2: " + res1_2)
+ if resp[10:16] != res5_3:
+ raise Exception("Unexpected response substring res5_3: " + res5_3)
+
+def test_gas_generic(dev, apdev):
+ """Generic GAS query"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ cmds = ["foo",
+ "00:11:22:33:44:55",
+ "00:11:22:33:44:55 ",
+ "00:11:22:33:44:55 ",
+ "00:11:22:33:44:55 1",
+ "00:11:22:33:44:55 1 1234",
+ "00:11:22:33:44:55 qq",
+ "00:11:22:33:44:55 qq 1234",
+ "00:11:22:33:44:55 00 1",
+ "00:11:22:33:44:55 00 123",
+ "00:11:22:33:44:55 00 ",
+ "00:11:22:33:44:55 00 qq"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("GAS_REQUEST " + cmd):
+ raise Exception("Invalid GAS_REQUEST accepted: " + cmd)
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+ ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+ get_gas_response(dev[0], bssid, ev, extra_test=True)
+
+ if "FAIL" not in dev[0].request("GAS_RESPONSE_GET ff"):
+ raise Exception("Invalid GAS_RESPONSE_GET accepted")
+
+def test_gas_rand_ta(dev, apdev, params):
+ """Generic GAS query with random TA"""
+ flags = int(dev[0].get_driver_status_field('capa.flags'), 16)
+ if flags & 0x0000400000000000 == 0:
+ raise HwsimSkip("Driver does not support random GAS TA")
+
+ try:
+ _test_gas_rand_ta(dev, apdev, params['logdir'])
+ finally:
+ dev[0].request("SET gas_rand_mac_addr 0")
+
+def _test_gas_rand_ta(dev, apdev, logdir):
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ req = dev[0].request("SET gas_rand_mac_addr 1")
+ if "FAIL" in req:
+ raise Exception("Failed to set gas_rand_mac_addr")
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+ ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+ get_gas_response(dev[0], bssid, ev, extra_test=True)
+
+ out = run_tshark(os.path.join(logdir, "hwsim0.pcapng"),
+ "wlan_mgt.fixed.category_code == 4 && (wlan_mgt.fixed.publicact == 0x0a || wlan_mgt.fixed.publicact == 0x0b)",
+ display=["wlan.ta", "wlan.ra"])
+ res = out.splitlines()
+ if len(res) != 2:
+ raise Exception("Unexpected number of GAS frames")
+ req_ta = res[0].split('\t')[0]
+ resp_ra = res[1].split('\t')[1]
+ logger.info("Request TA: %s, Response RA: %s" % (req_ta, resp_ra))
+ if req_ta != resp_ra:
+ raise Exception("Request TA does not match response RA")
+ if req_ta == dev[0].own_addr():
+ raise Exception("Request TA was own permanent MAC address, not random")
+
+def test_gas_concurrent_scan(dev, apdev):
+ """Generic GAS queries with concurrent scan operation"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ # get BSS entry available to allow GAS query
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+ logger.info("Request concurrent operations")
+ req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+ req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000801")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+ dev[0].scan(no_wait=True)
+ req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000201")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+ req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000501")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+
+ responses = 0
+ for i in range(0, 5):
+ ev = dev[0].wait_event(["GAS-RESPONSE-INFO", "CTRL-EVENT-SCAN-RESULTS"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Operation timed out")
+ if "GAS-RESPONSE-INFO" in ev:
+ responses = responses + 1
+ get_gas_response(dev[0], bssid, ev, allow_fetch_failure=True)
+
+ if responses != 4:
+ raise Exception("Unexpected number of GAS responses")
+
+def test_gas_concurrent_connect(dev, apdev):
+ """Generic GAS queries with concurrent connection operation"""
+ skip_with_fips(dev[0])
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+ logger.debug("Start concurrent connect and GAS request")
+ dev[0].connect("test-gas", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem", wait_connect=False,
+ scan_freq="2412")
+ req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "GAS-RESPONSE-INFO"],
+ timeout=20)
+ if ev is None:
+ raise Exception("Operation timed out")
+ if "CTRL-EVENT-CONNECTED" not in ev:
+ raise Exception("Unexpected operation order")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "GAS-RESPONSE-INFO"],
+ timeout=20)
+ if ev is None:
+ raise Exception("Operation timed out")
+ if "GAS-RESPONSE-INFO" not in ev:
+ raise Exception("Unexpected operation order")
+ get_gas_response(dev[0], bssid, ev)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=5)
+
+ logger.debug("Wait six seconds for expiration of connect-without-scan")
+ time.sleep(6)
+ dev[0].dump_monitor()
+
+ logger.debug("Start concurrent GAS request and connect")
+ req = dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+ dev[0].request("RECONNECT")
+
+ ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
+ if ev is None:
+ raise Exception("Operation timed out")
+ get_gas_response(dev[0], bssid, ev)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=20)
+ if ev is None:
+ raise Exception("No new scan results reported")
+
+ ev = dev[0].wait_connected(timeout=20, error="Operation tiemd out")
+ if "CTRL-EVENT-CONNECTED" not in ev:
+ raise Exception("Unexpected operation order")
+
+def gas_fragment_and_comeback(dev, apdev, frag_limit=0, comeback_delay=0):
+ hapd = start_ap(apdev)
+ if frag_limit:
+ hapd.set("gas_frag_limit", str(frag_limit))
+ if comeback_delay:
+ hapd.set("gas_comeback_delay", str(comeback_delay))
+
+ dev.scan_for_bss(apdev['bssid'], freq="2412", force_scan=True)
+ dev.request("FETCH_ANQP")
+ ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No GAS-QUERY-DONE event")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected GAS result: " + ev)
+ for i in range(0, 13):
+ ev = dev.wait_event(["RX-ANQP", "RX-HS20-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("Operation timed out")
+ ev = dev.wait_event(["ANQP-QUERY-DONE"], timeout=1)
+ if ev is None:
+ raise Exception("No ANQP-QUERY-DONE event")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected ANQP result: " + ev)
+
+def test_gas_fragment(dev, apdev):
+ """GAS fragmentation"""
+ gas_fragment_and_comeback(dev[0], apdev[0], frag_limit=50)
+
+def test_gas_fragment_mcc(dev, apdev):
+ """GAS fragmentation with mac80211_hwsim MCC enabled"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ gas_fragment_and_comeback(wpas, apdev[0], frag_limit=50)
+
+def test_gas_fragment_with_comeback_delay(dev, apdev):
+ """GAS fragmentation and comeback delay"""
+ gas_fragment_and_comeback(dev[0], apdev[0], frag_limit=50,
+ comeback_delay=500)
+
+def test_gas_fragment_with_comeback_delay_mcc(dev, apdev):
+ """GAS fragmentation and comeback delay with mac80211_hwsim MCC enabled"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ gas_fragment_and_comeback(wpas, apdev[0], frag_limit=50,
+ comeback_delay=500)
+
+def test_gas_comeback_delay(dev, apdev):
+ """GAS comeback delay"""
+ run_gas_comeback_delay(dev, apdev, 500)
+
+def test_gas_comeback_delay_long(dev, apdev):
+ """GAS long comeback delay"""
+ run_gas_comeback_delay(dev, apdev, 2500)
+
+def test_gas_comeback_delay_long2(dev, apdev):
+ """GAS long comeback delay over default STA timeout"""
+ run_gas_comeback_delay(dev, apdev, 6000)
+
+def run_gas_comeback_delay(dev, apdev, delay):
+ hapd = start_ap(apdev[0])
+ hapd.set("gas_comeback_delay", str(delay))
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[0].request("FETCH_ANQP")
+ if "FAIL-BUSY" not in dev[0].request("SCAN"):
+ raise Exception("SCAN accepted during FETCH_ANQP")
+ for i in range(0, 6):
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=10)
+ if ev is None:
+ raise Exception("Operation timed out")
+
+@remote_compatible
+def test_gas_stop_fetch_anqp(dev, apdev):
+ """Stop FETCH_ANQP operation"""
+ hapd = start_ap(apdev[0])
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("FETCH_ANQP")
+ dev[0].request("STOP_FETCH_ANQP")
+ hapd.set("ext_mgmt_frame_handling", "0")
+ ev = dev[0].wait_event(["RX-ANQP", "GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS-QUERY-DONE timed out")
+ if "RX-ANQP" in ev:
+ raise Exception("Unexpected ANQP response received")
+
+def test_gas_anqp_get(dev, apdev):
+ """GAS/ANQP query for both IEEE 802.11 and Hotspot 2.0 elements"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258,268,hs20:3,hs20:4"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+ if ev is None or "Venue Name" not in ev:
+ raise Exception("Did not receive Venue Name")
+
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+ if ev is None or "Domain Name list" not in ev:
+ raise Exception("Did not receive Domain Name list")
+
+ ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+ if ev is None or "Operator Friendly Name" not in ev:
+ raise Exception("Did not receive Operator Friendly Name")
+
+ ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+ if ev is None or "WAN Metrics" not in ev:
+ raise Exception("Did not receive WAN Metrics")
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE event not seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " hs20:3"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+ if ev is None or "Operator Friendly Name" not in ev:
+ raise Exception("Did not receive Operator Friendly Name")
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE event not seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ if "OK" not in dev[0].request("HS20_ANQP_GET " + bssid + " 3,4"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+ if ev is None or "Operator Friendly Name" not in ev:
+ raise Exception("Did not receive Operator Friendly Name")
+
+ ev = dev[0].wait_event(["RX-HS20-ANQP"], timeout=1)
+ if ev is None or "WAN Metrics" not in ev:
+ raise Exception("Did not receive WAN Metrics")
+
+ logger.info("Attempt an MBO request with an AP that does not support MBO")
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 272,mbo:2"):
+ raise Exception("ANQP_GET command failed (2)")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out (2)")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out (2)")
+
+ cmds = ["",
+ "foo",
+ "00:11:22:33:44:55 258,hs20:-1",
+ "00:11:22:33:44:55 258,hs20:0",
+ "00:11:22:33:44:55 258,hs20:32",
+ "00:11:22:33:44:55 hs20:-1",
+ "00:11:22:33:44:55 hs20:0",
+ "00:11:22:33:44:55 hs20:32",
+ "00:11:22:33:44:55 mbo:-1",
+ "00:11:22:33:44:55 mbo:0",
+ "00:11:22:33:44:55 mbo:999",
+ "00:11:22:33:44:55 mbo:1,258,mbo:2,mbo:3,259",
+ "00:11:22:33:44:55",
+ "00:11:22:33:44:55 ",
+ "00:11:22:33:44:55 0",
+ "00:11:22:33:44:55 1"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("ANQP_GET " + cmd):
+ raise Exception("Invalid ANQP_GET accepted")
+
+ cmds = ["",
+ "foo",
+ "00:11:22:33:44:55 -1",
+ "00:11:22:33:44:55 0",
+ "00:11:22:33:44:55 32",
+ "00:11:22:33:44:55",
+ "00:11:22:33:44:55 ",
+ "00:11:22:33:44:55 0",
+ "00:11:22:33:44:55 1"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("HS20_ANQP_GET " + cmd):
+ raise Exception("Invalid HS20_ANQP_GET accepted")
+
+def test_gas_anqp_get_no_scan(dev, apdev):
+ """GAS/ANQP query without scan"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " freq=2412 258"):
+ raise Exception("ANQP_GET command failed")
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP query timed out")
+ dev[0].dump_monitor()
+
+ if "OK" not in dev[0].request("ANQP_GET 02:11:22:33:44:55 freq=2417 258"):
+ raise Exception("ANQP_GET command failed")
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP query timed out [2]")
+ if "result=FAILURE" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+def test_gas_anqp_get_oom(dev, apdev):
+ """GAS/ANQP query OOM"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;anqp_send_req"):
+ if "FAIL" not in dev[0].request("ANQP_GET " + bssid + " 258,268,hs20:3,hs20:4"):
+ raise Exception("ANQP_GET command accepted during OOM")
+ with alloc_fail(dev[0], 1, "hs20_build_anqp_req;hs20_anqp_send_req"):
+ if "FAIL" not in dev[0].request("HS20_ANQP_GET " + bssid + " 1"):
+ raise Exception("HS20_ANQP_GET command accepted during OOM")
+ with alloc_fail(dev[0], 1, "gas_query_req;hs20_anqp_send_req"):
+ if "FAIL" not in dev[0].request("HS20_ANQP_GET " + bssid + " 1"):
+ raise Exception("HS20_ANQP_GET command accepted during OOM")
+ with alloc_fail(dev[0], 1, "=hs20_anqp_send_req"):
+ if "FAIL" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON command accepted during OOM")
+ with alloc_fail(dev[0], 2, "=hs20_anqp_send_req"):
+ if "FAIL" not in dev[0].request("REQ_HS20_ICON " + bssid + " w1fi_logo"):
+ raise Exception("REQ_HS20_ICON command accepted during OOM")
+
+def test_gas_anqp_icon_binary_proto(dev, apdev):
+ """GAS/ANQP and icon binary protocol testing"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ tests = ['010000', '01000000', '00000000', '00030000', '00020000',
+ '00000100', '0001ff0100ee', '0001ff0200ee']
+ for test in tests:
+ dev[0].request("HS20_ICON_REQUEST " + bssid + " w1fi_logo")
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+ resp = action_response(query)
+ data = binascii.unhexlify(test)
+ data = binascii.unhexlify('506f9a110b00') + data
+ data = struct.pack('<HHH', len(data) + 4, 0xdddd, len(data)) + data
+ resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + data
+ send_gas_resp(hapd, resp)
+ expect_gas_result(dev[0], "SUCCESS")
+
+def test_gas_anqp_hs20_proto(dev, apdev):
+ """GAS/ANQP and Hotspot 2.0 element protocol testing"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ tests = ['00', '0100', '0201', '0300', '0400', '0500', '0600', '0700',
+ '0800', '0900', '0a00', '0b0000000000']
+ for test in tests:
+ dev[0].request("HS20_ANQP_GET " + bssid + " 3,4")
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+ resp = action_response(query)
+ data = binascii.unhexlify(test)
+ data = binascii.unhexlify('506f9a11') + data
+ data = struct.pack('<HHH', len(data) + 4, 0xdddd, len(data)) + data
+ resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + data
+ send_gas_resp(hapd, resp)
+ expect_gas_result(dev[0], "SUCCESS")
+
+def expect_gas_result(dev, result, status=None):
+ ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+ if "result=" + result not in ev:
+ raise Exception("Unexpected GAS query result")
+ if status and "status_code=" + str(status) + ' ' not in ev:
+ raise Exception("Unexpected GAS status code")
+
+def anqp_get(dev, bssid, id):
+ if "OK" not in dev.request("ANQP_GET " + bssid + " " + str(id)):
+ raise Exception("ANQP_GET command failed")
+ ev = dev.wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+def test_gas_timeout(dev, apdev):
+ """GAS timeout"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ anqp_get(dev[0], bssid, 263)
+
+ ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT RX wait timed out")
+
+ expect_gas_result(dev[0], "TIMEOUT")
+
+MGMT_SUBTYPE_ACTION = 13
+ACTION_CATEG_PUBLIC = 4
+
+GAS_INITIAL_REQUEST = 10
+GAS_INITIAL_RESPONSE = 11
+GAS_COMEBACK_REQUEST = 12
+GAS_COMEBACK_RESPONSE = 13
+GAS_ACTIONS = [GAS_INITIAL_REQUEST, GAS_INITIAL_RESPONSE,
+ GAS_COMEBACK_REQUEST, GAS_COMEBACK_RESPONSE]
+
+def anqp_adv_proto():
+ return struct.pack('BBBB', 108, 2, 127, 0)
+
+def anqp_initial_resp(dialog_token, status_code, comeback_delay=0):
+ return struct.pack('<BBBHH', ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE,
+ dialog_token, status_code, comeback_delay) + anqp_adv_proto()
+
+def anqp_comeback_resp(dialog_token, status_code=0, id=0, more=False, comeback_delay=0, bogus_adv_proto=False):
+ if more:
+ id |= 0x80
+ if bogus_adv_proto:
+ adv = struct.pack('BBBB', 108, 2, 127, 1)
+ else:
+ adv = anqp_adv_proto()
+ return struct.pack('<BBBHBH', ACTION_CATEG_PUBLIC, GAS_COMEBACK_RESPONSE,
+ dialog_token, status_code, id, comeback_delay) + adv
+
+def gas_rx(hapd):
+ count = 0
+ while count < 30:
+ count = count + 1
+ query = hapd.mgmt_rx()
+ if query is None:
+ raise Exception("Action frame not received")
+ if query['subtype'] != MGMT_SUBTYPE_ACTION:
+ continue
+ payload = query['payload']
+ if len(payload) < 2:
+ continue
+ (category, action) = struct.unpack('BB', payload[0:2])
+ if category != ACTION_CATEG_PUBLIC or action not in GAS_ACTIONS:
+ continue
+ return query
+ raise Exception("No Action frame received")
+
+def parse_gas(payload):
+ pos = payload
+ (category, action, dialog_token) = struct.unpack('BBB', pos[0:3])
+ if category != ACTION_CATEG_PUBLIC:
+ return None
+ if action not in GAS_ACTIONS:
+ return None
+ gas = {}
+ gas['action'] = action
+ pos = pos[3:]
+
+ if len(pos) < 1 and action != GAS_COMEBACK_REQUEST:
+ return None
+
+ gas['dialog_token'] = dialog_token
+
+ if action == GAS_INITIAL_RESPONSE:
+ if len(pos) < 4:
+ return None
+ (status_code, comeback_delay) = struct.unpack('<HH', pos[0:4])
+ gas['status_code'] = status_code
+ gas['comeback_delay'] = comeback_delay
+
+ if action == GAS_COMEBACK_RESPONSE:
+ if len(pos) < 5:
+ return None
+ (status_code, frag, comeback_delay) = struct.unpack('<HBH', pos[0:5])
+ gas['status_code'] = status_code
+ gas['frag'] = frag
+ gas['comeback_delay'] = comeback_delay
+
+ return gas
+
+def action_response(req):
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ return resp
+
+def send_gas_resp(hapd, resp):
+ hapd.mgmt_tx(resp)
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Missing TX status for GAS response")
+ if "ok=1" not in ev:
+ raise Exception("GAS response not acknowledged")
+
+def test_gas_invalid_response_type(dev, apdev):
+ """GAS invalid response type"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ anqp_get(dev[0], bssid, 263)
+
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+
+ resp = action_response(query)
+ # GAS Comeback Response instead of GAS Initial Response
+ resp['payload'] = anqp_comeback_resp(gas['dialog_token']) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+
+ # station drops the invalid frame, so this needs to result in GAS timeout
+ expect_gas_result(dev[0], "TIMEOUT")
+
+def test_gas_failure_status_code(dev, apdev):
+ """GAS failure status code"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ anqp_get(dev[0], bssid, 263)
+
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+
+ resp = action_response(query)
+ resp['payload'] = anqp_initial_resp(gas['dialog_token'], 61) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+
+ expect_gas_result(dev[0], "FAILURE")
+
+def test_gas_malformed(dev, apdev):
+ """GAS malformed response frames"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ anqp_get(dev[0], bssid, 263)
+
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+
+ resp = action_response(query)
+
+ resp['payload'] = struct.pack('<BBBH', ACTION_CATEG_PUBLIC,
+ GAS_COMEBACK_RESPONSE,
+ gas['dialog_token'], 0)
+ hapd.mgmt_tx(resp)
+
+ resp['payload'] = struct.pack('<BBBHB', ACTION_CATEG_PUBLIC,
+ GAS_COMEBACK_RESPONSE,
+ gas['dialog_token'], 0, 0)
+ hapd.mgmt_tx(resp)
+
+ hdr = struct.pack('<BBBHH', ACTION_CATEG_PUBLIC, GAS_INITIAL_RESPONSE,
+ gas['dialog_token'], 0, 0)
+ resp['payload'] = hdr + struct.pack('B', 108)
+ hapd.mgmt_tx(resp)
+ resp['payload'] = hdr + struct.pack('BB', 108, 0)
+ hapd.mgmt_tx(resp)
+ resp['payload'] = hdr + struct.pack('BB', 108, 1)
+ hapd.mgmt_tx(resp)
+ resp['payload'] = hdr + struct.pack('BB', 108, 255)
+ hapd.mgmt_tx(resp)
+ resp['payload'] = hdr + struct.pack('BBB', 108, 1, 127)
+ hapd.mgmt_tx(resp)
+ resp['payload'] = hdr + struct.pack('BBB', 108, 2, 127)
+ hapd.mgmt_tx(resp)
+ resp['payload'] = hdr + struct.pack('BBBB', 0, 2, 127, 0)
+ hapd.mgmt_tx(resp)
+
+ resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<H', 1)
+ hapd.mgmt_tx(resp)
+
+ resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<HB', 2, 0)
+ hapd.mgmt_tx(resp)
+
+ resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<H', 65535)
+ hapd.mgmt_tx(resp)
+
+ resp['payload'] = anqp_initial_resp(gas['dialog_token'], 0) + struct.pack('<HBB', 1, 0, 0)
+ hapd.mgmt_tx(resp)
+
+ # Station drops invalid frames, but the last of the responses is valid from
+ # GAS view point even though it has an extra octet in the end and the ANQP
+ # part of the response is not valid. This is reported as successfully
+ # completed GAS exchange.
+ expect_gas_result(dev[0], "SUCCESS")
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE not reported")
+ if "result=INVALID_FRAME" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+def init_gas(hapd, bssid, dev):
+ anqp_get(dev, bssid, 263)
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+ dialog_token = gas['dialog_token']
+
+ resp = action_response(query)
+ resp['payload'] = anqp_initial_resp(dialog_token, 0, comeback_delay=1) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+ if gas['action'] != GAS_COMEBACK_REQUEST:
+ raise Exception("Unexpected request action")
+ if gas['dialog_token'] != dialog_token:
+ raise Exception("Unexpected dialog token change")
+ return query, dialog_token
+
+def allow_gas_initial_req(hapd, dialog_token):
+ msg = hapd.mgmt_rx(timeout=1)
+ if msg is not None:
+ gas = parse_gas(msg['payload'])
+ if gas['action'] != GAS_INITIAL_REQUEST or dialog_token == gas['dialog_token']:
+ raise Exception("Unexpected management frame")
+
+def test_gas_malformed_comeback_resp(dev, apdev):
+ """GAS malformed comeback response frames"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ logger.debug("Non-zero status code in comeback response")
+ query, dialog_token = init_gas(hapd, bssid, dev[0])
+ resp = action_response(query)
+ resp['payload'] = anqp_comeback_resp(dialog_token, status_code=2) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ expect_gas_result(dev[0], "FAILURE", status=2)
+
+ logger.debug("Different advertisement protocol in comeback response")
+ query, dialog_token = init_gas(hapd, bssid, dev[0])
+ resp = action_response(query)
+ resp['payload'] = anqp_comeback_resp(dialog_token, bogus_adv_proto=True) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ expect_gas_result(dev[0], "PEER_ERROR")
+
+ logger.debug("Non-zero frag id and comeback delay in comeback response")
+ query, dialog_token = init_gas(hapd, bssid, dev[0])
+ resp = action_response(query)
+ resp['payload'] = anqp_comeback_resp(dialog_token, id=1, comeback_delay=1) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ expect_gas_result(dev[0], "PEER_ERROR")
+
+ logger.debug("Unexpected frag id in comeback response")
+ query, dialog_token = init_gas(hapd, bssid, dev[0])
+ resp = action_response(query)
+ resp['payload'] = anqp_comeback_resp(dialog_token, id=1) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ expect_gas_result(dev[0], "PEER_ERROR")
+
+ logger.debug("Empty fragment and replay in comeback response")
+ query, dialog_token = init_gas(hapd, bssid, dev[0])
+ resp = action_response(query)
+ resp['payload'] = anqp_comeback_resp(dialog_token, more=True) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+ if gas['action'] != GAS_COMEBACK_REQUEST:
+ raise Exception("Unexpected request action")
+ if gas['dialog_token'] != dialog_token:
+ raise Exception("Unexpected dialog token change")
+ resp = action_response(query)
+ resp['payload'] = anqp_comeback_resp(dialog_token) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ resp['payload'] = anqp_comeback_resp(dialog_token, id=1) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ expect_gas_result(dev[0], "SUCCESS")
+
+ logger.debug("Unexpected initial response when waiting for comeback response")
+ query, dialog_token = init_gas(hapd, bssid, dev[0])
+ resp = action_response(query)
+ resp['payload'] = anqp_initial_resp(dialog_token, 0) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ allow_gas_initial_req(hapd, dialog_token)
+ expect_gas_result(dev[0], "TIMEOUT")
+
+ logger.debug("Too short comeback response")
+ query, dialog_token = init_gas(hapd, bssid, dev[0])
+ resp = action_response(query)
+ resp['payload'] = struct.pack('<BBBH', ACTION_CATEG_PUBLIC,
+ GAS_COMEBACK_RESPONSE, dialog_token, 0)
+ send_gas_resp(hapd, resp)
+ allow_gas_initial_req(hapd, dialog_token)
+ expect_gas_result(dev[0], "TIMEOUT")
+
+ logger.debug("Too short comeback response(2)")
+ query, dialog_token = init_gas(hapd, bssid, dev[0])
+ resp = action_response(query)
+ resp['payload'] = struct.pack('<BBBHBB', ACTION_CATEG_PUBLIC,
+ GAS_COMEBACK_RESPONSE, dialog_token, 0, 0x80,
+ 0)
+ send_gas_resp(hapd, resp)
+ allow_gas_initial_req(hapd, dialog_token)
+ expect_gas_result(dev[0], "TIMEOUT")
+
+ logger.debug("Maximum comeback response fragment claiming more fragments")
+ query, dialog_token = init_gas(hapd, bssid, dev[0])
+ resp = action_response(query)
+ resp['payload'] = anqp_comeback_resp(dialog_token, more=True) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ for i in range(1, 129):
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+ if gas['action'] != GAS_COMEBACK_REQUEST:
+ raise Exception("Unexpected request action")
+ if gas['dialog_token'] != dialog_token:
+ raise Exception("Unexpected dialog token change")
+ resp = action_response(query)
+ resp['payload'] = anqp_comeback_resp(dialog_token, id=i, more=True) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ expect_gas_result(dev[0], "PEER_ERROR")
+
+def test_gas_comeback_resp_additional_delay(dev, apdev):
+ """GAS comeback response requesting additional delay"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ query, dialog_token = init_gas(hapd, bssid, dev[0])
+ for i in range(0, 2):
+ resp = action_response(query)
+ resp['payload'] = anqp_comeback_resp(dialog_token, status_code=95, comeback_delay=50) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ query = gas_rx(hapd)
+ gas = parse_gas(query['payload'])
+ if gas['action'] != GAS_COMEBACK_REQUEST:
+ raise Exception("Unexpected request action")
+ if gas['dialog_token'] != dialog_token:
+ raise Exception("Unexpected dialog token change")
+ resp = action_response(query)
+ resp['payload'] = anqp_comeback_resp(dialog_token, status_code=0) + struct.pack('<H', 0)
+ send_gas_resp(hapd, resp)
+ expect_gas_result(dev[0], "SUCCESS")
+
+def test_gas_unknown_adv_proto(dev, apdev):
+ """Unknown advertisement protocol id"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ req = dev[0].request("GAS_REQUEST " + bssid + " 42 000102000101")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+ expect_gas_result(dev[0], "FAILURE", "59")
+ ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+ exp = r'<.>(GAS-RESPONSE-INFO) addr=([0-9a-f:]*) dialog_token=([0-9]*) status_code=([0-9]*) resp_len=([\-0-9]*)'
+ res = re.split(exp, ev)
+ if len(res) < 6:
+ raise Exception("Could not parse GAS-RESPONSE-INFO")
+ if res[2] != bssid:
+ raise Exception("Unexpected BSSID in response")
+ status = res[4]
+ if status != "59":
+ raise Exception("Unexpected GAS-RESPONSE-INFO status")
+
+def test_gas_request_oom(dev, apdev):
+ """GAS_REQUEST OOM"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+ with alloc_fail(dev[0], 1, "gas_build_req;gas_send_request"):
+ if "FAIL" not in dev[0].request("GAS_REQUEST " + bssid + " 42"):
+ raise Exception("GAS query request rejected")
+
+ with alloc_fail(dev[0], 1, "gas_query_req;gas_send_request"):
+ if "FAIL" not in dev[0].request("GAS_REQUEST " + bssid + " 42"):
+ raise Exception("GAS query request rejected")
+
+ with alloc_fail(dev[0], 1, "wpabuf_dup;gas_resp_cb"):
+ if "OK" not in dev[0].request("GAS_REQUEST " + bssid + " 00 000102000101"):
+ raise Exception("GAS query request rejected")
+ ev = dev[0].wait_event(["GAS-RESPONSE-INFO"], timeout=10)
+ if ev is None:
+ raise Exception("No GAS response")
+ if "status_code=0" not in ev:
+ raise Exception("GAS response indicated a failure")
+
+def test_gas_max_pending(dev, apdev):
+ """GAS and maximum pending query limit"""
+ hapd = start_ap(apdev[0])
+ hapd.set("gas_frag_limit", "50")
+ bssid = apdev[0]['bssid']
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ if "OK" not in wpas.request("P2P_SET listen_channel 1"):
+ raise Exception("Failed to set listen channel")
+ if "OK" not in wpas.p2p_listen():
+ raise Exception("Failed to start listen state")
+ if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+
+ anqp_query = struct.pack('<HHHHHHHHHH', 256, 16, 257, 258, 260, 261, 262, 263, 264, 268)
+ gas = struct.pack('<H', len(anqp_query)) + anqp_query
+
+ for dialog_token in range(1, 10):
+ msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST,
+ dialog_token) + anqp_adv_proto() + gas
+ req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg).decode())
+ if "OK" not in wpas.request(req):
+ raise Exception("Could not send management frame")
+ resp = wpas.mgmt_rx()
+ if resp is None:
+ raise Exception("MGMT-RX timeout")
+ if 'payload' not in resp:
+ raise Exception("Missing payload")
+ gresp = parse_gas(resp['payload'])
+ if gresp['dialog_token'] != dialog_token:
+ raise Exception("Dialog token mismatch")
+ status_code = gresp['status_code']
+ if dialog_token < 9 and status_code != 0:
+ raise Exception("Unexpected failure status code {} for dialog token {}".format(status_code, dialog_token))
+ if dialog_token > 8 and status_code == 0:
+ raise Exception("Unexpected success status code {} for dialog token {}".format(status_code, dialog_token))
+
+def test_gas_no_pending(dev, apdev):
+ """GAS and no pending query for comeback request"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ if "OK" not in wpas.request("P2P_SET listen_channel 1"):
+ raise Exception("Failed to set listen channel")
+ if "OK" not in wpas.p2p_listen():
+ raise Exception("Failed to start listen state")
+ if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+
+ msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_COMEBACK_REQUEST, 1)
+ req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg).decode())
+ if "OK" not in wpas.request(req):
+ raise Exception("Could not send management frame")
+ resp = wpas.mgmt_rx()
+ if resp is None:
+ raise Exception("MGMT-RX timeout")
+ if 'payload' not in resp:
+ raise Exception("Missing payload")
+ gresp = parse_gas(resp['payload'])
+ status_code = gresp['status_code']
+ if status_code != 60:
+ raise Exception("Unexpected status code {} (expected 60)".format(status_code))
+
+def test_gas_delete_at_deinit(dev, apdev):
+ """GAS query deleted at deinit"""
+ hapd = start_ap(apdev[0])
+ hapd.set("gas_comeback_delay", "1000")
+ bssid = apdev[0]['bssid']
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ wpas.request("ANQP_GET " + bssid + " 258")
+
+ wpas.global_request("INTERFACE_REMOVE " + wpas.ifname)
+ ev = wpas.wait_event(["GAS-QUERY-DONE"], timeout=2)
+ del wpas
+ if ev is None:
+ raise Exception("GAS-QUERY-DONE not seen")
+ if "result=DELETED_AT_DEINIT" not in ev:
+ raise Exception("Unexpected result code: " + ev)
+
+def test_gas_missing_payload(dev, apdev):
+ """No action code in the query frame"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+ cmd = "MGMT_TX {} {} freq=2412 action=040A".format(bssid, bssid)
+ if "FAIL" in dev[0].request(cmd):
+ raise Exception("Could not send test Action frame")
+ ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on MGMT-TX-STATUS")
+ if "result=SUCCESS" not in ev:
+ raise Exception("AP did not ack Action frame")
+
+ cmd = "MGMT_TX {} {} freq=2412 action=04".format(bssid, bssid)
+ if "FAIL" in dev[0].request(cmd):
+ raise Exception("Could not send test Action frame")
+ ev = dev[0].wait_event(["MGMT-TX-STATUS"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on MGMT-TX-STATUS")
+ if "result=SUCCESS" not in ev:
+ raise Exception("AP did not ack Action frame")
+
+def test_gas_query_deinit(dev, apdev):
+ """Pending GAS/ANQP query during deinit"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+
+ wpas.scan_for_bss(bssid, freq="2412", force_scan=True)
+ id = wpas.request("RADIO_WORK add block-work")
+ if "OK" not in wpas.request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ ev = wpas.wait_event(["GAS-QUERY-START", "EXT-RADIO-WORK-START"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start (2)")
+
+ # Remove the interface while the gas-query radio work is still pending and
+ # GAS query has not yet been started.
+ wpas.interface_remove("wlan5")
+
+@remote_compatible
+def test_gas_anqp_oom_wpas(dev, apdev):
+ """GAS/ANQP query and OOM in wpa_supplicant"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+ with alloc_fail(dev[0], 1, "wpa_bss_anqp_alloc"):
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP query did not complete")
+
+ with alloc_fail(dev[0], 1, "gas_build_req"):
+ if "FAIL" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("Unexpected ANQP_GET command success (OOM)")
+
+def test_gas_anqp_oom_hapd(dev, apdev):
+ """GAS/ANQP query and OOM in hostapd"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+ with alloc_fail(hapd, 1, "gas_build_resp"):
+ # This query will time out due to the AP not sending a response (OOM).
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+ if "result=TIMEOUT" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE event not seen")
+ if "result=FAILURE" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ with alloc_fail(hapd, 1, "gas_anqp_build_comeback_resp"):
+ hapd.set("gas_frag_limit", "50")
+
+ # The first attempt of this query will time out due to the AP not
+ # sending a response (OOM), but the retry succeeds.
+ dev[0].request("FETCH_ANQP")
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE event not seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+def test_gas_anqp_extra_elements(dev, apdev):
+ """GAS/ANQP and extra ANQP elements"""
+ geo_loc = "001052834d12efd2b08b9b4bf1cc2c00004104050000000000060100"
+ civic_loc = "0000f9555302f50102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5"
+ held_uri = "https://held.example.com/location"
+ held = struct.pack('BBB', 0, 1 + len(held_uri), 1) + held_uri.encode()
+ supl_fqdn = "supl.example.com"
+ supl = struct.pack('BBB', 0, 1 + len(supl_fqdn), 1) + supl_fqdn.encode()
+ public_id = binascii.hexlify(held + supl).decode()
+ params = {"ssid": "gas/anqp",
+ "interworking": "1",
+ "anqp_elem": ["265:" + geo_loc,
+ "266:" + civic_loc,
+ "262:1122334455",
+ "267:" + public_id,
+ "279:01020304",
+ "60000:01",
+ "299:0102"]}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 265,266"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ bss = dev[0].get_bss(bssid)
+
+ if 'anqp[265]' not in bss:
+ raise Exception("AP Geospatial Location ANQP-element not seen")
+ if bss['anqp[265]'] != geo_loc:
+ raise Exception("Unexpected AP Geospatial Location ANQP-element value: " + bss['anqp[265]'])
+
+ if 'anqp[266]' not in bss:
+ raise Exception("AP Civic Location ANQP-element not seen")
+ if bss['anqp[266]'] != civic_loc:
+ raise Exception("Unexpected AP Civic Location ANQP-element value: " + bss['anqp[266]'])
+
+ dev[1].scan_for_bss(bssid, freq="2412", force_scan=True)
+ if "OK" not in dev[1].request("ANQP_GET " + bssid + " 257,258,259,260,261,262,263,264,265,267,268,269,270,271,272,273,274,275,276,277,278,279,280,281,282,283,284,285,286,287,288,289,290,291,292,293,294,295,296,297,298,299"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[1].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ bss = dev[1].get_bss(bssid)
+
+ if 'anqp[265]' not in bss:
+ raise Exception("AP Geospatial Location ANQP-element not seen")
+ if bss['anqp[265]'] != geo_loc:
+ raise Exception("Unexpected AP Geospatial Location ANQP-element value: " + bss['anqp[265]'])
+
+ if 'anqp[266]' in bss:
+ raise Exception("AP Civic Location ANQP-element unexpectedly seen")
+
+ if 'anqp[267]' not in bss:
+ raise Exception("AP Location Public Identifier ANQP-element not seen")
+ if bss['anqp[267]'] != public_id:
+ raise Exception("Unexpected AP Location Public Identifier ANQP-element value: " + bss['anqp[267]'])
+
+ if 'anqp[279]' not in bss:
+ raise Exception("ANQP-element Info ID 279 not seen")
+ if bss['anqp[279]'] != "01020304":
+ raise Exception("Unexpected AP ANQP-element Info ID 279 value: " + bss['anqp[279]'])
+
+ if 'anqp[299]' not in bss:
+ raise Exception("ANQP-element Info ID 299 not seen")
+ if bss['anqp[299]'] != "0102":
+ raise Exception("Unexpected AP ANQP-element Info ID 299 value: " + bss['anqp[299]'])
+
+ if 'anqp_ip_addr_type_availability' not in bss:
+ raise Exception("ANQP-element Info ID 292 not seen")
+ if bss['anqp_ip_addr_type_availability'] != "1122334455":
+ raise Exception("Unexpected AP ANQP-element Info ID 262 value: " + bss['anqp_ip_addr_type_availability'])
+
+def test_gas_anqp_address3_not_assoc(dev, apdev, params):
+ """GAS/ANQP query using IEEE 802.11 compliant Address 3 value when not associated"""
+ try:
+ _test_gas_anqp_address3_not_assoc(dev, apdev, params)
+ finally:
+ dev[0].request("SET gas_address3 0")
+
+def _test_gas_anqp_address3_not_assoc(dev, apdev, params):
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ if "OK" not in dev[0].request("SET gas_address3 1"):
+ raise Exception("Failed to set gas_address3")
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+ if ev is None or "Venue Name" not in ev:
+ raise Exception("Did not receive Venue Name")
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE event not seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan_mgt.fixed.category_code == 4 && (wlan_mgt.fixed.publicact == 0x0a || wlan_mgt.fixed.publicact == 0x0b)",
+ display=["wlan.bssid"])
+ res = out.splitlines()
+ if len(res) != 2:
+ raise Exception("Unexpected number of GAS frames")
+ if res[0] != 'ff:ff:ff:ff:ff:ff':
+ raise Exception("GAS request used unexpected Address3 field value: " + res[0])
+ if res[1] != 'ff:ff:ff:ff:ff:ff':
+ raise Exception("GAS response used unexpected Address3 field value: " + res[1])
+
+def test_gas_anqp_address3_assoc(dev, apdev, params):
+ """GAS/ANQP query using IEEE 802.11 compliant Address 3 value when associated"""
+ try:
+ _test_gas_anqp_address3_assoc(dev, apdev, params)
+ finally:
+ dev[0].request("SET gas_address3 0")
+
+def _test_gas_anqp_address3_assoc(dev, apdev, params):
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ if "OK" not in dev[0].request("SET gas_address3 1"):
+ raise Exception("Failed to set gas_address3")
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("test-gas", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem", scan_freq="2412")
+
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+ if ev is None or "Venue Name" not in ev:
+ raise Exception("Did not receive Venue Name")
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE event not seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan_mgt.fixed.category_code == 4 && (wlan_mgt.fixed.publicact == 0x0a || wlan_mgt.fixed.publicact == 0x0b)",
+ display=["wlan.bssid"])
+ res = out.splitlines()
+ if len(res) != 2:
+ raise Exception("Unexpected number of GAS frames")
+ if res[0] != bssid:
+ raise Exception("GAS request used unexpected Address3 field value: " + res[0])
+ if res[1] != bssid:
+ raise Exception("GAS response used unexpected Address3 field value: " + res[1])
+
+def test_gas_anqp_address3_ap_forced(dev, apdev, params):
+ """GAS/ANQP query using IEEE 802.11 compliant Address 3 value on AP"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+ hapd.set("gas_address3", "1")
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+ if ev is None or "Venue Name" not in ev:
+ raise Exception("Did not receive Venue Name")
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE event not seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan_mgt.fixed.category_code == 4 && (wlan_mgt.fixed.publicact == 0x0a || wlan_mgt.fixed.publicact == 0x0b)",
+ display=["wlan.bssid"])
+ res = out.splitlines()
+ if len(res) != 2:
+ raise Exception("Unexpected number of GAS frames")
+ if res[0] != bssid:
+ raise Exception("GAS request used unexpected Address3 field value: " + res[0])
+ if res[1] != 'ff:ff:ff:ff:ff:ff':
+ raise Exception("GAS response used unexpected Address3 field value: " + res[1])
+
+def test_gas_anqp_address3_ap_non_compliant(dev, apdev, params):
+ """GAS/ANQP query using IEEE 802.11 non-compliant Address 3 (AP)"""
+ try:
+ _test_gas_anqp_address3_ap_non_compliant(dev, apdev, params)
+ finally:
+ dev[0].request("SET gas_address3 0")
+
+def _test_gas_anqp_address3_ap_non_compliant(dev, apdev, params):
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+ hapd.set("gas_address3", "2")
+
+ if "OK" not in dev[0].request("SET gas_address3 1"):
+ raise Exception("Failed to set gas_address3")
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+ if ev is None or "Venue Name" not in ev:
+ raise Exception("Did not receive Venue Name")
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE event not seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan_mgt.fixed.category_code == 4 && (wlan_mgt.fixed.publicact == 0x0a || wlan_mgt.fixed.publicact == 0x0b)",
+ display=["wlan.bssid"])
+ res = out.splitlines()
+ if len(res) != 2:
+ raise Exception("Unexpected number of GAS frames")
+ if res[0] != 'ff:ff:ff:ff:ff:ff':
+ raise Exception("GAS request used unexpected Address3 field value: " + res[0])
+ if res[1] != bssid:
+ raise Exception("GAS response used unexpected Address3 field value: " + res[1])
+
+def test_gas_anqp_address3_pmf(dev, apdev):
+ """GAS/ANQP query using IEEE 802.11 compliant Address 3 value with PMF"""
+ try:
+ _test_gas_anqp_address3_pmf(dev, apdev)
+ finally:
+ dev[0].request("SET gas_address3 0")
+
+def _test_gas_anqp_address3_pmf(dev, apdev):
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+ hapd.set("gas_comeback_delay", "2")
+ hapd.set("gas_address3", "1")
+
+ if "OK" not in dev[0].request("SET gas_address3 1"):
+ raise Exception("Failed to set gas_address3")
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("test-gas", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem", scan_freq="2412",
+ ieee80211w="2")
+
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=1)
+ if ev is None or "Venue Name" not in ev:
+ raise Exception("Did not receive Venue Name")
+
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("ANQP-QUERY-DONE event not seen")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected result: " + ev)
+
+ req = dev[0].request("GAS_REQUEST " + bssid + " 42 000102000101")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+ expect_gas_result(dev[0], "FAILURE", "59")
+
+def test_gas_prot_vs_not_prot(dev, apdev, params):
+ """GAS/ANQP query protected vs. not protected"""
+ hapd = start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("test-gas", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="DOMAIN\mschapv2 user", anonymous_identity="ttls",
+ password="password", phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem", scan_freq="2412",
+ ieee80211w="2")
+
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No GAS-QUERY-DONE event")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected GAS result: " + ev)
+
+ # GAS: Drop unexpected unprotected GAS frame when PMF is enabled
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ res = dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=d0003a010200000000000200000003000200000003001000040b00000005006c027f000000")
+ dev[0].request("SET ext_mgmt_frame_handling 0")
+ if "OK" not in res:
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # GAS: No pending query found for 02:00:00:00:03:00 dialog token 0
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ res = dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=d0003a010200000000000200000003000200000003001000040b00000005006c027f000000")
+ dev[0].request("SET ext_mgmt_frame_handling 0")
+ if "OK" not in res:
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ # GAS: Drop unexpected protected GAS frame when PMF is disabled
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ res = dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=d0003a010200000000000200000003000200000003001000090b00000005006c027f000000")
+ dev[0].request("SET ext_mgmt_frame_handling 0")
+ if "OK" not in res:
+ raise Exception("MGMT_RX_PROCESS failed")
+
+def test_gas_failures(dev, apdev):
+ """GAS failure cases"""
+ hapd = start_ap(apdev[0])
+ hapd.set("gas_comeback_delay", "5")
+ bssid = apdev[0]['bssid']
+
+ hapd2 = start_ap(apdev[1])
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ dev[0].scan_for_bss(bssid2, freq="2412")
+
+ tests = [(bssid, "gas_build_req;gas_query_tx_comeback_req"),
+ (bssid, "gas_query_tx;gas_query_tx_comeback_req"),
+ (bssid, "gas_query_append;gas_query_rx_comeback"),
+ (bssid2, "gas_query_append;gas_query_rx_initial"),
+ (bssid2, "wpabuf_alloc_copy;gas_query_rx_initial"),
+ (bssid, "gas_query_tx;gas_query_tx_initial_req")]
+ for addr, func in tests:
+ with alloc_fail(dev[0], 1, func):
+ dev[0].request("ANQP_GET " + addr + " 258")
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No GAS-QUERY-DONE seen")
+ if "result=INTERNAL_ERROR" not in ev:
+ raise Exception("Unexpected result code: " + ev)
+ dev[0].dump_monitor()
+
+ tests = ["=gas_query_req", "radio_add_work;gas_query_req"]
+ for func in tests:
+ with alloc_fail(dev[0], 1, func):
+ if "FAIL" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET succeeded unexpectedly during OOM")
+ dev[0].dump_monitor()
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.scan_for_bss(bssid2, freq="2412")
+ wpas.request("SET preassoc_mac_addr 1111")
+ wpas.request("ANQP_GET " + bssid2 + " 258")
+ ev = wpas.wait_event(["Failed to assign random MAC address for GAS"],
+ timeout=5)
+ wpas.request("SET preassoc_mac_addr 0")
+ if ev is None:
+ raise Exception("No random MAC address error seen")
+
+def test_gas_anqp_venue_url(dev, apdev):
+ """GAS/ANQP and Venue URL"""
+ venue_group = 1
+ venue_type = 13
+ venue_info = struct.pack('BB', venue_group, venue_type)
+ lang1 = "eng"
+ name1 = "Example venue"
+ lang2 = "fin"
+ name2 = "Esimerkkipaikka"
+ venue1 = struct.pack('B', len(lang1 + name1)) + lang1.encode() + name1.encode()
+ venue2 = struct.pack('B', len(lang2 + name2)) + lang2.encode() + name2.encode()
+ venue_name = binascii.hexlify(venue_info + venue1 + venue2).decode()
+
+ url1 = b"http://example.com/venue"
+ url2 = b"https://example.org/venue-info/"
+ duple1 = struct.pack('BB', 1 + len(url1), 1) + url1
+ duple2 = struct.pack('BB', 1 + len(url2), 2) + url2
+ venue_url = binascii.hexlify(duple1 + duple2).decode()
+
+ params = {"ssid": "gas/anqp",
+ "interworking": "1",
+ "venue_group": str(venue_group),
+ "venue_type": str(venue_type),
+ "venue_name": [lang1 + ":" + name1, lang2 + ":" + name2],
+ "anqp_elem": ["277:" + venue_url]}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 257,258,277"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-VENUE-URL"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected Venue URL indication without PMF")
+
+ bss = dev[0].get_bss(bssid)
+
+ if 'anqp_venue_name' not in bss:
+ raise Exception("Venue Name ANQP-element not seen")
+ if bss['anqp_venue_name'] != venue_name:
+ raise Exception("Unexpected Venue Name ANQP-element value: " + bss['anqp_venue_name'])
+ if 'anqp[277]' not in bss:
+ raise Exception("Venue URL ANQP-element not seen")
+ if bss['anqp[277]'] != venue_url:
+ raise Exception("Unexpected Venue URL ANQP-element value: " + bss['anqp[277]'])
+
+ if 'anqp_capability_list' not in bss:
+ raise Exception("Capability List ANQP-element not seen")
+ ids = struct.pack('<HHH', 257, 258, 277)
+ if not bss['anqp_capability_list'].startswith(binascii.hexlify(ids).decode()):
+ raise Exception("Unexpected Capability List ANQP-element value: " + bss['anqp_capability_list'])
+
+ if "anqp[277]" not in bss:
+ raise Exception("Venue-URL ANQP info not available")
+ if "protected-anqp-info[277]" in bss:
+ raise Exception("Unexpected Venue-URL protection info")
+
+def test_gas_anqp_venue_url2(dev, apdev):
+ """GAS/ANQP and Venue URL (hostapd venue_url)"""
+ venue_group = 1
+ venue_type = 13
+ venue_info = struct.pack('BB', venue_group, venue_type)
+ lang1 = "eng"
+ name1 = "Example venue"
+ lang2 = "fin"
+ name2 = "Esimerkkipaikka"
+ venue1 = struct.pack('B', len(lang1 + name1)) + lang1.encode() + name1.encode()
+ venue2 = struct.pack('B', len(lang2 + name2)) + lang2.encode() + name2.encode()
+ venue_name = binascii.hexlify(venue_info + venue1 + venue2).decode()
+
+ url1 = "http://example.com/venue"
+ url2 = "https://example.org/venue-info/"
+ duple1 = struct.pack('BB', 1 + len(url1.encode()), 1) + url1.encode()
+ duple2 = struct.pack('BB', 1 + len(url2.encode()), 2) + url2.encode()
+ venue_url = binascii.hexlify(duple1 + duple2).decode()
+
+ params = {"ssid": "gas/anqp",
+ "interworking": "1",
+ "venue_group": str(venue_group),
+ "venue_type": str(venue_type),
+ "venue_name": [lang1 + ":" + name1, lang2 + ":" + name2],
+ "venue_url": ["1:" + url1, "2:" + url2]}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 257,258,277"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ bss = dev[0].get_bss(bssid)
+
+ if 'anqp_venue_name' not in bss:
+ raise Exception("Venue Name ANQP-element not seen")
+ if bss['anqp_venue_name'] != venue_name:
+ raise Exception("Unexpected Venue Name ANQP-element value: " + bss['anqp_venue_name'])
+ if 'anqp[277]' not in bss:
+ raise Exception("Venue URL ANQP-element not seen")
+ if bss['anqp[277]'] != venue_url:
+ print(venue_url)
+ raise Exception("Unexpected Venue URL ANQP-element value: " + bss['anqp[277]'])
+
+ if 'anqp_capability_list' not in bss:
+ raise Exception("Capability List ANQP-element not seen")
+ ids = struct.pack('<HHH', 257, 258, 277)
+ if not bss['anqp_capability_list'].startswith(binascii.hexlify(ids).decode()):
+ raise Exception("Unexpected Capability List ANQP-element value: " + bss['anqp_capability_list'])
+
+def test_gas_anqp_venue_url_pmf(dev, apdev):
+ """GAS/ANQP and Venue URL with PMF"""
+ venue_group = 1
+ venue_type = 13
+ venue_info = struct.pack('BB', venue_group, venue_type)
+ lang1 = "eng"
+ name1 = "Example venue"
+ lang2 = "fin"
+ name2 = "Esimerkkipaikka"
+ venue1 = struct.pack('B', len(lang1 + name1)) + lang1.encode() + name1.encode()
+ venue2 = struct.pack('B', len(lang2 + name2)) + lang2.encode() + name2.encode()
+ venue_name = binascii.hexlify(venue_info + venue1 + venue2)
+
+ url1 = "http://example.com/venue"
+ url2 = "https://example.org/venue-info/"
+
+ params = {"ssid": "gas/anqp/pmf",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP",
+ "wpa_passphrase": "12345678",
+ "ieee80211w": "2",
+ "interworking": "1",
+ "venue_group": str(venue_group),
+ "venue_type": str(venue_type),
+ "venue_name": [lang1 + ":" + name1, lang2 + ":" + name2],
+ "venue_url": ["1:" + url1, "2:" + url2]}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("gas/anqp/pmf", psk="12345678", ieee80211w="2",
+ scan_freq="2412")
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 277"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ ev = dev[0].wait_event(["RX-VENUE-URL"], timeout=5)
+ if ev is None:
+ raise Exception("No Venue URL indication seen")
+ if "1 " + url1 not in ev:
+ raise Exception("Unexpected Venue URL information: " + ev)
+
+ ev = dev[0].wait_event(["RX-VENUE-URL"], timeout=5)
+ if ev is None:
+ raise Exception("No Venue URL indication seen (2)")
+ if "2 " + url2 not in ev:
+ raise Exception("Unexpected Venue URL information (2): " + ev)
+
+ bss = dev[0].get_bss(bssid)
+ if "anqp[277]" not in bss:
+ raise Exception("Venue-URL ANQP info not available")
+ if "protected-anqp-info[277]" not in bss:
+ raise Exception("Venue-URL protection info not available")
+ if bss["protected-anqp-info[277]"] != "1":
+ raise Exception("Venue-URL was not indicated to be protected")
+
+def test_gas_anqp_capab_list(dev, apdev):
+ """GAS/ANQP and Capability List ANQP-element"""
+ params = {"ssid": "gas/anqp",
+ "interworking": "1"}
+ params["anqp_elem"] = []
+ for i in range(0, 400):
+ if i not in [257]:
+ params["anqp_elem"] += ["%d:010203" % i]
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 257"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ bss = dev[0].get_bss(bssid)
+
+ if 'anqp_capability_list' not in bss:
+ raise Exception("Capability List ANQP-element not seen")
+ val = bss['anqp_capability_list']
+ logger.info("anqp_capability_list: " + val)
+ ids = []
+ while len(val) >= 4:
+ id_bin = binascii.unhexlify(val[0:4])
+ id = struct.unpack('<H', id_bin)[0]
+ if id == 0xdddd:
+ break
+ ids.append(id)
+ val = val[4:]
+ logger.info("InfoIDs: " + str(ids))
+ for i in range(257, 300):
+ if i in [273, 274]:
+ continue
+ if i not in ids:
+ raise Exception("Unexpected Capability List ANQP-element value (missing %d): %s" % (i, bss['anqp_capability_list']))
+
+def test_gas_server_oom(dev, apdev):
+ """GAS server OOM"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['gas_comeback_delay'] = "5"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+
+ tests = ["ap_sta_add;gas_dialog_create",
+ "=gas_dialog_create",
+ "wpabuf_alloc_copy;gas_serv_rx_gas_comeback_req"]
+ for t in tests:
+ with alloc_fail(hapd, 1, t):
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No GAS-QUERY-DONE seen")
+ dev[0].dump_monitor()
+
+ hapd.set("gas_comeback_delay", "0")
+
+ tests = ["gas_serv_build_gas_resp_payload"]
+ for t in tests:
+ with alloc_fail(hapd, 1, t):
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 258"):
+ raise Exception("ANQP_GET command failed")
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No GAS-QUERY-DONE seen")
+ dev[0].dump_monitor()
+
+ with alloc_fail(hapd, 1,
+ "gas_build_initial_resp;gas_serv_rx_gas_initial_req"):
+ req = dev[0].request("GAS_REQUEST " + bssid + " 42 000102000101")
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("No GAS-QUERY-DONE seen")
+ dev[0].dump_monitor()
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ if "OK" not in wpas.request("P2P_SET listen_channel 1"):
+ raise Exception("Failed to set listen channel")
+ if "OK" not in wpas.p2p_listen():
+ raise Exception("Failed to start listen state")
+ if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+
+ msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_COMEBACK_REQUEST, 1)
+ req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg).decode())
+ with alloc_fail(hapd, 1,
+ "gas_anqp_build_comeback_resp_buf;gas_serv_rx_gas_comeback_req"):
+ if "OK" not in wpas.request(req):
+ raise Exception("Could not send management frame")
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+
+def test_gas_anqp_overrides(dev, apdev):
+ """GAS and ANQP overrides"""
+ params = {"ssid": "gas/anqp",
+ "interworking": "1",
+ "anqp_elem": ["257:111111",
+ "258:222222",
+ "260:333333",
+ "261:444444",
+ "262:555555",
+ "263:666666",
+ "264:777777",
+ "268:888888",
+ "275:999999"]}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ if "OK" not in dev[0].request("ANQP_GET " + bssid + " 257,258,260,261,262,263,264,268,275"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+ elems = 9
+ capa = dev[0].get_capability("fils")
+ if capa is None or "FILS" not in capa:
+ # FILS Realm Info not supported in the build
+ elems -= 1
+ for i in range(elems):
+ ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
+ if ev is None:
+ raise Exception("ANQP response not seen")
+
+def test_gas_no_dialog_token_match(dev, apdev):
+ """GAS and no dialog token match for comeback request"""
+ hapd = start_ap(apdev[0])
+ hapd.set("gas_frag_limit", "50")
+ bssid = apdev[0]['bssid']
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ if "OK" not in wpas.request("P2P_SET listen_channel 1"):
+ raise Exception("Failed to set listen channel")
+ if "OK" not in wpas.p2p_listen():
+ raise Exception("Failed to start listen state")
+ if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+
+ anqp_query = struct.pack('<HHHHHHHHHH', 256, 16, 257, 258, 260, 261, 262, 263, 264, 268)
+ gas = struct.pack('<H', len(anqp_query)) + anqp_query
+
+ dialog_token = 100
+ msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST,
+ dialog_token) + anqp_adv_proto() + gas
+ req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg).decode())
+ if "OK" not in wpas.request(req):
+ raise Exception("Could not send management frame")
+ resp = wpas.mgmt_rx()
+ if resp is None:
+ raise Exception("MGMT-RX timeout")
+ if 'payload' not in resp:
+ raise Exception("Missing payload")
+ gresp = parse_gas(resp['payload'])
+ if gresp['dialog_token'] != dialog_token:
+ raise Exception("Dialog token mismatch")
+ status_code = gresp['status_code']
+ if status_code != 0:
+ raise Exception("Unexpected status code {}".format(status_code))
+
+ msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_COMEBACK_REQUEST,
+ dialog_token + 1)
+ req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(msg).decode())
+ if "OK" not in wpas.request(req):
+ raise Exception("Could not send management frame")
+ resp = wpas.mgmt_rx()
+ if resp is None:
+ raise Exception("MGMT-RX timeout")
+ if 'payload' not in resp:
+ raise Exception("Missing payload")
+ gresp = parse_gas(resp['payload'])
+ status_code = gresp['status_code']
+ if status_code != 60:
+ raise Exception("Unexpected failure status code {}".format(status_code))
+
+def test_gas_vendor_spec_errors(dev, apdev):
+ """GAS and vendor specific request error cases"""
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['osu_server_uri'] = "uri"
+ params['hs20_icon'] = "32:32:eng:image/png:icon32:/tmp/icon32.png"
+ del params['nai_realm']
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ tests = ["00 12340000",
+ "00 dddd0300506fff",
+ "00 dddd0400506fffff",
+ "00 dddd0400506f9aff",
+ "00 dddd0400506f9a11",
+ "00 dddd0600506f9a11ff00",
+ "00 dddd0600506f9a110600",
+ "00 dddd0600506f9a110600",
+ "00 dddd0700506f9a11060000",
+ "00 dddd0700506f9a110600ff",
+ "00 dddd0800506f9a110600ff00",
+ "00 dddd0900506f9a110600ff0000",
+ "00 dddd0900506f9a110600ff0001",
+ "00 dddd0900506f9a110600ffff00",
+ "00 dddd0a00506f9a110600ff00013b",
+ "00 dddd0700506f9a110100ff",
+ "00 dddd0700506f9a11010008",
+ "00 dddd14",
+ "00 dddd1400506f9a11"]
+ for t in tests:
+ req = dev[0].request("GAS_REQUEST " + bssid + " " + t)
+ if "FAIL" in req:
+ raise Exception("GAS query request rejected")
+ ev = dev[0].wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query did not start")
+ ev = dev[0].wait_event(["GAS-QUERY-DONE"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query did not complete")
+ if t == "00 dddd0600506f9a110600":
+ hapd.set("nai_realm", "0,another.example.com")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ if "OK" not in wpas.request("P2P_SET listen_channel 1"):
+ raise Exception("Failed to set listen channel")
+ if "OK" not in wpas.p2p_listen():
+ raise Exception("Failed to start listen state")
+ if "FAIL" in wpas.request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+
+ anqp_query = struct.pack('<HHHHHHHHHH', 256, 16, 257, 258, 260, 261, 262, 263, 264, 268)
+ gas = struct.pack('<H', len(anqp_query)) + anqp_query
+
+ dialog_token = 100
+ adv = struct.pack('BBBB', 109, 2, 0, 0)
+ adv2 = struct.pack('BBB', 108, 1, 0)
+ adv3 = struct.pack('BBBB', 108, 3, 0, 0)
+ msg = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST,
+ dialog_token) + adv + gas
+ msg2 = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST,
+ dialog_token) + adv2 + gas
+ msg3 = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST,
+ dialog_token) + adv3
+ msg4 = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST,
+ dialog_token) + anqp_adv_proto()
+ msg5 = struct.pack('<BBB', ACTION_CATEG_PUBLIC, GAS_INITIAL_REQUEST,
+ dialog_token) + anqp_adv_proto() + struct.pack('<H', 1)
+ msg6 = struct.pack('<BB', ACTION_CATEG_PUBLIC, GAS_COMEBACK_REQUEST)
+ tests = [msg, msg2, msg3, msg4, msg5, msg6]
+ for t in tests:
+ req = "MGMT_TX {} {} freq=2412 wait_time=10 action={}".format(bssid, bssid, binascii.hexlify(t).decode())
+ if "OK" not in wpas.request(req):
+ raise Exception("Could not send management frame")
+ ev = wpas.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("No ACK frame seen")
diff --git a/contrib/wpa/tests/hwsim/test_hapd_ctrl.py b/contrib/wpa/tests/hwsim/test_hapd_ctrl.py
new file mode 100644
index 000000000000..93d3d177eeee
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_hapd_ctrl.py
@@ -0,0 +1,1071 @@
+# hostapd control interface
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os
+from remotehost import remote_compatible
+import hostapd
+import hwsim_utils
+from utils import *
+
+@remote_compatible
+def test_hapd_ctrl_status(dev, apdev):
+ """hostapd ctrl_iface STATUS commands"""
+ ssid = "hapd-ctrl"
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+ status = hapd.get_status()
+ logger.info("STATUS: " + str(status))
+ driver = hapd.get_driver_status()
+ logger.info("STATUS-DRIVER: " + str(driver))
+
+ if status['bss[0]'] != apdev[0]['ifname']:
+ raise Exception("Unexpected bss[0]")
+ if status['ssid[0]'] != ssid:
+ raise Exception("Unexpected ssid[0]")
+ if status['bssid[0]'] != bssid:
+ raise Exception("Unexpected bssid[0]")
+ if status['freq'] != "2412":
+ raise Exception("Unexpected freq")
+ if status['beacon_int'] != "100":
+ raise Exception("Unexpected beacon_int")
+ if status['dtim_period'] != "2":
+ raise Exception("Unexpected dtim_period")
+ if "max_txpower" not in status:
+ raise Exception("Missing max_txpower")
+ if "ht_caps_info" not in status:
+ raise Exception("Missing ht_caps_info")
+
+ if driver['beacon_set'] != "1":
+ raise Exception("Unexpected beacon_set")
+ if driver['addr'] != bssid:
+ raise Exception("Unexpected addr")
+
+@remote_compatible
+def test_hapd_ctrl_p2p_manager(dev, apdev):
+ """hostapd as P2P Device manager"""
+ ssid = "hapd-p2p-mgr"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['manage_p2p'] = '1'
+ params['allow_cross_connection'] = '0'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("DEAUTHENTICATE " + addr + " p2p=2"):
+ raise Exception("DEAUTHENTICATE command failed")
+ dev[0].wait_disconnected(timeout=5)
+ dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+ if "OK" not in hapd.request("DISASSOCIATE " + addr + " p2p=2"):
+ raise Exception("DISASSOCIATE command failed")
+ dev[0].wait_disconnected(timeout=5)
+ dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+@remote_compatible
+def test_hapd_ctrl_sta(dev, apdev):
+ """hostapd and STA ctrl_iface commands"""
+ try:
+ run_hapd_ctrl_sta(dev, apdev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def run_hapd_ctrl_sta(dev, apdev):
+ ssid = "hapd-ctrl-sta"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ hglobal = hostapd.HostapdGlobal(apdev[0])
+ dev[0].request("VENDOR_ELEM_ADD 13 2102ff02")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ addr = dev[0].own_addr()
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=2)
+ if ev is None:
+ raise Exception("No hostapd per-interface event reported")
+ ev2 = hglobal.wait_event(["AP-STA-CONNECTED"], timeout=2)
+ if ev2 is None:
+ raise Exception("No hostapd global event reported")
+ if not ev2.startswith("IFNAME=" + apdev[0]['ifname'] + " <"):
+ raise Exception("Unexpected global event prefix: " + ev2)
+ if ev not in ev2:
+ raise Exception("Event mismatch (%s,%s)" % (ev, ev2))
+ if "FAIL" in hapd.request("STA " + addr):
+ raise Exception("Unexpected STA failure")
+ if "FAIL" not in hapd.request("STA " + addr + " eapol"):
+ raise Exception("Unexpected STA-eapol success")
+ if "FAIL" not in hapd.request("STA " + addr + " foo"):
+ raise Exception("Unexpected STA-foo success")
+ if "FAIL" not in hapd.request("STA 00:11:22:33:44"):
+ raise Exception("Unexpected STA success")
+ if "FAIL" not in hapd.request("STA 00:11:22:33:44:55"):
+ raise Exception("Unexpected STA success")
+
+ if len(hapd.request("STA-NEXT " + addr).splitlines()) > 0:
+ raise Exception("Unexpected STA-NEXT result")
+ if "FAIL" not in hapd.request("STA-NEXT 00:11:22:33:44"):
+ raise Exception("Unexpected STA-NEXT success")
+
+ sta = hapd.get_sta(addr)
+ logger.info("STA: " + str(sta))
+ if "ext_capab" not in sta:
+ raise Exception("Missing ext_capab in STA output")
+ if 'ht_caps_info' not in sta:
+ raise Exception("Missing ht_caps_info in STA output")
+ if 'min_txpower' not in sta:
+ raise Exception("Missing min_txpower in STA output")
+ if 'max_txpower' not in sta:
+ raise Exception("Missing min_txpower in STA output")
+ if sta['min_txpower'] != '-1':
+ raise Exception("Unxpected min_txpower value: " + sta['min_txpower'])
+ if sta['max_txpower'] != '2':
+ raise Exception("Unxpected max_txpower value: " + sta['max_txpower'])
+
+@remote_compatible
+def test_hapd_ctrl_disconnect(dev, apdev):
+ """hostapd and disconnection ctrl_iface commands"""
+ ssid = "hapd-ctrl"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ addr = dev[0].p2p_dev_addr()
+
+ if "FAIL" not in hapd.request("DEAUTHENTICATE 00:11:22:33:44"):
+ raise Exception("Unexpected DEAUTHENTICATE success")
+
+ if "OK" not in hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff"):
+ raise Exception("Unexpected DEAUTHENTICATE failure")
+ dev[0].wait_disconnected(timeout=5)
+ dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+ if "FAIL" not in hapd.request("DISASSOCIATE 00:11:22:33:44"):
+ raise Exception("Unexpected DISASSOCIATE success")
+
+ if "OK" not in hapd.request("DISASSOCIATE ff:ff:ff:ff:ff:ff"):
+ raise Exception("Unexpected DISASSOCIATE failure")
+ dev[0].wait_disconnected(timeout=5)
+ dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+@remote_compatible
+def test_hapd_ctrl_chan_switch(dev, apdev):
+ """hostapd and CHAN_SWITCH ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("CHAN_SWITCH "):
+ raise Exception("Unexpected CHAN_SWITCH success")
+ if "FAIL" not in hapd.request("CHAN_SWITCH qwerty 2422"):
+ raise Exception("Unexpected CHAN_SWITCH success")
+ if "FAIL" not in hapd.request("CHAN_SWITCH 5 qwerty"):
+ raise Exception("Unexpected CHAN_SWITCH success")
+ if "FAIL" not in hapd.request("CHAN_SWITCH 0 2432 center_freq1=123 center_freq2=234 bandwidth=1000 sec_channel_offset=20 ht vht"):
+ raise Exception("Unexpected CHAN_SWITCH success")
+
+@remote_compatible
+def test_hapd_ctrl_level(dev, apdev):
+ """hostapd and LEVEL ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("LEVEL 0"):
+ raise Exception("Unexpected LEVEL success on non-monitor interface")
+
+@remote_compatible
+def test_hapd_ctrl_new_sta(dev, apdev):
+ """hostapd and NEW_STA ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("NEW_STA 00:11:22:33:44"):
+ raise Exception("Unexpected NEW_STA success")
+ if "OK" not in hapd.request("NEW_STA 00:11:22:33:44:55"):
+ raise Exception("Unexpected NEW_STA failure")
+ if "AUTHORIZED" not in hapd.request("STA 00:11:22:33:44:55"):
+ raise Exception("Unexpected NEW_STA STA status")
+ if "OK" not in hapd.request("NEW_STA 00:11:22:33:44:55"):
+ raise Exception("Unexpected NEW_STA failure")
+ with alloc_fail(hapd, 1, "ap_sta_add;hostapd_ctrl_iface_new_sta"):
+ if "FAIL" not in hapd.request("NEW_STA 00:11:22:33:44:66"):
+ raise Exception("Unexpected NEW_STA success during OOM")
+
+@remote_compatible
+def test_hapd_ctrl_get(dev, apdev):
+ """hostapd and GET ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("GET foo"):
+ raise Exception("Unexpected GET success")
+ if "FAIL" in hapd.request("GET version"):
+ raise Exception("Unexpected GET version failure")
+
+@remote_compatible
+def test_hapd_ctrl_unknown(dev, apdev):
+ """hostapd and unknown ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "UNKNOWN COMMAND" not in hapd.request("FOO"):
+ raise Exception("Unexpected response")
+
+@remote_compatible
+def test_hapd_ctrl_hs20_wnm_notif(dev, apdev):
+ """hostapd and HS20_WNM_NOTIF ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("HS20_WNM_NOTIF 00:11:22:33:44 http://example.com/"):
+ raise Exception("Unexpected HS20_WNM_NOTIF success")
+ if "FAIL" not in hapd.request("HS20_WNM_NOTIF 00:11:22:33:44:55http://example.com/"):
+ raise Exception("Unexpected HS20_WNM_NOTIF success")
+
+@remote_compatible
+def test_hapd_ctrl_hs20_deauth_req(dev, apdev):
+ """hostapd and HS20_DEAUTH_REQ ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("HS20_DEAUTH_REQ 00:11:22:33:44 1 120 http://example.com/"):
+ raise Exception("Unexpected HS20_DEAUTH_REQ success")
+ if "FAIL" not in hapd.request("HS20_DEAUTH_REQ 00:11:22:33:44:55"):
+ raise Exception("Unexpected HS20_DEAUTH_REQ success")
+ if "FAIL" not in hapd.request("HS20_DEAUTH_REQ 00:11:22:33:44:55 1"):
+ raise Exception("Unexpected HS20_DEAUTH_REQ success")
+
+@remote_compatible
+def test_hapd_ctrl_disassoc_imminent(dev, apdev):
+ """hostapd and DISASSOC_IMMINENT ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("DISASSOC_IMMINENT 00:11:22:33:44"):
+ raise Exception("Unexpected DISASSOC_IMMINENT success")
+ if "FAIL" not in hapd.request("DISASSOC_IMMINENT 00:11:22:33:44:55"):
+ raise Exception("Unexpected DISASSOC_IMMINENT success")
+ if "FAIL" not in hapd.request("DISASSOC_IMMINENT 00:11:22:33:44:55 2"):
+ raise Exception("Unexpected DISASSOC_IMMINENT success")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].p2p_interface_addr()
+ if "OK" not in hapd.request("DISASSOC_IMMINENT " + addr + " 2"):
+ raise Exception("Unexpected DISASSOC_IMMINENT failure")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+ if ev is None:
+ raise Exception("Scan timed out")
+
+@remote_compatible
+def test_hapd_ctrl_ess_disassoc(dev, apdev):
+ """hostapd and ESS_DISASSOC ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("ESS_DISASSOC 00:11:22:33:44"):
+ raise Exception("Unexpected ESS_DISASSOCT success")
+ if "FAIL" not in hapd.request("ESS_DISASSOC 00:11:22:33:44:55"):
+ raise Exception("Unexpected ESS_DISASSOC success")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].p2p_interface_addr()
+ if "FAIL" not in hapd.request("ESS_DISASSOC " + addr):
+ raise Exception("Unexpected ESS_DISASSOC success")
+ if "FAIL" not in hapd.request("ESS_DISASSOC " + addr + " -1"):
+ raise Exception("Unexpected ESS_DISASSOC success")
+ if "FAIL" not in hapd.request("ESS_DISASSOC " + addr + " 1"):
+ raise Exception("Unexpected ESS_DISASSOC success")
+ if "OK" not in hapd.request("ESS_DISASSOC " + addr + " 20 http://example.com/"):
+ raise Exception("Unexpected ESS_DISASSOC failure")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+ if ev is None:
+ raise Exception("Scan timed out")
+
+def test_hapd_ctrl_set_deny_mac_file(dev, apdev):
+ """hostapd and SET deny_mac_file ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.macaddr')
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ hapd.send_file(filename, filename)
+ if "OK" not in hapd.request("SET deny_mac_file " + filename):
+ raise Exception("Unexpected SET failure")
+ dev[0].wait_disconnected(timeout=15)
+ ev = dev[1].wait_event(["CTRL-EVENT-DISCONNECTED"], 1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_hapd_ctrl_set_accept_mac_file(dev, apdev):
+ """hostapd and SET accept_mac_file ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.macaddr')
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ hapd.send_file(filename, filename)
+ hapd.request("SET macaddr_acl 1")
+ if "OK" not in hapd.request("SET accept_mac_file " + filename):
+ raise Exception("Unexpected SET failure")
+ dev[1].wait_disconnected(timeout=15)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], 1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+def test_hapd_ctrl_set_accept_mac_file_vlan(dev, apdev):
+ """hostapd and SET accept_mac_file ctrl_iface command (VLAN ID)"""
+ ssid = "hapd-ctrl"
+ filename = hostapd.acl_file(dev, apdev, 'hostapd.accept')
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+ hapd.send_file(filename, filename)
+ hapd.request("SET macaddr_acl 1")
+ if "OK" not in hapd.request("SET accept_mac_file " + filename):
+ raise Exception("Unexpected SET failure")
+ dev[1].wait_disconnected(timeout=15)
+ dev[0].wait_disconnected(timeout=15)
+ if filename.startswith('/tmp/'):
+ os.unlink(filename)
+
+@remote_compatible
+def test_hapd_ctrl_set_error_cases(dev, apdev):
+ """hostapd and SET error cases"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ errors = ["wpa_key_mgmt FOO",
+ "wpa_key_mgmt WPA-PSK \t FOO",
+ "wpa_key_mgmt \t ",
+ "wpa_pairwise FOO",
+ "wpa_pairwise \t ",
+ 'wep_key0 "',
+ 'wep_key0 "abcde',
+ "wep_key0 1",
+ "wep_key0 12q3456789",
+ "wep_key_len_broadcast 20",
+ "wep_rekey_period -1",
+ "wep_default_key 4",
+ "r0kh 02:00:00:00:03:0q nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
+ "r0kh 02:00:00:00:03:00 12345678901234567890123456789012345678901234567890.nas1.w1.fi 100102030405060708090a0b0c0d0e0f100102030405060708090a0b0c0d0e0f",
+ "r0kh 02:00:00:00:03:00 nas1.w1.fi 100q02030405060708090a0b0c0d0e0f100q02030405060708090a0b0c0d0e0f",
+ "r1kh 02:00:00:00:04:q0 00:01:02:03:04:06 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f",
+ "r1kh 02:00:00:00:04:00 00:01:02:03:04:q6 200102030405060708090a0b0c0d0e0f200102030405060708090a0b0c0d0e0f",
+ "r1kh 02:00:00:00:04:00 00:01:02:03:04:06 2q0102030405060708090a0b0c0d0e0f2q0102030405060708090a0b0c0d0e0f",
+ "roaming_consortium 1",
+ "roaming_consortium 12",
+ "roaming_consortium 112233445566778899aabbccddeeff00",
+ 'venue_name P"engExample venue"',
+ 'venue_name P"engExample venue',
+ "venue_name engExample venue",
+ "venue_name e:Example venue",
+ "venue_name eng1:Example venue",
+ "venue_name eng:Example venue 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890",
+ "anqp_3gpp_cell_net abc",
+ "anqp_3gpp_cell_net ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
+ "anqp_3gpp_cell_net 244",
+ "anqp_3gpp_cell_net 24,123",
+ "anqp_3gpp_cell_net 244,1",
+ "anqp_3gpp_cell_net 244,1234",
+ "nai_realm 0",
+ "nai_realm 0,1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.nas1.w1.fi",
+ "nai_realm 0,example.org,1,2,3,4,5,6,7,8",
+ "nai_realm 0,example.org,1[1:1][2:2][3:3][4:4][5:5]",
+ "nai_realm 0,example.org,1[1]",
+ "nai_realm 0,example.org,1[1:1",
+ "nai_realm 0,a.example.org;b.example.org;c.example.org;d.example.org;e.example.org;f.example.org;g.example.org;h.example.org;i.example.org;j.example.org;k.example.org",
+ "qos_map_set 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60",
+ "qos_map_set 53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,300",
+ "qos_map_set 53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,-1",
+ "qos_map_set 53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,255,1",
+ "qos_map_set 1",
+ "qos_map_set 1,2",
+ "hs20_conn_capab 1",
+ "hs20_conn_capab 6:22",
+ "hs20_wan_metrics 0q:8000:1000:80:240:3000",
+ "hs20_wan_metrics 01",
+ "hs20_wan_metrics 01:8000",
+ "hs20_wan_metrics 01:8000:1000",
+ "hs20_wan_metrics 01:8000:1000:80",
+ "hs20_wan_metrics 01:8000:1000:80:240",
+ "hs20_oper_friendly_name eng1:Example",
+ "hs20_icon 32",
+ "hs20_icon 32:32",
+ "hs20_icon 32:32:eng",
+ "hs20_icon 32:32:eng:image/png",
+ "hs20_icon 32:32:eng:image/png:icon32",
+ "hs20_icon 32:32:eng:image/png:123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890:/tmp/icon32.png",
+ "hs20_icon 32:32:eng:image/png:name:/tmp/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.png",
+ "osu_ssid ",
+ "osu_ssid P",
+ 'osu_ssid P"abc',
+ 'osu_ssid "1234567890123456789012345678901234567890"',
+ "osu_friendly_name eng:Example",
+ "osu_nai anonymous@example.com",
+ "osu_nai2 anonymous@example.com",
+ "osu_method_list 1 0",
+ "osu_icon foo",
+ "osu_service_desc eng:Example services",
+ "ssid 1234567890123456789012345678901234567890",
+ "pac_opaque_encr_key 123456",
+ "eap_fast_a_id 12345",
+ "eap_fast_a_id 12345q",
+ "own_ip_addr foo",
+ "auth_server_addr foo2",
+ "auth_server_shared_secret ",
+ "acct_server_addr foo3",
+ "acct_server_shared_secret ",
+ "radius_auth_req_attr 123::",
+ "radius_acct_req_attr 123::",
+ "radius_das_client 192.168.1.123",
+ "radius_das_client 192.168.1.1a foo",
+ "auth_algs 0",
+ "max_num_sta -1",
+ "max_num_sta 1000000",
+ "wpa_passphrase 1234567",
+ "wpa_passphrase 1234567890123456789012345678901234567890123456789012345678901234",
+ "wpa_psk 1234567890123456789012345678901234567890123456789012345678901234a",
+ "wpa_psk 12345678901234567890123456789012345678901234567890123456789012",
+ "wpa_psk_radius 123",
+ "wpa_pairwise NONE",
+ "wpa_pairwise WEP40",
+ "wpa_pairwise WEP104",
+ "rsn_pairwise NONE",
+ "rsn_pairwise WEP40",
+ "rsn_pairwise WEP104",
+ "mobility_domain 01",
+ "r1_key_holder 0011223344",
+ "ctrl_interface_group nosuchgrouphere",
+ "hw_mode foo",
+ "wps_rf_bands foo",
+ "beacon_int 0",
+ "beacon_int 65536",
+ "acs_num_scans 0",
+ "acs_num_scans 101",
+ "rts_threshold -2",
+ "rts_threshold 65536",
+ "fragm_threshold -2",
+ "fragm_threshold 2347",
+ "send_probe_response -1",
+ "send_probe_response 2",
+ "vlan_naming -1",
+ "vlan_naming 10000000",
+ "group_mgmt_cipher FOO",
+ "assoc_sa_query_max_timeout 0",
+ "assoc_sa_query_retry_timeout 0",
+ "wps_state -1",
+ "wps_state 3",
+ "uuid FOO",
+ "device_name 1234567890123456789012345678901234567890",
+ "manufacturer 1234567890123456789012345678901234567890123456789012345678901234567890",
+ "model_name 1234567890123456789012345678901234567890",
+ "model_number 1234567890123456789012345678901234567890",
+ "serial_number 1234567890123456789012345678901234567890",
+ "device_type FOO",
+ "os_version 1",
+ "ap_settings /tmp/does/not/exist/ap-settings.foo",
+ "wps_nfc_dev_pw_id 4",
+ "wps_nfc_dev_pw_id 100000",
+ "time_zone A",
+ "access_network_type -1",
+ "access_network_type 16",
+ "hessid 00:11:22:33:44",
+ "network_auth_type 0q",
+ "ipaddr_type_availability 1q",
+ "hs20_operating_class 0",
+ "hs20_operating_class 0q",
+ "bss_load_test ",
+ "bss_load_test 12",
+ "bss_load_test 12:80",
+ "vendor_elements 0",
+ "vendor_elements 0q",
+ "assocresp_elements 0",
+ "assocresp_elements 0q",
+ "local_pwr_constraint -1",
+ "local_pwr_constraint 256",
+ "wmm_ac_bk_cwmin -1",
+ "wmm_ac_be_cwmin 16",
+ "wmm_ac_vi_cwmax -1",
+ "wmm_ac_vo_cwmax 16",
+ "wmm_ac_foo_cwmax 6",
+ "wmm_ac_bk_aifs 0",
+ "wmm_ac_bk_aifs 256",
+ "wmm_ac_bk_txop_limit -1",
+ "wmm_ac_bk_txop_limit 65536",
+ "wmm_ac_bk_acm -1",
+ "wmm_ac_bk_acm 2",
+ "wmm_ac_bk_foo 2",
+ "tx_queue_foo_aifs 3",
+ "tx_queue_data3_cwmin 4",
+ "tx_queue_data3_cwmax 4",
+ "tx_queue_data3_aifs -4",
+ "tx_queue_data3_foo 1"]
+ for e in errors:
+ if "FAIL" not in hapd.request("SET " + e):
+ raise Exception("Unexpected SET success: '%s'" % e)
+
+ if "OK" not in hapd.request("SET osu_server_uri https://example.com/"):
+ raise Exception("Unexpected SET osu_server_uri failure")
+ if "OK" not in hapd.request("SET osu_friendly_name eng:Example"):
+ raise Exception("Unexpected SET osu_friendly_name failure")
+
+ errors = ["osu_friendly_name eng1:Example",
+ "osu_service_desc eng1:Example services"]
+ for e in errors:
+ if "FAIL" not in hapd.request("SET " + e):
+ raise Exception("Unexpected SET success: '%s'" % e)
+
+ no_err = ["wps_nfc_dh_pubkey 0",
+ "wps_nfc_dh_privkey 0q",
+ "wps_nfc_dev_pw 012",
+ "manage_p2p 0",
+ "disassoc_low_ack 0",
+ "network_auth_type 01",
+ "tdls_prohibit 0",
+ "tdls_prohibit_chan_switch 0"]
+ for e in no_err:
+ if "OK" not in hapd.request("SET " + e):
+ raise Exception("Unexpected SET failure: '%s'" % e)
+
+@remote_compatible
+def test_hapd_ctrl_global(dev, apdev):
+ """hostapd and GET ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ ifname = apdev[0]['ifname']
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd_global = hostapd.HostapdGlobal(apdev[0])
+ res = hapd_global.request("IFNAME=" + ifname + " PING")
+ if "PONG" not in res:
+ raise Exception("Could not ping hostapd interface " + ifname + " via global control interface")
+ res = hapd_global.request("IFNAME=" + ifname + " GET version")
+ if "FAIL" in res:
+ raise Exception("Could not get hostapd version for " + ifname + " via global control interface")
+ res = hapd_global.request("IFNAME=no-such-ifname GET version")
+ if "FAIL-NO-IFNAME-MATCH" not in res:
+ raise Exception("Invalid ifname not reported")
+ res = hapd_global.request("INTERFACES")
+ if "FAIL" in res:
+ raise Exception("INTERFACES command failed")
+ if apdev[0]['ifname'] not in res.splitlines():
+ raise Exception("AP interface missing from INTERFACES")
+ res = hapd_global.request("INTERFACES ctrl")
+ if "FAIL" in res:
+ raise Exception("INTERFACES ctrl command failed")
+ if apdev[0]['ifname'] + " ctrl_iface=" not in res:
+ raise Exception("AP interface missing from INTERFACES ctrl")
+
+ if "FAIL" not in hapd_global.request("DETACH"):
+ raise Exception("DETACH succeeded unexpectedly")
+
+def dup_network(hapd_global, src, dst, param):
+ res = hapd_global.request("DUP_NETWORK %s %s %s" % (src, dst, param))
+ if "OK" not in res:
+ raise Exception("Could not dup %s param from %s to %s" % (param, src,
+ dst))
+
+def test_hapd_dup_network_global_wpa2(dev, apdev):
+ """hostapd and DUP_NETWORK command (WPA2)"""
+ passphrase = "12345678"
+ src_ssid = "hapd-ctrl-src"
+ dst_ssid = "hapd-ctrl-dst"
+
+ src_params = hostapd.wpa2_params(ssid=src_ssid, passphrase=passphrase)
+ src_ifname = apdev[0]['ifname']
+ src_hapd = hostapd.add_ap(apdev[0], src_params)
+
+ dst_params = {"ssid": dst_ssid}
+ dst_ifname = apdev[1]['ifname']
+ dst_hapd = hostapd.add_ap(apdev[1], dst_params, no_enable=True)
+
+ hapd_global = hostapd.HostapdGlobal()
+
+ for param in ["wpa", "wpa_passphrase", "wpa_key_mgmt", "rsn_pairwise"]:
+ dup_network(hapd_global, src_ifname, dst_ifname, param)
+
+ dst_hapd.enable()
+
+ dev[0].connect(dst_ssid, psk=passphrase, proto="RSN", pairwise="CCMP",
+ scan_freq="2412")
+ addr = dev[0].own_addr()
+ if "FAIL" in dst_hapd.request("STA " + addr):
+ raise Exception("Could not connect using duplicated wpa params")
+
+ tests = ["a",
+ "no-such-ifname no-such-ifname",
+ src_ifname + " no-such-ifname",
+ src_ifname + " no-such-ifname no-such-param",
+ src_ifname + " " + dst_ifname + " no-such-param"]
+ for t in tests:
+ if "FAIL" not in hapd_global.request("DUP_NETWORK " + t):
+ raise Exception("Invalid DUP_NETWORK accepted: " + t)
+ with alloc_fail(src_hapd, 1, "hostapd_ctrl_iface_dup_param"):
+ if "FAIL" not in hapd_global.request("DUP_NETWORK %s %s wpa" % (src_ifname, dst_ifname)):
+ raise Exception("DUP_NETWORK accepted during OOM")
+
+def test_hapd_dup_network_global_wpa(dev, apdev):
+ """hostapd and DUP_NETWORK command (WPA)"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ src_ssid = "hapd-ctrl-src"
+ dst_ssid = "hapd-ctrl-dst"
+
+ src_params = hostapd.wpa_params(ssid=src_ssid)
+ src_params['wpa_psk'] = psk
+ src_ifname = apdev[0]['ifname']
+ src_hapd = hostapd.add_ap(apdev[0], src_params)
+
+ dst_params = {"ssid": dst_ssid}
+ dst_ifname = apdev[1]['ifname']
+ dst_hapd = hostapd.add_ap(apdev[1], dst_params, no_enable=True)
+
+ hapd_global = hostapd.HostapdGlobal()
+
+ for param in ["wpa", "wpa_psk", "wpa_key_mgmt", "wpa_pairwise"]:
+ dup_network(hapd_global, src_ifname, dst_ifname, param)
+
+ dst_hapd.enable()
+
+ dev[0].connect(dst_ssid, raw_psk=psk, proto="WPA", pairwise="TKIP",
+ scan_freq="2412")
+ addr = dev[0].own_addr()
+ if "FAIL" in dst_hapd.request("STA " + addr):
+ raise Exception("Could not connect using duplicated wpa params")
+
+@remote_compatible
+def test_hapd_ctrl_log_level(dev, apdev):
+ """hostapd ctrl_iface LOG_LEVEL"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ level = hapd.request("LOG_LEVEL")
+ if "Current level: MSGDUMP" not in level:
+ raise Exception("Unexpected debug level(1): " + level)
+ if "Timestamp: 1" not in level:
+ raise Exception("Unexpected timestamp(1): " + level)
+
+ if "OK" not in hapd.request("LOG_LEVEL MSGDUMP 0"):
+ raise Exception("LOG_LEVEL failed")
+ level = hapd.request("LOG_LEVEL")
+ if "Current level: MSGDUMP" not in level:
+ raise Exception("Unexpected debug level(2): " + level)
+ if "Timestamp: 0" not in level:
+ raise Exception("Unexpected timestamp(2): " + level)
+
+ if "OK" not in hapd.request("LOG_LEVEL MSGDUMP 1"):
+ raise Exception("LOG_LEVEL failed")
+ level = hapd.request("LOG_LEVEL")
+ if "Current level: MSGDUMP" not in level:
+ raise Exception("Unexpected debug level(3): " + level)
+ if "Timestamp: 1" not in level:
+ raise Exception("Unexpected timestamp(3): " + level)
+
+ if "FAIL" not in hapd.request("LOG_LEVEL FOO"):
+ raise Exception("Invalid LOG_LEVEL accepted")
+
+ for lev in ["EXCESSIVE", "MSGDUMP", "DEBUG", "INFO", "WARNING", "ERROR"]:
+ if "OK" not in hapd.request("LOG_LEVEL " + lev):
+ raise Exception("LOG_LEVEL failed for " + lev)
+ level = hapd.request("LOG_LEVEL")
+ if "Current level: " + lev not in level:
+ raise Exception("Unexpected debug level: " + level)
+
+ if "OK" not in hapd.request("LOG_LEVEL MSGDUMP 1"):
+ raise Exception("LOG_LEVEL failed")
+ level = hapd.request("LOG_LEVEL")
+ if "Current level: MSGDUMP" not in level:
+ raise Exception("Unexpected debug level(3): " + level)
+ if "Timestamp: 1" not in level:
+ raise Exception("Unexpected timestamp(3): " + level)
+
+@remote_compatible
+def test_hapd_ctrl_disconnect_no_tx(dev, apdev):
+ """hostapd disconnecting STA without transmitting Deauth/Disassoc"""
+ ssid = "hapd-test"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ addr0 = dev[0].own_addr()
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2412")
+ addr1 = dev[1].own_addr()
+
+ # Disconnect the STA without sending out Deauthentication frame
+ if "OK" not in hapd.request("DEAUTHENTICATE " + addr0 + " tx=0"):
+ raise Exception("DEAUTHENTICATE command failed")
+ # Force disconnection due to AP receiving a frame from not-asssociated STA
+ dev[0].request("DATA_TEST_CONFIG 1")
+ dev[0].request("DATA_TEST_TX " + bssid + " " + addr0)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ dev[0].request("DATA_TEST_CONFIG 0")
+ if ev is None:
+ raise Exception("Disconnection event not seen after TX attempt")
+ if "reason=7" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+ # Disconnect the STA without sending out Disassociation frame
+ if "OK" not in hapd.request("DISASSOCIATE " + addr1 + " tx=0"):
+ raise Exception("DISASSOCIATE command failed")
+ # Force disconnection due to AP receiving a frame from not-asssociated STA
+ dev[1].request("DATA_TEST_CONFIG 1")
+ dev[1].request("DATA_TEST_TX " + bssid + " " + addr1)
+ ev = dev[1].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ dev[1].request("DATA_TEST_CONFIG 0")
+ if ev is None:
+ raise Exception("Disconnection event not seen after TX attempt")
+ if "reason=7" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+def test_hapd_ctrl_mib(dev, apdev):
+ """hostapd and MIB ctrl_iface command with open network"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ mib = hapd.request("MIB")
+ if len(mib) != 0:
+ raise Exception("Unexpected MIB response: " + mib)
+
+ mib = hapd.request("MIB radius_server")
+ if len(mib) != 0:
+ raise Exception("Unexpected 'MIB radius_server' response: " + mib)
+
+ if "FAIL" not in hapd.request("MIB foo"):
+ raise Exception("'MIB foo' succeeded")
+
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+ mib = hapd.request("MIB")
+ if "FAIL" in mib:
+ raise Exception("Unexpected MIB response: " + mib)
+
+ mib = hapd.request("MIB radius_server")
+ if len(mib) != 0:
+ raise Exception("Unexpected 'MIB radius_server' response: " + mib)
+
+ if "FAIL" not in hapd.request("MIB foo"):
+ raise Exception("'MIB foo' succeeded")
+
+def test_hapd_ctrl_not_yet_fully_enabled(dev, apdev):
+ """hostapd and ctrl_iface commands when BSS not yet fully enabled"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+
+ if not hapd.ping():
+ raise Exception("PING failed")
+ if "FAIL" in hapd.request("MIB"):
+ raise Exception("MIB failed")
+ if len(hapd.request("MIB radius_server")) != 0:
+ raise Exception("Unexpected 'MIB radius_server' response")
+ if "state=UNINITIALIZED" not in hapd.request("STATUS"):
+ raise Exception("Unexpected STATUS response")
+ if "FAIL" not in hapd.request("STATUS-DRIVER"):
+ raise Exception("Unexpected response to STATUS-DRIVER")
+ if len(hapd.request("STA-FIRST")) != 0:
+ raise Exception("Unexpected response to STA-FIRST")
+ if "FAIL" not in hapd.request("STA ff:ff:ff:ff:ff:ff"):
+ raise Exception("Unexpected response to STA")
+ cmds = ["NEW_STA 02:ff:ff:ff:ff:ff",
+ "DEAUTHENTICATE 02:ff:ff:ff:ff:ff",
+ "DEAUTHENTICATE 02:ff:ff:ff:ff:ff test=0",
+ "DEAUTHENTICATE 02:ff:ff:ff:ff:ff p2p=0",
+ "DEAUTHENTICATE 02:ff:ff:ff:ff:ff tx=0",
+ "DISASSOCIATE 02:ff:ff:ff:ff:ff",
+ "DISASSOCIATE 02:ff:ff:ff:ff:ff test=0",
+ "DISASSOCIATE 02:ff:ff:ff:ff:ff p2p=0",
+ "DISASSOCIATE 02:ff:ff:ff:ff:ff tx=0",
+ "SA_QUERY 02:ff:ff:ff:ff:ff",
+ "WPS_PIN any 12345670",
+ "WPS_PBC",
+ "WPS_CANCEL",
+ "WPS_AP_PIN random",
+ "WPS_AP_PIN disable",
+ "WPS_CHECK_PIN 123456789",
+ "WPS_GET_STATUS",
+ "WPS_NFC_TAG_READ 00",
+ "WPS_NFC_CONFIG_TOKEN NDEF",
+ "WPS_NFC_TOKEN WPS",
+ "NFC_GET_HANDOVER_SEL NDEF WPS-CR",
+ "NFC_REPORT_HANDOVER RESP WPS 00 00",
+ "SET_QOS_MAP_SET 22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,48,55",
+ "SEND_QOS_MAP_CONF 02:ff:ff:ff:ff:ff",
+ "HS20_WNM_NOTIF 02:ff:ff:ff:ff:ff https://example.com/",
+ "HS20_DEAUTH_REQ 02:ff:ff:ff:ff:ff 1 120 https://example.com/",
+ "DISASSOC_IMMINENT 02:ff:ff:ff:ff:ff 10",
+ "ESS_DISASSOC 02:ff:ff:ff:ff:ff 10 https://example.com/",
+ "BSS_TM_REQ 02:ff:ff:ff:ff:ff",
+ "GET_CONFIG",
+ "RADAR DETECTED freq=5260 ht_enabled=1 chan_width=1",
+ "CHAN_SWITCH 5 5200 ht sec_channel_offset=-1 bandwidth=40",
+ "TRACK_STA_LIST",
+ "PMKSA",
+ "PMKSA_FLUSH",
+ "SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\"",
+ "REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\"",
+ "REQ_LCI 00:11:22:33:44:55",
+ "REQ_RANGE 00:11:22:33:44:55",
+ "DRIVER_FLAGS",
+ "STOP_AP"]
+ for cmd in cmds:
+ hapd.request(cmd)
+
+def test_hapd_ctrl_set(dev, apdev):
+ """hostapd and SET ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ tests = ["foo",
+ "wps_version_number 300",
+ "gas_frag_limit 0",
+ "mbo_assoc_disallow 0"]
+ for t in tests:
+ if "FAIL" not in hapd.request("SET " + t):
+ raise Exception("Invalid SET command accepted: " + t)
+
+def test_hapd_ctrl_radar(dev, apdev):
+ """hostapd and RADAR ctrl_iface command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tests = ["foo", "foo bar"]
+ for t in tests:
+ if "FAIL" not in hapd.request("RADAR " + t):
+ raise Exception("Invalid RADAR command accepted: " + t)
+
+ tests = ["DETECTED freq=2412 chan_offset=12 cf1=1234 cf2=2345",
+ "CAC-FINISHED freq=2412",
+ "CAC-ABORTED freq=2412",
+ "NOP-FINISHED freq=2412"]
+ for t in tests:
+ hapd.request("RADAR " + t)
+
+def test_hapd_ctrl_ext_io_errors(dev, apdev):
+ """hostapd and external I/O errors"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ tests = ["MGMT_TX 1",
+ "MGMT_TX 1q",
+ "MGMT_RX_PROCESS freq=2412",
+ "MGMT_TX_STATUS_PROCESS style=1 ok=0 buf=12345678",
+ "EAPOL_RX foo",
+ "EAPOL_RX 00:11:22:33:44:55 1",
+ "EAPOL_RX 00:11:22:33:44:55 1q"]
+ for t in tests:
+ if "FAIL" not in hapd.request(t):
+ raise Exception("Invalid command accepted: " + t)
+ with alloc_fail(hapd, 1, "=hostapd_ctrl_iface_mgmt_tx"):
+ if "FAIL" not in hapd.request("MGMT_TX 12"):
+ raise Exception("MGMT_TX accepted during OOM")
+ with alloc_fail(hapd, 1, "=hostapd_ctrl_iface_eapol_rx"):
+ if "FAIL" not in hapd.request("EAPOL_RX 00:11:22:33:44:55 11"):
+ raise Exception("EAPOL_RX accepted during OOM")
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ tests = ["MGMT_RX_PROCESS freq=2412",
+ "MGMT_RX_PROCESS freq=2412 ssi_signal=0",
+ "MGMT_RX_PROCESS freq=2412 frame=1",
+ "MGMT_RX_PROCESS freq=2412 frame=1q",
+ "MGMT_TX_STATUS_PROCESS style=1 ok=0",
+ "MGMT_TX_STATUS_PROCESS style=1 ok=0 buf=1234567",
+ "MGMT_TX_STATUS_PROCESS style=1 ok=0 buf=1234567q"]
+ for t in tests:
+ if "FAIL" not in hapd.request(t):
+ raise Exception("Invalid command accepted: " + t)
+ with alloc_fail(hapd, 1, "=hostapd_ctrl_iface_mgmt_rx_process"):
+ if "FAIL" not in hapd.request("MGMT_RX_PROCESS freq=2412 frame=11"):
+ raise Exception("MGMT_RX_PROCESS accepted during OOM")
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+ if "OK" not in hapd.request("DATA_TEST_CONFIG 1"):
+ raise Exception("Failed to enable l2_test")
+ if "OK" not in hapd.request("DATA_TEST_CONFIG 1"):
+ raise Exception("Failed to enable l2_test(2)")
+ tests = ["DATA_TEST_TX foo",
+ "DATA_TEST_TX 00:11:22:33:44:55 foo",
+ "DATA_TEST_TX 00:11:22:33:44:55 00:11:22:33:44:55 -1",
+ "DATA_TEST_TX 00:11:22:33:44:55 00:11:22:33:44:55 256"]
+ for t in tests:
+ if "FAIL" not in hapd.request(t):
+ raise Exception("Invalid command accepted: " + t)
+ if "OK" not in hapd.request("DATA_TEST_CONFIG 0"):
+ raise Exception("Failed to disable l2_test")
+ tests = ["DATA_TEST_TX 00:11:22:33:44:55 00:11:22:33:44:55 0",
+ "DATA_TEST_FRAME ifname=foo",
+ "DATA_TEST_FRAME 1",
+ "DATA_TEST_FRAME 11",
+ "DATA_TEST_FRAME 112233445566778899aabbccddeefq"]
+ for t in tests:
+ if "FAIL" not in hapd.request(t):
+ raise Exception("Invalid command accepted: " + t)
+ with alloc_fail(hapd, 1, "=hostapd_ctrl_iface_data_test_frame"):
+ if "FAIL" not in hapd.request("DATA_TEST_FRAME 112233445566778899aabbccddeeff"):
+ raise Exception("DATA_TEST_FRAME accepted during OOM")
+
+def test_hapd_ctrl_vendor_test(dev, apdev):
+ """hostapd and VENDOR test command"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ OUI_QCA = 0x001374
+ QCA_NL80211_VENDOR_SUBCMD_TEST = 1
+ QCA_WLAN_VENDOR_ATTR_TEST = 8
+ attr = struct.pack("@HHI", 4 + 4, QCA_WLAN_VENDOR_ATTR_TEST, 123)
+ cmd = "VENDOR %x %d %s" % (OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_TEST, binascii.hexlify(attr).decode())
+
+ res = hapd.request(cmd)
+ if "FAIL" in res:
+ raise Exception("VENDOR command failed")
+ val, = struct.unpack("@I", binascii.unhexlify(res))
+ if val != 125:
+ raise Exception("Incorrect response value")
+
+ res = hapd.request(cmd + " nested=1")
+ if "FAIL" in res:
+ raise Exception("VENDOR command failed")
+ val, = struct.unpack("@I", binascii.unhexlify(res))
+ if val != 125:
+ raise Exception("Incorrect response value")
+
+ res = hapd.request(cmd + " nested=0")
+ if "FAIL" not in res:
+ raise Exception("VENDOR command with invalid (not nested) data accepted")
+
+def test_hapd_ctrl_vendor_errors(dev, apdev):
+ """hostapd and VENDOR errors"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ tests = ["q",
+ "10q",
+ "10 10q",
+ "10 10 123q",
+ "10 10"]
+ for t in tests:
+ if "FAIL" not in hapd.request("VENDOR " + t):
+ raise Exception("Invalid VENDOR command accepted: " + t)
+ with alloc_fail(hapd, 1, "=hostapd_ctrl_iface_vendor"):
+ if "FAIL" not in hapd.request("VENDOR 10 10 10"):
+ raise Exception("VENDOR accepted during OOM")
+ with alloc_fail(hapd, 1, "wpabuf_alloc;hostapd_ctrl_iface_vendor"):
+ if "FAIL" not in hapd.request("VENDOR 10 10"):
+ raise Exception("VENDOR accepted during OOM")
+
+def test_hapd_ctrl_eapol_reauth_errors(dev, apdev):
+ """hostapd and EAPOL_REAUTH errors"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ tests = ["foo",
+ "11:22:33:44:55:66"]
+ for t in tests:
+ if "FAIL" not in hapd.request("EAPOL_REAUTH " + t):
+ raise Exception("Invalid EAPOL_REAUTH command accepted: " + t)
+
+def test_hapd_ctrl_eapol_relog(dev, apdev):
+ """hostapd and RELOG"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "OK" not in hapd.request("RELOG"):
+ raise Exception("RELOG failed")
+
+def test_hapd_ctrl_poll_sta_errors(dev, apdev):
+ """hostapd and POLL_STA errors"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ tests = ["foo",
+ "11:22:33:44:55:66"]
+ for t in tests:
+ if "FAIL" not in hapd.request("POLL_STA " + t):
+ raise Exception("Invalid POLL_STA command accepted: " + t)
+
+def test_hapd_ctrl_update_beacon(dev, apdev):
+ """hostapd and UPDATE_BEACON"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "OK" not in hapd.request("UPDATE_BEACON"):
+ raise Exception("UPDATE_BEACON failed")
+ with fail_test(hapd, 1, "ieee802_11_set_beacon"):
+ if "FAIL" not in hapd.request("UPDATE_BEACON"):
+ raise Exception("UPDATE_BEACON succeeded unexpectedly")
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+def test_hapd_ctrl_test_fail(dev, apdev):
+ """hostapd and TEST_ALLOC_FAIL/TEST_FAIL"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "OK" not in hapd.request("TEST_ALLOC_FAIL 1:unknownfunc"):
+ raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+ if "OK" not in hapd.request("TEST_ALLOC_FAIL "):
+ raise Exception("TEST_ALLOC_FAIL clearing failed")
+ if "OK" not in hapd.request("TEST_FAIL "):
+ raise Exception("TEST_FAIL clearing failed")
+
+def test_hapd_ctrl_setband(dev, apdev):
+ """hostapd and setband"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ # The actual setband driver operations are not supported without vendor
+ # commands, so only check minimal parsing items here.
+ if "FAIL" not in hapd.request("SET setband foo"):
+ raise Exception("Invalid setband value accepted")
+ vals = ["5G", "6G", "2G", "2G,6G", "2G,5G,6G", "AUTO"]
+ for val in vals:
+ if "OK" not in hapd.request("SET setband " + val):
+ raise Exception("SET setband %s failed" % val)
+
+def test_hapd_ctrl_get_capability(dev, apdev):
+ """hostapd GET_CAPABILITY"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" not in hapd.request("GET_CAPABILITY "):
+ raise Exception("Invalid GET_CAPABILITY accepted")
+ res = hapd.request("GET_CAPABILITY dpp")
+ logger.info("DPP capability: " + res)
+
+def test_hapd_ctrl_pmksa_add_failures(dev, apdev):
+ """hostapd PMKSA_ADD failures"""
+ ssid = "hapd-ctrl"
+ params = {"ssid": ssid}
+ hapd = hostapd.add_ap(apdev[0], params)
+ tests = ["q",
+ "22:22:22:22:22:22",
+ "22:22:22:22:22:22 q",
+ "22:22:22:22:22:22 " + 16*'00',
+ "22:22:22:22:22:22 " + 16*"00" + " " + 10*"00",
+ "22:22:22:22:22:22 " + 16*"00" + " q",
+ "22:22:22:22:22:22 " + 16*"00" + " " + 200*"00",
+ "22:22:22:22:22:22 " + 16*"00" + " " + 32*"00" + " 12345",
+ "22:22:22:22:22:22 " + 16*"00" + " " + 32*"00" + " 12345 1",
+ ""]
+ for t in tests:
+ if "FAIL" not in hapd.request("PMKSA_ADD " + t):
+ raise Exception("Invalid PMKSA_ADD accepted: " + t)
+
+def test_hapd_ctrl_attach_errors(dev, apdev):
+ """hostapd ATTACH errors"""
+ params = {"ssid": "hapd-ctrl"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hglobal = hostapd.HostapdGlobal(apdev[0])
+ with alloc_fail(hapd, 1, "ctrl_iface_attach"):
+ if "FAIL" not in hapd.request("ATTACH foo"):
+ raise Exception("Invalid ATTACH accepted")
+ with alloc_fail(hapd, 1, "ctrl_iface_attach"):
+ if "FAIL" not in hglobal.request("ATTACH foo"):
+ raise Exception("Invalid ATTACH accepted")
diff --git a/contrib/wpa/tests/hwsim/test_he.py b/contrib/wpa/tests/hwsim/test_he.py
new file mode 100644
index 000000000000..2593f35f0bf1
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_he.py
@@ -0,0 +1,1188 @@
+# HE tests
+# Copyright (c) 2019, The Linux Foundation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os
+import subprocess, time
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+from test_dfs import wait_dfs_event
+
+def test_he_open(dev, apdev):
+ """HE AP with open mode configuration"""
+ params = {"ssid": "he",
+ "ieee80211ax": "1",
+ "he_bss_color": "42",
+ "he_mu_edca_ac_be_ecwmin": "7",
+ "he_mu_edca_ac_be_ecwmax": "15"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if hapd.get_status_field("ieee80211ax") != "1":
+ raise Exception("STATUS did not indicate ieee80211ax=1")
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="2412")
+ sta = hapd.get_sta(dev[0].own_addr())
+ if "[HE]" not in sta['flags']:
+ raise Exception("Missing STA flag: HE")
+
+def test_he_disabled_on_sta(dev, apdev):
+ """HE AP and HE disabled on STA"""
+ params = {"ssid": "he",
+ "ieee80211ax": "1",
+ "he_bss_color": "42",
+ "he_mu_edca_ac_be_ecwmin": "7",
+ "he_mu_edca_ac_be_ecwmax": "15"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="2412", disable_he="1")
+ sta = hapd.get_sta(dev[0].own_addr())
+ if "[HE]" in sta['flags']:
+ raise Exception("Unexpected STA flag: HE")
+
+def test_he_params(dev, apdev):
+ """HE AP parameters"""
+ params = {"ssid": "he",
+ "ieee80211ax": "1",
+ "he_bss_color": "42",
+ "he_mu_edca_ac_be_ecwmin": "7",
+ "he_mu_edca_ac_be_ecwmax": "15",
+ "he_su_beamformer": "0",
+ "he_su_beamformee": "0",
+ "he_default_pe_duration": "4",
+ "he_twt_required": "1",
+ "he_rts_threshold": "64",
+ "he_basic_mcs_nss_set": "65535",
+ "he_mu_edca_qos_info_param_count": "0",
+ "he_mu_edca_qos_info_q_ack": "0",
+ "he_mu_edca_qos_info_queue_request": "1",
+ "he_mu_edca_qos_info_txop_request": "0",
+ "he_mu_edca_ac_be_aifsn": "0",
+ "he_mu_edca_ac_be_ecwmin": "15",
+ "he_mu_edca_ac_be_ecwmax": "15",
+ "he_mu_edca_ac_be_timer": "255",
+ "he_mu_edca_ac_bk_aifsn": "0",
+ "he_mu_edca_ac_bk_aci": "1",
+ "he_mu_edca_ac_bk_ecwmin": "15",
+ "he_mu_edca_ac_bk_ecwmax": "15",
+ "he_mu_edca_ac_bk_timer": "255",
+ "he_mu_edca_ac_vi_ecwmin": "15",
+ "he_mu_edca_ac_vi_ecwmax": "15",
+ "he_mu_edca_ac_vi_aifsn": "0",
+ "he_mu_edca_ac_vi_aci": "2",
+ "he_mu_edca_ac_vi_timer": "255",
+ "he_mu_edca_ac_vo_aifsn": "0",
+ "he_mu_edca_ac_vo_aci": "3",
+ "he_mu_edca_ac_vo_ecwmin": "15",
+ "he_mu_edca_ac_vo_ecwmax": "15",
+ "he_mu_edca_ac_vo_timer": "255",
+ "he_spr_sr_control": "0",
+ "he_spr_non_srg_obss_pd_max_offset": "0",
+ "he_spr_srg_obss_pd_min_offset": "0",
+ "he_spr_srg_obss_pd_max_offset": "0",
+ "he_spr_srg_bss_colors": "1 2 10 63",
+ "he_spr_srg_partial_bssid": "0 1 3 63",
+ "he_6ghz_max_ampdu_len_exp": "7",
+ "he_6ghz_rx_ant_pat": "1",
+ "he_6ghz_tx_ant_pat": "1",
+ "he_6ghz_max_mpdu": "2",
+ "he_oper_chwidth": "0",
+ "he_oper_centr_freq_seg0_idx": "1",
+ "he_oper_centr_freq_seg1_idx": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if hapd.get_status_field("ieee80211ax") != "1":
+ raise Exception("STATUS did not indicate ieee80211ax=1")
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="2412")
+
+def test_he_spr_params(dev, apdev):
+ """HE AP spatial reuse parameters"""
+ params = {"ssid": "he",
+ "ieee80211ax": "1",
+ "he_spr_sr_control": "12",
+ "he_spr_non_srg_obss_pd_max_offset": "1",
+ "he_spr_srg_obss_pd_min_offset": "2",
+ "he_spr_srg_obss_pd_max_offset": "3",
+ "he_spr_srg_bss_colors": "1 2 10 63",
+ "he_spr_srg_partial_bssid": "0 1 3 63",
+ "he_oper_chwidth": "0",
+ "he_oper_centr_freq_seg0_idx": "1",
+ "he_oper_centr_freq_seg1_idx": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ if hapd.get_status_field("ieee80211ax") != "1":
+ raise Exception("STATUS did not indicate ieee80211ax=1")
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="2412")
+
+def he_supported():
+ cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
+ reg = cmd.stdout.read().decode()
+ if "@ 80)" in reg or "@ 160)" in reg:
+ return True
+ return False
+
+def test_he80(dev, apdev):
+ """HE with 80 MHz channel width"""
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "1",
+ "vht_capab": "[MAX-MPDU-11454]",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ est = dev[0].get_bss(bssid)['est_throughput']
+ if est != "390001":
+ raise Exception("Unexpected BSS est_throughput: " + est)
+ status = dev[0].get_status()
+ if status["ieee80211ac"] != "1":
+ raise Exception("Unexpected STATUS ieee80211ac value (STA)")
+ status = hapd.get_status()
+ logger.info("hostapd STATUS: " + str(status))
+ if status["ieee80211n"] != "1":
+ raise Exception("Unexpected STATUS ieee80211n value")
+ if status["ieee80211ac"] != "1":
+ raise Exception("Unexpected STATUS ieee80211ac value")
+ if status["ieee80211ax"] != "1":
+ raise Exception("Unexpected STATUS ieee80211ax value")
+ if status["secondary_channel"] != "1":
+ raise Exception("Unexpected STATUS secondary_channel value")
+ if status["vht_oper_chwidth"] != "1":
+ raise Exception("Unexpected STATUS vht_oper_chwidth value")
+ if status["vht_oper_centr_freq_seg0_idx"] != "42":
+ raise Exception("Unexpected STATUS vht_oper_centr_freq_seg0_idx value")
+ if "vht_caps_info" not in status:
+ raise Exception("Missing vht_caps_info")
+ if status["he_oper_chwidth"] != "1":
+ raise Exception("Unexpected STATUS he_oper_chwidth value")
+ if status["he_oper_centr_freq_seg0_idx"] != "42":
+ raise Exception("Unexpected STATUS he_oper_centr_freq_seg0_idx value")
+
+ sta = hapd.get_sta(dev[0].own_addr())
+ logger.info("hostapd STA: " + str(sta))
+ if "[HT]" not in sta['flags']:
+ raise Exception("Missing STA flag: HT")
+ if "[VHT]" not in sta['flags']:
+ raise Exception("Missing STA flag: VHT")
+ if "[HE]" not in sta['flags']:
+ raise Exception("Missing STA flag: HE")
+
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def _test_he_wifi_generation(dev, apdev, conf, scan_freq):
+ """HE and wifi_generation"""
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "FI",
+ "ieee80211n": "1",
+ "ieee80211ax": "1"}
+ params.update(conf)
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("he", key_mgmt="NONE", scan_freq=scan_freq)
+ status = dev[0].get_status()
+ if 'wifi_generation' not in status:
+ # For now, assume this is because of missing kernel support
+ raise HwsimSkip("Association Request IE reporting not supported")
+ #raise Exception("Missing wifi_generation information")
+ if status['wifi_generation'] != "6":
+ raise Exception("Unexpected wifi_generation value: " + status['wifi_generation'])
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("he", key_mgmt="NONE", scan_freq=scan_freq)
+ status = wpas.get_status()
+ if 'wifi_generation' not in status:
+ # For now, assume this is because of missing kernel support
+ raise HwsimSkip("Association Request IE reporting not supported")
+ #raise Exception("Missing wifi_generation information (connect)")
+ if status['wifi_generation'] != "6":
+ raise Exception("Unexpected wifi_generation value (connect): " + status['wifi_generation'])
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_he_wifi_generation(dev, apdev):
+ conf = {
+ "vht_oper_chwidth": "1",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "42",
+ "vht_capab": "[MAX-MPDU-11454]",
+ "ieee80211ac": "1",
+ }
+ _test_he_wifi_generation(dev, apdev, conf, "5180")
+
+def test_he_wifi_generation_24(dev, apdev):
+ conf = {
+ "hw_mode": "g",
+ "channel": "1",
+ }
+ _test_he_wifi_generation(dev, apdev, conf, "2412")
+
+def he80_test(apdev, dev, channel, ht_capab):
+ clear_scan_cache(apdev)
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": str(channel),
+ "ht_capab": ht_capab,
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev, params)
+ bssid = apdev['bssid']
+
+ dev[0].connect("he", key_mgmt="NONE",
+ scan_freq=str(5000 + 5 * channel))
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_he80b(dev, apdev):
+ """HE with 80 MHz channel width (HT40- channel 40)"""
+ he80_test(apdev[0], dev, 40, "[HT40-]")
+
+def test_he80c(dev, apdev):
+ """HE with 80 MHz channel width (HT40+ channel 44)"""
+ he80_test(apdev[0], dev, 44, "[HT40+]")
+
+def test_he80d(dev, apdev):
+ """HE with 80 MHz channel width (HT40- channel 48)"""
+ he80_test(apdev[0], dev, 48, "[HT40-]")
+
+def test_he80_params(dev, apdev):
+ """HE with 80 MHz channel width and number of optional features enabled"""
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+][SHORT-GI-40][DSS_CCK-40]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "1",
+ "vht_capab": "[MAX-MPDU-11454][RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-STBC-1][MAX-A-MPDU-LEN-EXP0]",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "require_vht": "1",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "42",
+ "he_su_beamformer": "1",
+ "he_mu_beamformer": "1",
+ "he_bss_color":"1",
+ "he_default_pe_duration":"1",
+ "he_twt_required":"1",
+ "he_rts_threshold":"1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[1].connect("he", key_mgmt="NONE", scan_freq="5180",
+ disable_vht="1", wait_connect=False)
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="5180")
+ dev[2].connect("he", key_mgmt="NONE", scan_freq="5180",
+ disable_sgi="1")
+ ev = dev[1].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+ if ev is None:
+ raise Exception("Association rejection timed out")
+ if "status_code=104" not in ev:
+ raise Exception("Unexpected rejection status code")
+ dev[1].request("DISCONNECT")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sta0 = hapd.get_sta(dev[0].own_addr())
+ sta2 = hapd.get_sta(dev[2].own_addr())
+ capab0 = int(sta0['vht_caps_info'], base=16)
+ capab2 = int(sta2['vht_caps_info'], base=16)
+ if capab0 & 0x60 == 0:
+ raise Exception("dev[0] did not support SGI")
+ if capab2 & 0x60 != 0:
+ raise Exception("dev[2] claimed support for SGI")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev, count=3)
+
+def test_he80_invalid(dev, apdev):
+ """HE with invalid 80 MHz channel configuration (seg1)"""
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "vht_oper_centr_freq_seg1_idx": "159",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "42",
+ "he_oper_centr_freq_seg1_idx": "155",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ # This fails due to unexpected seg1 configuration
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("AP-DISABLED not reported")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_he80_invalid2(dev, apdev):
+ """HE with invalid 80 MHz channel configuration (seg0)"""
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "46",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ # This fails due to invalid seg0 configuration
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("AP-DISABLED not reported")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_he_20(devs, apdevs):
+ """HE and 20 MHz channel"""
+ dev = devs[0]
+ ap = apdevs[0]
+ try:
+ hapd = None
+ params = {"ssid": "test-he20",
+ "country_code": "DE",
+ "hw_mode": "a",
+ "channel": "36",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "ht_capab": "",
+ "vht_capab": "",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "0",
+ "supported_rates": "60 120 240 360 480 540",
+ "require_vht": "1",
+ "he_oper_chwidth": "0",
+ "he_oper_centr_freq_seg0_idx": "0"}
+ hapd = hostapd.add_ap(ap, params)
+ dev.connect("test-he20", scan_freq="5180", key_mgmt="NONE")
+ hwsim_utils.test_connectivity(dev, hapd)
+ finally:
+ dev.request("DISCONNECT")
+ clear_regdom(hapd, devs)
+
+def test_he_40(devs, apdevs):
+ """HE and 40 MHz channel"""
+ dev = devs[0]
+ ap = apdevs[0]
+ try:
+ hapd = None
+ params = {"ssid": "test-he40",
+ "country_code": "DE",
+ "hw_mode": "a",
+ "channel": "36",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "38",
+ "he_oper_chwidth": "0",
+ "he_oper_centr_freq_seg0_idx": "38",
+ "he_su_beamformer": "1",
+ "he_mu_beamformer": "1"}
+ hapd = hostapd.add_ap(ap, params)
+ dev.connect("test-he40", scan_freq="5180", key_mgmt="NONE")
+ hwsim_utils.test_connectivity(dev, hapd)
+ finally:
+ dev.request("DISCONNECT")
+ clear_regdom(hapd, devs)
+
+@long_duration_test
+def test_he160(dev, apdev):
+ """HE with 160 MHz channel width (1)"""
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "[VHT160]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "50",
+ "he_oper_chwidth": "2",
+ "he_oper_centr_freq_seg0_idx": "50",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ if "DFS-CAC-START" not in ev:
+ raise Exception("Unexpected DFS event")
+
+ state = hapd.get_status_field("state")
+ if state != "DFS":
+ if state == "DISABLED" and not os.path.exists("dfs"):
+ # Not all systems have recent enough CRDA version and
+ # wireless-regdb changes to support 160 MHz and DFS. For now,
+ # do not report failures for this test case.
+ raise HwsimSkip("CRDA or wireless-regdb did not support 160 MHz")
+ raise Exception("Unexpected interface state: " + state)
+
+ logger.info("Waiting for CAC to complete")
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected DFS freq result")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state")
+
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="5180")
+ dev[0].wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=160 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+@long_duration_test
+def test_he160b(dev, apdev):
+ """HE with 160 MHz channel width (2)"""
+ try:
+ hapd = None
+
+ params = {"ssid": "he",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "104",
+ "ht_capab": "[HT40-]",
+ "vht_capab": "[VHT160]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "114",
+ "he_oper_chwidth": "2",
+ "he_oper_centr_freq_seg0_idx": "114",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[1], params, wait_enabled=False)
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-START", 5)
+ if "DFS-CAC-START" not in ev:
+ raise Exception("Unexpected DFS event(2)")
+
+ state = hapd.get_status_field("state")
+ if state != "DFS":
+ if state == "DISABLED" and not os.path.exists("dfs"):
+ # Not all systems have recent enough CRDA version and
+ # wireless-regdb changes to support 160 MHz and DFS. For now,
+ # do not report failures for this test case.
+ raise HwsimSkip("CRDA or wireless-regdb did not support 160 MHz")
+ raise Exception("Unexpected interface state: " + state)
+
+ logger.info("Waiting for CAC to complete")
+
+ ev = wait_dfs_event(hapd, "DFS-CAC-COMPLETED", 70)
+ if "success=1" not in ev:
+ raise Exception("CAC failed(2)")
+ if "freq=5520" not in ev:
+ raise Exception("Unexpected DFS freq result(2)")
+
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out(2)")
+
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state(2)")
+
+ freq = hapd.get_status_field("freq")
+ if freq != "5520":
+ raise Exception("Unexpected frequency(2)")
+
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="5520")
+ dev[0].wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5520" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=160 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def test_he160_no_dfs_100_plus(dev, apdev):
+ """HE with 160 MHz channel width and no DFS (100 plus)"""
+ run_ap_he160_no_dfs(dev, apdev, "100", "[HT40+]")
+
+def test_he160_no_dfs(dev, apdev):
+ """HE with 160 MHz channel width and no DFS (104 minus)"""
+ run_ap_he160_no_dfs(dev, apdev, "104", "[HT40-]")
+
+def test_he160_no_dfs_108_plus(dev, apdev):
+ """HE with 160 MHz channel width and no DFS (108 plus)"""
+ run_ap_he160_no_dfs(dev, apdev, "108", "[HT40+]")
+
+def test_he160_no_dfs_112_minus(dev, apdev):
+ """HE with 160 MHz channel width and no DFS (112 minus)"""
+ run_ap_he160_no_dfs(dev, apdev, "112", "[HT40-]")
+
+def test_he160_no_dfs_116_plus(dev, apdev):
+ """HE with 160 MHz channel width and no DFS (116 plus)"""
+ run_ap_he160_no_dfs(dev, apdev, "116", "[HT40+]")
+
+def test_he160_no_dfs_120_minus(dev, apdev):
+ """HE with 160 MHz channel width and no DFS (120 minus)"""
+ run_ap_he160_no_dfs(dev, apdev, "120", "[HT40-]")
+
+def test_he160_no_dfs_124_plus(dev, apdev):
+ """HE with 160 MHz channel width and no DFS (124 plus)"""
+ run_ap_he160_no_dfs(dev, apdev, "124", "[HT40+]")
+
+def test_he160_no_dfs_128_minus(dev, apdev):
+ """HE with 160 MHz channel width and no DFS (128 minus)"""
+ run_ap_he160_no_dfs(dev, apdev, "128", "[HT40-]")
+
+def run_ap_he160_no_dfs(dev, apdev, channel, ht_capab):
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "ZA",
+ "hw_mode": "a",
+ "channel": channel,
+ "ht_capab": ht_capab,
+ "vht_capab": "[VHT160]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "114",
+ "he_oper_chwidth": "2",
+ "he_oper_centr_freq_seg0_idx": "114",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-ENABLED"], timeout=2)
+ if not ev:
+ cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
+ reg = cmd.stdout.readlines()
+ for r in reg:
+ if b"5490" in r and b"DFS" in r:
+ raise HwsimSkip("ZA regulatory rule did not have DFS requirement removed")
+ raise Exception("AP setup timed out")
+
+ freq = str(int(channel) * 5 + 5000)
+ dev[0].connect("he", key_mgmt="NONE", scan_freq=freq)
+ dev[0].wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=" + freq not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=160 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_he160_no_ht40(dev, apdev):
+ """HE with 160 MHz channel width and HT40 disabled"""
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "ZA",
+ "hw_mode": "a",
+ "channel": "108",
+ "ht_capab": "",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "114",
+ "he_oper_chwidth": "2",
+ "he_oper_centr_freq_seg0_idx": "114",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=2)
+ if not ev:
+ cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
+ reg = cmd.stdout.readlines()
+ for r in reg:
+ if "5490" in r and "DFS" in r:
+ raise HwsimSkip("ZA regulatory rule did not have DFS requirement removed")
+ raise Exception("AP setup timed out")
+ if "AP-ENABLED" in ev:
+ # This was supposed to fail due to sec_channel_offset == 0
+ raise Exception("Unexpected AP-ENABLED")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_he80plus80(dev, apdev):
+ """HE with 80+80 MHz channel width"""
+ try:
+ hapd = None
+ hapd2 = None
+ params = {"ssid": "he",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "52",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "[VHT160-80PLUS80]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "3",
+ "vht_oper_centr_freq_seg0_idx": "58",
+ "vht_oper_centr_freq_seg1_idx": "155",
+ "he_oper_chwidth": "3",
+ "he_oper_centr_freq_seg0_idx": "58",
+ "he_oper_centr_freq_seg1_idx": "155",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ # This will actually fail since DFS on 80+80 is not yet supported
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+ # ignore result to avoid breaking the test once 80+80 DFS gets enabled
+
+ params = {"ssid": "he2",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "[VHT160-80PLUS80]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "3",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "vht_oper_centr_freq_seg1_idx": "155",
+ "he_oper_chwidth": "3",
+ "he_oper_centr_freq_seg0_idx": "42",
+ "he_oper_centr_freq_seg1_idx": "155"}
+ hapd2 = hostapd.add_ap(apdev[1], params, wait_enabled=False)
+
+ ev = hapd2.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=5)
+ if not ev:
+ raise Exception("AP setup timed out(2)")
+ if "AP-DISABLED" in ev:
+ # Assume this failed due to missing regulatory update for now
+ raise HwsimSkip("80+80 MHz channel not supported in regulatory information")
+
+ state = hapd2.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("Unexpected interface state(2)")
+
+ dev[1].connect("he2", key_mgmt="NONE", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[1], hapd2)
+ sig = dev[1].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80+80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "CENTER_FRQ1=5210" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+ if "CENTER_FRQ2=5775" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(4): " + str(sig))
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ if hapd2:
+ hapd2.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_he80plus80_invalid(dev, apdev):
+ """HE with invalid 80+80 MHz channel"""
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "3",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "vht_oper_centr_freq_seg1_idx": "0",
+ "he_oper_chwidth": "3",
+ "he_oper_centr_freq_seg0_idx": "42",
+ "he_oper_centr_freq_seg1_idx": "0",
+ 'ieee80211d': '1',
+ 'ieee80211h': '1'}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ # This fails due to missing(invalid) seg1 configuration
+ ev = hapd.wait_event(["AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("AP-DISABLED not reported")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80/160 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_he80_csa(dev, apdev):
+ """HE with 80 MHz channel width and CSA"""
+ csa_supported(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "US",
+ "hw_mode": "a",
+ "channel": "149",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "155",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "155"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="5745")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5180 ht vht he blocktx center_freq1=5210 sec_channel_offset=1 bandwidth=80")
+ ev = hapd.wait_event(["CTRL-EVENT-STARTED-CHANNEL-SWITCH"], timeout=10)
+ if ev is None:
+ raise Exception("Channel switch start event not seen")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected channel in CS started")
+ ev = hapd.wait_event(["CTRL-EVENT-CHANNEL-SWITCH"], timeout=10)
+ if ev is None:
+ raise Exception("Channel switch completion event not seen")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected channel in CS completed")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5180" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ time.sleep(0.5)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("CHAN_SWITCH 5 5745")
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=5745" not in ev:
+ raise Exception("Unexpected channel in CSA finished event")
+ time.sleep(0.5)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ # This CSA to same channel will fail in kernel, so use this only for
+ # extra code coverage.
+ hapd.request("CHAN_SWITCH 5 5745")
+ hapd.wait_event(["AP-CSA-FINISHED"], timeout=1)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_he_on_24ghz(dev, apdev):
+ """Subset of HE features on 2.4 GHz"""
+ hapd = None
+ params = {"ssid": "test-he-2g",
+ "hw_mode": "g",
+ "channel": "1",
+ "ieee80211n": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "1",
+ "he_oper_chwidth": "0",
+ "he_oper_centr_freq_seg0_idx": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ dev[0].connect("test-he-2g", scan_freq="2412", key_mgmt="NONE")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sta = hapd.get_sta(dev[0].own_addr())
+
+ dev[1].connect("test-he-2g", scan_freq="2412", key_mgmt="NONE")
+ sta = hapd.get_sta(dev[1].own_addr())
+
+ finally:
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ if hapd:
+ hapd.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_he80_pwr_constraint(dev, apdev):
+ """HE with 80 MHz channel width and local power constraint"""
+ hapd = None
+ try:
+ params = {"ssid": "he",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211d": "1",
+ "local_pwr_constraint": "3",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="5180")
+ dev[0].wait_regdom(country_ie=True)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def test_he_use_sta_nsts(dev, apdev):
+ """HE with 80 MHz channel width and use_sta_nsts=1"""
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "42",
+ "use_sta_nsts": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_he_tkip(dev, apdev):
+ """HE and TKIP"""
+ skip_without_tkip(dev[0])
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "wpa": "1",
+ "wpa_key_mgmt": "WPA-PSK",
+ "wpa_pairwise": "TKIP",
+ "wpa_passphrase": "12345678",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("he", psk="12345678", scan_freq="5180")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=20 MHz (no HT)" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ status = hapd.get_status()
+ logger.info("hostapd STATUS: " + str(status))
+ if status["ieee80211n"] != "0":
+ raise Exception("Unexpected STATUS ieee80211n value")
+ if status["ieee80211ac"] != "0":
+ raise Exception("Unexpected STATUS ieee80211ac value")
+ if status["ieee80211ax"] != "0":
+ raise Exception("Unexpected STATUS ieee80211ax value")
+ if status["secondary_channel"] != "0":
+ raise Exception("Unexpected STATUS secondary_channel value")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_he_40_fallback_to_20(devs, apdevs):
+ """HE and 40 MHz channel configuration falling back to 20 MHz"""
+ dev = devs[0]
+ ap = apdevs[0]
+ try:
+ hapd = None
+ params = {"ssid": "test-he40",
+ "country_code": "US",
+ "hw_mode": "a",
+ "basic_rates": "60 120 240",
+ "channel": "161",
+ "ieee80211d": "1",
+ "ieee80211h": "1",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "ht_capab": "[HT40+][SHORT-GI-20][SHORT-GI-40][DSSS_CCK-40]",
+ "vht_capab": "[RXLDPC][SHORT-GI-80][TX-STBC-2BY1][RX-STBC1][MAX-MPDU-11454][MAX-A-MPDU-LEN-EXP7]",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "155",
+ "he_oper_chwidth": "0",
+ "he_oper_centr_freq_seg0_idx": "155"}
+ hapd = hostapd.add_ap(ap, params)
+ dev.connect("test-he40", scan_freq="5805", key_mgmt="NONE")
+ dev.wait_regdom(country_ie=True)
+ hwsim_utils.test_connectivity(dev, hapd)
+ finally:
+ clear_regdom(hapd, devs)
+
+def test_he80_to_24g_he(dev, apdev):
+ """HE with 80 MHz channel width reconfigured to 2.4 GHz HE"""
+ try:
+ hapd = None
+ params = {"ssid": "he",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ieee80211ax": "1",
+ "vht_oper_chwidth": "1",
+ "vht_capab": "[MAX-MPDU-11454]",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "he_oper_chwidth": "1",
+ "he_oper_centr_freq_seg0_idx": "42"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ hapd.disable()
+ hapd.set("ieee80211ac", "0")
+ hapd.set("hw_mode", "g")
+ hapd.set("channel", "1")
+ hapd.set("ht_capab", "")
+ hapd.set("vht_capab", "")
+ hapd.set("he_oper_chwidth", "")
+ hapd.set("he_oper_centr_freq_seg0_idx", "")
+ hapd.set("vht_oper_chwidth", "")
+ hapd.set("vht_oper_centr_freq_seg0_idx", "")
+ hapd.enable()
+
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="2412")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not he_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_he_twt(dev, apdev):
+ """HE and TWT"""
+ params = {"ssid": "he",
+ "ieee80211ax": "1",
+ "he_bss_color": "42",
+ "he_twt_required":"1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("he", key_mgmt="NONE", scan_freq="2412")
+ if "OK" not in dev[0].request("TWT_SETUP"):
+ raise Exception("TWT_SETUP failed")
+ if "OK" not in dev[0].request("TWT_TEARDOWN"):
+ raise Exception("TWT_SETUP failed")
+ if "OK" not in dev[0].request("TWT_SETUP dialog=123 exponent=9 mantissa=10 min_twt=254 setup_cmd=1 twt=1234567890 requestor=1 trigger=0 implicit=0 flow_type=0 flow_id=2 protection=1 twt_channel=3 control=16"):
+ raise Exception("TWT_SETUP failed")
+ if "OK" not in dev[0].request("TWT_TEARDOWN flags=255"):
+ raise Exception("TWT_SETUP failed")
+
+def test_he_6ghz_security(dev, apdev):
+ """HE AP and 6 GHz security parameter validation"""
+ params = {"ssid": "he",
+ "ieee80211ax": "1",
+ "op_class": "131",
+ "channel": "1"}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+
+ # Pre-RSNA security methods are not allowed in 6 GHz
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Invalid configuration accepted(1)")
+
+ # Management frame protection is required in 6 GHz"
+ hapd.set("wpa", "2")
+ hapd.set("wpa_passphrase", "12345678")
+ hapd.set("wpa_key_mgmt", "SAE")
+ hapd.set("rsn_pairwise", "CCMP")
+ hapd.set("ieee80211w", "1")
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Invalid configuration accepted(2)")
+
+ # Invalid AKM suite for 6 GHz
+ hapd.set("ieee80211w", "2")
+ hapd.set("wpa_key_mgmt", "SAE WPA-PSK")
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Invalid configuration accepted(3)")
+
+ # Invalid pairwise cipher suite for 6 GHz
+ hapd.set("wpa_key_mgmt", "SAE")
+ hapd.set("rsn_pairwise", "CCMP TKIP")
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Invalid configuration accepted(4)")
+
+ # Invalid group cipher suite for 6 GHz
+ hapd.set("wpa_key_mgmt", "SAE")
+ hapd.set("rsn_pairwise", "CCMP")
+ hapd.set("group_cipher", "TKIP")
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Invalid configuration accepted(5)")
diff --git a/contrib/wpa/tests/hwsim/test_hostapd_oom.py b/contrib/wpa/tests/hwsim/test_hostapd_oom.py
new file mode 100644
index 000000000000..169ae015f8fd
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_hostapd_oom.py
@@ -0,0 +1,173 @@
+# hostapd and out-of-memory error paths
+# Copyright (c) 2015, Jouni Malinen
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import time
+
+import hostapd
+from utils import *
+
+def hostapd_oom_loop(apdev, params, start_func="main"):
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "ctrl"})
+
+ count = 0
+ for i in range(1, 1000):
+ if "OK" not in hapd.request("TEST_ALLOC_FAIL %d:%s" % (i, start_func)):
+ raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+ try:
+ hostapd.add_ap(apdev[1], params, timeout=2.5)
+ logger.info("Iteration %d - success" % i)
+ hostapd.remove_bss(apdev[1])
+
+ state = hapd.request('GET_ALLOC_FAIL')
+ logger.info("GET_ALLOC_FAIL: " + state)
+ hapd.request("TEST_ALLOC_FAIL 0:")
+ if i < 3:
+ raise Exception("AP setup succeeded during out-of-memory")
+ if state.startswith('0:'):
+ count = 0
+ else:
+ count += 1
+ if count == 5:
+ break
+ except Exception as e:
+ logger.info("Iteration %d - %s" % (i, str(e)))
+
+@remote_compatible
+def test_hostapd_oom_open(dev, apdev):
+ """hostapd failing to setup open mode due to OOM"""
+ params = {"ssid": "open"}
+ hostapd_oom_loop(apdev, params)
+
+def test_hostapd_oom_wpa2_psk(dev, apdev):
+ """hostapd failing to setup WPA2-PSK mode due to OOM"""
+ params = hostapd.wpa2_params(ssid="test", passphrase="12345678")
+ params['wpa_psk_file'] = 'hostapd.wpa_psk'
+ hostapd_oom_loop(apdev, params)
+
+ tests = ["hostapd_config_read_wpa_psk", "hostapd_derive_psk"]
+ for t in tests:
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "ctrl"})
+ hapd.request("TEST_ALLOC_FAIL 1:%s" % t)
+ try:
+ hostapd.add_ap(apdev[1], params, timeout=2.5)
+ raise Exception("Unexpected add_ap() success during OOM")
+ except Exception as e:
+ if "Failed to enable hostapd" in str(e):
+ pass
+ else:
+ raise
+ state = hapd.request('GET_ALLOC_FAIL')
+ if state != "0:%s" % t:
+ raise Exception("OOM not triggered")
+
+@remote_compatible
+def test_hostapd_oom_wpa2_eap(dev, apdev):
+ """hostapd failing to setup WPA2-EAP mode due to OOM"""
+ params = hostapd.wpa2_eap_params(ssid="test")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hostapd_oom_loop(apdev, params)
+
+@remote_compatible
+def test_hostapd_oom_wpa2_eap_radius(dev, apdev):
+ """hostapd failing to setup WPA2-EAP mode due to OOM in RADIUS"""
+ params = hostapd.wpa2_eap_params(ssid="test")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hostapd_oom_loop(apdev, params, start_func="accounting_init")
+
+def test_hostapd_oom_wpa2_psk_connect(dev, apdev):
+ """hostapd failing during WPA2-PSK mode connection due to OOM"""
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SCAN_INTERVAL 1")
+ count = 0
+ for i in range(1, 1000):
+ logger.info("Iteration %d" % i)
+ if "OK" not in hapd.request("TEST_ALLOC_FAIL %d:main" % i):
+ raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+ id = dev[0].connect("test-wpa2-psk", psk="12345678",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=5)
+ if ev is None:
+ logger.info("Timeout while waiting for connection in iteration %d" % i)
+ dev[0].request("REMOVE_NETWORK all")
+ time.sleep(0.1)
+ else:
+ if "CTRL-EVENT-SSID-TEMP-DISABLED" in ev:
+ logger.info("Re-select to avoid long wait for temp disavle")
+ dev[0].select_network(id)
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ for i in range(3):
+ dev[i].dump_monitor()
+ hapd.dump_monitor()
+
+ state = hapd.request('GET_ALLOC_FAIL')
+ logger.info("GET_ALLOC_FAIL: " + state)
+ hapd.request("TEST_ALLOC_FAIL 0:")
+ if state.startswith('0:'):
+ count = 0
+ else:
+ count += 1
+ if count == 5:
+ break
+ dev[0].request("SCAN_INTERVAL 5")
+
+@long_duration_test
+def test_hostapd_oom_wpa2_eap_connect(dev, apdev):
+ """hostapd failing during WPA2-EAP mode connection due to OOM"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SCAN_INTERVAL 1")
+ count = 0
+ for i in range(1, 1000):
+ logger.info("Iteration %d" % i)
+ if "OK" not in hapd.request("TEST_ALLOC_FAIL %d:main" % i):
+ raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+ id = dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=5)
+ if ev is None:
+ logger.info("Timeout while waiting for connection in iteration %d" % i)
+ dev[0].request("REMOVE_NETWORK all")
+ time.sleep(0.1)
+ else:
+ if "CTRL-EVENT-SSID-TEMP-DISABLED" in ev:
+ logger.info("Re-select to avoid long wait for temp disavle")
+ dev[0].select_network(id)
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ for i in range(3):
+ dev[i].dump_monitor()
+ hapd.dump_monitor()
+
+ state = hapd.request('GET_ALLOC_FAIL')
+ logger.info("GET_ALLOC_FAIL: " + state)
+ hapd.request("TEST_ALLOC_FAIL 0:")
+ if state.startswith('0:'):
+ count = 0
+ else:
+ count += 1
+ if count == 5:
+ break
+ dev[0].request("SCAN_INTERVAL 5")
diff --git a/contrib/wpa/tests/hwsim/test_hs20_filter.py b/contrib/wpa/tests/hwsim/test_hs20_filter.py
new file mode 100644
index 000000000000..11cf34756319
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_hs20_filter.py
@@ -0,0 +1,205 @@
+# Hotspot 2.0 filtering tests
+# Copyright (c) 2015, Intel Deutschland GmbH
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hostapd
+import hwsim_utils
+import socket
+import subprocess
+import binascii
+from utils import HwsimSkip, require_under_vm
+import os
+import time
+from test_ap_hs20 import build_arp, build_na, hs20_ap_params
+from test_ap_hs20 import interworking_select, interworking_connect
+import struct
+import logging
+logger = logging.getLogger()
+
+class IPAssign(object):
+ def __init__(self, iface, addr, ipv6=False):
+ self._iface = iface
+ self._addr = addr
+ self._cmd = ['ip']
+ if ipv6:
+ self._cmd.append('-6')
+ self._cmd.append('addr')
+ self._ipv6 = ipv6
+ def __enter__(self):
+ subprocess.call(self._cmd + ['add', self._addr, 'dev', self._iface])
+ if self._ipv6:
+ # wait for DAD to finish
+ while True:
+ o = subprocess.check_output(self._cmd + ['show', 'tentative', 'dev', self._iface]).decode()
+ if self._addr not in o:
+ break
+ time.sleep(0.1)
+ def __exit__(self, type, value, traceback):
+ subprocess.call(self._cmd + ['del', self._addr, 'dev', self._iface])
+
+def hs20_filters_connect(dev, apdev, disable_dgaf=False, proxy_arp=False):
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+
+ # Do not disable dgaf, to test that the station drops unicast IP packets
+ # encrypted with GTK.
+ params['disable_dgaf'] = '0'
+ params['proxy_arp'] = '1'
+ params['ap_isolate'] = '1'
+ params['bridge'] = 'ap-br0'
+
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except:
+ # For now, do not report failures due to missing kernel support.
+ raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in the kernel")
+
+ subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+ dev[0].hs20_enable()
+
+ id = dev[0].add_cred_values({'realm': "example.com",
+ 'username': "hs20-test",
+ 'password': "password",
+ 'ca_cert': "auth_serv/ca.pem",
+ 'domain': "example.com",
+ 'update_identifier': "1234"})
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+
+ time.sleep(0.1)
+
+ return dev[0], hapd
+
+def _test_ip4_gtk_drop(devs, apdevs, params, dst):
+ require_under_vm()
+ procfile = '/proc/sys/net/ipv4/conf/%s/drop_unicast_in_l2_multicast' % devs[0].ifname
+ if not os.path.exists(procfile):
+ raise HwsimSkip("kernel doesn't have capability")
+
+ [dev, hapd] = hs20_filters_connect(devs, apdevs)
+ with IPAssign(dev.ifname, '10.0.0.1/24'):
+ s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ s.bind(("10.0.0.1", 12345))
+ s.settimeout(0.1)
+
+ pkt = dst
+ pkt += hapd.own_addr().replace(':', '')
+ pkt += '0800'
+ pkt += '45000020786840004011ae600a0000040a000001'
+ pkt += '30393039000c0000'
+ pkt += '61736466' # "asdf"
+ if "OK" not in hapd.request('DATA_TEST_FRAME ' + pkt):
+ raise Exception("DATA_TEST_FRAME failed")
+ try:
+ logger.info(s.recvfrom(1024))
+ logger.info("procfile=" + procfile + " val=" + open(procfile, 'r').read().rstrip())
+ raise Exception("erroneously received frame!")
+ except socket.timeout:
+ # this is the expected behaviour
+ pass
+
+def test_ip4_gtk_drop_bcast(devs, apdevs, params):
+ """Hotspot 2.0 frame filtering - IPv4 GTK drop broadcast"""
+ _test_ip4_gtk_drop(devs, apdevs, params, dst='ffffffffffff')
+
+def test_ip4_gtk_drop_mcast(devs, apdevs, params):
+ """Hotspot 2.0 frame filtering - IPv4 GTK drop multicast"""
+ _test_ip4_gtk_drop(devs, apdevs, params, dst='ff0000000000')
+
+def _test_ip6_gtk_drop(devs, apdevs, params, dst):
+ require_under_vm()
+ dev = devs[0]
+ procfile = '/proc/sys/net/ipv6/conf/%s/drop_unicast_in_l2_multicast' % devs[0].ifname
+ if not os.path.exists(procfile):
+ raise HwsimSkip("kernel doesn't have capability")
+
+ [dev, hapd] = hs20_filters_connect(devs, apdevs)
+
+ with IPAssign(dev.ifname, 'fdaa::1/48', ipv6=True):
+ s = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
+ s.bind(("fdaa::1", 12345))
+ s.settimeout(0.1)
+
+ pkt = dst
+ pkt += hapd.own_addr().replace(':', '')
+ pkt += '86dd'
+ pkt += '60000000000c1140fdaa0000000000000000000000000002fdaa0000000000000000000000000001'
+ pkt += '30393039000cde31'
+ pkt += '61736466' # "asdf"
+ if "OK" not in hapd.request('DATA_TEST_FRAME ' + pkt):
+ raise Exception("DATA_TEST_FRAME failed")
+ try:
+ logger.info(s.recvfrom(1024))
+ logger.info("procfile=" + procfile + " val=" + open(procfile, 'r').read().rstrip())
+ raise Exception("erroneously received frame!")
+ except socket.timeout:
+ # this is the expected behaviour
+ pass
+
+def test_ip6_gtk_drop_bcast(devs, apdevs, params):
+ """Hotspot 2.0 frame filtering - IPv6 GTK drop broadcast"""
+ _test_ip6_gtk_drop(devs, apdevs, params, dst='ffffffffffff')
+
+def test_ip6_gtk_drop_mcast(devs, apdevs, params):
+ """Hotspot 2.0 frame filtering - IPv6 GTK drop multicast"""
+ _test_ip6_gtk_drop(devs, apdevs, params, dst='ff0000000000')
+
+def test_ip4_drop_gratuitous_arp(devs, apdevs, params):
+ """Hotspot 2.0 frame filtering - IPv4 drop gratuitous ARP"""
+ require_under_vm()
+ procfile = '/proc/sys/net/ipv4/conf/%s/drop_gratuitous_arp' % devs[0].ifname
+ if not os.path.exists(procfile):
+ raise HwsimSkip("kernel doesn't have capability")
+
+ [dev, hapd] = hs20_filters_connect(devs, apdevs)
+
+ with IPAssign(dev.ifname, '10.0.0.2/24'):
+ # add an entry that can be updated by gratuitous ARP
+ subprocess.call(['ip', 'neigh', 'add', '10.0.0.1', 'lladdr', '02:00:00:00:00:ff', 'nud', 'reachable', 'dev', dev.ifname])
+ # wait for lock-time
+ time.sleep(1)
+ try:
+ ap_addr = hapd.own_addr()
+ cl_addr = dev.own_addr()
+ pkt = build_arp(cl_addr, ap_addr, 2, ap_addr, '10.0.0.1', ap_addr, '10.0.0.1')
+ pkt = binascii.hexlify(pkt).decode()
+
+ if "OK" not in hapd.request('DATA_TEST_FRAME ' + pkt):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ if hapd.own_addr() in subprocess.check_output(['ip', 'neigh', 'show']).decode():
+ raise Exception("gratuitous ARP frame updated erroneously")
+ finally:
+ subprocess.call(['ip', 'neigh', 'del', '10.0.0.1', 'dev', dev.ifname])
+
+def test_ip6_drop_unsolicited_na(devs, apdevs, params):
+ """Hotspot 2.0 frame filtering - IPv6 drop unsolicited NA"""
+ require_under_vm()
+ procfile = '/proc/sys/net/ipv6/conf/%s/drop_unsolicited_na' % devs[0].ifname
+ if not os.path.exists(procfile):
+ raise HwsimSkip("kernel doesn't have capability")
+
+ [dev, hapd] = hs20_filters_connect(devs, apdevs)
+
+ with IPAssign(dev.ifname, 'fdaa::1/48', ipv6=True):
+ # add an entry that can be updated by unsolicited NA
+ subprocess.call(['ip', '-6', 'neigh', 'add', 'fdaa::2', 'lladdr', '02:00:00:00:00:ff', 'nud', 'reachable', 'dev', dev.ifname])
+ try:
+ ap_addr = hapd.own_addr()
+ cl_addr = dev.own_addr()
+ pkt = build_na(ap_addr, 'fdaa::2', 'ff02::1', 'fdaa::2', flags=0x20,
+ opt=binascii.unhexlify('0201' + ap_addr.replace(':', '')))
+ pkt = binascii.hexlify(pkt).decode()
+
+ if "OK" not in hapd.request('DATA_TEST_FRAME ' + pkt):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ if hapd.own_addr() in subprocess.check_output(['ip', 'neigh', 'show']).decode():
+ raise Exception("unsolicited NA frame updated erroneously")
+ finally:
+ subprocess.call(['ip', '-6', 'neigh', 'del', 'fdaa::2', 'dev', dev.ifname])
diff --git a/contrib/wpa/tests/hwsim/test_hs20_pps_mo.py b/contrib/wpa/tests/hwsim/test_hs20_pps_mo.py
new file mode 100644
index 000000000000..5b0cf12025c8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_hs20_pps_mo.py
@@ -0,0 +1,43 @@
+# Hotspot 2.0 PPS MO tests
+# Copyright (c) 2018, The Linux Foundation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os.path
+import subprocess
+
+import hostapd
+from utils import HwsimSkip
+from test_ap_hs20 import hs20_ap_params, interworking_select, interworking_connect, check_sp_type
+from test_ap_eap import check_eap_capa, check_domain_suffix_match
+
+def check_hs20_osu_client():
+ if not os.path.exists("../../hs20/client/hs20-osu-client"):
+ raise HwsimSkip("No hs20-osu-client available")
+
+def set_pps(pps_mo):
+ res = subprocess.check_output(["../../hs20/client/hs20-osu-client",
+ "set_pps", pps_mo]).decode()
+ logger.info("set_pps result: " + res)
+
+def test_hs20_pps_mo_1(dev, apdev):
+ """Hotspot 2.0 PPS MO with username/password credential"""
+ check_hs20_osu_client()
+ check_eap_capa(dev[0], "MSCHAPV2")
+ check_domain_suffix_match(dev[0])
+ bssid = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid
+ params['nai_realm'] = ["0,w1.fi,13[5:6],21[2:4][5:7]",
+ "0,another.example.com"]
+ params['domain_name'] = "w1.fi"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].hs20_enable()
+ set_pps("pps-mo-1.xml")
+ interworking_select(dev[0], bssid, "home", freq="2412")
+ interworking_connect(dev[0], bssid, "TTLS")
+ check_sp_type(dev[0], "home")
diff --git a/contrib/wpa/tests/hwsim/test_ibss.py b/contrib/wpa/tests/hwsim/test_ibss.py
new file mode 100644
index 000000000000..29ebd8129ff1
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ibss.py
@@ -0,0 +1,601 @@
+# IBSS test cases
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import time
+import re
+import subprocess
+
+import hwsim_utils
+from utils import *
+
+def connect_ibss_cmd(dev, id, freq=2412):
+ dev.dump_monitor()
+ dev.select_network(id, freq=str(freq))
+
+def wait_ibss_connection(dev):
+ logger.info(dev.ifname + " waiting for IBSS start/join to complete")
+ ev = dev.wait_connected(timeout=20,
+ error="Connection to the IBSS timed out")
+ exp = r'<.>(CTRL-EVENT-CONNECTED) - Connection to ([0-9a-f:]*) completed.*'
+ s = re.split(exp, ev)
+ if len(s) < 3:
+ return None
+ return s[2]
+
+def wait_4way_handshake(dev1, dev2):
+ logger.info(dev1.ifname + " waiting for 4-way handshake completion with " + dev2.ifname + " " + dev2.p2p_interface_addr())
+ ev = dev1.wait_event(["IBSS-RSN-COMPLETED " + dev2.p2p_interface_addr()],
+ timeout=20)
+ if ev is None:
+ raise Exception("4-way handshake in IBSS timed out")
+
+def wait_4way_handshake2(dev1, dev2, dev3):
+ logger.info(dev1.ifname + " waiting for 4-way handshake completion with " + dev2.ifname + " " + dev2.p2p_interface_addr() + " and " + dev3.p2p_interface_addr())
+ ev = dev1.wait_event(["IBSS-RSN-COMPLETED " + dev2.p2p_interface_addr(),
+ "IBSS-RSN-COMPLETED " + dev3.p2p_interface_addr()],
+ timeout=20)
+ if ev is None:
+ raise Exception("4-way handshake in IBSS timed out")
+ ev = dev1.wait_event(["IBSS-RSN-COMPLETED " + dev2.p2p_interface_addr(),
+ "IBSS-RSN-COMPLETED " + dev3.p2p_interface_addr()],
+ timeout=20)
+ if ev is None:
+ raise Exception("4-way handshake in IBSS timed out")
+
+def add_ibss(dev, ssid, psk=None, proto=None, key_mgmt=None, pairwise=None,
+ group=None, beacon_int=None, bssid=None, scan_freq=None,
+ wep_key0=None, freq=2412, chwidth=0, group_rekey=0):
+ id = dev.add_network()
+ dev.set_network(id, "mode", "1")
+ dev.set_network(id, "frequency", str(freq))
+ if chwidth > 0:
+ dev.set_network(id, "max_oper_chwidth", str(chwidth))
+ if scan_freq:
+ dev.set_network(id, "scan_freq", str(scan_freq))
+ dev.set_network_quoted(id, "ssid", ssid)
+ if psk:
+ dev.set_network_quoted(id, "psk", psk)
+ if proto:
+ dev.set_network(id, "proto", proto)
+ if key_mgmt:
+ dev.set_network(id, "key_mgmt", key_mgmt)
+ if pairwise:
+ dev.set_network(id, "pairwise", pairwise)
+ if group:
+ dev.set_network(id, "group", group)
+ if beacon_int:
+ dev.set_network(id, "beacon_int", beacon_int)
+ if bssid:
+ dev.set_network(id, "bssid", bssid)
+ if wep_key0:
+ dev.set_network(id, "wep_key0", wep_key0)
+ if group_rekey:
+ dev.set_network(id, "group_rekey", str(group_rekey))
+ dev.request("ENABLE_NETWORK " + str(id) + " no-connect")
+ return id
+
+def add_ibss_rsn(dev, ssid, group_rekey=0, scan_freq=None):
+ return add_ibss(dev, ssid, "12345678", "RSN", "WPA-PSK", "CCMP", "CCMP",
+ group_rekey=group_rekey, scan_freq=scan_freq)
+
+def add_ibss_rsn_tkip(dev, ssid):
+ return add_ibss(dev, ssid, "12345678", "RSN", "WPA-PSK", "TKIP", "TKIP")
+
+def add_ibss_wpa_none(dev, ssid):
+ return add_ibss(dev, ssid, "12345678", "WPA", "WPA-NONE", "TKIP", "TKIP")
+
+def add_ibss_wpa_none_ccmp(dev, ssid):
+ return add_ibss(dev, ssid, "12345678", "WPA", "WPA-NONE", "CCMP", "CCMP")
+
+def test_ibss_rsn(dev):
+ """IBSS RSN"""
+ ssid = "ibss-rsn"
+
+ logger.info("Start IBSS on the first STA")
+ id = add_ibss_rsn(dev[0], ssid)
+ # FIX: For now, this disables HT to avoid a strange issue with mac80211
+ # frame reordering during the final test_connectivity() call. Once that is
+ # figured out, these disable_ht=1 calls should be removed from the test
+ # case.
+ dev[0].set_network(id, "disable_ht", "1")
+ connect_ibss_cmd(dev[0], id)
+ bssid0 = wait_ibss_connection(dev[0])
+
+ logger.info("Join two STAs to the IBSS")
+
+ id = add_ibss_rsn(dev[1], ssid)
+ dev[1].set_network(id, "disable_ht", "1")
+ connect_ibss_cmd(dev[1], id)
+ bssid1 = wait_ibss_connection(dev[1])
+ if bssid0 != bssid1:
+ logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+ # try to merge with a scan
+ dev[1].scan()
+ wait_4way_handshake(dev[0], dev[1])
+ wait_4way_handshake(dev[1], dev[0])
+
+ id = add_ibss_rsn(dev[2], ssid)
+ connect_ibss_cmd(dev[2], id)
+ bssid2 = wait_ibss_connection(dev[2])
+ if bssid0 != bssid2:
+ logger.info("STA0 BSSID " + bssid0 + " differs from STA2 BSSID " + bssid2)
+ # try to merge with a scan
+ dev[2].scan()
+ wait_4way_handshake(dev[0], dev[2])
+ wait_4way_handshake2(dev[2], dev[0], dev[1])
+
+ # Allow some time for all peers to complete key setup
+ time.sleep(3)
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ hwsim_utils.test_connectivity(dev[0], dev[2])
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+
+ dev[1].request("REMOVE_NETWORK all")
+ time.sleep(1)
+ id = add_ibss_rsn(dev[1], ssid)
+ dev[1].set_network(id, "disable_ht", "1")
+ connect_ibss_cmd(dev[1], id)
+ bssid1 = wait_ibss_connection(dev[1])
+ if bssid0 != bssid1:
+ logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+ # try to merge with a scan
+ dev[1].scan()
+ wait_4way_handshake(dev[0], dev[1])
+ wait_4way_handshake(dev[1], dev[0])
+ time.sleep(3)
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+ if "OK" not in dev[0].request("IBSS_RSN " + dev[1].p2p_interface_addr()):
+ raise Exception("IBSS_RSN command failed")
+
+ key_mgmt = dev[0].get_status_field("key_mgmt")
+ if key_mgmt != "WPA2-PSK":
+ raise Exception("Unexpected STATUS key_mgmt: " + key_mgmt)
+
+def test_ibss_rsn_group_rekey(dev):
+ """IBSS RSN group rekeying"""
+ ssid = "ibss-rsn"
+
+ logger.info("Start IBSS on the first STA")
+ id = add_ibss_rsn(dev[0], ssid, group_rekey=4, scan_freq=2412)
+ connect_ibss_cmd(dev[0], id)
+ bssid0 = wait_ibss_connection(dev[0])
+ dev[0].dump_monitor()
+
+ logger.info("Join two STAs to the IBSS")
+
+ dev[1].scan_for_bss(bssid0, freq=2412)
+ id = add_ibss_rsn(dev[1], ssid, scan_freq=2412)
+ connect_ibss_cmd(dev[1], id)
+ bssid1 = wait_ibss_connection(dev[1])
+ if bssid0 != bssid1:
+ raise Exception("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+ wait_4way_handshake(dev[0], dev[1])
+ wait_4way_handshake(dev[1], dev[0])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ ev = dev[1].wait_event(["WPA: Group rekeying completed"], timeout=10)
+ if ev is None:
+ raise Exception("No group rekeying reported")
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def test_ibss_wpa_none(dev):
+ """IBSS WPA-None"""
+ skip_without_tkip(dev[0])
+ skip_without_tkip(dev[1])
+ skip_without_tkip(dev[2])
+ ssid = "ibss-wpa-none"
+
+ logger.info("Start IBSS on the first STA")
+ id = add_ibss_wpa_none(dev[0], ssid)
+ connect_ibss_cmd(dev[0], id)
+ bssid0 = wait_ibss_connection(dev[0])
+
+ # This is a bit ugly, but no one really cares about WPA-None, so there may
+ # not be enough justification to clean this up.. For now, wpa_supplicant
+ # will show two connection events with mac80211_hwsim where the first one
+ # comes with all zeros address.
+ if bssid0 == "00:00:00:00:00:00":
+ logger.info("Waiting for real BSSID on the first STA")
+ bssid0 = wait_ibss_connection(dev[0])
+
+ logger.info("Join two STAs to the IBSS")
+
+ id = add_ibss_wpa_none(dev[1], ssid)
+ connect_ibss_cmd(dev[1], id)
+ id = add_ibss_wpa_none(dev[2], ssid)
+ connect_ibss_cmd(dev[2], id)
+
+ bssid1 = wait_ibss_connection(dev[1])
+ if bssid0 != bssid1:
+ logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+ bssid1 = wait_ibss_connection(dev[1])
+
+ bssid2 = wait_ibss_connection(dev[2])
+ if bssid0 != bssid2:
+ logger.info("STA0 BSSID " + bssid0 + " differs from STA2 BSSID " + bssid2)
+ bssid2 = wait_ibss_connection(dev[2])
+
+ logger.info("bssid0=%s bssid1=%s bssid2=%s" % (bssid0, bssid1, bssid2))
+
+ bss = dev[0].get_bss(bssid0)
+ if not bss:
+ bss = dev[1].get_bss(bssid1)
+ if not bss:
+ raise Exception("Could not find BSS entry for IBSS")
+ if 'flags' not in bss:
+ raise Exception("Could not get BSS flags from BSS table")
+ if "[WPA-None-TKIP]" not in bss['flags']:
+ raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+ # Allow some time for all peers to complete key setup
+ time.sleep(1)
+
+ # This is supposed to work, but looks like WPA-None does not work with
+ # mac80211 currently..
+ try:
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ except Exception as e:
+ logger.info("Ignoring known connectivity failure: " + str(e))
+ try:
+ hwsim_utils.test_connectivity(dev[0], dev[2])
+ except Exception as e:
+ logger.info("Ignoring known connectivity failure: " + str(e))
+ try:
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+ except Exception as e:
+ logger.info("Ignoring known connectivity failure: " + str(e))
+
+ key_mgmt = dev[0].get_status_field("key_mgmt")
+ if key_mgmt != "WPA-NONE":
+ raise Exception("Unexpected STATUS key_mgmt: " + key_mgmt)
+
+def test_ibss_wpa_none_ccmp(dev):
+ """IBSS WPA-None/CCMP"""
+ skip_without_tkip(dev[0])
+ skip_without_tkip(dev[1])
+ ssid = "ibss-wpa-none"
+
+ logger.info("Start IBSS on the first STA")
+ id = add_ibss_wpa_none(dev[0], ssid)
+ connect_ibss_cmd(dev[0], id)
+ bssid0 = wait_ibss_connection(dev[0])
+
+ # This is a bit ugly, but no one really cares about WPA-None, so there may
+ # not be enough justification to clean this up.. For now, wpa_supplicant
+ # will show two connection events with mac80211_hwsim where the first one
+ # comes with all zeros address.
+ if bssid0 == "00:00:00:00:00:00":
+ logger.info("Waiting for real BSSID on the first STA")
+ bssid0 = wait_ibss_connection(dev[0])
+
+
+ logger.info("Join a STA to the IBSS")
+ id = add_ibss_wpa_none(dev[1], ssid)
+ connect_ibss_cmd(dev[1], id)
+
+ bssid1 = wait_ibss_connection(dev[1])
+ if bssid0 != bssid1:
+ logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+ bssid1 = wait_ibss_connection(dev[1])
+
+ logger.info("bssid0=%s bssid1=%s" % (bssid0, bssid1))
+
+ # Allow some time for all peers to complete key setup
+ time.sleep(1)
+
+ # This is supposed to work, but looks like WPA-None does not work with
+ # mac80211 currently..
+ try:
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ except Exception as e:
+ logger.info("Ignoring known connectivity failure: " + str(e))
+
+def test_ibss_open(dev):
+ """IBSS open (no security)"""
+ ssid = "ibss"
+ id = add_ibss(dev[0], ssid, key_mgmt="NONE", beacon_int="150")
+ connect_ibss_cmd(dev[0], id)
+ bssid0 = wait_ibss_connection(dev[0])
+
+ id = add_ibss(dev[1], ssid, key_mgmt="NONE", beacon_int="200")
+ connect_ibss_cmd(dev[1], id)
+ bssid1 = wait_ibss_connection(dev[1])
+ if bssid0 != bssid1:
+ logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+
+ res = dev[0].request("SCAN_RESULTS")
+ if "[IBSS]" not in res:
+ res = dev[1].request("SCAN_RESULTS")
+ if "[IBSS]" not in res:
+ raise Exception("IBSS flag missing from scan results: " + res)
+ bss = dev[0].get_bss(bssid0)
+ if not bss:
+ bss = dev[1].get_bss(bssid1)
+ if not bss:
+ raise Exception("Could not find BSS entry for IBSS")
+ if 'flags' not in bss:
+ raise Exception("Could not get BSS flags from BSS table")
+ if "[IBSS]" not in bss['flags']:
+ raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+ freq0 = dev[0].get_status_field("freq")
+ freq1 = dev[1].get_status_field("freq")
+ if freq0 != "2412" or freq1 != "2412":
+ raise Exception("IBSS operating frequency not reported correctly (%s %s)" % (freq0, freq1))
+
+ key_mgmt = dev[0].get_status_field("key_mgmt")
+ if key_mgmt != "NONE":
+ raise Exception("Unexpected STATUS key_mgmt: " + key_mgmt)
+
+def test_ibss_open_fixed_bssid(dev):
+ """IBSS open (no security) and fixed BSSID"""
+ ssid = "ibss"
+ bssid = "02:11:22:33:44:55"
+ try:
+ dev[0].request("AP_SCAN 2")
+ add_ibss(dev[0], ssid, key_mgmt="NONE", bssid=bssid, beacon_int="150")
+ dev[0].request("REASSOCIATE")
+
+ dev[1].request("AP_SCAN 2")
+ add_ibss(dev[1], ssid, key_mgmt="NONE", bssid=bssid, beacon_int="200")
+ dev[1].request("REASSOCIATE")
+
+ bssid0 = wait_ibss_connection(dev[0])
+ bssid1 = wait_ibss_connection(dev[1])
+ if bssid0 != bssid:
+ raise Exception("STA0 BSSID " + bssid0 + " differs from fixed BSSID " + bssid)
+ if bssid1 != bssid:
+ raise Exception("STA0 BSSID " + bssid0 + " differs from fixed BSSID " + bssid)
+ finally:
+ dev[0].request("AP_SCAN 1")
+ dev[1].request("AP_SCAN 1")
+
+def test_ibss_open_retry(dev):
+ """IBSS open (no security) with cfg80211 retry workaround"""
+ subprocess.check_call(['iw', 'dev', dev[0].ifname, 'set', 'type', 'adhoc'])
+ subprocess.check_call(['iw', 'dev', dev[0].ifname, 'ibss', 'join',
+ 'ibss-test', '2412', 'HT20', 'fixed-freq',
+ '02:22:33:44:55:66'])
+ ssid = "ibss"
+ try:
+ dev[0].request("AP_SCAN 2")
+ id = add_ibss(dev[0], ssid, key_mgmt="NONE", beacon_int="150",
+ bssid="02:33:44:55:66:77", scan_freq=2412)
+ #connect_ibss_cmd(dev[0], id)
+ dev[0].request("REASSOCIATE")
+ bssid0 = wait_ibss_connection(dev[0])
+
+ subprocess.check_call(['iw', 'dev', dev[0].ifname, 'ibss', 'leave'])
+ time.sleep(1)
+ dev[0].request("DISCONNECT")
+ finally:
+ dev[0].request("AP_SCAN 1")
+
+def test_ibss_rsn_tkip(dev):
+ """IBSS RSN with TKIP as the cipher"""
+ skip_without_tkip(dev[0])
+ skip_without_tkip(dev[1])
+ ssid = "ibss-rsn-tkip"
+
+ id = add_ibss_rsn_tkip(dev[0], ssid)
+ connect_ibss_cmd(dev[0], id)
+ bssid0 = wait_ibss_connection(dev[0])
+
+ id = add_ibss_rsn_tkip(dev[1], ssid)
+ connect_ibss_cmd(dev[1], id)
+ bssid1 = wait_ibss_connection(dev[1])
+ if bssid0 != bssid1:
+ logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+ # try to merge with a scan
+ dev[1].scan()
+ wait_4way_handshake(dev[0], dev[1])
+ wait_4way_handshake(dev[1], dev[0])
+
+def test_ibss_wep(dev):
+ """IBSS with WEP"""
+ check_wep_capa(dev[0])
+ check_wep_capa(dev[1])
+
+ ssid = "ibss-wep"
+
+ id = add_ibss(dev[0], ssid, key_mgmt="NONE", wep_key0='"hello"')
+ connect_ibss_cmd(dev[0], id)
+ bssid0 = wait_ibss_connection(dev[0])
+
+ id = add_ibss(dev[1], ssid, key_mgmt="NONE", wep_key0='"hello"')
+ connect_ibss_cmd(dev[1], id)
+ bssid1 = wait_ibss_connection(dev[1])
+
+@remote_compatible
+def test_ibss_rsn_error_case(dev):
+ """IBSS RSN regression test for IBSS_RSN prior IBSS setup"""
+ if "FAIL" not in dev[0].request("IBSS_RSN 02:03:04:05:06:07"):
+ raise Exception("Unexpected IBSS_RSN result")
+
+def test_ibss_5ghz(dev):
+ """IBSS on 5 GHz band"""
+ try:
+ _test_ibss_5ghz(dev)
+ finally:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def _test_ibss_5ghz(dev):
+ subprocess.call(['iw', 'reg', 'set', 'US'])
+ for i in range(2):
+ for j in range(5):
+ ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=US" in ev:
+ break
+ dev[i].dump_monitor()
+
+ ssid = "ibss"
+ id = add_ibss(dev[0], ssid, key_mgmt="NONE", beacon_int="150", freq=5180)
+ connect_ibss_cmd(dev[0], id, freq=5180)
+ bssid0 = wait_ibss_connection(dev[0])
+
+ dev[1].scan_for_bss(bssid0, freq=5180)
+ id = add_ibss(dev[1], ssid, key_mgmt="NONE", beacon_int="200", freq=5180)
+ connect_ibss_cmd(dev[1], id, freq=5180)
+ bssid1 = wait_ibss_connection(dev[1])
+ if bssid0 != bssid1:
+ logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_ibss_vht_80p80(dev):
+ """IBSS on VHT 80+80 MHz channel"""
+ try:
+ _test_ibss_vht_80p80(dev)
+ finally:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def _test_ibss_vht_80p80(dev):
+ subprocess.call(['iw', 'reg', 'set', 'US'])
+ for i in range(2):
+ for j in range(5):
+ ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=US" in ev:
+ break
+ dev[i].dump_monitor()
+
+ ssid = "ibss"
+ id = add_ibss(dev[0], ssid, key_mgmt="NONE", freq=5180, chwidth=3)
+ connect_ibss_cmd(dev[0], id, freq=5180)
+ bssid0 = wait_ibss_connection(dev[0])
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80+80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "CENTER_FRQ1=5210" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+ if "CENTER_FRQ2=5775" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(4): " + str(sig))
+
+ dev[1].scan_for_bss(bssid0, freq=5180)
+ id = add_ibss(dev[1], ssid, key_mgmt="NONE", freq=5180, chwidth=3)
+ connect_ibss_cmd(dev[1], id, freq=5180)
+ bssid1 = wait_ibss_connection(dev[1])
+ if bssid0 != bssid1:
+ logger.info("STA0 BSSID " + bssid0 + " differs from STA1 BSSID " + bssid1)
+
+ sig = dev[1].request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1b): " + str(sig))
+ logger.info("STA1 SIGNAL_POLL: " + str(sig))
+ # For now, don't report errors on joining STA failing to get 80+80 MHZ
+ # since mac80211 missed functionality for that to work.
+
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_ibss_rsn_oom(dev):
+ """IBSS RSN OOM during wpa_init"""
+ with alloc_fail(dev[0], 1, "wpa_init"):
+ ssid = "ibss-rsn"
+ id = add_ibss_rsn(dev[0], ssid, scan_freq=2412)
+ connect_ibss_cmd(dev[0], id)
+ bssid0 = wait_ibss_connection(dev[0])
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ with alloc_fail(dev[0], 1, "=ibss_rsn_init"):
+ ssid = "ibss-rsn"
+ id = add_ibss_rsn(dev[0], ssid, scan_freq=2412)
+ connect_ibss_cmd(dev[0], id)
+ bssid0 = wait_ibss_connection(dev[0])
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def send_eapol_rx(dev, dst):
+ if "OK" not in dev.request("EAPOL_RX %s 0203005f02008a001000000000000000013a54fb19d8a785f5986bdc2ba800553550bc9513e6603eb50809154588c22b110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" % dst):
+ raise Exception("EAPOL_RX for %s failed" % dst)
+
+def test_ibss_rsn_eapol_trigger(dev):
+ """IBSS RSN and EAPOL trigger for a new peer"""
+ ssid = "ibss-rsn"
+
+ id = add_ibss_rsn(dev[0], ssid, scan_freq=2412)
+ connect_ibss_cmd(dev[0], id)
+ bssid0 = wait_ibss_connection(dev[0])
+
+ send_eapol_rx(dev[0], "02:ff:00:00:00:01")
+ send_eapol_rx(dev[0], "02:ff:00:00:00:01")
+
+ dst = "02:ff:00:00:00:01"
+ logger.info("Too short EAPOL frame")
+ if "OK" not in dev[0].request("EAPOL_RX %s 0203005e02008a001000000000000000013a54fb19d8a785f5986bdc2ba800553550bc9513e6603eb50809154588c22b1100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" % dst):
+ raise Exception("EAPOL_RX for %s failed" % dst)
+ logger.info("RSN: EAPOL frame (type 255) discarded, not a Key frame")
+ if "OK" not in dev[0].request("EAPOL_RX %s 02ff005f02008a001000000000000000013a54fb19d8a785f5986bdc2ba800553550bc9513e6603eb50809154588c22b110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" % dst):
+ raise Exception("EAPOL_RX for %s failed" % dst)
+ logger.info("RSN: EAPOL frame payload size 96 invalid (frame size 99)")
+ if "OK" not in dev[0].request("EAPOL_RX %s 0203006002008a001000000000000000013a54fb19d8a785f5986bdc2ba800553550bc9513e6603eb50809154588c22b110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" % dst):
+ raise Exception("EAPOL_RX for %s failed" % dst)
+ logger.info("RSN: EAPOL-Key type (255) unknown, discarded")
+ if "OK" not in dev[0].request("EAPOL_RX %s 0203005fff008a001000000000000000013a54fb19d8a785f5986bdc2ba800553550bc9513e6603eb50809154588c22b110000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" % dst):
+ raise Exception("EAPOL_RX for %s failed" % dst)
+
+ with alloc_fail(dev[0], 1, "ibss_rsn_rx_eapol"):
+ send_eapol_rx(dev[0], "02:ff:00:00:00:02")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with alloc_fail(dev[0], 1, "wpa_auth_sta_init;ibss_rsn_auth_init"):
+ send_eapol_rx(dev[0], "02:ff:00:00:00:03")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with alloc_fail(dev[0], 1, "=ibss_rsn_peer_init"):
+ send_eapol_rx(dev[0], "02:ff:00:00:00:04")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with alloc_fail(dev[0], 1, "ibss_rsn_process_rx_eapol"):
+ send_eapol_rx(dev[0], "02:ff:00:00:00:05")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with alloc_fail(dev[0], 1,
+ "wpa_sm_set_assoc_wpa_ie_default;ibss_rsn_supp_init"):
+ send_eapol_rx(dev[0], "02:ff:00:00:00:06")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with alloc_fail(dev[0], 1, "wpa_sm_init;ibss_rsn_supp_init"):
+ send_eapol_rx(dev[0], "02:ff:00:00:00:07")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with alloc_fail(dev[0], 1, "=ibss_rsn_supp_init"):
+ send_eapol_rx(dev[0], "02:ff:00:00:00:08")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with alloc_fail(dev[0], 1, "supp_alloc_eapol"):
+ send_eapol_rx(dev[0], "02:ff:00:00:00:09")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with alloc_fail(dev[0], 1, "wpa_validate_wpa_ie;ibss_rsn_auth_init"):
+ send_eapol_rx(dev[0], "02:ff:00:00:00:0a")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ logger.info("RSN: Timeout on waiting Authentication frame response")
+ if "OK" not in dev[0].request("IBSS_RSN 02:ff:00:00:00:0b"):
+ raise Exception("Unexpected IBSS_RSN result")
+ time.sleep(1.1)
diff --git a/contrib/wpa/tests/hwsim/test_ieee8021x.py b/contrib/wpa/tests/hwsim/test_ieee8021x.py
new file mode 100644
index 000000000000..630d6d0dbe92
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ieee8021x.py
@@ -0,0 +1,531 @@
+# IEEE 802.1X tests
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import hmac
+import logging
+import os
+import time
+
+import hostapd
+import hwsim_utils
+from utils import *
+from tshark import run_tshark
+
+logger = logging.getLogger()
+
+def test_ieee8021x_wep104(dev, apdev):
+ """IEEE 802.1X connection using dynamic WEP104"""
+ check_wep_capa(dev[0])
+ skip_with_fips(dev[0])
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-wep"
+ params["ieee8021x"] = "1"
+ params["wep_key_len_broadcast"] = "13"
+ params["wep_key_len_unicast"] = "13"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="PSK",
+ identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_wep40(dev, apdev):
+ """IEEE 802.1X connection using dynamic WEP40"""
+ check_wep_capa(dev[0])
+ skip_with_fips(dev[0])
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-wep"
+ params["ieee8021x"] = "1"
+ params["wep_key_len_broadcast"] = "5"
+ params["wep_key_len_unicast"] = "5"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="PSK",
+ identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_wep_index_workaround(dev, apdev):
+ """IEEE 802.1X and EAPOL-Key index workaround"""
+ check_wep_capa(dev[0])
+ skip_with_fips(dev[0])
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-wep"
+ params["ieee8021x"] = "1"
+ params["wep_key_len_broadcast"] = "5"
+ params["eapol_key_index_workaround"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eapol_flags="1",
+ eap="PSK",
+ identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+def test_ieee8021x_open(dev, apdev):
+ """IEEE 802.1X connection using open network"""
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ id = dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ logger.info("Test EAPOL-Logoff")
+ dev[0].request("LOGOFF")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"])
+ if ev is None:
+ raise Exception("Did not get disconnected")
+ if "reason=23" not in ev:
+ raise Exception("Unexpected disconnection reason")
+
+ dev[0].request("LOGON")
+ dev[0].connect_network(id)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_static_wep40(dev, apdev):
+ """IEEE 802.1X connection using static WEP40"""
+ run_static_wep(dev, apdev, '"hello"')
+
+def test_ieee8021x_static_wep104(dev, apdev):
+ """IEEE 802.1X connection using static WEP104"""
+ run_static_wep(dev, apdev, '"hello-there-/"')
+
+def run_static_wep(dev, apdev, key):
+ check_wep_capa(dev[0])
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-wep"
+ params["ieee8021x"] = "1"
+ params["wep_key0"] = key
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="PSK",
+ identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wep_key0=key, eapol_flags="0",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_proto(dev, apdev):
+ """IEEE 802.1X and EAPOL supplicant protocol testing"""
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[1].request("SET ext_eapol_frame_io 1")
+ dev[1].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412", wait_connect=False)
+ id = dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ ev = dev[1].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+
+ start = dev[0].get_mib()
+
+ tests = ["11",
+ "11223344",
+ "020000050a93000501",
+ "020300050a93000501",
+ "0203002c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0203002c0100000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "0203002c0100050000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "02aa00050a93000501"]
+ for frame in tests:
+ res = dev[0].request("EAPOL_RX " + bssid + " " + frame)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+ dev[1].request("EAPOL_RX " + bssid + " " + frame)
+
+ stop = dev[0].get_mib()
+
+ logger.info("MIB before test frames: " + str(start))
+ logger.info("MIB after test frames: " + str(stop))
+
+ vals = ['dot1xSuppInvalidEapolFramesRx',
+ 'dot1xSuppEapLengthErrorFramesRx']
+ for val in vals:
+ if int(stop[val]) <= int(start[val]):
+ raise Exception(val + " did not increase")
+
+@remote_compatible
+def test_ieee8021x_eapol_start(dev, apdev):
+ """IEEE 802.1X and EAPOL-Start retransmissions"""
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ addr0 = dev[0].own_addr()
+
+ hapd.set("ext_eapol_frame_io", "1")
+ try:
+ dev[0].request("SET EAPOL::startPeriod 1")
+ dev[0].request("SET EAPOL::maxStart 1")
+ dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412", wait_connect=False)
+ held = False
+ for i in range(30):
+ pae = dev[0].get_status_field('Supplicant PAE state')
+ if pae == "HELD":
+ mib = hapd.get_sta(addr0, info="eapol")
+ if mib['auth_pae_state'] != 'AUTHENTICATING':
+ raise Exception("Unexpected Auth PAE state: " + mib['auth_pae_state'])
+ held = True
+ break
+ time.sleep(0.25)
+ if not held:
+ raise Exception("PAE state HELD not reached")
+ dev[0].wait_disconnected()
+ finally:
+ dev[0].request("SET EAPOL::startPeriod 30")
+ dev[0].request("SET EAPOL::maxStart 3")
+
+def test_ieee8021x_held(dev, apdev):
+ """IEEE 802.1X and HELD state"""
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ hapd.set("ext_eapol_frame_io", "1")
+ try:
+ dev[0].request("SET EAPOL::startPeriod 1")
+ dev[0].request("SET EAPOL::maxStart 0")
+ dev[0].request("SET EAPOL::heldPeriod 1")
+ dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412", wait_connect=False)
+ held = False
+ for i in range(30):
+ pae = dev[0].get_status_field('Supplicant PAE state')
+ if pae == "HELD":
+ held = True
+ break
+ time.sleep(0.25)
+ if not held:
+ raise Exception("PAE state HELD not reached")
+
+ hapd.set("ext_eapol_frame_io", "0")
+ for i in range(30):
+ pae = dev[0].get_status_field('Supplicant PAE state')
+ if pae != "HELD":
+ held = False
+ break
+ time.sleep(0.25)
+ if held:
+ raise Exception("PAE state HELD not left")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection timed out")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ raise Exception("Unexpected disconnection")
+ finally:
+ dev[0].request("SET EAPOL::startPeriod 30")
+ dev[0].request("SET EAPOL::maxStart 3")
+ dev[0].request("SET EAPOL::heldPeriod 60")
+
+def test_ieee8021x_force_unauth(dev, apdev):
+ """IEEE 802.1X and FORCE_UNAUTH state"""
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ dev[0].request("SET EAPOL::portControl ForceUnauthorized")
+ pae = dev[0].get_status_field('Supplicant PAE state')
+ dev[0].wait_disconnected()
+ dev[0].request("SET EAPOL::portControl Auto")
+
+def send_eapol_key(dev, bssid, signkey, frame_start, frame_end):
+ zero_sign = "00000000000000000000000000000000"
+ frame = frame_start + zero_sign + frame_end
+ hmac_obj = hmac.new(binascii.unhexlify(signkey), digestmod='MD5')
+ hmac_obj.update(binascii.unhexlify(frame))
+ sign = hmac_obj.digest()
+ frame = frame_start + binascii.hexlify(sign).decode() + frame_end
+ dev.request("EAPOL_RX " + bssid + " " + frame)
+
+def test_ieee8021x_eapol_key(dev, apdev):
+ """IEEE 802.1X connection and EAPOL-Key protocol tests"""
+ check_wep_capa(dev[0])
+ skip_with_fips(dev[0])
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-wep"
+ params["ieee8021x"] = "1"
+ params["wep_key_len_broadcast"] = "5"
+ params["wep_key_len_unicast"] = "5"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X", eap="VENDOR-TEST",
+ identity="vendor-test", scan_freq="2412")
+
+ # Hardcoded MSK from VENDOR-TEST
+ encrkey = "1111111111111111111111111111111111111111111111111111111111111111"
+ signkey = "2222222222222222222222222222222222222222222222222222222222222222"
+
+ # EAPOL-Key replay counter does not increase
+ send_eapol_key(dev[0], bssid, signkey,
+ "02030031" + "010005" + "0000000000000000" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
+ "1c636a30a4")
+
+ # EAPOL-Key too large Key Length field value
+ send_eapol_key(dev[0], bssid, signkey,
+ "02030031" + "010021" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
+ "1c636a30a4")
+
+ # EAPOL-Key too much key data
+ send_eapol_key(dev[0], bssid, signkey,
+ "0203004d" + "010005" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
+ 33*"ff")
+
+ # EAPOL-Key too little key data
+ send_eapol_key(dev[0], bssid, signkey,
+ "02030030" + "010005" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
+ "1c636a30")
+
+ # EAPOL-Key with no key data and too long WEP key length
+ send_eapol_key(dev[0], bssid, signkey,
+ "0203002c" + "010020" + "ffffffffffffffff" + "056c22d109f29d4d9fb9b9ccbad33283" + "02",
+ "")
+
+def test_ieee8021x_reauth(dev, apdev):
+ """IEEE 802.1X and EAPOL_REAUTH request"""
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not succeed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_reauth_wep(dev, apdev, params):
+ """IEEE 802.1X and EAPOL_REAUTH request with WEP"""
+ check_wep_capa(dev[0])
+ logdir = params['logdir']
+
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ params["wep_key_len_broadcast"] = "13"
+ params["wep_key_len_unicast"] = "13"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not succeed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ out = run_tshark(os.path.join(logdir, "hwsim0.pcapng"),
+ "llc.type == 0x888e", ["eapol.type", "eap.code"])
+ if out is None:
+ raise Exception("Could not find EAPOL frames in capture")
+ num_eapol_key = 0
+ num_eap_req = 0
+ num_eap_resp = 0
+ for line in out.splitlines():
+ vals = line.split()
+ if vals[0] == '3':
+ num_eapol_key += 1
+ if vals[0] == '0' and len(vals) == 2:
+ if vals[1] == '1':
+ num_eap_req += 1
+ elif vals[1] == '2':
+ num_eap_resp += 1
+ logger.info("num_eapol_key: %d" % num_eapol_key)
+ logger.info("num_eap_req: %d" % num_eap_req)
+ logger.info("num_eap_resp: %d" % num_eap_resp)
+ if num_eapol_key < 4:
+ raise Exception("Did not see four unencrypted EAPOL-Key frames")
+ if num_eap_req < 6:
+ raise Exception("Did not see six unencrypted EAP-Request frames")
+ if num_eap_resp < 6:
+ raise Exception("Did not see six unencrypted EAP-Response frames")
+
+def test_ieee8021x_set_conf(dev, apdev):
+ """IEEE 802.1X and EAPOL_SET command"""
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+ addr0 = dev[0].own_addr()
+ tests = ["EAPOL_SET 1",
+ "EAPOL_SET %sfoo bar" % addr0,
+ "EAPOL_SET %s foo" % addr0,
+ "EAPOL_SET %s foo bar" % addr0,
+ "EAPOL_SET %s AdminControlledDirections bar" % addr0,
+ "EAPOL_SET %s AdminControlledPortControl bar" % addr0,
+ "EAPOL_SET %s reAuthEnabled bar" % addr0,
+ "EAPOL_SET %s KeyTransmissionEnabled bar" % addr0,
+ "EAPOL_SET 11:22:33:44:55:66 AdminControlledDirections Both"]
+ for t in tests:
+ if "FAIL" not in hapd.request(t):
+ raise Exception("Invalid EAPOL_SET command accepted: " + t)
+
+ tests = [("AdminControlledDirections", "adminControlledDirections", "In"),
+ ("AdminControlledDirections", "adminControlledDirections",
+ "Both"),
+ ("quietPeriod", "quietPeriod", "13"),
+ ("serverTimeout", "serverTimeout", "7"),
+ ("reAuthPeriod", "reAuthPeriod", "1234"),
+ ("reAuthEnabled", "reAuthEnabled", "FALSE"),
+ ("reAuthEnabled", "reAuthEnabled", "TRUE"),
+ ("KeyTransmissionEnabled", "keyTxEnabled", "TRUE"),
+ ("KeyTransmissionEnabled", "keyTxEnabled", "FALSE"),
+ ("AdminControlledPortControl", "portControl", "ForceAuthorized"),
+ ("AdminControlledPortControl", "portControl",
+ "ForceUnauthorized"),
+ ("AdminControlledPortControl", "portControl", "Auto")]
+ for param, mibparam, val in tests:
+ if "OK" not in hapd.request("EAPOL_SET %s %s %s" % (addr0, param, val)):
+ raise Exception("Failed to set %s %s" % (param, val))
+ mib = hapd.get_sta(addr0, info="eapol")
+ if mib[mibparam] != val:
+ raise Exception("Unexpected %s value: %s (expected %s)" % (param, mib[mibparam], val))
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not succeed")
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ieee8021x_auth_awhile(dev, apdev):
+ """IEEE 802.1X and EAPOL Authenticator aWhile handling"""
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ params['auth_server_port'] = "18129"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ addr0 = dev[0].own_addr()
+
+ params = {}
+ params['ssid'] = 'as'
+ params['beacon_int'] = '2000'
+ params['radius_server_clients'] = 'auth_serv/radius_clients.conf'
+ params['radius_server_auth_port'] = '18129'
+ params['eap_server'] = '1'
+ params['eap_user_file'] = 'auth_serv/eap_user.conf'
+ params['ca_cert'] = 'auth_serv/ca.pem'
+ params['server_cert'] = 'auth_serv/server.pem'
+ params['private_key'] = 'auth_serv/server.key'
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ hapd1.disable()
+ if "OK" not in hapd.request("EAPOL_SET %s serverTimeout 1" % addr0):
+ raise Exception("Failed to set serverTimeout")
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+
+ for i in range(40):
+ mib = hapd.get_sta(addr0, info="eapol")
+ val = int(mib['aWhile'])
+ if val > 0:
+ break
+ time.sleep(1)
+ if val == 0:
+ raise Exception("aWhile did not increase")
+
+ hapd.dump_monitor()
+ for i in range(40):
+ mib = hapd.get_sta(addr0, info="eapol")
+ val = int(mib['aWhile'])
+ if val < 5:
+ break
+ time.sleep(1)
+ ev = hapd.wait_event(["CTRL-EVENT-EAP-PROPOSED"], timeout=10)
+ if ev is None:
+ raise Exception("Authentication restart not seen")
+
+def test_ieee8021x_open_leap(dev, apdev):
+ """IEEE 802.1X connection with LEAP included in configuration"""
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-open"
+ params["ieee8021x"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[1].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="LEAP", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412", wait_connect=False)
+ dev[0].connect("ieee8021x-open", key_mgmt="IEEE8021X", eapol_flags="0",
+ eap="PSK LEAP", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ ev = dev[1].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=5)
+ dev[1].request("DISCONNECT")
+
+def test_ieee8021x_and_wpa_enabled(dev, apdev):
+ """IEEE 802.1X connection using dynamic WEP104 when WPA enabled"""
+ check_wep_capa(dev[0])
+ skip_with_fips(dev[0])
+ params = hostapd.radius_params()
+ params["ssid"] = "ieee8021x-wep"
+ params["ieee8021x"] = "1"
+ params["wep_key_len_broadcast"] = "13"
+ params["wep_key_len_unicast"] = "13"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("ieee8021x-wep", key_mgmt="IEEE8021X WPA-EAP", eap="PSK",
+ identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
diff --git a/contrib/wpa/tests/hwsim/test_kernel.py b/contrib/wpa/tests/hwsim/test_kernel.py
new file mode 100644
index 000000000000..d0c4faec9da4
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_kernel.py
@@ -0,0 +1,128 @@
+# Test a few kernel bugs and functionality
+# Copyright (c) 2016, Intel Deutschland GmbH
+#
+# Author: Johannes Berg <johannes.berg@intel.com>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hostapd
+import binascii
+import os
+import struct
+from test_wnm import expect_ack
+from tshark import run_tshark
+
+def _test_kernel_bss_leak(dev, apdev, deauth):
+ ssid = "test-bss-leak"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False)
+ while True:
+ pkt = hapd.mgmt_rx()
+ if not pkt:
+ raise Exception("MGMT RX wait timed out for auth frame")
+ if pkt['fc'] & 0xc:
+ continue
+ if pkt['subtype'] == 0: # assoc request
+ if deauth:
+ # return a deauth immediately
+ hapd.mgmt_tx({
+ 'fc': 0xc0,
+ 'sa': pkt['da'],
+ 'da': pkt['sa'],
+ 'bssid': pkt['bssid'],
+ 'payload': b'\x01\x00',
+ })
+ break
+ else:
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % (
+ binascii.hexlify(pkt['frame']).decode(), ))
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+ hapd.request("STOP_AP")
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].flush_scan_cache(freq=5180)
+ res = dev[0].request("SCAN_RESULTS")
+ if len(res.splitlines()) > 1:
+ raise Exception("BSS entry should no longer be around")
+
+def test_kernel_bss_leak_deauth(dev, apdev):
+ """cfg80211/mac80211 BSS leak on deauthentication"""
+ return _test_kernel_bss_leak(dev, apdev, deauth=True)
+
+def test_kernel_bss_leak_timeout(dev, apdev):
+ """cfg80211/mac80211 BSS leak on timeout"""
+ return _test_kernel_bss_leak(dev, apdev, deauth=False)
+
+MGMT_SUBTYPE_ACTION = 13
+
+def expect_no_ack(hapd):
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Missing TX status")
+ if "ok=0" not in ev:
+ raise Exception("Action frame unexpectedly acknowledged")
+
+def test_kernel_unknown_action_frame_rejection_sta(dev, apdev, params):
+ """mac80211 and unknown Action frame rejection in STA mode"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unknown-action"})
+ dev[0].connect("unknown-action", key_mgmt="NONE", scan_freq="2412")
+ bssid = hapd.own_addr()
+ addr = dev[0].own_addr()
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ # Unicast Action frame with unknown category (response expected)
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = addr
+ msg['sa'] = bssid
+ msg['bssid'] = bssid
+ msg['payload'] = struct.pack("<BB", 0x70, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ # Note: mac80211 does not allow group-addressed Action frames in unknown
+ # categories to be transmitted in AP mode, so for now, these steps are
+ # commented out.
+
+ # Multicast Action frame with unknown category (no response expected)
+ #msg['da'] = "01:ff:ff:ff:ff:ff"
+ #msg['payload'] = struct.pack("<BB", 0x71, 1)
+ #hapd.mgmt_tx(msg)
+ #expect_no_ack(hapd)
+
+ # Broadcast Action frame with unknown category (no response expected)
+ #msg['da'] = "ff:ff:ff:ff:ff:ff"
+ #msg['payload'] = struct.pack("<BB", 0x72, 2)
+ #hapd.mgmt_tx(msg)
+ #expect_no_ack(hapd)
+
+ # Unicast Action frame with error indication category (no response expected)
+ msg['da'] = addr
+ msg['payload'] = struct.pack("<BB", 0xf3, 3)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ # Unicast Action frame with unknown category (response expected)
+ msg['da'] = addr
+ msg['payload'] = struct.pack("<BB", 0x74, 4)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan.sa == %s && wlan.fc.type_subtype == 0x0d" % addr,
+ display=["wlan_mgt.fixed.category_code"])
+ res = out.splitlines()
+ categ = [int(x) for x in res]
+
+ if 0xf2 in categ or 0xf3 in categ:
+ raise Exception("Unexpected Action frame rejection: " + str(categ))
+ if 0xf0 not in categ or 0xf4 not in categ:
+ raise Exception("Action frame rejection missing: " + str(categ))
diff --git a/contrib/wpa/tests/hwsim/test_macsec.py b/contrib/wpa/tests/hwsim/test_macsec.py
new file mode 100644
index 000000000000..e521c6b3d337
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_macsec.py
@@ -0,0 +1,890 @@
+# Test cases for MACsec/MKA
+# Copyright (c) 2018-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import binascii
+import os
+import signal
+import subprocess
+import time
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+import hwsim_utils
+from utils import HwsimSkip, alloc_fail, fail_test, wait_fail_trigger
+from wlantest import WlantestCapture
+
+def cleanup_macsec():
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5', monitor=False)
+ wpas.interface_remove("veth0")
+ wpas.interface_remove("veth1")
+ del wpas
+ subprocess.call(["ip", "link", "del", "veth0"],
+ stderr=open('/dev/null', 'w'))
+
+def test_macsec_psk(dev, apdev, params):
+ """MACsec PSK"""
+ try:
+ run_macsec_psk(dev, apdev, params, "macsec_psk")
+ finally:
+ cleanup_macsec()
+
+def test_macsec_psk_mka_life_time(dev, apdev, params):
+ """MACsec PSK - MKA life time"""
+ try:
+ run_macsec_psk(dev, apdev, params, "macsec_psk_mka_life_time")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5', monitor=False)
+ wpas.interface_remove("veth1")
+ del wpas
+ # Wait for live peer to be removed on veth0
+ time.sleep(6.1)
+ finally:
+ cleanup_macsec()
+
+def test_macsec_psk_integ_only(dev, apdev, params):
+ """MACsec PSK (integrity only)"""
+ try:
+ run_macsec_psk(dev, apdev, params, "macsec_psk_integ_only",
+ integ_only=True)
+ finally:
+ cleanup_macsec()
+
+def test_macsec_psk_port(dev, apdev, params):
+ """MACsec PSK (port)"""
+ try:
+ run_macsec_psk(dev, apdev, params, "macsec_psk_port",
+ port0=65534, port1=65534)
+ finally:
+ cleanup_macsec()
+
+def test_macsec_psk_different_ports(dev, apdev, params):
+ """MACsec PSK (different ports)"""
+ try:
+ run_macsec_psk(dev, apdev, params, "macsec_psk_different_ports",
+ port0=2, port1=3)
+ finally:
+ cleanup_macsec()
+
+def test_macsec_psk_shorter_ckn(dev, apdev, params):
+ """MACsec PSK (shorter CKN)"""
+ try:
+ ckn = "11223344"
+ run_macsec_psk(dev, apdev, params, "macsec_psk_shorter_ckn",
+ ckn0=ckn, ckn1=ckn)
+ finally:
+ cleanup_macsec()
+
+def test_macsec_psk_shorter_ckn2(dev, apdev, params):
+ """MACsec PSK (shorter CKN, unaligned)"""
+ try:
+ ckn = "112233"
+ run_macsec_psk(dev, apdev, params, "macsec_psk_shorter_ckn2",
+ ckn0=ckn, ckn1=ckn)
+ finally:
+ cleanup_macsec()
+
+def test_macsec_psk_ckn_mismatch(dev, apdev, params):
+ """MACsec PSK (CKN mismatch)"""
+ try:
+ ckn0 = "11223344"
+ ckn1 = "1122334455667788"
+ run_macsec_psk(dev, apdev, params, "macsec_psk_ckn_mismatch",
+ ckn0=ckn0, ckn1=ckn1, expect_failure=True)
+ finally:
+ cleanup_macsec()
+
+def test_macsec_psk_cak_mismatch(dev, apdev, params):
+ """MACsec PSK (CAK mismatch)"""
+ try:
+ cak0 = 16*"11"
+ cak1 = 16*"22"
+ run_macsec_psk(dev, apdev, params, "macsec_psk_cak_mismatch",
+ cak0=cak0, cak1=cak1, expect_failure=True)
+ finally:
+ cleanup_macsec()
+
+def test_macsec_psk_256(dev, apdev, params):
+ """MACsec PSK with 256-bit keys"""
+ try:
+ cak = "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f"
+ run_macsec_psk(dev, apdev, params, "macsec_psk_256", cak0=cak, cak1=cak)
+ finally:
+ cleanup_macsec()
+
+def set_mka_psk_config(dev, mka_priority=None, integ_only=False, port=None,
+ ckn=None, cak=None):
+ dev.set("eapol_version", "3")
+ dev.set("ap_scan", "0")
+ dev.set("fast_reauth", "1")
+
+ id = dev.add_network()
+ dev.set_network(id, "key_mgmt", "NONE")
+ if cak is None:
+ cak = "000102030405060708090a0b0c0d0e0f"
+ dev.set_network(id, "mka_cak", cak)
+ if ckn is None:
+ ckn = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+ dev.set_network(id, "mka_ckn", ckn)
+ dev.set_network(id, "eapol_flags", "0")
+ dev.set_network(id, "macsec_policy", "1")
+ if integ_only:
+ dev.set_network(id, "macsec_integ_only", "1")
+ if mka_priority is not None:
+ dev.set_network(id, "mka_priority", str(mka_priority))
+ if port is not None:
+ dev.set_network(id, "macsec_port", str(port))
+
+ dev.select_network(id)
+
+def set_mka_eap_config(dev, mka_priority=None, integ_only=False, port=None):
+ dev.set("eapol_version", "3")
+ dev.set("ap_scan", "0")
+ dev.set("fast_reauth", "1")
+
+ id = dev.add_network()
+ dev.set_network(id, "key_mgmt", "NONE")
+ dev.set_network(id, "eapol_flags", "0")
+ dev.set_network(id, "macsec_policy", "1")
+ if integ_only:
+ dev.set_network(id, "macsec_integ_only", "1")
+ if mka_priority is not None:
+ dev.set_network(id, "mka_priority", str(mka_priority))
+ if port is not None:
+ dev.set_network(id, "macsec_port", str(port))
+
+ dev.set_network(id, "key_mgmt", "IEEE8021X")
+ dev.set_network(id, "eap", "TTLS")
+ dev.set_network_quoted(id, "ca_cert", "auth_serv/ca.pem")
+ dev.set_network_quoted(id, "phase2", "auth=MSCHAPV2")
+ dev.set_network_quoted(id, "anonymous_identity", "ttls")
+ dev.set_network_quoted(id, "identity", "DOMAIN\mschapv2 user")
+ dev.set_network_quoted(id, "password", "password")
+
+ dev.select_network(id)
+
+def log_ip_macsec():
+ cmd = subprocess.Popen(["ip", "macsec", "show"],
+ stdout=subprocess.PIPE,
+ stderr=open('/dev/null', 'w'))
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ logger.info("ip macsec:\n" + res)
+
+def log_ip_link():
+ cmd = subprocess.Popen(["ip", "link", "show"],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ logger.info("ip link:\n" + res)
+
+def add_veth():
+ try:
+ subprocess.check_call(["ip", "link", "add", "veth0", "type", "veth",
+ "peer", "name", "veth1"])
+ except subprocess.CalledProcessError:
+ raise HwsimSkip("veth not supported (kernel CONFIG_VETH)")
+
+def add_wpas_interfaces(count=2):
+ wpa = []
+ try:
+ for i in range(count):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("veth%d" % i, driver="macsec_linux")
+ wpa.append(wpas)
+ except Exception as e:
+ if "Failed to add a dynamic wpa_supplicant interface" in str(e):
+ raise HwsimSkip("macsec supported (wpa_supplicant CONFIG_MACSEC, CONFIG_DRIVER_MACSEC_LINUX; kernel CONFIG_MACSEC)")
+ raise
+
+ return wpa
+
+def lower_addr(addr1, addr2):
+ a1 = addr1.split(':')
+ a2 = addr2.split(':')
+ for i in range(6):
+ if binascii.unhexlify(a1[i]) < binascii.unhexlify(a2[i]):
+ return True
+ if binascii.unhexlify(a1[i]) > binascii.unhexlify(a2[i]):
+ return False
+ return False
+
+def wait_mka_done(wpa, expect_failure=False, hostapd=False):
+ max_iter = 14 if expect_failure else 40
+ for i in range(max_iter):
+ done = True
+ for w in wpa:
+ secured = w.get_status_field("Secured")
+ live_peers = w.get_status_field("live_peers")
+ peers = int(live_peers) if live_peers else 0
+ if expect_failure and (secured == "Yes" or peers > 0):
+ raise Exception("MKA completed unexpectedly")
+ expect_peers = len(wpa) - 1
+ if hostapd:
+ expect_peers += 1
+ if peers != expect_peers or secured != "Yes":
+ done = False
+ break
+ w.dump_monitor()
+ if done:
+ break
+ time.sleep(0.5)
+
+ if expect_failure:
+ return
+
+ if not done:
+ raise Exception("MKA not completed successfully")
+
+ if hostapd:
+ # TODO: check that hostapd is the key server
+ return
+
+ key_server = None
+ ks_prio = 999
+ for w in wpa:
+ logger.info("%s STATUS:\n%s" % (w.ifname, w.request("STATUS")))
+ addr = w.get_status_field("address")
+ prio = int(w.get_status_field("Actor Priority"))
+ if key_server is None or prio < ks_prio or \
+ (prio == ks_prio and lower_addr(addr, ks_addr)):
+ key_server = w
+ ks_addr = addr
+ ks_prio = prio
+
+ logger.info("Expected key server: " + key_server.ifname)
+ if key_server.get_status_field("is_key_server") != "Yes":
+ raise Exception("Expected key server was not elected")
+ for w in wpa:
+ if w != key_server and w.get_status_field("is_key_server") == "Yes":
+ raise Exception("Unexpected key server")
+
+def run_macsec_psk(dev, apdev, params, prefix, integ_only=False, port0=None,
+ port1=None, ckn0=None, ckn1=None, cak0=None, cak1=None,
+ expect_failure=False):
+ add_veth()
+
+ cap_veth0 = os.path.join(params['logdir'], prefix + ".veth0.pcap")
+ cap_veth1 = os.path.join(params['logdir'], prefix + ".veth1.pcap")
+ cap_macsec0 = os.path.join(params['logdir'], prefix + ".macsec0.pcap")
+ cap_macsec1 = os.path.join(params['logdir'], prefix + ".macsec1.pcap")
+
+ for i in range(2):
+ subprocess.check_call(["ip", "link", "set", "dev", "veth%d" % i, "up"])
+
+ cmd = {}
+ cmd[0] = WlantestCapture('veth0', cap_veth0)
+ cmd[1] = WlantestCapture('veth1', cap_veth1)
+
+ wpa = add_wpas_interfaces()
+ wpas0 = wpa[0]
+ wpas1 = wpa[1]
+
+ set_mka_psk_config(wpas0, integ_only=integ_only, port=port0, ckn=ckn0,
+ cak=cak0)
+ set_mka_psk_config(wpas1, mka_priority=100, integ_only=integ_only,
+ port=port1, ckn=ckn1, cak=cak1)
+
+ log_ip_macsec()
+ log_ip_link()
+
+ logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS"))
+ logger.info("wpas1 STATUS:\n" + wpas1.request("STATUS"))
+ logger.info("wpas0 STATUS-DRIVER:\n" + wpas0.request("STATUS-DRIVER"))
+ logger.info("wpas1 STATUS-DRIVER:\n" + wpas1.request("STATUS-DRIVER"))
+ macsec_ifname0 = wpas0.get_driver_status_field("parent_ifname")
+ macsec_ifname1 = wpas1.get_driver_status_field("parent_ifname")
+
+ wait_mka_done(wpa, expect_failure=expect_failure)
+
+ if expect_failure:
+ for i in range(len(cmd)):
+ cmd[i].close()
+ return
+
+ cmd[2] = WlantestCapture(macsec_ifname0, cap_macsec0)
+ cmd[3] = WlantestCapture(macsec_ifname1, cap_macsec1)
+ time.sleep(0.5)
+
+ mi0 = wpas0.get_status_field("mi")
+ mi1 = wpas1.get_status_field("mi")
+ sci0 = wpas0.get_status_field("actor_sci")
+ sci1 = wpas1.get_status_field("actor_sci")
+ logger.info("wpas0 MIB:\n" + wpas0.request("MIB"))
+ logger.info("wpas1 MIB:\n" + wpas1.request("MIB"))
+ mib0 = wpas0.get_mib()
+ mib1 = wpas1.get_mib()
+
+ if mib0['ieee8021XKayMkaPeerListMI'] != mi1:
+ raise Exception("Unexpected ieee8021XKayMkaPeerListMI value (0)")
+ if mib0['ieee8021XKayMkaPeerListType'] != "1":
+ raise Exception("Unexpected ieee8021XKayMkaPeerListType value (0)")
+ if mib0['ieee8021XKayMkaPeerListSCI'] != sci1:
+ raise Exception("Unexpected ieee8021XKayMkaPeerListSCI value (0)")
+ if mib1['ieee8021XKayMkaPeerListMI'] != mi0:
+ raise Exception("Unexpected ieee8021XKayMkaPeerListMI value (1)")
+ if mib1['ieee8021XKayMkaPeerListType'] != "1":
+ raise Exception("Unexpected ieee8021XKayMkaPeerListType value (1)")
+ if mib1['ieee8021XKayMkaPeerListSCI'] != sci0:
+ raise Exception("Unexpected ieee8021XKayMkaPeerListSCI value (1)")
+
+ logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS"))
+ logger.info("wpas1 STATUS:\n" + wpas1.request("STATUS"))
+ log_ip_macsec()
+ hwsim_utils.test_connectivity(wpas0, wpas1,
+ ifname1=macsec_ifname0,
+ ifname2=macsec_ifname1,
+ send_len=1400)
+ log_ip_macsec()
+
+ time.sleep(1)
+ for i in range(len(cmd)):
+ cmd[i].close()
+
+def cleanup_macsec_br(count):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5', monitor=False)
+ for i in range(count):
+ wpas.interface_remove("veth%d" % i)
+ subprocess.call(["ip", "link", "del", "veth%d" % i],
+ stderr=open('/dev/null', 'w'))
+ del wpas
+ subprocess.call(["ip", "link", "set", "brveth", "down"])
+ subprocess.call(["brctl", "delbr", "brveth"])
+
+def test_macsec_psk_br2(dev, apdev):
+ """MACsec PSK (bridge; 2 devices)"""
+ try:
+ run_macsec_psk_br(dev, apdev, 2, [10, 20])
+ finally:
+ cleanup_macsec_br(count=2)
+
+def test_macsec_psk_br2_same_prio(dev, apdev):
+ """MACsec PSK (bridge; 2 devices, same mka_priority)"""
+ try:
+ run_macsec_psk_br(dev, apdev, 2, [None, None])
+ finally:
+ cleanup_macsec_br(count=2)
+
+def test_macsec_psk_br3(dev, apdev):
+ """MACsec PSK (bridge; 3 devices)"""
+ try:
+ run_macsec_psk_br(dev, apdev, 3, [10, 20, 30])
+ finally:
+ cleanup_macsec_br(count=3)
+
+def test_macsec_psk_br3_same_prio(dev, apdev):
+ """MACsec PSK (bridge; 3 devices, same mka_priority)"""
+ try:
+ run_macsec_psk_br(dev, apdev, 3, [None, None, None])
+ finally:
+ cleanup_macsec_br(count=3)
+
+def run_macsec_psk_br(dev, apdev, count, mka_priority):
+ subprocess.check_call(["brctl", "addbr", "brveth"])
+ subprocess.call(["echo 8 > /sys/devices/virtual/net/brveth/bridge/group_fwd_mask"],
+ shell=True)
+
+ try:
+ for i in range(count):
+ subprocess.check_call(["ip", "link", "add", "veth%d" % i,
+ "type", "veth",
+ "peer", "name", "vethbr%d" % i])
+ subprocess.check_call(["ip", "link", "set", "vethbr%d" % i, "up"])
+ subprocess.check_call(["brctl", "addif", "brveth",
+ "vethbr%d" % i])
+ except subprocess.CalledProcessError:
+ raise HwsimSkip("veth not supported (kernel CONFIG_VETH)")
+
+ subprocess.check_call(["ip", "link", "set", "brveth", "up"])
+
+ log_ip_link()
+
+ wpa = add_wpas_interfaces(count=count)
+ for i in range(count):
+ set_mka_psk_config(wpa[i], mka_priority=mka_priority[i])
+ wpa[i].dump_monitor()
+ wait_mka_done(wpa)
+
+ macsec_ifname = []
+ for i in range(count):
+ macsec_ifname.append(wpa[i].get_driver_status_field("parent_ifname"))
+
+ timeout = 2
+ max_tries = 2 if count > 2 else 1
+ success_seen = False
+ failure_seen = False
+ for i in range(1, count):
+ try:
+ hwsim_utils.test_connectivity(wpa[0], wpa[i],
+ ifname1=macsec_ifname[0],
+ ifname2=macsec_ifname[i],
+ send_len=1400,
+ timeout=timeout, max_tries=max_tries)
+ success_seen = True
+ logger.info("Traffic test %d<->%d success" % (0, i))
+ except:
+ failure_seen = True
+ logger.info("Traffic test %d<->%d failure" % (0, i))
+ for i in range(2, count):
+ try:
+ hwsim_utils.test_connectivity(wpa[1], wpa[i],
+ ifname1=macsec_ifname[1],
+ ifname2=macsec_ifname[i],
+ send_len=1400,
+ timeout=timeout, max_tries=max_tries)
+ success_seen = True
+ logger.info("Traffic test %d<->%d success" % (1, i))
+ except:
+ failure_seen = True
+ logger.info("Traffic test %d<->%d failure" % (1, i))
+
+ if not success_seen:
+ raise Exception("None of the data traffic tests succeeded")
+
+ # Something seems to be failing with three device tests semi-regularly, so
+ # do not report this as a failed test case until the real reason behind
+ # those failures have been determined.
+ if failure_seen:
+ if count < 3:
+ raise Exception("Data traffic test failed")
+ else:
+ logger.info("Data traffic test failed - ignore for now for >= 3 device cases")
+
+ for i in range(count):
+ wpa[i].close_monitor()
+ for i in range(count):
+ wpa[0].close_control()
+ del wpa[0]
+
+def test_macsec_psk_ns(dev, apdev, params):
+ """MACsec PSK (netns)"""
+ try:
+ run_macsec_psk_ns(dev, apdev, params)
+ finally:
+ prefix = "macsec_psk_ns"
+ pidfile = os.path.join(params['logdir'], prefix + ".pid")
+ for i in range(2):
+ was_running = False
+ if os.path.exists(pidfile + str(i)):
+ with open(pidfile + str(i), 'r') as f:
+ pid = int(f.read().strip())
+ logger.info("wpa_supplicant for wpas%d still running with pid %d - kill it" % (i, pid))
+ was_running = True
+ os.kill(pid, signal.SIGTERM)
+ if was_running:
+ time.sleep(1)
+
+ subprocess.call(["ip", "netns", "exec", "ns0",
+ "ip", "link", "del", "veth0"],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(["ip", "link", "del", "veth0"],
+ stderr=open('/dev/null', 'w'))
+ log_ip_link_ns()
+ subprocess.call(["ip", "netns", "delete", "ns0"],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(["ip", "netns", "delete", "ns1"],
+ stderr=open('/dev/null', 'w'))
+
+def log_ip_macsec_ns():
+ cmd = subprocess.Popen(["ip", "macsec", "show"],
+ stdout=subprocess.PIPE,
+ stderr=open('/dev/null', 'w'))
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ logger.info("ip macsec show:\n" + res)
+
+ cmd = subprocess.Popen(["ip", "netns", "exec", "ns0",
+ "ip", "macsec", "show"],
+ stdout=subprocess.PIPE,
+ stderr=open('/dev/null', 'w'))
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ logger.info("ip macsec show (ns0):\n" + res)
+
+ cmd = subprocess.Popen(["ip", "netns", "exec", "ns1",
+ "ip", "macsec", "show"],
+ stdout=subprocess.PIPE,
+ stderr=open('/dev/null', 'w'))
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ logger.info("ip macsec show (ns1):\n" + res)
+
+def log_ip_link_ns():
+ cmd = subprocess.Popen(["ip", "link", "show"],
+ stdout=subprocess.PIPE)
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ logger.info("ip link:\n" + res)
+
+ cmd = subprocess.Popen(["ip", "netns", "exec", "ns0",
+ "ip", "link", "show"],
+ stdout=subprocess.PIPE,
+ stderr=open('/dev/null', 'w'))
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ logger.info("ip link show (ns0):\n" + res)
+
+ cmd = subprocess.Popen(["ip", "netns", "exec", "ns1",
+ "ip", "link", "show"],
+ stdout=subprocess.PIPE,
+ stderr=open('/dev/null', 'w'))
+ res = cmd.stdout.read().decode()
+ cmd.stdout.close()
+ logger.info("ip link show (ns1):\n" + res)
+
+def write_conf(conffile, mka_priority=None):
+ with open(conffile, 'w') as f:
+ f.write("ctrl_interface=DIR=/var/run/wpa_supplicant\n")
+ f.write("eapol_version=3\n")
+ f.write("ap_scan=0\n")
+ f.write("fast_reauth=1\n")
+ f.write("network={\n")
+ f.write(" key_mgmt=NONE\n")
+ f.write(" mka_cak=000102030405060708090a0b0c0d0e0f\n")
+ f.write(" mka_ckn=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\n")
+ if mka_priority is not None:
+ f.write(" mka_priority=%d\n" % mka_priority)
+ f.write(" eapol_flags=0\n")
+ f.write(" macsec_policy=1\n")
+ f.write("}\n")
+
+def run_macsec_psk_ns(dev, apdev, params):
+ try:
+ subprocess.check_call(["ip", "link", "add", "veth0", "type", "veth",
+ "peer", "name", "veth1"])
+ except subprocess.CalledProcessError:
+ raise HwsimSkip("veth not supported (kernel CONFIG_VETH)")
+
+ prefix = "macsec_psk_ns"
+ conffile = os.path.join(params['logdir'], prefix + ".conf")
+ pidfile = os.path.join(params['logdir'], prefix + ".pid")
+ logfile0 = os.path.join(params['logdir'], prefix + ".veth0.log")
+ logfile1 = os.path.join(params['logdir'], prefix + ".veth1.log")
+ cap_veth0 = os.path.join(params['logdir'], prefix + ".veth0.pcap")
+ cap_veth1 = os.path.join(params['logdir'], prefix + ".veth1.pcap")
+ cap_macsec0 = os.path.join(params['logdir'], prefix + ".macsec0.pcap")
+ cap_macsec1 = os.path.join(params['logdir'], prefix + ".macsec1.pcap")
+
+ for i in range(2):
+ try:
+ subprocess.check_call(["ip", "netns", "add", "ns%d" % i])
+ except subprocess.CalledProcessError:
+ raise HwsimSkip("network namespace not supported (kernel CONFIG_NAMESPACES, CONFIG_NET_NS)")
+ subprocess.check_call(["ip", "link", "set", "veth%d" % i,
+ "netns", "ns%d" %i])
+ subprocess.check_call(["ip", "netns", "exec", "ns%d" % i,
+ "ip", "link", "set", "dev", "veth%d" % i,
+ "up"])
+
+ cmd = {}
+ cmd[0] = WlantestCapture('veth0', cap_veth0, netns='ns0')
+ cmd[1] = WlantestCapture('veth1', cap_veth1, netns='ns1')
+
+ write_conf(conffile + '0')
+ write_conf(conffile + '1', mka_priority=100)
+
+ prg = os.path.join(params['logdir'],
+ 'alt-wpa_supplicant/wpa_supplicant/wpa_supplicant')
+ if not os.path.exists(prg):
+ prg = '../../wpa_supplicant/wpa_supplicant'
+
+ arg = ["ip", "netns", "exec", "ns0",
+ prg, '-BdddtKW', '-P', pidfile + '0', '-f', logfile0,
+ '-g', '/tmp/wpas-veth0',
+ '-Dmacsec_linux', '-c', conffile + '0', '-i', "veth0"]
+ logger.info("Start wpa_supplicant: " + str(arg))
+ try:
+ subprocess.check_call(arg)
+ except subprocess.CalledProcessError:
+ raise HwsimSkip("macsec supported (wpa_supplicant CONFIG_MACSEC, CONFIG_DRIVER_MACSEC_LINUX; kernel CONFIG_MACSEC)")
+
+ if os.path.exists("wpa_supplicant-macsec2"):
+ logger.info("Use alternative wpa_supplicant binary for one of the macsec devices")
+ prg = "wpa_supplicant-macsec2"
+
+ arg = ["ip", "netns", "exec", "ns1",
+ prg, '-BdddtKW', '-P', pidfile + '1', '-f', logfile1,
+ '-g', '/tmp/wpas-veth1',
+ '-Dmacsec_linux', '-c', conffile + '1', '-i', "veth1"]
+ logger.info("Start wpa_supplicant: " + str(arg))
+ subprocess.check_call(arg)
+
+ wpas0 = WpaSupplicant('veth0', '/tmp/wpas-veth0')
+ wpas1 = WpaSupplicant('veth1', '/tmp/wpas-veth1')
+
+ log_ip_macsec_ns()
+ log_ip_link_ns()
+
+ logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS"))
+ logger.info("wpas1 STATUS:\n" + wpas1.request("STATUS"))
+ logger.info("wpas0 STATUS-DRIVER:\n" + wpas0.request("STATUS-DRIVER"))
+ logger.info("wpas1 STATUS-DRIVER:\n" + wpas1.request("STATUS-DRIVER"))
+
+ for i in range(10):
+ macsec_ifname0 = wpas0.get_driver_status_field("parent_ifname")
+ macsec_ifname1 = wpas1.get_driver_status_field("parent_ifname")
+ if "Number of Keys" in wpas0.request("STATUS"):
+ key_tx0 = int(wpas0.get_status_field("Number of Keys Distributed"))
+ key_rx0 = int(wpas0.get_status_field("Number of Keys Received"))
+ else:
+ key_tx0 = 0
+ key_rx0 = 0
+ if "Number of Keys" in wpas1.request("STATUS"):
+ key_tx1 = int(wpas1.get_status_field("Number of Keys Distributed"))
+ key_rx1 = int(wpas1.get_status_field("Number of Keys Received"))
+ else:
+ key_tx1 = 0
+ key_rx1 = 0
+ if key_rx0 > 0 and key_tx1 > 0:
+ break
+ time.sleep(1)
+
+ cmd[2] = WlantestCapture(macsec_ifname0, cap_macsec0, netns='ns0')
+ cmd[3] = WlantestCapture(macsec_ifname1, cap_macsec1, netns='ns0')
+ time.sleep(0.5)
+
+ logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS"))
+ logger.info("wpas1 STATUS:\n" + wpas1.request("STATUS"))
+ log_ip_macsec_ns()
+ hwsim_utils.test_connectivity(wpas0, wpas1,
+ ifname1=macsec_ifname0,
+ ifname2=macsec_ifname1,
+ send_len=1400)
+ log_ip_macsec_ns()
+
+ subprocess.check_call(['ip', 'netns', 'exec', 'ns0',
+ 'ip', 'addr', 'add', '192.168.248.17/30',
+ 'dev', macsec_ifname0])
+ subprocess.check_call(['ip', 'netns', 'exec', 'ns1',
+ 'ip', 'addr', 'add', '192.168.248.18/30',
+ 'dev', macsec_ifname1])
+ c = subprocess.Popen(['ip', 'netns', 'exec', 'ns0',
+ 'ping', '-c', '2', '192.168.248.18'],
+ stdout=subprocess.PIPE)
+ res = c.stdout.read().decode()
+ c.stdout.close()
+ logger.info("ping:\n" + res)
+ if "2 packets transmitted, 2 received" not in res:
+ raise Exception("ping did not work")
+
+ wpas0.close_monitor()
+ wpas0.request("TERMINATE")
+ wpas0.close_control()
+ del wpas0
+ wpas1.close_monitor()
+ wpas1.request("TERMINATE")
+ wpas1.close_control()
+ del wpas1
+
+ time.sleep(1)
+ for i in range(len(cmd)):
+ cmd[i].close()
+
+def test_macsec_psk_fail_cp(dev, apdev):
+ """MACsec PSK local failures in CP state machine"""
+ try:
+ add_veth()
+ wpa = add_wpas_interfaces()
+ set_mka_psk_config(wpa[0])
+ with alloc_fail(wpa[0], 1, "sm_CP_RECEIVE_Enter"):
+ set_mka_psk_config(wpa[1])
+ wait_fail_trigger(wpa[0], "GET_ALLOC_FAIL", max_iter=100)
+
+ wait_mka_done(wpa)
+ finally:
+ cleanup_macsec()
+
+def test_macsec_psk_fail_cp2(dev, apdev):
+ """MACsec PSK local failures in CP state machine (2)"""
+ try:
+ add_veth()
+ wpa = add_wpas_interfaces()
+ set_mka_psk_config(wpa[0])
+ with alloc_fail(wpa[1], 1, "ieee802_1x_cp_sm_init"):
+ set_mka_psk_config(wpa[1])
+ wait_fail_trigger(wpa[1], "GET_ALLOC_FAIL", max_iter=100)
+
+ wait_mka_done(wpa)
+ finally:
+ cleanup_macsec()
+
+def cleanup_macsec_hostapd():
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5', monitor=False)
+ wpas.interface_remove("veth0")
+ del wpas
+ hapd = hostapd.HostapdGlobal()
+ hapd.remove('veth1')
+ subprocess.call(["ip", "link", "del", "veth0"],
+ stderr=open('/dev/null', 'w'))
+ log_ip_link()
+
+def test_macsec_hostapd_psk(dev, apdev, params):
+ """MACsec PSK with hostapd"""
+ try:
+ run_macsec_hostapd_psk(dev, apdev, params, "macsec_hostapd_psk")
+ finally:
+ cleanup_macsec_hostapd()
+
+def run_macsec_hostapd_psk(dev, apdev, params, prefix, integ_only=False,
+ port0=None, port1=None, ckn0=None, ckn1=None,
+ cak0=None, cak1=None, expect_failure=False):
+ add_veth()
+
+ cap_veth0 = os.path.join(params['logdir'], prefix + ".veth0.pcap")
+ cap_veth1 = os.path.join(params['logdir'], prefix + ".veth1.pcap")
+ cap_macsec0 = os.path.join(params['logdir'], prefix + ".macsec0.pcap")
+ cap_macsec1 = os.path.join(params['logdir'], prefix + ".macsec1.pcap")
+
+ for i in range(2):
+ subprocess.check_call(["ip", "link", "set", "dev", "veth%d" % i, "up"])
+
+ cmd = {}
+ cmd[0] = WlantestCapture('veth0', cap_veth0)
+ cmd[1] = WlantestCapture('veth1', cap_veth1)
+
+ wpa = add_wpas_interfaces(count=1)
+ wpas0 = wpa[0]
+
+ set_mka_psk_config(wpas0, integ_only=integ_only, port=port0, ckn=ckn0,
+ cak=cak0, mka_priority=100)
+
+ if cak1 is None:
+ cak1 = "000102030405060708090a0b0c0d0e0f"
+ if ckn1 is None:
+ ckn1 = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"
+ params = {"driver": "macsec_linux",
+ "interface": "veth1",
+ "eapol_version": "3",
+ "mka_cak": cak1,
+ "mka_ckn": ckn1,
+ "macsec_policy": "1",
+ "mka_priority": "1"}
+ if integ_only:
+ params["macsec_integ_only"] = "1"
+ if port1 is not None:
+ params["macsec_port"] = str(port1)
+ apdev = {'ifname': 'veth1'}
+ try:
+ hapd = hostapd.add_ap(apdev, params, driver="macsec_linux")
+ except:
+ raise HwsimSkip("No CONFIG_MACSEC=y in hostapd")
+
+ log_ip_macsec()
+ log_ip_link()
+
+ logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS"))
+ logger.info("wpas0 STATUS-DRIVER:\n" + wpas0.request("STATUS-DRIVER"))
+
+ wait_mka_done(wpa, expect_failure=expect_failure, hostapd=True)
+ log_ip_link()
+
+ if expect_failure:
+ for i in range(len(cmd)):
+ cmd[i].close()
+ return
+
+ macsec_ifname0 = wpas0.get_driver_status_field("parent_ifname")
+ macsec_ifname1 = hapd.get_driver_status_field("parent_ifname")
+
+ cmd[2] = WlantestCapture(macsec_ifname0, cap_macsec0)
+ cmd[3] = WlantestCapture(macsec_ifname1, cap_macsec1)
+ time.sleep(0.5)
+
+ logger.info("wpas0 MIB:\n" + wpas0.request("MIB"))
+ logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS"))
+ log_ip_macsec()
+ hwsim_utils.test_connectivity(wpas0, hapd,
+ ifname1=macsec_ifname0,
+ ifname2=macsec_ifname1,
+ send_len=1400)
+ log_ip_macsec()
+
+ time.sleep(1)
+ for i in range(len(cmd)):
+ cmd[i].close()
+
+def test_macsec_hostapd_eap(dev, apdev, params):
+ """MACsec EAP with hostapd"""
+ try:
+ run_macsec_hostapd_eap(dev, apdev, params, "macsec_hostapd_eap")
+ finally:
+ cleanup_macsec_hostapd()
+
+def run_macsec_hostapd_eap(dev, apdev, params, prefix, integ_only=False,
+ port0=None, port1=None, expect_failure=False):
+ add_veth()
+
+ cap_veth0 = os.path.join(params['logdir'], prefix + ".veth0.pcap")
+ cap_veth1 = os.path.join(params['logdir'], prefix + ".veth1.pcap")
+ cap_macsec0 = os.path.join(params['logdir'], prefix + ".macsec0.pcap")
+ cap_macsec1 = os.path.join(params['logdir'], prefix + ".macsec1.pcap")
+
+ for i in range(2):
+ subprocess.check_call(["ip", "link", "set", "dev", "veth%d" % i, "up"])
+
+ cmd = {}
+ cmd[0] = WlantestCapture('veth0', cap_veth0)
+ cmd[1] = WlantestCapture('veth1', cap_veth1)
+
+ wpa = add_wpas_interfaces(count=1)
+ wpas0 = wpa[0]
+
+ set_mka_eap_config(wpas0, integ_only=integ_only, port=port0,
+ mka_priority=100)
+
+ params = {"driver": "macsec_linux",
+ "interface": "veth1",
+ "eapol_version": "3",
+ "macsec_policy": "1",
+ "mka_priority": "1",
+ "ieee8021x": "1",
+ "auth_server_addr": "127.0.0.1",
+ "auth_server_port": "1812",
+ "auth_server_shared_secret": "radius",
+ "nas_identifier": "nas.w1.fi"}
+ if integ_only:
+ params["macsec_integ_only"] = "1"
+ if port1 is not None:
+ params["macsec_port"] = str(port1)
+ apdev = {'ifname': 'veth1'}
+ try:
+ hapd = hostapd.add_ap(apdev, params, driver="macsec_linux")
+ except:
+ raise HwsimSkip("No CONFIG_MACSEC=y in hostapd")
+
+ log_ip_macsec()
+ log_ip_link()
+
+ logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS"))
+ logger.info("wpas0 STATUS-DRIVER:\n" + wpas0.request("STATUS-DRIVER"))
+
+ wait_mka_done(wpa, expect_failure=expect_failure, hostapd=True)
+ log_ip_link()
+
+ if expect_failure:
+ for i in range(len(cmd)):
+ cmd[i].close()
+ return
+
+ macsec_ifname0 = wpas0.get_driver_status_field("parent_ifname")
+ macsec_ifname1 = hapd.get_driver_status_field("parent_ifname")
+
+ cmd[2] = WlantestCapture(macsec_ifname0, cap_macsec0)
+ cmd[3] = WlantestCapture(macsec_ifname1, cap_macsec1)
+ time.sleep(0.5)
+
+ logger.info("wpas0 MIB:\n" + wpas0.request("MIB"))
+ logger.info("wpas0 STATUS:\n" + wpas0.request("STATUS"))
+ log_ip_macsec()
+ hwsim_utils.test_connectivity(wpas0, hapd,
+ ifname1=macsec_ifname0,
+ ifname2=macsec_ifname1,
+ send_len=1400)
+ log_ip_macsec()
+
+ time.sleep(1)
+ for i in range(len(cmd)):
+ cmd[i].close()
diff --git a/contrib/wpa/tests/hwsim/test_mbo.py b/contrib/wpa/tests/hwsim/test_mbo.py
new file mode 100644
index 000000000000..36efd6a0e0ce
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_mbo.py
@@ -0,0 +1,613 @@
+# MBO tests
+# Copyright (c) 2016, Intel Deutschland GmbH
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+
+import hostapd
+import os
+import time
+
+import hostapd
+from tshark import run_tshark
+from utils import *
+
+def set_reg(country_code, apdev0=None, apdev1=None, dev0=None):
+ if apdev0:
+ hostapd.cmd_execute(apdev0, ['iw', 'reg', 'set', country_code])
+ if apdev1:
+ hostapd.cmd_execute(apdev1, ['iw', 'reg', 'set', country_code])
+ if dev0:
+ dev0.cmd_execute(['iw', 'reg', 'set', country_code])
+
+def run_mbo_supp_oper_classes(dev, apdev, hapd, hapd2, country, freq_list=None,
+ disable_ht=False, disable_vht=False):
+ """MBO and supported operating classes"""
+ addr = dev[0].own_addr()
+
+ res2 = None
+ res5 = None
+
+ dev[0].flush_scan_cache()
+ dev[0].dump_monitor()
+
+ logger.info("Country: " + country)
+ dev[0].note("Setting country code " + country)
+ set_reg(country, apdev[0], apdev[1], dev[0])
+ for j in range(5):
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=" + country in ev:
+ break
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+ _disable_ht = "1" if disable_ht else "0"
+ _disable_vht = "1" if disable_vht else "0"
+ if hapd:
+ hapd.set("country_code", country)
+ hapd.enable()
+ dev[0].scan_for_bss(hapd.own_addr(), 5180, force_scan=True)
+ dev[0].connect("test-wnm-mbo", key_mgmt="NONE", scan_freq="5180",
+ freq_list=freq_list, disable_ht=_disable_ht,
+ disable_vht=_disable_vht)
+ sta = hapd.get_sta(addr)
+ res5 = sta['supp_op_classes'][2:]
+ dev[0].wait_regdom(country_ie=True)
+ time.sleep(0.1)
+ hapd.disable()
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].request("ABORT_SCAN")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ hapd2.set("country_code", country)
+ hapd2.enable()
+ dev[0].scan_for_bss(hapd2.own_addr(), 2412, force_scan=True)
+ dev[0].connect("test-wnm-mbo-2", key_mgmt="NONE", scan_freq="2412",
+ freq_list=freq_list, disable_ht=_disable_ht,
+ disable_vht=_disable_vht)
+ sta = hapd2.get_sta(addr)
+ res2 = sta['supp_op_classes'][2:]
+ dev[0].wait_regdom(country_ie=True)
+ time.sleep(0.1)
+ hapd2.disable()
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].request("ABORT_SCAN")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ return res2, res5
+
+def run_mbo_supp_oper_class(dev, apdev, country, expected, inc5,
+ freq_list=None, disable_ht=False,
+ disable_vht=False):
+ if inc5:
+ params = {'ssid': "test-wnm-mbo",
+ 'mbo': '1',
+ "country_code": "US",
+ 'ieee80211d': '1',
+ "ieee80211n": "1",
+ "hw_mode": "a",
+ "channel": "36"}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ else:
+ hapd = None
+
+ params = {'ssid': "test-wnm-mbo-2",
+ 'mbo': '1',
+ "country_code": "US",
+ 'ieee80211d': '1',
+ "ieee80211n": "1",
+ "hw_mode": "g",
+ "channel": "1"}
+ hapd2 = hostapd.add_ap(apdev[1], params, no_enable=True)
+
+ try:
+ dev[0].request("STA_AUTOCONNECT 0")
+ res2, res5 = run_mbo_supp_oper_classes(dev, apdev, hapd, hapd2, country,
+ freq_list=freq_list,
+ disable_ht=disable_ht,
+ disable_vht=disable_vht)
+ finally:
+ dev[0].dump_monitor()
+ dev[0].request("STA_AUTOCONNECT 1")
+ wait_regdom_changes(dev[0])
+ country1 = dev[0].get_driver_status_field("country")
+ logger.info("Country code at the end (1): " + country1)
+ set_reg("00", apdev[0], apdev[1], dev[0])
+ country2 = dev[0].get_driver_status_field("country")
+ logger.info("Country code at the end (2): " + country2)
+ for i in range(5):
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+ if ev is None or "init=USER type=WORLD" in ev:
+ break
+ wait_regdom_changes(dev[0])
+ country3 = dev[0].get_driver_status_field("country")
+ logger.info("Country code at the end (3): " + country3)
+ if country3 != "00":
+ clear_country(dev)
+
+ # For now, allow operating class 129 to be missing since not all
+ # installed regdb files include the 160 MHz channels.
+ expected2 = expected.replace('808182', '8082')
+ # For now, allow operating classes 121-123 to be missing since not all
+ # installed regdb files include the related US DFS channels.
+ expected2 = expected2.replace('78797a7b7c', '787c')
+ expected3 = expected
+ # For now, allow operating classes 124-127 to be missing for Finland
+ # since they were added only recently in regdb.
+ if country == "FI":
+ expected3 = expected3.replace("7b7c7d7e7f80", "7b80")
+ if res2 != expected and res2 != expected2 and res2 != expected3:
+ raise Exception("Unexpected supp_op_class string (country=%s, 2.4 GHz): %s (expected: %s)" % (country, res2, expected))
+ if inc5 and res5 != expected and res5 != expected2 and res5 != expected3:
+ raise Exception("Unexpected supp_op_class string (country=%s, 5 GHz): %s (expected: %s)" % (country, res5, expected))
+
+def test_mbo_supp_oper_classes_za(dev, apdev):
+ """MBO and supported operating classes (ZA)"""
+ run_mbo_supp_oper_class(dev, apdev, "ZA",
+ "515354737475767778797a7b808182", True)
+
+def test_mbo_supp_oper_classes_fi(dev, apdev):
+ """MBO and supported operating classes (FI)"""
+ run_mbo_supp_oper_class(dev, apdev, "FI",
+ "515354737475767778797a7b7c7d7e7f808182", True)
+
+def test_mbo_supp_oper_classes_us(dev, apdev):
+ """MBO and supported operating classes (US)"""
+ run_mbo_supp_oper_class(dev, apdev, "US",
+ "515354737475767778797a7b7c7d7e7f808182", True)
+
+def test_mbo_supp_oper_classes_jp(dev, apdev):
+ """MBO and supported operating classes (JP)"""
+ run_mbo_supp_oper_class(dev, apdev, "JP",
+ "51525354737475767778797a7b808182", True)
+
+def test_mbo_supp_oper_classes_bd(dev, apdev):
+ """MBO and supported operating classes (BD)"""
+ run_mbo_supp_oper_class(dev, apdev, "BD",
+ "5153547c7d7e7f80", False)
+
+def test_mbo_supp_oper_classes_sy(dev, apdev):
+ """MBO and supported operating classes (SY)"""
+ run_mbo_supp_oper_class(dev, apdev, "SY",
+ "515354", False)
+
+def test_mbo_supp_oper_classes_us_freq_list(dev, apdev):
+ """MBO and supported operating classes (US) - freq_list"""
+ run_mbo_supp_oper_class(dev, apdev, "US", "515354", False,
+ freq_list="2412 2437 2462")
+
+def test_mbo_supp_oper_classes_us_disable_ht(dev, apdev):
+ """MBO and supported operating classes (US) - disable_ht"""
+ run_mbo_supp_oper_class(dev, apdev, "US", "517376797c7d", False,
+ disable_ht=True)
+
+def test_mbo_supp_oper_classes_us_disable_vht(dev, apdev):
+ """MBO and supported operating classes (US) - disable_vht"""
+ run_mbo_supp_oper_class(dev, apdev, "US",
+ "515354737475767778797a7b7c7d7e7f", False,
+ disable_vht=True)
+
+def test_mbo_assoc_disallow(dev, apdev, params):
+ """MBO and association disallowed"""
+ hapd1 = hostapd.add_ap(apdev[0], {"ssid": "MBO", "mbo": "1"})
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "MBO", "mbo": "1"})
+
+ logger.debug("Set mbo_assoc_disallow with invalid value")
+ if "FAIL" not in hapd1.request("SET mbo_assoc_disallow 2"):
+ raise Exception("Set mbo_assoc_disallow for AP1 succeeded unexpectedly with value 2")
+
+ logger.debug("Disallow associations to AP1 and allow association to AP2")
+ if "OK" not in hapd1.request("SET mbo_assoc_disallow 1"):
+ raise Exception("Failed to set mbo_assoc_disallow for AP1")
+ if "OK" not in hapd2.request("SET mbo_assoc_disallow 0"):
+ raise Exception("Failed to set mbo_assoc_disallow for AP2")
+
+ dev[0].connect("MBO", key_mgmt="NONE", scan_freq="2412")
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan.fc.type == 0 && wlan.fc.type_subtype == 0x00",
+ wait=False)
+ if "Destination address: " + hapd1.own_addr() in out:
+ raise Exception("Association request sent to disallowed AP")
+
+ timestamp = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan.fc.type_subtype == 0x00",
+ display=['frame.time'], wait=False)
+
+ logger.debug("Allow associations to AP1 and disallow associations to AP2")
+ if "OK" not in hapd1.request("SET mbo_assoc_disallow 0"):
+ raise Exception("Failed to set mbo_assoc_disallow for AP1")
+ if "OK" not in hapd2.request("SET mbo_assoc_disallow 1"):
+ raise Exception("Failed to set mbo_assoc_disallow for AP2")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ # Force new scan, so the assoc_disallowed indication is updated */
+ dev[0].request("FLUSH")
+
+ dev[0].connect("MBO", key_mgmt="NONE", scan_freq="2412")
+
+ filter = 'wlan.fc.type == 0 && wlan.fc.type_subtype == 0x00 && frame.time > "' + timestamp.rstrip() + '"'
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ filter, wait=False)
+ if "Destination address: " + hapd2.own_addr() in out:
+ raise Exception("Association request sent to disallowed AP 2")
+
+def test_mbo_assoc_disallow_ignore(dev, apdev):
+ """MBO and ignoring disallowed association"""
+ try:
+ _test_mbo_assoc_disallow_ignore(dev, apdev)
+ finally:
+ dev[0].request("SCAN_INTERVAL 5")
+
+def _test_mbo_assoc_disallow_ignore(dev, apdev):
+ hapd1 = hostapd.add_ap(apdev[0], {"ssid": "MBO", "mbo": "1"})
+ if "OK" not in hapd1.request("SET mbo_assoc_disallow 1"):
+ raise Exception("Failed to set mbo_assoc_disallow for AP1")
+
+ if "OK" not in dev[0].request("SCAN_INTERVAL 1"):
+ raise Exception("Failed to set scan interval")
+ dev[0].connect("MBO", key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("CTRL-EVENT-NETWORK-NOT-FOUND not seen")
+
+ if "OK" not in dev[0].request("SET ignore_assoc_disallow 1"):
+ raise Exception("Failed to set ignore_assoc_disallow")
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("CTRL-EVENT-ASSOC-REJECT not seen")
+ if "status_code=17" not in ev:
+ raise Exception("Unexpected association reject reason: " + ev)
+
+ if "OK" not in hapd1.request("SET mbo_assoc_disallow 0"):
+ raise Exception("Failed to set mbo_assoc_disallow for AP1")
+ dev[0].wait_connected()
+
+@remote_compatible
+def test_mbo_cell_capa_update(dev, apdev):
+ """MBO cellular data capability update"""
+ ssid = "test-wnm-mbo"
+ params = {'ssid': ssid, 'mbo': '1'}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
+ raise Exception("Failed to set STA as cellular data capable")
+
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+ addr = dev[0].own_addr()
+ sta = hapd.get_sta(addr)
+ if 'mbo_cell_capa' not in sta or sta['mbo_cell_capa'] != '1':
+ raise Exception("mbo_cell_capa missing after association")
+
+ if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
+ raise Exception("Failed to set STA as cellular data not-capable")
+ # Duplicate update for additional code coverage
+ if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
+ raise Exception("Failed to set STA as cellular data not-capable")
+
+ time.sleep(0.2)
+ sta = hapd.get_sta(addr)
+ if 'mbo_cell_capa' not in sta:
+ raise Exception("mbo_cell_capa missing after update")
+ if sta['mbo_cell_capa'] != '3':
+ raise Exception("mbo_cell_capa not updated properly")
+
+@remote_compatible
+def test_mbo_cell_capa_update_pmf(dev, apdev):
+ """MBO cellular data capability update with PMF required"""
+ ssid = "test-wnm-mbo"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ params['mbo'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
+ raise Exception("Failed to set STA as cellular data capable")
+
+ dev[0].connect(ssid, psk=passphrase, key_mgmt="WPA-PSK-SHA256",
+ proto="WPA2", ieee80211w="2", scan_freq="2412")
+ hapd.wait_sta()
+
+ addr = dev[0].own_addr()
+ sta = hapd.get_sta(addr)
+ if 'mbo_cell_capa' not in sta or sta['mbo_cell_capa'] != '1':
+ raise Exception("mbo_cell_capa missing after association")
+
+ if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
+ raise Exception("Failed to set STA as cellular data not-capable")
+
+ time.sleep(0.2)
+ sta = hapd.get_sta(addr)
+ if 'mbo_cell_capa' not in sta:
+ raise Exception("mbo_cell_capa missing after update")
+ if sta['mbo_cell_capa'] != '3':
+ raise Exception("mbo_cell_capa not updated properly")
+
+def test_mbo_wnm_token_wrap(dev, apdev):
+ """MBO WNM token wrap around"""
+ ssid = "test-wnm-mbo"
+ params = {'ssid': ssid, 'mbo': '1'}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+ # Trigger transmission of 256 WNM-Notification frames to wrap around the
+ # 8-bit mbo_wnm_token counter.
+ for i in range(128):
+ if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
+ raise Exception("Failed to set STA as cellular data capable")
+ if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
+ raise Exception("Failed to set STA as cellular data not-capable")
+
+@remote_compatible
+def test_mbo_non_pref_chan(dev, apdev):
+ """MBO non-preferred channel list"""
+ ssid = "test-wnm-mbo"
+ params = {'ssid': ssid, 'mbo': '1'}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:99"):
+ raise Exception("Invalid non_pref_chan value accepted")
+ if "FAIL" not in dev[0].request("SET non_pref_chan 81:15:200:3"):
+ raise Exception("Invalid non_pref_chan value accepted")
+ if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:3 81:7:201:3"):
+ raise Exception("Invalid non_pref_chan value accepted")
+ if "OK" not in dev[0].request("SET non_pref_chan 81:7:200:3"):
+ raise Exception("Failed to set non-preferred channel list")
+ if "OK" not in dev[0].request("SET non_pref_chan 81:7:200:1 81:9:100:2"):
+ raise Exception("Failed to set non-preferred channel list")
+
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+ addr = dev[0].own_addr()
+ sta = hapd.get_sta(addr)
+ logger.debug("STA: " + str(sta))
+ if 'non_pref_chan[0]' not in sta:
+ raise Exception("Missing non_pref_chan[0] value (assoc)")
+ if sta['non_pref_chan[0]'] != '81:200:1:7':
+ raise Exception("Unexpected non_pref_chan[0] value (assoc)")
+ if 'non_pref_chan[1]' not in sta:
+ raise Exception("Missing non_pref_chan[1] value (assoc)")
+ if sta['non_pref_chan[1]'] != '81:100:2:9':
+ raise Exception("Unexpected non_pref_chan[1] value (assoc)")
+ if 'non_pref_chan[2]' in sta:
+ raise Exception("Unexpected non_pref_chan[2] value (assoc)")
+
+ if "OK" not in dev[0].request("SET non_pref_chan 81:9:100:2"):
+ raise Exception("Failed to update non-preferred channel list")
+ time.sleep(0.1)
+ sta = hapd.get_sta(addr)
+ logger.debug("STA: " + str(sta))
+ if 'non_pref_chan[0]' not in sta:
+ raise Exception("Missing non_pref_chan[0] value (update 1)")
+ if sta['non_pref_chan[0]'] != '81:100:2:9':
+ raise Exception("Unexpected non_pref_chan[0] value (update 1)")
+ if 'non_pref_chan[1]' in sta:
+ raise Exception("Unexpected non_pref_chan[1] value (update 1)")
+
+ if "OK" not in dev[0].request("SET non_pref_chan 81:9:100:2 81:10:100:2 81:8:100:2 81:7:100:1 81:5:100:1"):
+ raise Exception("Failed to update non-preferred channel list")
+ time.sleep(0.1)
+ sta = hapd.get_sta(addr)
+ logger.debug("STA: " + str(sta))
+ if 'non_pref_chan[0]' not in sta:
+ raise Exception("Missing non_pref_chan[0] value (update 2)")
+ if sta['non_pref_chan[0]'] != '81:100:1:7,5':
+ raise Exception("Unexpected non_pref_chan[0] value (update 2)")
+ if 'non_pref_chan[1]' not in sta:
+ raise Exception("Missing non_pref_chan[1] value (update 2)")
+ if sta['non_pref_chan[1]'] != '81:100:2:9,10,8':
+ raise Exception("Unexpected non_pref_chan[1] value (update 2)")
+ if 'non_pref_chan[2]' in sta:
+ raise Exception("Unexpected non_pref_chan[2] value (update 2)")
+
+ if "OK" not in dev[0].request("SET non_pref_chan 81:5:90:2 82:14:91:2"):
+ raise Exception("Failed to update non-preferred channel list")
+ time.sleep(0.1)
+ sta = hapd.get_sta(addr)
+ logger.debug("STA: " + str(sta))
+ if 'non_pref_chan[0]' not in sta:
+ raise Exception("Missing non_pref_chan[0] value (update 3)")
+ if sta['non_pref_chan[0]'] != '81:90:2:5':
+ raise Exception("Unexpected non_pref_chan[0] value (update 3)")
+ if 'non_pref_chan[1]' not in sta:
+ raise Exception("Missing non_pref_chan[1] value (update 3)")
+ if sta['non_pref_chan[1]'] != '82:91:2:14':
+ raise Exception("Unexpected non_pref_chan[1] value (update 3)")
+ if 'non_pref_chan[2]' in sta:
+ raise Exception("Unexpected non_pref_chan[2] value (update 3)")
+
+ if "OK" not in dev[0].request("SET non_pref_chan "):
+ raise Exception("Failed to update non-preferred channel list")
+ time.sleep(0.1)
+ sta = hapd.get_sta(addr)
+ logger.debug("STA: " + str(sta))
+ if 'non_pref_chan[0]' in sta:
+ raise Exception("Unexpected non_pref_chan[0] value (update 4)")
+
+@remote_compatible
+def test_mbo_sta_supp_op_classes(dev, apdev):
+ """MBO STA supported operating classes"""
+ ssid = "test-wnm-mbo"
+ params = {'ssid': ssid, 'mbo': '1'}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+ addr = dev[0].own_addr()
+ sta = hapd.get_sta(addr)
+ logger.debug("STA: " + str(sta))
+ if 'supp_op_classes' not in sta:
+ raise Exception("No supp_op_classes")
+ supp = bytearray(binascii.unhexlify(sta['supp_op_classes']))
+ if supp[0] != 81:
+ raise Exception("Unexpected current operating class %d" % supp[0])
+ if 115 not in supp:
+ raise Exception("Operating class 115 missing")
+
+def test_mbo_failures(dev, apdev):
+ """MBO failure cases"""
+ ssid = "test-wnm-mbo"
+ params = {'ssid': ssid, 'mbo': '1'}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with alloc_fail(dev[0], 1, "wpas_mbo_ie"):
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+ with alloc_fail(dev[0], 1, "wpas_mbo_send_wnm_notification"):
+ if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
+ raise Exception("Failed to set STA as cellular data capable")
+ with fail_test(dev[0], 1, "wpas_mbo_send_wnm_notification"):
+ if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
+ raise Exception("Failed to set STA as cellular data not-capable")
+ with alloc_fail(dev[0], 1, "wpas_mbo_update_non_pref_chan"):
+ if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:3"):
+ raise Exception("non_pref_chan value accepted during OOM")
+ with alloc_fail(dev[0], 2, "wpas_mbo_update_non_pref_chan"):
+ if "FAIL" not in dev[0].request("SET non_pref_chan 81:7:200:3"):
+ raise Exception("non_pref_chan value accepted during OOM")
+
+def test_mbo_wnm_bss_tm_ie_parsing(dev, apdev):
+ """MBO BSS transition request MBO IE parsing"""
+ ssid = "test-wnm-mbo"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ addr = dev[0].own_addr()
+ dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
+ proto="WPA2", ieee80211w="0", scan_freq="2412")
+
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ hdr = "d0003a01" + addr.replace(':', '') + bssid.replace(':', '') + bssid.replace(':', '') + "3000"
+ btm_hdr = "0a070100030001"
+
+ tests = [("Truncated attribute in MBO IE", "dd06506f9a160101"),
+ ("Unexpected cell data capa attribute length in MBO IE",
+ "dd09506f9a160501030500"),
+ ("Unexpected transition reason attribute length in MBO IE",
+ "dd06506f9a160600"),
+ ("Unexpected assoc retry delay attribute length in MBO IE",
+ "dd0c506f9a160100080200000800"),
+ ("Unknown attribute id 255 in MBO IE",
+ "dd06506f9a16ff00")]
+
+ for test, mbo_ie in tests:
+ logger.info(test)
+ dev[0].request("NOTE " + test)
+ frame = hdr + btm_hdr + mbo_ie
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ logger.info("Unexpected association retry delay")
+ dev[0].request("NOTE Unexpected association retry delay")
+ btm_hdr = "0a070108030001112233445566778899aabbcc"
+ mbo_ie = "dd08506f9a1608020000"
+ frame = hdr + btm_hdr + mbo_ie
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ dev[0].request("SET ext_mgmt_frame_handling 0")
+
+def test_mbo_without_pmf(dev, apdev):
+ """MBO and WPA2 without PMF"""
+ ssid = "test-wnm-mbo"
+ params = {'ssid': ssid, 'mbo': '1', "wpa": '2',
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wpa_passphrase": "12345678"}
+ try:
+ # "MBO: PMF needs to be enabled whenever using WPA2 with MBO"
+ hostapd.add_ap(apdev[0], params)
+ raise Exception("AP setup succeeded unexpectedly")
+ except Exception as e:
+ if "Failed to enable hostapd" in str(e):
+ pass
+ else:
+ raise
+
+def test_mbo_without_pmf_workaround(dev, apdev):
+ """MBO and WPA2 without PMF on misbehaving AP"""
+ ssid = "test-wnm-mbo"
+ params0 = {'ssid': ssid, "wpa": '2',
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wpa_passphrase": "12345678",
+ "vendor_elements": "dd07506f9a16010100"}
+ params1 = {'ssid': ssid, "mbo": '1', "wpa": '2',
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "wpa_passphrase": "12345678", "ieee80211w": "1"}
+ hapd0 = hostapd.add_ap(apdev[0], params0)
+ dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
+ proto="WPA2", ieee80211w="1", scan_freq="2412")
+ hapd0.wait_sta()
+ sta = hapd0.get_sta(dev[0].own_addr())
+ ext_capab = bytearray(binascii.unhexlify(sta['ext_capab']))
+ if ext_capab[2] & 0x08:
+ raise Exception("STA did not disable BSS Transition capability")
+ hapd1 = hostapd.add_ap(apdev[1], params1)
+ dev[0].scan_for_bss(hapd1.own_addr(), 2412, force_scan=True)
+ dev[0].roam(hapd1.own_addr())
+ hapd1.wait_sta()
+ sta = hapd1.get_sta(dev[0].own_addr())
+ ext_capab = bytearray(binascii.unhexlify(sta['ext_capab']))
+ if not ext_capab[2] & 0x08:
+ raise Exception("STA disabled BSS Transition capability")
+ dev[0].roam(hapd0.own_addr())
+ hapd0.wait_sta()
+ sta = hapd0.get_sta(dev[0].own_addr())
+ ext_capab = bytearray(binascii.unhexlify(sta['ext_capab']))
+ if ext_capab[2] & 0x08:
+ raise Exception("STA did not disable BSS Transition capability")
+
+def check_mbo_anqp(dev, bssid, cell_data_conn_pref):
+ if "OK" not in dev.request("ANQP_GET " + bssid + " 272,mbo:2"):
+ raise Exception("ANQP_GET command failed")
+
+ ev = dev.wait_event(["GAS-QUERY-START"], timeout=5)
+ if ev is None:
+ raise Exception("GAS query start timed out")
+
+ ev = dev.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+
+ if cell_data_conn_pref is not None:
+ ev = dev.wait_event(["RX-MBO-ANQP"], timeout=1)
+ if ev is None or "cell_conn_pref" not in ev:
+ raise Exception("Did not receive MBO Cellular Data Connection Preference")
+ if cell_data_conn_pref != int(ev.split('=')[1]):
+ raise Exception("Unexpected cell_conn_pref value: " + ev)
+
+ dev.dump_monitor()
+
+def test_mbo_anqp(dev, apdev):
+ """MBO ANQP"""
+ params = {'ssid': "test-wnm-mbo",
+ 'mbo': '1',
+ 'interworking': '1',
+ 'mbo_cell_data_conn_pref': '1'}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412", force_scan=True)
+ check_mbo_anqp(dev[0], bssid, 1)
+
+ hapd.set('mbo_cell_data_conn_pref', '255')
+ check_mbo_anqp(dev[0], bssid, 255)
+
+ hapd.set('mbo_cell_data_conn_pref', '-1')
+ check_mbo_anqp(dev[0], bssid, None)
diff --git a/contrib/wpa/tests/hwsim/test_module_tests.py b/contrib/wpa/tests/hwsim/test_module_tests.py
new file mode 100644
index 000000000000..2e96c45d2364
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_module_tests.py
@@ -0,0 +1,28 @@
+# Module tests
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import time
+
+import hostapd
+
+def test_module_wpa_supplicant(dev, apdev, params):
+ """wpa_supplicant module tests"""
+ if "OK" not in dev[0].global_request("MODULE_TESTS"):
+ raise Exception("Module tests failed")
+ # allow eloop test to complete
+ time.sleep(0.75)
+ dev[0].relog()
+ with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+ res = f.read()
+ if "FAIL - should not have called this function" in res:
+ raise Exception("eloop test failed")
+
+def test_module_hostapd(dev):
+ """hostapd module tests"""
+ hapd_global = hostapd.HostapdGlobal()
+ if "OK" not in hapd_global.ctrl.request("MODULE_TESTS"):
+ raise Exception("Module tests failed")
diff --git a/contrib/wpa/tests/hwsim/test_monitor_interface.py b/contrib/wpa/tests/hwsim/test_monitor_interface.py
new file mode 100644
index 000000000000..e1a48aeb0c1e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_monitor_interface.py
@@ -0,0 +1,94 @@
+# AP mode using the older monitor interface design
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import time
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import radiotap_build, start_monitor, stop_monitor
+
+def test_monitor_iface_open(dev, apdev):
+ """Open connection using cfg80211 monitor interface on AP"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="use_monitor=1")
+ id = wpas.add_network()
+ wpas.set_network(id, "mode", "2")
+ wpas.set_network_quoted(id, "ssid", "monitor-iface")
+ wpas.set_network(id, "key_mgmt", "NONE")
+ wpas.set_network(id, "frequency", "2412")
+ wpas.connect_network(id)
+
+ dev[0].connect("monitor-iface", key_mgmt="NONE", scan_freq="2412")
+
+def test_monitor_iface_wpa2_psk(dev, apdev):
+ """WPA2-PSK connection using cfg80211 monitor interface on AP"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="use_monitor=1")
+ id = wpas.add_network()
+ wpas.set_network(id, "mode", "2")
+ wpas.set_network_quoted(id, "ssid", "monitor-iface-wpa2")
+ wpas.set_network(id, "proto", "WPA2")
+ wpas.set_network(id, "key_mgmt", "WPA-PSK")
+ wpas.set_network_quoted(id, "psk", "12345678")
+ wpas.set_network(id, "pairwise", "CCMP")
+ wpas.set_network(id, "group", "CCMP")
+ wpas.set_network(id, "frequency", "2412")
+ wpas.connect_network(id)
+
+ dev[0].connect("monitor-iface-wpa2", psk="12345678", scan_freq="2412")
+
+def test_monitor_iface_multi_bss(dev, apdev):
+ """AP mode mmonitor interface with hostapd multi-BSS setup"""
+ params = {"ssid": "monitor-iface", "driver_params": "use_monitor=1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hostapd.add_bss(apdev[0], apdev[0]['ifname'] + '-2', 'bss-2.conf')
+ dev[0].connect("monitor-iface", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("bss-2", key_mgmt="NONE", scan_freq="2412")
+
+@remote_compatible
+def test_monitor_iface_unknown_sta(dev, apdev):
+ """AP mode monitor interface and Data frame from unknown STA"""
+ ssid = "monitor-iface-pmf"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ params['driver_params'] = "use_monitor=1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ bssid = apdev[0]['bssid']
+ addr = dev[0].p2p_interface_addr()
+ dev[0].connect(ssid, psk=passphrase, ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ dev[0].request("DROP_SA")
+ # This protected Deauth will be ignored by the STA
+ hapd.request("DEAUTHENTICATE " + addr)
+ # But the unprotected Deauth from TX frame-from-unassoc-STA will now be
+ # processed
+ try:
+ sock = start_monitor(apdev[1]["ifname"])
+ radiotap = radiotap_build()
+
+ bssid = hapd.own_addr().replace(':', '')
+ addr = dev[0].own_addr().replace(':', '')
+
+ # Inject Data frame from STA to AP since we not have SA in place
+ # anymore for normal data TX
+ frame = binascii.unhexlify("48010000" + bssid + addr + bssid + "0000")
+ sock.send(radiotap + frame)
+ finally:
+ stop_monitor(apdev[1]["ifname"])
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No disconnection")
+ dev[0].request("DISCONNECT")
diff --git a/contrib/wpa/tests/hwsim/test_mscs.py b/contrib/wpa/tests/hwsim/test_mscs.py
new file mode 100644
index 000000000000..b200550b3ac3
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_mscs.py
@@ -0,0 +1,231 @@
+# Test cases for MSCS
+# Copyright (c) 2021, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import struct
+import time
+
+import hostapd
+from utils import *
+
+def register_mcsc_req(hapd):
+ type = 0x00d0
+ match = "1304"
+ if "OK" not in hapd.request("REGISTER_FRAME %04x %s" % (type, match)):
+ raise Exception("Could not register frame reception for Robust AV Streaming")
+
+def handle_mscs_req(hapd, wrong_dialog=False, status_code=0):
+ msg = hapd.mgmt_rx()
+ if msg['subtype'] != 13:
+ logger.info("RX:" + str(msg))
+ raise Exception("Received unexpected Management frame")
+ categ, act, dialog_token = struct.unpack('BBB', msg['payload'][0:3])
+ if categ != 19 or act != 4:
+ logger.info("RX:" + str(msg))
+ raise Exception("Received unexpected Action frame")
+
+ if wrong_dialog:
+ dialog_token = (dialog_token + 1) % 256
+ msg['da'] = msg['sa']
+ msg['sa'] = hapd.own_addr()
+ msg['payload'] = struct.pack('<BBBH', 19, 5, dialog_token, status_code)
+ hapd.mgmt_tx(msg)
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None or "stype=13 ok=1" not in ev:
+ raise Exception("No TX status reported")
+
+def wait_mscs_result(dev, expect_status=0):
+ ev = dev.wait_event(["CTRL-EVENT-MSCS-RESULT"], timeout=1)
+ if ev is None:
+ raise Exception("No MSCS result reported")
+ if "status_code=%d" % expect_status not in ev:
+ raise Exception("Unexpected MSCS result: " + ev)
+
+def test_mscs_invalid_params(dev, apdev):
+ """MSCS command invalid parameters"""
+ tests = ["",
+ "add Xp_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F",
+ "add up_bitmap=F0 Xp_limit=7 stream_timeout=12345 frame_classifier=045F",
+ "add up_bitmap=F0 up_limit=7 Xtream_timeout=12345 frame_classifier=045F",
+ "add up_bitmap=F0 up_limit=7 stream_timeout=12345 Xrame_classifier=045F",
+ "add up_bitmap=X0 up_limit=7 stream_timeout=12345 frame_classifier=045F",
+ "add up_bitmap=F0 up_limit=7 stream_timeout=0 frame_classifier=045F",
+ "add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=X45F",
+ "change "]
+ for t in tests:
+ if "FAIL" not in dev[0].request("MSCS " + t):
+ raise Exception("Invalid MSCS parameters accepted: " + t)
+
+def test_mscs_without_ap_support(dev, apdev):
+ """MSCS without AP support"""
+ try:
+ run_mscs_without_ap_support(dev, apdev)
+ finally:
+ dev[0].request("MSCS remove")
+
+def run_mscs_without_ap_support(dev, apdev):
+ params = {"ssid": "mscs",
+ "ext_capa_mask": 10*"00" + "20"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("Failed to configure MSCS")
+
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412")
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "FAIL" not in dev[0].request(cmd):
+ raise Exception("MSCS change accepted unexpectedly")
+
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "FAIL" not in dev[0].request(cmd):
+ raise Exception("MSCS add accepted unexpectedly")
+
+def test_mscs_post_assoc(dev, apdev):
+ """MSCS configuration post-association"""
+ try:
+ run_mscs_post_assoc(dev, apdev)
+ finally:
+ dev[0].request("MSCS remove")
+
+def run_mscs_post_assoc(dev, apdev):
+ params = {"ssid": "mscs",
+ "ext_capa": 10*"00" + "20"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ register_mcsc_req(hapd)
+
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412")
+
+ hapd.dump_monitor()
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "FAIL" not in dev[0].request(cmd):
+ raise Exception("MSCS change accepted unexpectedly")
+
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS add failed")
+
+ handle_mscs_req(hapd)
+ wait_mscs_result(dev[0])
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS change failed")
+
+ handle_mscs_req(hapd)
+ wait_mscs_result(dev[0])
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS change failed")
+
+ handle_mscs_req(hapd, status_code=23456)
+ wait_mscs_result(dev[0], expect_status=23456)
+
+def test_mscs_pre_assoc(dev, apdev):
+ """MSCS configuration pre-association"""
+ try:
+ run_mscs_pre_assoc(dev, apdev)
+ finally:
+ dev[0].request("MSCS remove")
+
+def run_mscs_pre_assoc(dev, apdev):
+ params = {"ssid": "mscs",
+ "ext_capa": 10*"00" + "20",
+ "assocresp_elements": "ff0c5800000000000000" + "01020000"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ register_mcsc_req(hapd)
+
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS add failed")
+
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ wait_mscs_result(dev[0])
+ dev[0].wait_connected()
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS change failed")
+
+ handle_mscs_req(hapd)
+ wait_mscs_result(dev[0])
+
+ cmd = "MSCS change up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS change failed")
+
+ handle_mscs_req(hapd, wrong_dialog=True)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-MSCS-RESULT"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected MSCS result reported")
+
+def test_mscs_assoc_failure(dev, apdev):
+ """MSCS configuration failure during association exchange"""
+ try:
+ run_mscs_assoc_failure(dev, apdev)
+ finally:
+ dev[0].request("MSCS remove")
+
+def run_mscs_assoc_failure(dev, apdev):
+ params = {"ssid": "mscs",
+ "ext_capa": 10*"00" + "20",
+ "assocresp_elements": "ff0c5800000000000000" + "01020001"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ register_mcsc_req(hapd)
+
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("MSCS add failed")
+
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ wait_mscs_result(dev[0], expect_status=256)
+ dev[0].wait_connected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ hapd.dump_monitor()
+ # No MSCS Status subelement
+ hapd.set("assocresp_elements", "ff085800000000000000")
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-MSCS-RESULT"],
+ timeout=10)
+ if ev is None:
+ raise Exception("No connection event")
+ if "CTRL-EVENT-MSCS-RESULT" in ev:
+ raise Exception("Unexpected MSCS result")
+
+def test_mscs_local_errors(dev, apdev):
+ """MSCS configuration local errors"""
+ try:
+ run_mscs_local_errors(dev, apdev)
+ finally:
+ dev[0].request("MSCS remove")
+
+def run_mscs_local_errors(dev, apdev):
+ params = {"ssid": "mscs",
+ "ext_capa": 10*"00" + "20"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ register_mcsc_req(hapd)
+
+ dev[0].connect("mscs", key_mgmt="NONE", scan_freq="2412")
+
+ hapd.dump_monitor()
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ for count in range(1, 3):
+ with alloc_fail(dev[0], count, "wpas_send_mscs_req"):
+ cmd = "MSCS add up_bitmap=F0 up_limit=7 stream_timeout=12345 frame_classifier=045F"
+ if "FAIL" not in dev[0].request(cmd):
+ raise Exception("MSCS add succeeded in error case")
diff --git a/contrib/wpa/tests/hwsim/test_multi_ap.py b/contrib/wpa/tests/hwsim/test_multi_ap.py
new file mode 100644
index 000000000000..ca8ea3a31f90
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_multi_ap.py
@@ -0,0 +1,363 @@
+# Test cases for Multi-AP
+# Copyright (c) 2018, The Linux Foundation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+
+def test_multi_ap_association(dev, apdev):
+ """Multi-AP association in backhaul BSS"""
+ run_multi_ap_association(dev, apdev, 1)
+ dev[1].connect("multi-ap", psk="12345678", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[1].wait_event(["CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-ASSOC-REJECT"],
+ timeout=5)
+ dev[1].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Connection result not reported")
+ if "CTRL-EVENT-ASSOC-REJECT" not in ev:
+ raise Exception("Association rejection not reported")
+ if "status_code=12" not in ev:
+ raise Exception("Unexpected association status code: " + ev)
+
+def test_multi_ap_association_shared_bss(dev, apdev):
+ """Multi-AP association in backhaul BSS (with fronthaul BSS enabled)"""
+ run_multi_ap_association(dev, apdev, 3)
+ dev[1].connect("multi-ap", psk="12345678", scan_freq="2412")
+
+def run_multi_ap_association(dev, apdev, multi_ap, wait_connect=True):
+ params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
+ if multi_ap:
+ params["multi_ap"] = str(multi_ap)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("multi-ap", psk="12345678", scan_freq="2412",
+ multi_ap_backhaul_sta="1", wait_connect=wait_connect)
+
+def test_multi_ap_backhaul_roam_with_bridge(dev, apdev):
+ """Multi-AP backhaul BSS reassociation to another BSS with bridge"""
+ br_ifname = 'sta-br0'
+ ifname = 'wlan5'
+ try:
+ run_multi_ap_backhaul_roam_with_bridge(dev, apdev)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'down'])
+ subprocess.call(['brctl', 'delif', br_ifname, ifname])
+ subprocess.call(['brctl', 'delbr', br_ifname])
+ subprocess.call(['iw', ifname, 'set', '4addr', 'off'])
+
+def run_multi_ap_backhaul_roam_with_bridge(dev, apdev):
+ br_ifname = 'sta-br0'
+ ifname = 'wlan5'
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ subprocess.call(['brctl', 'addbr', br_ifname])
+ subprocess.call(['brctl', 'setfd', br_ifname, '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', br_ifname, 'up'])
+ subprocess.call(['iw', ifname, 'set', '4addr', 'on'])
+ subprocess.check_call(['brctl', 'addif', br_ifname, ifname])
+ wpas.interface_add(ifname, br_ifname=br_ifname)
+ wpas.flush_scan_cache()
+
+ params = hostapd.wpa2_params(ssid="multi-ap", passphrase="12345678")
+ params["multi_ap"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas.connect("multi-ap", psk="12345678", scan_freq="2412",
+ multi_ap_backhaul_sta="1")
+
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+ wpas.scan_for_bss(bssid2, freq="2412", force_scan=True)
+ wpas.roam(bssid2)
+
+def test_multi_ap_disabled_on_ap(dev, apdev):
+ """Multi-AP association attempt when disabled on AP"""
+ run_multi_ap_association(dev, apdev, 0, wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-CONNECTED"],
+ timeout=5)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Connection result not reported")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection result")
+
+def test_multi_ap_fronthaul_on_ap(dev, apdev):
+ """Multi-AP association attempt when only fronthaul BSS on AP"""
+ run_multi_ap_association(dev, apdev, 2, wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-ASSOC-REJECT"],
+ timeout=5)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Connection result not reported")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection result")
+
+def run_multi_ap_wps(dev, apdev, params, params_backhaul=None, add_apdev=False,
+ run_csa=False, allow_csa_fail=False):
+ """Helper for running Multi-AP WPS tests
+
+ dev[0] does multi_ap WPS, dev[1] does normal WPS. apdev[0] is the fronthaul
+ BSS. If there is a separate backhaul BSS, it must have been set up by the
+ caller. params are the normal SSID parameters, they will be extended with
+ the WPS parameters. multi_ap_bssid must be given if it is not equal to the
+ fronthaul BSSID."""
+
+ wpas_apdev = None
+
+ if params_backhaul:
+ hapd_backhaul = hostapd.add_ap(apdev[1], params_backhaul)
+ multi_ap_bssid = hapd_backhaul.own_addr()
+ else:
+ multi_ap_bssid = apdev[0]['bssid']
+
+ params.update({"wps_state": "2", "eap_server": "1"})
+
+ # WPS with multi-ap station dev[0]
+ hapd = hostapd.add_ap(apdev[0], params)
+ conf = hapd.request("GET_CONFIG").splitlines()
+ if "ssid=" + params['ssid'] not in conf:
+ raise Exception("GET_CONFIG did not show correct ssid entry")
+ if "multi_ap" in params and \
+ "multi_ap=" + params["multi_ap"] not in conf:
+ raise Exception("GET_CONFIG did not show correct multi_ap entry")
+ if "multi_ap_backhaul_ssid" in params and \
+ "multi_ap_backhaul_ssid=" + params["multi_ap_backhaul_ssid"].strip('"') not in conf:
+ raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_ssid entry")
+ if "wpa" in params and "multi_ap_backhaul_wpa_passphrase" in params and \
+ "multi_ap_backhaul_wpa_passphrase=" + params["multi_ap_backhaul_wpa_passphrase"] not in conf:
+ raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_wpa_passphrase entry")
+ if "multi_ap_backhaul_wpa_psk" in params and \
+ "multi_ap_backhaul_wpa_psk=" + params["multi_ap_backhaul_wpa_psk"] not in conf:
+ raise Exception("GET_CONFIG did not show correct multi_ap_backhaul_wpa_psk entry")
+ hapd.request("WPS_PBC")
+ if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+
+ dev[0].request("WPS_PBC multi_ap=1")
+ dev[0].wait_connected(timeout=20)
+ status = dev[0].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != multi_ap_bssid:
+ raise Exception("Not fully connected")
+ if status['ssid'] != params['multi_ap_backhaul_ssid'].strip('"'):
+ raise Exception("Unexpected SSID %s != %s" % (status['ssid'], params["multi_ap_backhaul_ssid"]))
+ if status['pairwise_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration %s" % status['pairwise_cipher'])
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+
+ status = hapd.request("WPS_GET_STATUS")
+ if "PBC Status: Disabled" not in status:
+ raise Exception("PBC status not shown correctly")
+ if "Last WPS result: Success" not in status:
+ raise Exception("Last WPS result not shown correctly")
+ if "Peer Address: " + dev[0].own_addr() not in status:
+ raise Exception("Peer address not shown correctly")
+
+ if len(dev[0].list_networks()) != 1:
+ raise Exception("Unexpected number of network blocks")
+
+ # WPS with non-Multi-AP station dev[1]
+ hapd.request("WPS_PBC")
+ if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+
+ dev[1].request("WPS_PBC")
+ dev[1].wait_connected(timeout=20)
+ status = dev[1].get_status()
+ if status['wpa_state'] != 'COMPLETED' or status['bssid'] != apdev[0]['bssid']:
+ raise Exception("Not fully connected")
+ if status['ssid'] != params["ssid"]:
+ raise Exception("Unexpected SSID")
+ # Fronthaul may be something else than WPA2-PSK so don't test it.
+
+ status = hapd.request("WPS_GET_STATUS")
+ if "PBC Status: Disabled" not in status:
+ raise Exception("PBC status not shown correctly")
+ if "Last WPS result: Success" not in status:
+ raise Exception("Last WPS result not shown correctly")
+ if "Peer Address: " + dev[1].own_addr() not in status:
+ raise Exception("Peer address not shown correctly")
+
+ if len(dev[1].list_networks()) != 1:
+ raise Exception("Unexpected number of network blocks")
+
+ try:
+ # Add apdev to the same phy that dev[0]
+ if add_apdev:
+ wpas_apdev = {}
+ wpas_apdev['ifname'] = dev[0].ifname + "_ap"
+ status, buf = dev[0].cmd_execute(['iw', dev[0].ifname,
+ 'interface', 'add',
+ wpas_apdev['ifname'],
+ 'type', 'managed'])
+ if status != 0:
+ raise Exception("iw interface add failed")
+ wpas_hapd = hostapd.add_ap(wpas_apdev, params)
+
+ if run_csa:
+ if 'OK' not in hapd.request("CHAN_SWITCH 5 2462 ht"):
+ raise Exception("chan switch request failed")
+
+ ev = hapd.wait_event(["AP-CSA-FINISHED"], timeout=5)
+ if not ev:
+ raise Exception("chan switch failed")
+
+ # now check station
+ ev = dev[0].wait_event(["CTRL-EVENT-CHANNEL-SWITCH",
+ "CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if not ev:
+ raise Exception("sta - no chanswitch event")
+ if "CTRL-EVENT-CHANNEL-SWITCH" not in ev and not allow_csa_fail:
+ raise Exception("Received disconnection event instead of channel switch event")
+
+ if add_apdev:
+ dev[0].cmd_execute(['iw', wpas_apdev['ifname'], 'del'])
+ except:
+ if wpas_apdev:
+ dev[0].cmd_execute(['iw', wpas_apdev['ifname'], 'del'])
+ raise
+
+ return hapd
+
+def test_multi_ap_wps_shared(dev, apdev):
+ """WPS on shared fronthaul/backhaul AP"""
+ ssid = "multi-ap-wps"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params.update({"multi_ap": "3",
+ "multi_ap_backhaul_ssid": '"%s"' % ssid,
+ "multi_ap_backhaul_wpa_passphrase": passphrase})
+ hapd = run_multi_ap_wps(dev, apdev, params)
+ # Verify WPS parameter update with Multi-AP
+ if "OK" not in hapd.request("RELOAD"):
+ raise Exception("hostapd RELOAD failed")
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ hapd.request("WPS_PBC")
+ dev[0].request("WPS_PBC multi_ap=1")
+ dev[0].wait_connected(timeout=20)
+
+def test_multi_ap_wps_shared_csa(dev, apdev):
+ """WPS on shared fronthaul/backhaul AP, run CSA"""
+ ssid = "multi-ap-wps-csa"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params.update({"multi_ap": "3",
+ "multi_ap_backhaul_ssid": '"%s"' % ssid,
+ "multi_ap_backhaul_wpa_passphrase": passphrase})
+ run_multi_ap_wps(dev, apdev, params, run_csa=True)
+
+def test_multi_ap_wps_shared_apdev_csa(dev, apdev):
+ """WPS on shared fronthaul/backhaul AP add apdev on same phy and run CSA"""
+ ssid = "multi-ap-wps-apdev-csa"
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params.update({"multi_ap": "3",
+ "multi_ap_backhaul_ssid": '"%s"' % ssid,
+ "multi_ap_backhaul_wpa_passphrase": passphrase})
+ # This case is currently failing toc omplete CSA on the station interface.
+ # For the time being, ignore that to avoid always failing tests. Full
+ # validation can be enabled once the issue behind this is fixed.
+ run_multi_ap_wps(dev, apdev, params, add_apdev=True, run_csa=True,
+ allow_csa_fail=True)
+
+def test_multi_ap_wps_shared_psk(dev, apdev):
+ """WPS on shared fronthaul/backhaul AP using PSK"""
+ ssid = "multi-ap-wps"
+ psk = "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+ params = hostapd.wpa2_params(ssid=ssid)
+ params.update({"wpa_psk": psk,
+ "multi_ap": "3",
+ "multi_ap_backhaul_ssid": '"%s"' % ssid,
+ "multi_ap_backhaul_wpa_psk": psk})
+ run_multi_ap_wps(dev, apdev, params)
+
+def test_multi_ap_wps_split(dev, apdev):
+ """WPS on split fronthaul and backhaul AP"""
+ backhaul_ssid = "multi-ap-backhaul-wps"
+ backhaul_passphrase = "87654321"
+ params = hostapd.wpa2_params(ssid="multi-ap-fronthaul-wps",
+ passphrase="12345678")
+ params.update({"multi_ap": "2",
+ "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
+ "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase})
+ params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid,
+ passphrase=backhaul_passphrase)
+ params_backhaul.update({"multi_ap": "1"})
+
+ run_multi_ap_wps(dev, apdev, params, params_backhaul)
+
+def test_multi_ap_wps_split_psk(dev, apdev):
+ """WPS on split fronthaul and backhaul AP"""
+ backhaul_ssid = "multi-ap-backhaul-wps"
+ backhaul_psk = "1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+ params = hostapd.wpa2_params(ssid="multi-ap-fronthaul-wps",
+ passphrase="12345678")
+ params.update({"multi_ap": "2",
+ "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
+ "multi_ap_backhaul_wpa_psk": backhaul_psk})
+ params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid)
+ params_backhaul.update({"multi_ap": "1", "wpa_psk": backhaul_psk})
+
+ run_multi_ap_wps(dev, apdev, params, params_backhaul)
+
+def test_multi_ap_wps_split_mixed(dev, apdev):
+ """WPS on split fronthaul and backhaul AP with mixed-mode fronthaul"""
+ skip_without_tkip(dev[0])
+ backhaul_ssid = "multi-ap-backhaul-wps"
+ backhaul_passphrase = "87654321"
+ params = hostapd.wpa_mixed_params(ssid="multi-ap-fronthaul-wps",
+ passphrase="12345678")
+ params.update({"multi_ap": "2",
+ "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
+ "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase})
+ params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid,
+ passphrase=backhaul_passphrase)
+ params_backhaul.update({"multi_ap": "1"})
+
+ run_multi_ap_wps(dev, apdev, params, params_backhaul)
+
+def test_multi_ap_wps_split_open(dev, apdev):
+ """WPS on split fronthaul and backhaul AP with open fronthaul"""
+ backhaul_ssid = "multi-ap-backhaul-wps"
+ backhaul_passphrase = "87654321"
+ params = {"ssid": "multi-ap-wps-fronthaul", "multi_ap": "2",
+ "multi_ap_backhaul_ssid": '"%s"' % backhaul_ssid,
+ "multi_ap_backhaul_wpa_passphrase": backhaul_passphrase}
+ params_backhaul = hostapd.wpa2_params(ssid=backhaul_ssid,
+ passphrase=backhaul_passphrase)
+ params_backhaul.update({"multi_ap": "1"})
+
+ run_multi_ap_wps(dev, apdev, params, params_backhaul)
+
+def test_multi_ap_wps_fail_non_multi_ap(dev, apdev):
+ """Multi-AP WPS on non-WPS AP fails"""
+
+ params = hostapd.wpa2_params(ssid="non-multi-ap-wps", passphrase="12345678")
+ params.update({"wps_state": "2", "eap_server": "1"})
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("WPS_PBC")
+ if "PBC Status: Active" not in hapd.request("WPS_GET_STATUS"):
+ raise Exception("PBC status not shown correctly")
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].request("WPS_PBC %s multi_ap=1" % apdev[0]['bssid'])
+ # Since we will fail to associate and WPS doesn't even get started, there
+ # isn't much we can do except wait for timeout. For PBC, it is not possible
+ # to change the timeout from 2 minutes. Instead of waiting for the timeout,
+ # just check that WPS doesn't finish within reasonable time.
+ for i in range(2):
+ ev = dev[0].wait_event(["WPS-SUCCESS", "WPS-FAIL",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev and "WPS-" in ev:
+ raise Exception("WPS operation completed: " + ev)
+ dev[0].request("WPS_CANCEL")
diff --git a/contrib/wpa/tests/hwsim/test_nfc_p2p.py b/contrib/wpa/tests/hwsim/test_nfc_p2p.py
new file mode 100644
index 000000000000..3139dc4d33b4
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_nfc_p2p.py
@@ -0,0 +1,848 @@
+# P2P+NFC tests
+# Copyright (c) 2013, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import logging
+logger = logging.getLogger(__name__)
+
+import hwsim_utils
+from utils import alloc_fail
+
+grpform_events = ["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED",
+ "WPS-M2D",
+ "WPS-FAIL"]
+
+def set_ip_addr_info(dev):
+ dev.global_request("SET ip_addr_go 192.168.42.1")
+ dev.global_request("SET ip_addr_mask 255.255.255.0")
+ dev.global_request("SET ip_addr_start 192.168.42.100")
+ dev.global_request("SET ip_addr_end 192.168.42.199")
+
+def check_ip_addr(res):
+ if 'ip_addr' not in res:
+ raise Exception("Did not receive IP address from GO")
+ if '192.168.42.' not in res['ip_addr']:
+ raise Exception("Unexpected IP address received from GO")
+ if 'ip_mask' not in res:
+ raise Exception("Did not receive IP address mask from GO")
+ if '255.255.255.' not in res['ip_mask']:
+ raise Exception("Unexpected IP address mask received from GO")
+ if 'go_ip_addr' not in res:
+ raise Exception("Did not receive GO IP address from GO")
+ if '192.168.42.' not in res['go_ip_addr']:
+ raise Exception("Unexpected GO IP address received from GO")
+
+def test_nfc_p2p_go_neg(dev):
+ """NFC connection handover to form a new P2P group (initiator becomes GO)"""
+ try:
+ _test_nfc_p2p_go_neg(dev)
+ finally:
+ dev[0].global_request("SET p2p_go_intent 7")
+
+def _test_nfc_p2p_go_neg(dev):
+ set_ip_addr_info(dev[0])
+ ip = dev[0].p2pdev_request("GET ip_addr_go")
+ if ip != "192.168.42.1":
+ raise Exception("Unexpected ip_addr_go returned: " + ip)
+ dev[0].global_request("SET p2p_go_intent 10")
+ logger.info("Perform NFC connection handover")
+ req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+ res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res0 = dev[0].group_form_result(ev)
+
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED"], timeout=1)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res1 = dev[1].group_form_result(ev)
+ logger.info("Group formed")
+
+ if res1['role'] != 'client' or res0['role'] != 'GO':
+ raise Exception("Unexpected roles negotiated")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res1)
+
+def test_nfc_p2p_go_neg_ip_pool_oom(dev):
+ """NFC connection handover to form a new P2P group and IP pool OOM"""
+ try:
+ _test_nfc_p2p_go_neg_ip_pool_oom(dev)
+ finally:
+ dev[0].global_request("SET p2p_go_intent 7")
+
+def _test_nfc_p2p_go_neg_ip_pool_oom(dev):
+ set_ip_addr_info(dev[0])
+ ip = dev[0].p2pdev_request("GET ip_addr_go")
+ if ip != "192.168.42.1":
+ raise Exception("Unexpected ip_addr_go returned: " + ip)
+ dev[0].global_request("SET p2p_go_intent 10")
+ logger.info("Perform NFC connection handover")
+ req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ with alloc_fail(dev[0], 1, "bitfield_alloc;wpa_init"):
+ res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+ res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res0 = dev[0].group_form_result(ev)
+
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED"], timeout=1)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res1 = dev[1].group_form_result(ev)
+ logger.info("Group formed")
+
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ if 'ip_addr' in res1:
+ raise Exception("Unexpectedly received IP address from GO")
+
+def test_nfc_p2p_go_neg_reverse(dev):
+ """NFC connection handover to form a new P2P group (responder becomes GO)"""
+ try:
+ _test_nfc_p2p_go_neg_reverse(dev)
+ finally:
+ dev[0].global_request("SET p2p_go_intent 7")
+
+def _test_nfc_p2p_go_neg_reverse(dev):
+ set_ip_addr_info(dev[1])
+ dev[0].global_request("SET p2p_go_intent 3")
+ logger.info("Perform NFC connection handover")
+ req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+ res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res0 = dev[0].group_form_result(ev)
+
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED"], timeout=1)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res1 = dev[1].group_form_result(ev)
+ logger.info("Group formed")
+
+ if res0['role'] != 'client' or res1['role'] != 'GO':
+ raise Exception("Unexpected roles negotiated")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res0)
+
+def test_nfc_p2p_initiator_go(dev):
+ """NFC connection handover with initiator already GO"""
+ set_ip_addr_info(dev[0])
+ logger.info("Start autonomous GO")
+ dev[0].p2p_start_go()
+ logger.info("Perform NFC connection handover")
+ req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+ res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection to the group timed out")
+ res1 = dev[1].group_form_result(ev)
+ if res1['result'] != 'success':
+ raise Exception("Unexpected connection failure")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res1)
+
+def test_nfc_p2p_responder_go(dev):
+ """NFC connection handover with responder already GO"""
+ set_ip_addr_info(dev[1])
+ logger.info("Start autonomous GO")
+ dev[1].p2p_start_go()
+ logger.info("Perform NFC connection handover")
+ req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+ res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection to the group timed out")
+ res0 = dev[0].group_form_result(ev)
+ if res0['result'] != 'success':
+ raise Exception("Unexpected connection failure")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res0)
+
+def test_nfc_p2p_both_go(dev):
+ """NFC connection handover with both devices already GOs"""
+ set_ip_addr_info(dev[0])
+ set_ip_addr_info(dev[1])
+ logger.info("Start autonomous GOs")
+ dev[0].p2p_start_go()
+ dev[1].p2p_start_go()
+ logger.info("Perform NFC connection handover")
+ req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[1].request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ res = dev[1].request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+ res = dev[0].request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+ ev = dev[0].wait_event(["P2P-NFC-BOTH-GO"], timeout=15)
+ if ev is None:
+ raise Exception("Time out waiting for P2P-NFC-BOTH-GO (dev0)")
+ ev = dev[1].wait_event(["P2P-NFC-BOTH-GO"], timeout=1)
+ if ev is None:
+ raise Exception("Time out waiting for P2P-NFC-BOTH-GO (dev1)")
+ dev[0].remove_group()
+ dev[1].remove_group()
+
+def test_nfc_p2p_client(dev):
+ """NFC connection handover when one device is P2P client"""
+ logger.info("Start autonomous GOs")
+ go_res = dev[0].p2p_start_go()
+ logger.info("Connect one device as a P2P client")
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin,
+ freq=int(go_res['freq']), timeout=60)
+ logger.info("Client connected")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+ logger.info("NFC connection handover between P2P client and P2P device")
+ req = dev[1].request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[2].request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+ res = dev[2].request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+ res = dev[1].request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+ ev = dev[1].wait_event(["P2P-NFC-WHILE-CLIENT"], timeout=15)
+ if ev is None:
+ raise Exception("Time out waiting for P2P-NFC-WHILE-CLIENT")
+ ev = dev[2].wait_event(["P2P-NFC-PEER-CLIENT"], timeout=1)
+ if ev is None:
+ raise Exception("Time out waiting for P2P-NFC-PEER-CLIENT")
+
+ logger.info("Connect to group based on upper layer trigger")
+ pin = dev[2].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[2].p2p_connect_group(dev[0].p2p_dev_addr(), pin,
+ freq=int(go_res['freq']), timeout=60)
+ logger.info("Client connected")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+ dev[2].remove_group()
+ dev[1].remove_group()
+ dev[0].remove_group()
+
+def test_nfc_p2p_static_handover_tagdev_client(dev):
+ """NFC static handover to form a new P2P group (NFC Tag device becomes P2P Client)"""
+ try:
+ _test_nfc_p2p_static_handover_tagdev_client(dev)
+ finally:
+ dev[0].global_request("SET p2p_go_intent 7")
+
+def _test_nfc_p2p_static_handover_tagdev_client(dev):
+ set_ip_addr_info(dev[0])
+
+ logger.info("Perform NFC connection handover")
+
+ res = dev[1].global_request("SET p2p_listen_reg_class 81")
+ res2 = dev[1].global_request("SET p2p_listen_channel 6")
+ if "FAIL" in res or "FAIL" in res2:
+ raise Exception("Could not set Listen channel")
+ pw = dev[1].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = dev[1].global_request("P2P_SET nfc_tag 1").rstrip()
+ if "FAIL" in res:
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = dev[1].global_request("P2P_LISTEN")
+ if "FAIL" in res:
+ raise Exception("Failed to start Listen mode")
+ dev[1].dump_monitor()
+
+ dev[0].dump_monitor()
+ dev[0].global_request("SET p2p_go_intent 10")
+ res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+ ev = dev[0].wait_global_event(grpform_events, timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res0 = dev[0].group_form_result(ev)
+
+ ev = dev[1].wait_global_event(grpform_events, timeout=1)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res1 = dev[1].group_form_result(ev)
+ logger.info("Group formed")
+
+ if res1['role'] != 'client' or res0['role'] != 'GO':
+ raise Exception("Unexpected roles negotiated")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res1)
+
+def test_nfc_p2p_static_handover_tagdev_client_group_iface(dev):
+ """NFC static handover to form a new P2P group (NFC Tag device becomes P2P Client with group iface)"""
+ try:
+ _test_nfc_p2p_static_handover_tagdev_client_group_iface(dev)
+ finally:
+ dev[0].global_request("SET p2p_go_intent 7")
+
+def _test_nfc_p2p_static_handover_tagdev_client_group_iface(dev):
+ set_ip_addr_info(dev[0])
+
+ logger.info("Perform NFC connection handover")
+
+ res = dev[1].global_request("SET p2p_listen_reg_class 81")
+ res2 = dev[1].global_request("SET p2p_listen_channel 6")
+ if "FAIL" in res or "FAIL" in res2:
+ raise Exception("Could not set Listen channel")
+ pw = dev[1].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ res = dev[1].global_request("P2P_SET nfc_tag 1").rstrip()
+ if "FAIL" in res:
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = dev[1].global_request("P2P_LISTEN")
+ if "FAIL" in res:
+ raise Exception("Failed to start Listen mode")
+ dev[1].dump_monitor()
+
+ dev[0].dump_monitor()
+ dev[0].global_request("SET p2p_go_intent 10")
+ res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+ ev = dev[0].wait_global_event(grpform_events, timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res0 = dev[0].group_form_result(ev)
+
+ ev = dev[1].wait_global_event(grpform_events, timeout=1)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res1 = dev[1].group_form_result(ev)
+ logger.info("Group formed")
+
+ if res1['role'] != 'client' or res0['role'] != 'GO':
+ raise Exception("Unexpected roles negotiated")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res1)
+
+def test_nfc_p2p_static_handover_tagdev_go(dev):
+ """NFC static handover to form a new P2P group (NFC Tag device becomes GO)"""
+ try:
+ _test_nfc_p2p_static_handover_tagdev_go(dev)
+ finally:
+ dev[0].global_request("SET p2p_go_intent 7")
+
+def _test_nfc_p2p_static_handover_tagdev_go(dev):
+ set_ip_addr_info(dev[1])
+
+ logger.info("Perform NFC connection handover")
+
+ res = dev[1].global_request("SET p2p_listen_reg_class 81")
+ res2 = dev[1].global_request("SET p2p_listen_channel 6")
+ if "FAIL" in res or "FAIL" in res2:
+ raise Exception("Could not set Listen channel")
+ pw = dev[1].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = dev[1].global_request("P2P_SET nfc_tag 1").rstrip()
+ if "FAIL" in res:
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = dev[1].global_request("P2P_LISTEN")
+ if "FAIL" in res:
+ raise Exception("Failed to start Listen mode")
+ dev[1].dump_monitor()
+
+ dev[0].dump_monitor()
+ dev[0].global_request("SET p2p_go_intent 3")
+ res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+ ev = dev[0].wait_global_event(grpform_events, timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res0 = dev[0].group_form_result(ev)
+
+ ev = dev[1].wait_global_event(grpform_events, timeout=1)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res1 = dev[1].group_form_result(ev)
+ logger.info("Group formed")
+
+ if res0['role'] != 'client' or res1['role'] != 'GO':
+ raise Exception("Unexpected roles negotiated")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res0)
+
+def test_nfc_p2p_static_handover_tagdev_go_forced_freq(dev):
+ """NFC static handover to form a new P2P group on forced channel (NFC Tag device becomes GO)"""
+ try:
+ _test_nfc_p2p_static_handover_tagdev_go_forced_freq(dev)
+ finally:
+ dev[0].global_request("SET p2p_go_intent 7")
+
+def _test_nfc_p2p_static_handover_tagdev_go_forced_freq(dev):
+ set_ip_addr_info(dev[1])
+
+ logger.info("Perform NFC connection handover")
+
+ res = dev[1].global_request("SET p2p_listen_reg_class 81")
+ res2 = dev[1].global_request("SET p2p_listen_channel 6")
+ if "FAIL" in res or "FAIL" in res2:
+ raise Exception("Could not set Listen channel")
+ pw = dev[1].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = dev[1].global_request("P2P_SET nfc_tag 1").rstrip()
+ if "FAIL" in res:
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = dev[1].global_request("P2P_LISTEN")
+ if "FAIL" in res:
+ raise Exception("Failed to start Listen mode")
+ dev[1].dump_monitor()
+
+ dev[0].dump_monitor()
+ dev[0].global_request("SET p2p_go_intent 3")
+ res = dev[0].global_request("WPS_NFC_TAG_READ " + sel + " freq=2442")
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+ ev = dev[0].wait_global_event(grpform_events, timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res0 = dev[0].group_form_result(ev)
+
+ ev = dev[1].wait_global_event(grpform_events, timeout=1)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res1 = dev[1].group_form_result(ev)
+ logger.info("Group formed")
+
+ if res0['role'] != 'client' or res1['role'] != 'GO':
+ raise Exception("Unexpected roles negotiated")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res0)
+
+def test_nfc_p2p_static_handover_join_tagdev_go(dev):
+ """NFC static handover to join a P2P group (NFC Tag device is the GO)"""
+
+ logger.info("Start autonomous GO")
+ set_ip_addr_info(dev[0])
+ dev[0].p2p_start_go()
+
+ logger.info("Write NFC Tag on the GO")
+ pw = dev[0].request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = dev[0].request("P2P_SET nfc_tag 1").rstrip()
+ if "FAIL" in res:
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ sel = dev[0].request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+
+ logger.info("Read NFC Tag on a P2P Device to join a group")
+ res = dev[1].request("WPS_NFC_TAG_READ " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+ ev = dev[1].wait_event(grpform_events, timeout=30)
+ if ev is None:
+ raise Exception("Joining the group timed out")
+ res = dev[1].group_form_result(ev)
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res)
+
+ logger.info("Read NFC Tag on another P2P Device to join a group")
+ res = dev[2].request("WPS_NFC_TAG_READ " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+ ev = dev[2].wait_event(grpform_events, timeout=30)
+ if ev is None:
+ raise Exception("Joining the group timed out")
+ res = dev[2].group_form_result(ev)
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[2])
+ check_ip_addr(res)
+
+def test_nfc_p2p_static_handover_join_tagdev_client(dev):
+ """NFC static handover to join a P2P group (NFC Tag device is the P2P Client)"""
+ try:
+ _test_nfc_p2p_static_handover_join_tagdev_client(dev)
+ finally:
+ dev[1].global_request("SET ignore_old_scan_res 0")
+ dev[2].global_request("SET ignore_old_scan_res 0")
+
+def _test_nfc_p2p_static_handover_join_tagdev_client(dev):
+ set_ip_addr_info(dev[0])
+ logger.info("Start autonomous GO")
+ dev[0].p2p_start_go()
+
+ dev[1].global_request("SET ignore_old_scan_res 1")
+ dev[2].global_request("SET ignore_old_scan_res 1")
+
+ logger.info("Write NFC Tag on the P2P Client")
+ res = dev[1].global_request("P2P_LISTEN")
+ if "FAIL" in res:
+ raise Exception("Failed to start Listen mode")
+ pw = dev[1].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = dev[1].global_request("P2P_SET nfc_tag 1").rstrip()
+ if "FAIL" in res:
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+
+ logger.info("Read NFC Tag on the GO to trigger invitation")
+ res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+ ev = dev[1].wait_global_event(grpform_events, timeout=30)
+ if ev is None:
+ raise Exception("Joining the group timed out")
+ res = dev[1].group_form_result(ev)
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res)
+
+ logger.info("Write NFC Tag on another P2P Client")
+ res = dev[2].global_request("P2P_LISTEN")
+ if "FAIL" in res:
+ raise Exception("Failed to start Listen mode")
+ pw = dev[2].global_request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = dev[2].global_request("P2P_SET nfc_tag 1").rstrip()
+ if "FAIL" in res:
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ sel = dev[2].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+
+ logger.info("Read NFC Tag on the GO to trigger invitation")
+ res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+ ev = dev[2].wait_global_event(grpform_events, timeout=30)
+ if ev is None:
+ raise Exception("Joining the group timed out")
+ res = dev[2].group_form_result(ev)
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[2])
+ check_ip_addr(res)
+
+def test_nfc_p2p_go_legacy_config_token(dev):
+ """NFC config token from P2P GO to legacy WPS STA"""
+ logger.info("Start autonomous GOs")
+ dev[0].p2p_start_go()
+ logger.info("Connect legacy WPS STA with configuration token")
+ conf = dev[0].group_request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip()
+ if "FAIL" in conf:
+ raise Exception("Failed to generate configuration token")
+ dev[1].dump_monitor()
+ res = dev[1].request("WPS_NFC_TAG_READ " + conf)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+ dev[1].wait_connected(timeout=15, error="Joining the group timed out")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ dev[1].request("DISCONNECT")
+ dev[0].remove_group()
+
+def test_nfc_p2p_go_legacy_handover(dev):
+ """NFC token from legacy WPS STA to P2P GO"""
+ logger.info("Start autonomous GOs")
+ dev[0].p2p_start_go()
+ logger.info("Connect legacy WPS STA with connection handover")
+ req = dev[1].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[0].group_request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = dev[0].group_request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant (GO)")
+ dev[1].dump_monitor()
+ res = dev[1].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant (legacy STA)")
+ dev[1].wait_connected(timeout=15, error="Joining the group timed out")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ dev[1].request("DISCONNECT")
+ dev[0].remove_group()
+
+def test_nfc_p2p_ip_addr_assignment(dev):
+ """NFC connection handover and legacy station IP address assignment"""
+ try:
+ _test_nfc_p2p_ip_addr_assignment(dev)
+ finally:
+ dev[0].global_request("SET p2p_go_intent 7")
+
+def _test_nfc_p2p_ip_addr_assignment(dev):
+ set_ip_addr_info(dev[1])
+ dev[0].global_request("SET p2p_go_intent 3")
+ logger.info("Perform NFC connection handover")
+ req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+ res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res0 = dev[0].group_form_result(ev)
+
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED"], timeout=1)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res1 = dev[1].group_form_result(ev)
+ logger.info("Group formed")
+
+ if res0['role'] != 'client' or res1['role'] != 'GO':
+ raise Exception("Unexpected roles negotiated")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res0)
+
+ logger.info("Connect legacy P2P client that does not use new IP address assignment")
+ res = dev[2].global_request("P2P_SET disable_ip_addr_req 1")
+ if "FAIL" in res:
+ raise Exception("Failed to disable IP address assignment request")
+ pin = dev[2].wps_read_pin()
+ dev[1].p2p_go_authorize_client(pin)
+ res = dev[2].p2p_connect_group(dev[1].p2p_dev_addr(), pin, timeout=60)
+ logger.info("Client connected")
+ res = dev[2].global_request("P2P_SET disable_ip_addr_req 0")
+ hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+ if 'ip_addr' in res:
+ raise Exception("Unexpected IP address assignment")
+
+def test_nfc_p2p_ip_addr_assignment2(dev):
+ """NFC connection handover and IP address assignment for two clients"""
+ try:
+ _test_nfc_p2p_ip_addr_assignment2(dev)
+ finally:
+ dev[0].global_request("SET p2p_go_intent 7")
+
+def _test_nfc_p2p_ip_addr_assignment2(dev):
+ set_ip_addr_info(dev[1])
+ dev[0].global_request("SET p2p_go_intent 3")
+ logger.info("Perform NFC connection handover")
+ req = dev[0].global_request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[1].global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ res = dev[1].global_request("NFC_REPORT_HANDOVER RESP P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(resp)")
+ res = dev[0].global_request("NFC_REPORT_HANDOVER INIT P2P " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to wpa_supplicant(init)")
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res0 = dev[0].group_form_result(ev)
+
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GO-NEG-FAILURE",
+ "P2P-GROUP-FORMATION-FAILURE",
+ "WPS-PIN-NEEDED"], timeout=1)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res1 = dev[1].group_form_result(ev)
+ logger.info("Group formed")
+
+ if res0['role'] != 'client' or res1['role'] != 'GO':
+ raise Exception("Unexpected roles negotiated")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ check_ip_addr(res0)
+ logger.info("Client 1 IP address: " + res0['ip_addr'])
+
+ logger.info("Connect a P2P client")
+ pin = dev[2].wps_read_pin()
+ dev[1].p2p_go_authorize_client(pin)
+ res = dev[2].p2p_connect_group(dev[1].p2p_dev_addr(), pin, timeout=60)
+ logger.info("Client connected")
+ hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+ check_ip_addr(res)
+ logger.info("Client 2 IP address: " + res['ip_addr'])
+ if res['ip_addr'] == res0['ip_addr']:
+ raise Exception("Same IP address assigned to both clients")
+
+@remote_compatible
+def test_nfc_p2p_tag_enable_disable(dev):
+ """NFC tag enable/disable for P2P"""
+ if "FAIL" in dev[0].request("WPS_NFC_TOKEN NDEF").rstrip():
+ raise Exception("Failed to generate password token")
+ if "OK" not in dev[0].request("P2P_SET nfc_tag 1"):
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ if "OK" not in dev[0].request("P2P_SET nfc_tag 0"):
+ raise Exception("Failed to disable NFC Tag for P2P static handover")
+
+ dev[0].request("SET p2p_no_group_iface 0")
+ if "OK" not in dev[0].request("P2P_SET nfc_tag 1"):
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ if "OK" not in dev[0].request("P2P_SET nfc_tag 0"):
+ raise Exception("Failed to disable NFC Tag for P2P static handover")
+ if "OK" not in dev[0].request("P2P_SET nfc_tag 1"):
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ if "OK" not in dev[0].request("P2P_SET nfc_tag 0"):
+ raise Exception("Failed to disable NFC Tag for P2P static handover")
+
+@remote_compatible
+def test_nfc_p2p_static_handover_invalid(dev):
+ """NFC static handover with invalid contents"""
+ logger.info("Unknown OOB GO Neg channel")
+ sel = "D217A36170706C69636174696F6E2F766E642E7766612E7032700071102100012010230001201024000120102C0036C3B2ADB8D26F53CE1CB7F000BEEDA762922FF5307E87CCE484EF4B5DAD440D0A4752579767610AD1293F7A76A66B09A7C9D58A66994E103C000103104200012010470010572CF82FC95756539B16B5CFB298ABF11049000600372A000120002E02020025000D1D000200000001001108000000000000000000101100084465766963652042130600585804ff0B00"
+ if "FAIL" not in dev[0].global_request("WPS_NFC_TAG_READ " + sel):
+ raise Exception("Invalid tag contents accepted (1)")
+
+ logger.info("No OOB GO Neg channel attribute")
+ sel = "D2179A6170706C69636174696F6E2F766E642E7766612E7032700071102100012010230001201024000120102C0036C3B2ADB8D26F53CE1CB7F000BEEDA762922FF5307E87CCE484EF4B5DAD440D0A4752579767610AD1293F7A76A66B09A7C9D58A66994E103C000103104200012010470010572CF82FC95756539B16B5CFB298ABF11049000600372A000120002502020025000D1D000200000001001108000000000000000000101100084465766963652042"
+ if "FAIL" not in dev[0].global_request("WPS_NFC_TAG_READ " + sel):
+ raise Exception("Invalid tag contents accepted (2)")
+
+ logger.info("No Device Info attribute")
+ sel = "D217836170706C69636174696F6E2F766E642E7766612E7032700071102100012010230001201024000120102C0036C3B2ADB8D26F53CE1CB7F000BEEDA762922FF5307E87CCE484EF4B5DAD440D0A4752579767610AD1293F7A76A66B09A7C9D58A66994E103C000103104200012010470010572CF82FC95756539B16B5CFB298ABF11049000600372A000120000E0202002500130600585804510B00"
+ if "FAIL" not in dev[0].global_request("WPS_NFC_TAG_READ " + sel):
+ raise Exception("Invalid tag contents accepted (3)")
diff --git a/contrib/wpa/tests/hwsim/test_nfc_wps.py b/contrib/wpa/tests/hwsim/test_nfc_wps.py
new file mode 100644
index 000000000000..a0e2d454ffe9
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_nfc_wps.py
@@ -0,0 +1,709 @@
+# WPS+NFC tests
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+import hostapd
+from utils import *
+
+def check_wpa2_connection(sta, ap, hapd, ssid, mixed=False):
+ status = sta.get_status()
+ if status['wpa_state'] != 'COMPLETED':
+ raise Exception("Not fully connected")
+ if status['bssid'] != ap['bssid']:
+ raise Exception("Unexpected BSSID")
+ if status['ssid'] != ssid:
+ raise Exception("Unexpected SSID")
+ if status['pairwise_cipher'] != 'CCMP':
+ raise Exception("Unexpected encryption configuration")
+ if status['group_cipher'] != 'CCMP' and not mixed:
+ raise Exception("Unexpected encryption configuration")
+ if status['key_mgmt'] != 'WPA2-PSK':
+ raise Exception("Unexpected key_mgmt")
+ hwsim_utils.test_connectivity(sta, hapd)
+
+def ap_wps_params(ssid):
+ return {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"}
+
+@remote_compatible
+def test_nfc_wps_password_token_sta(dev, apdev):
+ """NFC tag with password token on the station/Enrollee"""
+ ssid = "test-wps-nfc-pw-token-conf"
+ params = ap_wps_params(ssid)
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("WPS provisioning step using password token from station")
+ wps = dev[0].request("WPS_NFC_TOKEN WPS").rstrip()
+ if "FAIL" in wps:
+ raise Exception("Failed to generate password token (WPS only)")
+ pw = dev[0].request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = hapd.request("WPS_NFC_TAG_READ " + pw)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to hostapd")
+ dev[0].dump_monitor()
+ res = dev[0].request("WPS_NFC")
+ if "FAIL" in res:
+ raise Exception("Failed to start Enrollee using NFC password token")
+ dev[0].wait_connected(timeout=30)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+
+ if "FAIL" not in hapd.request("WPS_NFC_TAG_READ 0"):
+ raise Exception("Invalid WPS_NFC_TAG_READ accepted")
+ if "FAIL" not in hapd.request("WPS_NFC_TAG_READ 0q"):
+ raise Exception("Invalid WPS_NFC_TAG_READ accepted")
+ with alloc_fail(hapd, 1,
+ "wpabuf_alloc;hostapd_ctrl_iface_wps_nfc_tag_read"):
+ if "FAIL" not in hapd.request("WPS_NFC_TAG_READ 00"):
+ raise Exception("WPS_NFC_TAG_READ accepted during OOM")
+
+def test_nfc_wps_config_token(dev, apdev):
+ """NFC tag with configuration token from AP"""
+ ssid = "test-wps-nfc-conf-token"
+ params = ap_wps_params(ssid)
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("NFC configuration token from AP to station")
+ conf = hapd.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip()
+ if "FAIL" in conf:
+ raise Exception("Failed to generate configuration token")
+ ndef_conf = conf
+ dev[0].dump_monitor()
+ res = dev[0].request("WPS_NFC_TAG_READ " + conf)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+ dev[0].wait_connected(timeout=15)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+
+ with alloc_fail(hapd, 1, "wps_get_oob_cred"):
+ conf = hapd.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip()
+ if "FAIL" not in conf:
+ raise Exception("Unexpected configuration token received during OOM")
+
+ wps_conf = hapd.request("WPS_NFC_CONFIG_TOKEN WPS").rstrip()
+ if "FAIL" in wps_conf:
+ raise Exception("Failed to generate configuration token (WPS)")
+ if wps_conf not in ndef_conf:
+ raise Exception("WPS config token not within NDEF encapsulated one")
+
+ conf = hapd.request("WPS_NFC_CONFIG_TOKEN FOO").rstrip()
+ if "FAIL" not in conf:
+ raise Exception("Invalid WPS_NFC_CONFIG_TOKEN accepted")
+
+def test_nfc_wps_config_token_init(dev, apdev):
+ """NFC tag with configuration token from AP with auto configuration"""
+ skip_without_tkip(dev[0])
+ ssid = "test-wps-nfc-conf-token-init"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1"})
+ logger.info("NFC configuration token from AP to station")
+ conf = hapd.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip()
+ if "FAIL" in conf:
+ raise Exception("Failed to generate configuration token")
+ dev[0].dump_monitor()
+ res = dev[0].request("WPS_NFC_TAG_READ " + conf)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+ dev[0].wait_connected(timeout=15)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, ssid, mixed=True)
+
+@remote_compatible
+def test_nfc_wps_password_token_sta_init(dev, apdev):
+ """Initial AP configuration with first WPS NFC Enrollee"""
+ skip_without_tkip(dev[0])
+ ssid = "test-wps-nfc-pw-token-init"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1"})
+ logger.info("WPS provisioning step using password token from station")
+ pw = dev[0].request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = hapd.request("WPS_NFC_TAG_READ " + pw)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to hostapd")
+ dev[0].dump_monitor()
+ res = dev[0].request("WPS_NFC")
+ if "FAIL" in res:
+ raise Exception("Failed to start Enrollee using NFC password token")
+ dev[0].wait_connected(timeout=30)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, ssid, mixed=True)
+
+@remote_compatible
+def test_nfc_wps_password_token_ap(dev, apdev):
+ """WPS registrar configuring an AP using AP password token"""
+ ssid = "test-wps-nfc-pw-token-init"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1"})
+ logger.info("WPS configuration step")
+ pw = hapd.request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = hapd.request("WPS_NFC_TOKEN enable")
+ if "FAIL" in res:
+ raise Exception("Failed to enable AP password token")
+ res = dev[0].request("WPS_NFC_TAG_READ " + pw)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+ dev[0].dump_monitor()
+ new_ssid = "test-wps-nfc-pw-token-new-ssid"
+ new_passphrase = "1234567890"
+ res = dev[0].request("WPS_REG " + apdev[0]['bssid'] + " nfc-pw " +
+ binascii.hexlify(new_ssid.encode()).decode() +
+ " WPA2PSK CCMP " +
+ binascii.hexlify(new_passphrase.encode()).decode())
+ if "FAIL" in res:
+ raise Exception("Failed to start Registrar using NFC password token")
+ dev[0].wait_connected(timeout=30)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, new_ssid, mixed=True)
+ if "FAIL" in hapd.request("WPS_NFC_TOKEN disable"):
+ raise Exception("Failed to disable AP password token")
+ if "FAIL" in hapd.request("WPS_NFC_TOKEN WPS"):
+ raise Exception("Unexpected WPS_NFC_TOKEN WPS failure")
+
+ with fail_test(hapd, 1, "os_get_random;wps_nfc_token_gen"):
+ if "FAIL" not in hapd.request("WPS_NFC_TOKEN WPS"):
+ raise Exception("Unexpected WPS_NFC_TOKEN success")
+ with fail_test(hapd, 2, "os_get_random;wps_nfc_token_gen"):
+ if "FAIL" not in hapd.request("WPS_NFC_TOKEN WPS"):
+ raise Exception("Unexpected WPS_NFC_TOKEN success")
+
+ if "FAIL" not in hapd.request("WPS_NFC_TOKEN foo"):
+ raise Exception("Invalid WPS_NFC_TOKEN accepted")
+
+def test_nfc_wps_password_token_ap_preconf(dev, apdev):
+ """WPS registrar configuring an AP using preconfigured AP password token"""
+ ssid = "test-wps-nfc-pw-token-init"
+ params = {"ssid": ssid, "eap_server": "1",
+ "wps_state": "1",
+ "wps_nfc_dev_pw_id": "49067",
+ "wps_nfc_dh_pubkey": "991B7F54406226505D56C6C701ED2C725E4F4866611357CA1C4D92219B2E91CFC9E4172EB0899421657534DB396A6A11361663ACDC48417541DB8610428773BC18AAA00387775F14EEE49335B574165EF915D055F818B82F99CEF4C5F176E0C5D9055CBAF055A5B20B73B26D74816BA42C1A911FF0B8EDF77C7CEA76F9F6EABBFBF12742AA3E67BE7597FB7321C3B258C57B9EA045B0A7472558F9AA8E810E2E0462FFD9001A7E21C38006529B9FEDAAF47612D3817922F2335A5D541BAA9B7F",
+ "wps_nfc_dh_privkey": "06F35FDA777F6EFF1F7F008AD68C49572C5F2913B1DC96E0AC3AB67D75329D40EEE850C79D83EEA82CE35FADCCB1F2AF08560268B9E9B67BE66C9B7B3E6F462CF91647830CB0A40184CCF8AA74261E0308AB8973FB799C9EA46011C70215AEA83293E0C89AA4EB6CA753A9E689FA3A0A3FB40D0A8D9AD258F3E4DA1625F63C4B347660D17504B25856DE9D18EB76C239EDFF090A0A1779BE848C0F23C20CF83022C91EA56B0375DED0A62DF0B8B91348F667F5A7EAD23F0F033E071DCE11B786",
+ "wps_nfc_dev_pw": "CB7FE7A25053F8F5BF822660C21E66D8A58D3393BB78494E239031D6AABCB90C"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("WPS configuration step")
+ res = hapd.request("WPS_NFC_TOKEN enable")
+ if "FAIL" in res:
+ raise Exception("Failed to enable AP password token")
+ pw = "D217446170706C69636174696F6E2F766E642E7766612E777363102C0036691F6C35AC5FF23180FFBF899BF3E563D047AA68BFABCB7FE7A25053F8F5BF822660C21E66D8A58D3393BB78494E239031D6AABCB90C1049000600372A000120"
+ res = dev[0].request("WPS_NFC_TAG_READ " + pw)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+ dev[0].dump_monitor()
+ new_ssid = "test-wps-nfc-pw-token-new-ssid"
+ new_passphrase = "1234567890"
+ res = dev[0].request("WPS_REG " + apdev[0]['bssid'] + " nfc-pw " +
+ binascii.hexlify(new_ssid.encode()).decode() +
+ " WPA2PSK CCMP " +
+ binascii.hexlify(new_passphrase.encode()).decode())
+ if "FAIL" in res:
+ raise Exception("Failed to start Registrar using NFC password token")
+ dev[0].wait_connected(timeout=30)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, new_ssid, mixed=True)
+
+def test_nfc_wps_handover_init(dev, apdev):
+ """Connect to WPS AP with NFC connection handover and move to configured state"""
+ skip_without_tkip(dev[0])
+ try:
+ _test_nfc_wps_handover_init(dev, apdev)
+ finally:
+ dev[0].request("SET ignore_old_scan_res 0")
+
+def _test_nfc_wps_handover_init(dev, apdev):
+ dev[0].request("SET ignore_old_scan_res 1")
+ ssid = "test-wps-nfc-handover-init"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1"})
+ logger.info("NFC connection handover")
+ req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[0].dump_monitor()
+ res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ dev[0].wait_connected(timeout=15)
+ # WPS provisioning
+ hapd.wait_sta()
+ # data connection
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, ssid, mixed=True)
+
+ with alloc_fail(hapd, 1, "wps_build_nfc_handover_sel"):
+ if "FAIL" not in hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR"):
+ raise Exception("Unexpected NFC_GET_HANDOVER_SEL success during OOM")
+
+ if "FAIL" not in hapd.request("NFC_GET_HANDOVER_SEL NDEF").rstrip():
+ raise Exception("Invalid NFC_GET_HANDOVER_SEL accepted")
+ if "FAIL" not in hapd.request("NFC_GET_HANDOVER_SEL foo foo").rstrip():
+ raise Exception("Invalid NFC_GET_HANDOVER_SEL accepted")
+ if "FAIL" not in hapd.request("NFC_GET_HANDOVER_SEL NDEF foo").rstrip():
+ raise Exception("Invalid NFC_GET_HANDOVER_SEL accepted")
+ res_ndef = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ res_wps = hapd.request("NFC_GET_HANDOVER_SEL WPS WPS-CR").rstrip()
+ if res_wps not in res_ndef:
+ raise Exception("WPS handover select not in NDEF encapsulated version")
+
+@remote_compatible
+def test_nfc_wps_handover_errors(dev, apdev):
+ """WPS AP NFC handover report error cases"""
+ ssid = "test-wps-nfc-handover"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": ssid, "eap_server": "1", "wps_state": "1"})
+ sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER "):
+ raise Exception("Unexpected handover report success")
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP"):
+ raise Exception("Unexpected handover report success")
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS"):
+ raise Exception("Unexpected handover report success")
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 001122"):
+ raise Exception("Unexpected handover report success")
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 001122 00"):
+ raise Exception("Unexpected handover report success")
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 0 00"):
+ raise Exception("Unexpected handover report success")
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 001122 0"):
+ raise Exception("Unexpected handover report success")
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 00q122 001122"):
+ raise Exception("Unexpected handover report success")
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 001122 001q22"):
+ raise Exception("Unexpected handover report success")
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP FOO 001122 00"):
+ raise Exception("Unexpected handover report success")
+ for i in range(1, 3):
+ with alloc_fail(hapd, i,
+ "wpabuf_alloc;hostapd_ctrl_iface_nfc_report_handover"):
+ if "FAIL" not in hapd.request("NFC_REPORT_HANDOVER RESP WPS 001122 001122"):
+ raise Exception("NFC_REPORT_HANDOVER RESP succeeded during OOM")
+
+def test_nfc_wps_handover(dev, apdev):
+ """Connect to WPS AP with NFC connection handover"""
+ ssid = "test-wps-nfc-handover"
+ params = ap_wps_params(ssid)
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("NFC connection handover")
+ req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[0].dump_monitor()
+ res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ dev[0].wait_connected(timeout=30)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+
+def test_nfc_wps_handover_5ghz(dev, apdev):
+ """Connect to WPS AP with NFC connection handover on 5 GHz band"""
+ hapd = None
+ try:
+ ssid = "test-wps-nfc-handover"
+ params = ap_wps_params(ssid)
+ params["country_code"] = "FI"
+ params["hw_mode"] = "a"
+ params["channel"] = "36"
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("NFC connection handover")
+ req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[0].dump_monitor()
+ res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ dev[0].wait_connected(timeout=30)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_nfc_wps_handover_chan14(dev, apdev):
+ """Connect to WPS AP with NFC connection handover on channel 14"""
+ hapd = None
+ try:
+ ssid = "test-wps-nfc-handover"
+ params = ap_wps_params(ssid)
+ params["country_code"] = "JP"
+ params["hw_mode"] = "b"
+ params["channel"] = "14"
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("NFC connection handover")
+ req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[0].dump_monitor()
+ res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ dev[0].wait_connected(timeout=30)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_nfc_wps_handover_with_pw_token_set(dev, apdev):
+ """Connect to WPS AP with NFC connection handover (wps_nfc_* set)"""
+ ssid = "test-wps-nfc-handover2"
+ params = ap_wps_params(ssid)
+ hapd = hostapd.add_ap(apdev[0], params)
+ # enable a password token (which won't be used in this test case)
+ pw = hapd.request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = hapd.request("WPS_NFC_TOKEN enable")
+ if "FAIL" in pw:
+ raise Exception("Failed to enable AP password token")
+ logger.info("NFC connection handover")
+ req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[0].dump_monitor()
+ res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ dev[0].wait_connected(timeout=15)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+
+def test_nfc_wps_handover_pk_hash_mismatch_sta(dev, apdev):
+ """WPS NFC connection handover with invalid pkhash from station (negative)"""
+ ssid = "wps-nfc-handover-pkhash-sta"
+ if "FAIL" in dev[0].request("SET wps_corrupt_pkhash 1"):
+ raise Exception("Could not enable wps_corrupt_pkhash")
+ params = ap_wps_params(ssid)
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("NFC connection handover")
+ req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[0].dump_monitor()
+ res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("Timed out")
+ if "WPS-FAIL" not in ev:
+ raise Exception("Public key hash mismatch not detected")
+
+def test_nfc_wps_handover_pk_hash_mismatch_ap(dev, apdev):
+ """WPS NFC connection handover with invalid pkhash from AP (negative)"""
+ ssid = "wps-nfc-handover-pkhash-ap"
+ params = ap_wps_params(ssid)
+ hapd = hostapd.add_ap(apdev[0], params)
+ if "FAIL" in hapd.request("SET wps_corrupt_pkhash 1"):
+ raise Exception("Could not enable wps_corrupt_pkhash")
+ logger.info("NFC connection handover")
+ req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[0].dump_monitor()
+ res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("Timed out")
+ if "WPS-FAIL" not in ev:
+ raise Exception("Public key hash mismatch not detected")
+
+def start_ap_er(er, ap, ssid):
+ ap_pin = "12345670"
+ ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+ params = {"ssid": ssid, "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
+ "device_name": "Wireless AP", "manufacturer": "Company",
+ "model_name": "WAP", "model_number": "123",
+ "serial_number": "12345", "device_type": "6-0050F204-1",
+ "os_version": "01020300",
+ "config_methods": "label push_button",
+ "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"}
+ hapd = hostapd.add_ap(ap, params)
+ logger.info("Learn AP configuration")
+ er.dump_monitor()
+ try:
+ er.request("SET ignore_old_scan_res 1")
+ er.wps_reg(ap['bssid'], ap_pin)
+ finally:
+ er.request("SET ignore_old_scan_res 0")
+
+ logger.info("Start ER")
+ er.request("WPS_ER_STOP")
+ time.sleep(1)
+ er.request("WPS_ER_START ifname=lo")
+ ev = er.wait_event(["WPS-ER-AP-ADD"], timeout=15)
+ if ev is None:
+ raise Exception("AP discovery timed out")
+ if ap_uuid not in ev:
+ raise Exception("Expected AP UUID not found")
+
+ logger.info("Use learned network configuration on ER")
+ er.request("WPS_ER_SET_CONFIG " + ap_uuid + " 0")
+ return hapd
+
+@remote_compatible
+def test_nfc_wps_er_pw_token(dev, apdev):
+ """WPS NFC password token from Enrollee to ER"""
+ try:
+ _test_nfc_wps_er_pw_token(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+ dev[1].request("SET ignore_old_scan_res 0")
+
+def _test_nfc_wps_er_pw_token(dev, apdev):
+ ssid = "wps-nfc-er-pw-token"
+ hapd = start_ap_er(dev[0], apdev[0], ssid)
+ logger.info("WPS provisioning step using password token from station")
+ dev[1].request("SET ignore_old_scan_res 1")
+ pw = dev[1].request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = dev[0].request("WPS_NFC_TAG_READ " + pw)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to WPS ER")
+ dev[0].dump_monitor()
+ res = dev[1].request("WPS_NFC")
+ if "FAIL" in res:
+ raise Exception("Failed to start Enrollee using NFC password token")
+ ev = dev[0].wait_event(["WPS-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("WPS ER did not report success")
+ dev[1].wait_connected(timeout=15)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[1], apdev[0], hapd, ssid)
+
+@remote_compatible
+def test_nfc_wps_er_config_token(dev, apdev):
+ """WPS NFC configuration token from ER to Enrollee"""
+ try:
+ _test_nfc_wps_er_config_token(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+ dev[1].request("SET ignore_old_scan_res 0")
+
+def _test_nfc_wps_er_config_token(dev, apdev):
+ ssid = "wps-nfc-er-config-token"
+ hapd = start_ap_er(dev[0], apdev[0], ssid)
+ logger.info("WPS provisioning step using configuration token from ER")
+ wps = dev[0].request("WPS_ER_NFC_CONFIG_TOKEN WPS " + apdev[0]['bssid']).rstrip()
+ if "FAIL" in wps:
+ raise Exception("Failed to generate configuration token (WPS format)")
+ conf = dev[0].request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + apdev[0]['bssid']).rstrip()
+ if "FAIL" in conf:
+ raise Exception("Failed to generate configuration token")
+ dev[1].request("SET ignore_old_scan_res 1")
+ res = dev[1].request("WPS_NFC_TAG_READ " + conf)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+ dev[1].wait_connected(timeout=15)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[1], apdev[0], hapd, ssid)
+
+def test_nfc_wps_er_handover(dev, apdev):
+ """WPS NFC connection handover between Enrollee and ER"""
+ try:
+ _test_nfc_wps_er_handover(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+
+def _test_nfc_wps_er_handover(dev, apdev):
+ ssid = "wps-nfc-er-handover"
+ hapd = start_ap_er(dev[0], apdev[0], ssid)
+ logger.info("WPS provisioning step using connection handover")
+ req = dev[1].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[0].request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + apdev[0]['bssid']).rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = dev[0].request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[1].dump_monitor()
+ res = dev[1].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ dev[1].wait_connected(timeout=15)
+ hapd.wait_sta()
+ check_wpa2_connection(dev[1], apdev[0], hapd, ssid)
+
+def test_nfc_wps_er_handover_pk_hash_mismatch_sta(dev, apdev):
+ """WPS NFC connection handover with invalid pkhash from station to ER (negative)"""
+ try:
+ _test_nfc_wps_er_handover_pk_hash_mismatch_sta(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+ dev[1].request("SET ignore_old_scan_res 0")
+
+def _test_nfc_wps_er_handover_pk_hash_mismatch_sta(dev, apdev):
+ ssid = "wps-nfc-er-handover-pkhash-sta"
+ hapd = start_ap_er(dev[0], apdev[0], ssid)
+ logger.info("WPS provisioning step using connection handover")
+ if "FAIL" in dev[1].request("SET wps_corrupt_pkhash 1"):
+ raise Exception("Could not enable wps_corrupt_pkhash")
+ dev[1].request("SET ignore_old_scan_res 1")
+ req = dev[1].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[0].request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + apdev[0]['bssid']).rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = dev[0].request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[1].dump_monitor()
+ res = dev[1].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED", "WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("Timed out")
+ if "WPS-FAIL" not in ev:
+ raise Exception("Public key hash mismatch not detected")
+
+def test_nfc_wps_er_handover_pk_hash_mismatch_er(dev, apdev):
+ """WPS NFC connection handover with invalid pkhash from ER to station (negative)"""
+ try:
+ _test_nfc_wps_er_handover_pk_hash_mismatch_er(dev, apdev)
+ finally:
+ dev[0].request("WPS_ER_STOP")
+ dev[1].request("SET ignore_old_scan_res 0")
+
+def _test_nfc_wps_er_handover_pk_hash_mismatch_er(dev, apdev):
+ ssid = "wps-nfc-er-handover-pkhash-er"
+ hapd = start_ap_er(dev[0], apdev[0], ssid)
+ logger.info("WPS provisioning step using connection handover")
+ if "FAIL" in dev[0].request("SET wps_corrupt_pkhash 1"):
+ raise Exception("Could not enable wps_corrupt_pkhash")
+ dev[1].request("SET ignore_old_scan_res 1")
+ req = dev[1].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = dev[0].request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + apdev[0]['bssid']).rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = dev[0].request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[1].dump_monitor()
+ res = dev[1].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED", "WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("Timed out")
+ if "WPS-FAIL" not in ev:
+ raise Exception("Public key hash mismatch not detected")
+
+@remote_compatible
+def test_nfc_invalid_ndef_record(dev, apdev):
+ """Invalid NFC NDEF record handling"""
+ tests = ["11223344",
+ "00112233",
+ "0000112233445566",
+ "0800112233445566",
+ "080011223344",
+ "18000000",
+ "18010000",
+ "90000050",
+ "9000005000",
+ "9001013344",
+ "98010101334455",
+ "0017ffffffe3",
+ "0017ffffffe4",
+ "0017ffffffe9",
+ "0000fffffffa",
+ "0017ffffffe46170706c69636174696f6e2f766e642e7766612e777363",
+ "0017ffffffff6170706c69636174696f6e2f766e642e7766612e777363",
+ "0017000000006170706c69636174696f6e2f766e642e7766612e7773ff",
+ "080000000000"]
+ for test in tests:
+ if "FAIL" not in dev[0].request("WPS_NFC_TAG_READ " + test):
+ raise Exception("Invalid tag accepted: " + test)
+
+def test_nfc_wps_handover_failure(dev, apdev):
+ """Connect to WPS AP with NFC connection handover (local failure)"""
+ ssid = "test-wps-nfc-handover"
+ params = ap_wps_params(ssid)
+ hapd = hostapd.add_ap(apdev[0], params)
+ logger.info("NFC connection handover")
+ req = dev[0].request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+ if "FAIL" in req:
+ raise Exception("Failed to generate NFC connection handover request")
+ sel = hapd.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ res = hapd.request("NFC_REPORT_HANDOVER RESP WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to hostapd")
+ dev[0].dump_monitor()
+
+ with alloc_fail(hapd, 1, "wpabuf_dup;wps_build_public_key"):
+ res = dev[0].request("NFC_REPORT_HANDOVER INIT WPS " + req + " " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to report NFC connection handover to to wpa_supplicant")
+ ev = dev[0].wait_event(["WPS-FAIL"], timeout=10)
+ if ev is None:
+ raise Exception("WPS failure not reported")
diff --git a/contrib/wpa/tests/hwsim/test_oce.py b/contrib/wpa/tests/hwsim/test_oce.py
new file mode 100644
index 000000000000..39ec5df5a7ca
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_oce.py
@@ -0,0 +1,185 @@
+# OCE tests
+# Copyright (c) 2016, Intel Deutschland GmbH
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+
+from hwsim_utils import set_rx_rssi, reset_rx_rssi
+import time
+import os
+from datetime import datetime
+from utils import HwsimSkip
+
+def check_set_tx_power(dev, apdev):
+ hapd = hostapd.add_ap(apdev[0], {'ssid': 'check_tx_power'})
+ set_rx_rssi(hapd, -50)
+
+ dev[0].scan(freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 2)
+
+ res = dev[0].request("SCAN_RESULTS")
+ if '-50' not in res:
+ raise HwsimSkip('set_rx_rssi not supported')
+
+ reset_rx_rssi(hapd)
+
+ dev[0].scan(freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 2)
+
+ res = dev[0].request("SCAN_RESULTS")
+ if '-30' not in res:
+ raise HwsimSkip('set_rx_rssi not supported')
+
+def run_rssi_based_assoc_rej_timeout(dev, apdev, params):
+ rssi_retry_to = 5
+
+ ap_params = {'ssid': "test-RSSI-ar-to",
+ 'rssi_reject_assoc_rssi': '-45',
+ 'rssi_reject_assoc_timeout': str(rssi_retry_to)}
+
+ logger.info("Set APs RSSI rejection threshold to -45 dBm, retry timeout: " +
+ str(rssi_retry_to))
+ hapd = hostapd.add_ap(apdev[0], ap_params)
+
+ logger.info("Set STAs TX RSSI to -50")
+ set_rx_rssi(dev[0], -50)
+
+ logger.info("STA is trying to connect")
+ dev[0].connect("test-RSSI-ar-to", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+
+ ev = dev[0].wait_event(['CTRL-EVENT-ASSOC-REJECT'], 2)
+ if ev is None:
+ raise Exception("Association not rejected")
+ if 'status_code=34' not in ev:
+ raise Exception("STA assoc request was not rejected with status code 34: " + ev)
+ t_rej = datetime.now()
+
+ # Set the scan interval to make dev[0] look for connections
+ if 'OK' not in dev[0].request("SCAN_INTERVAL 1"):
+ raise Exception("Failed to set scan interval")
+
+ logger.info("Validate that STA did not connect or sent assoc request within retry timeout")
+ ev = dev[0].wait_event(['CTRL-EVENT-CONNECTED', 'CTRL-EVENT-ASSOC-REJECT'],
+ rssi_retry_to + 2)
+ t_ev = datetime.now()
+
+ if ((t_ev - t_rej).total_seconds() < rssi_retry_to):
+ raise Exception("STA sent assoc request within retry timeout")
+
+ if 'CTRL-EVENT-CONNECTED' in ev:
+ raise Exception("STA connected with low RSSI")
+
+ if not ev:
+ raise Exception("STA didn't send association request after retry timeout!")
+
+def test_rssi_based_assoc_rej_timeout(dev, apdev, params):
+ """RSSI-based association rejection: no assoc request during retry timeout"""
+ check_set_tx_power(dev, apdev)
+ try:
+ run_rssi_based_assoc_rej_timeout(dev, apdev, params)
+ finally:
+ reset_rx_rssi(dev[0])
+ dev[0].request("SCAN_INTERVAL 5")
+
+def run_rssi_based_assoc_rej_good_rssi(dev, apdev):
+ ap_params = {'ssid': "test-RSSI-ar-to",
+ 'rssi_reject_assoc_rssi': '-45',
+ 'rssi_reject_assoc_timeout': '60'}
+
+ logger.info("Set APs RSSI rejection threshold to -45 dBm")
+ hapd = hostapd.add_ap(apdev[0], ap_params)
+
+ logger.info("Set STAs TX RSSI to -45")
+ set_rx_rssi(dev[0], -45)
+
+ logger.info("STA is trying to connect")
+ dev[0].connect("test-RSSI-ar-to", key_mgmt="NONE", scan_freq="2412")
+
+def test_rssi_based_assoc_rej_good_rssi(dev, apdev):
+ """RSSI-based association rejection: STA with RSSI above the threshold connects"""
+ check_set_tx_power(dev, apdev)
+ try:
+ run_rssi_based_assoc_rej_good_rssi(dev, apdev)
+ finally:
+ reset_rx_rssi(dev[0])
+
+def run_rssi_based_assoc_rssi_change(dev, hapd):
+ logger.info("Set STAs and APs TX RSSI to -50")
+ set_rx_rssi(dev[0], -50)
+ set_rx_rssi(hapd, -50)
+
+ # Set the scan interval to make dev[0] look for connections
+ if 'OK' not in dev[0].request("SCAN_INTERVAL 1"):
+ raise Exception("Failed to set scan interval")
+
+ logger.info("STA is trying to connect")
+ dev[0].connect("test-RSSI-ar-to", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+
+ try:
+ dev[0].wait_completed(2)
+ except:
+ logger.info("STA didn't connect after 2 seconds.")
+ else:
+ raise Exception("STA connected with low RSSI")
+
+ logger.info("Set STAs and APs TX RSSI to -40dBm, validate that STA connects")
+ set_rx_rssi(dev[0], -40)
+ set_rx_rssi(hapd, -40)
+
+ dev[0].wait_completed(2)
+
+def test_rssi_based_assoc_rssi_change(dev, apdev):
+ """RSSI-based association rejection: connect after improving RSSI"""
+ check_set_tx_power(dev, apdev)
+ try:
+ ap_params = {'ssid': "test-RSSI-ar-to",
+ 'rssi_reject_assoc_rssi': '-45',
+ 'rssi_reject_assoc_timeout': '60'}
+
+ logger.info("Set APs RSSI rejection threshold to -45 dBm, retry timeout: 60")
+ hapd = hostapd.add_ap(apdev[0], ap_params)
+
+ run_rssi_based_assoc_rssi_change(dev, hapd)
+ finally:
+ reset_rx_rssi(dev[0])
+ reset_rx_rssi(hapd)
+ dev[0].request("SCAN_INTERVAL 5")
+
+def test_oce_ap(dev, apdev):
+ """OCE AP"""
+ ssid = "test-oce"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ params['ieee80211w'] = "1"
+ params['mbo'] = "1"
+ params['oce'] = "4"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, ieee80211w="1", scan_freq="2412")
+
+def test_oce_ap_open(dev, apdev):
+ """OCE AP (open)"""
+ ssid = "test-oce"
+ params = {"ssid": ssid}
+ params['mbo'] = "1"
+ params['oce'] = "4"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, key_mgmt="NONE", scan_freq="2412")
+
+def test_oce_ap_open_connect_cmd(dev, apdev):
+ """OCE AP (open, connect command)"""
+ ssid = "test-oce"
+ params = {"ssid": ssid}
+ params['mbo'] = "1"
+ params['oce'] = "4"
+ hapd = hostapd.add_ap(apdev[0], params)
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect(ssid, key_mgmt="NONE", scan_freq="2412")
diff --git a/contrib/wpa/tests/hwsim/test_ocv.py b/contrib/wpa/tests/hwsim/test_ocv.py
new file mode 100644
index 000000000000..e93cea6ffa18
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ocv.py
@@ -0,0 +1,1247 @@
+# WPA2-Personal OCV tests
+# Copyright (c) 2018, Mathy Vanhoef
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details
+
+from remotehost import remote_compatible
+import binascii, struct
+import logging, time
+logger = logging.getLogger()
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+import hwsim_utils
+from utils import *
+from test_erp import start_erp_as
+from test_ap_ft import ft_params1, ft_params2
+from test_ap_psk import parse_eapol, build_eapol, pmk_to_ptk, eapol_key_mic, recv_eapol, send_eapol, reply_eapol, build_eapol_key_3_4, aes_wrap, pad_key_data
+
+#TODO: Refuse setting up AP with OCV but without MFP support
+#TODO: Refuse to connect to AP that advertises OCV but not MFP
+
+def make_ocikde(op_class, channel, seg1_idx):
+ WLAN_EID_VENDOR_SPECIFIC = 221
+ RSN_KEY_DATA_OCI = b"\x00\x0f\xac\x0d"
+
+ data = RSN_KEY_DATA_OCI + struct.pack("<BBB", op_class, channel, seg1_idx)
+ ocikde = struct.pack("<BB", WLAN_EID_VENDOR_SPECIFIC, len(data)) + data
+
+ return ocikde
+
+def ocv_setup_ap(apdev, params):
+ ssid = "test-wpa2-ocv"
+ passphrase = "qwertyuiop"
+ params.update(hostapd.wpa2_params(ssid=ssid, passphrase=passphrase))
+ try:
+ hapd = hostapd.add_ap(apdev, params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+ return hapd, ssid, passphrase
+
+def build_eapol_key_1_2(kck, key_data, replay_counter=3, key_info=0x1382,
+ extra_len=0, descr_type=2, key_len=16):
+ msg = {}
+ msg['version'] = 2
+ msg['type'] = 3
+ msg['length'] = 95 + len(key_data) + extra_len
+
+ msg['descr_type'] = descr_type
+ msg['rsn_key_info'] = key_info
+ msg['rsn_key_len'] = key_len
+ msg['rsn_replay_counter'] = struct.pack('>Q', replay_counter)
+ msg['rsn_key_nonce'] = binascii.unhexlify('0000000000000000000000000000000000000000000000000000000000000000')
+ msg['rsn_key_iv'] = binascii.unhexlify('00000000000000000000000000000000')
+ msg['rsn_key_rsc'] = binascii.unhexlify('0000000000000000')
+ msg['rsn_key_id'] = binascii.unhexlify('0000000000000000')
+ msg['rsn_key_data_len'] = len(key_data)
+ msg['rsn_key_data'] = key_data
+ eapol_key_mic(kck, msg)
+ return msg
+
+def build_eapol_key_2_2(kck, key_data, replay_counter=3, key_info=0x0302,
+ extra_len=0, descr_type=2, key_len=16):
+ return build_eapol_key_1_2(kck, key_data, replay_counter, key_info,
+ extra_len, descr_type, key_len)
+
+@remote_compatible
+def test_wpa2_ocv(dev, apdev):
+ """OCV on 2.4 GHz"""
+ params = {"channel": "1",
+ "ieee80211w": "2",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ for ocv in range(2):
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv=str(ocv),
+ ieee80211w="1")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+@remote_compatible
+def test_wpa2_ocv_5ghz(dev, apdev):
+ """OCV on 5 GHz"""
+ try:
+ run_wpa2_ocv_5ghz(dev, apdev)
+ finally:
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].flush_scan_cache()
+
+def run_wpa2_ocv_5ghz(dev, apdev):
+ params = {"hw_mode": "a",
+ "channel": "40",
+ "ieee80211w": "2",
+ "country_code": "US",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ for ocv in range(2):
+ dev[0].connect(ssid, psk=passphrase, scan_freq="5200", ocv=str(ocv),
+ ieee80211w="1")
+ dev[0].wait_regdom(country_ie=True)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+@remote_compatible
+def test_wpa2_ocv_ht20(dev, apdev):
+ """OCV with HT20 channel"""
+ params = {"channel": "6",
+ "ieee80211n": "1",
+ "ieee80211w": "1",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ for ocv in range(2):
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2437", ocv=str(ocv),
+ ieee80211w="1", disable_ht="1")
+ dev[1].connect(ssid, psk=passphrase, scan_freq="2437", ocv=str(ocv),
+ ieee80211w="1")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[1].wait_disconnected()
+
+@remote_compatible
+def test_wpa2_ocv_ht40(dev, apdev):
+ """OCV with HT40 channel"""
+ try:
+ run_wpa2_ocv_ht40(dev, apdev)
+ finally:
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def run_wpa2_ocv_ht40(dev, apdev):
+ for channel, capab, freq, mode in [("6", "[HT40-]", "2437", "g"),
+ ("6", "[HT40+]", "2437", "g"),
+ ("40", "[HT40-]", "5200", "a"),
+ ("36", "[HT40+]", "5180", "a")]:
+ params = {"hw_mode": mode,
+ "channel": channel,
+ "country_code": "US",
+ "ieee80211n": "1",
+ "ht_capab": capab,
+ "ieee80211w": "1",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ for ocv in range(2):
+ dev[0].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1", disable_ht="1")
+ dev[1].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1")
+ dev[0].wait_regdom(country_ie=True)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[1].wait_disconnected()
+ hapd.disable()
+
+@remote_compatible
+def test_wpa2_ocv_vht40(dev, apdev):
+ """OCV with VHT40 channel"""
+ try:
+ run_wpa2_ocv_vht40(dev, apdev)
+ finally:
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ dev[2].flush_scan_cache()
+
+def run_wpa2_ocv_vht40(dev, apdev):
+ for channel, capab, freq in [("40", "[HT40-]", "5200"),
+ ("36", "[HT40+]", "5180")]:
+ params = {"hw_mode": "a",
+ "channel": channel,
+ "country_code": "US",
+ "ht_capab": capab,
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "38",
+ "ieee80211w": "1",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ dev[2].flush_scan_cache()
+ for ocv in range(2):
+ dev[0].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1", disable_ht="1")
+ dev[1].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1", disable_vht="1")
+ dev[2].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1")
+ dev[0].wait_regdom(country_ie=True)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[1].wait_disconnected()
+ dev[2].wait_disconnected()
+ hapd.disable()
+
+@remote_compatible
+def test_wpa2_ocv_vht80(dev, apdev):
+ """OCV with VHT80 channel"""
+ try:
+ run_wpa2_ocv_vht80(dev, apdev)
+ finally:
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ dev[2].flush_scan_cache()
+
+def run_wpa2_ocv_vht80(dev, apdev):
+ for channel, capab, freq in [("40", "[HT40-]", "5200"),
+ ("36", "[HT40+]", "5180")]:
+ params = {"hw_mode": "a",
+ "channel": channel,
+ "country_code": "US",
+ "ht_capab": capab,
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "ieee80211w": "1",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ for ocv in range(2):
+ dev[0].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1", disable_ht="1")
+ dev[1].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1", disable_vht="1")
+ dev[2].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1")
+ dev[0].wait_regdom(country_ie=True)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[1].wait_disconnected()
+ dev[2].wait_disconnected()
+ hapd.disable()
+
+@remote_compatible
+def test_wpa2_ocv_vht160(dev, apdev):
+ """OCV with VHT160 channel"""
+ try:
+ run_wpa2_ocv_vht160(dev, apdev)
+ finally:
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ dev[2].flush_scan_cache()
+
+def run_wpa2_ocv_vht160(dev, apdev):
+ for channel, capab, freq in [("100", "[HT40+]", "5500"),
+ ("104", "[HT40-]", "5520")]:
+ params = {"hw_mode": "a",
+ "channel": channel,
+ "country_code": "ZA",
+ "ht_capab": capab,
+ "vht_capab": "[VHT160]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "114",
+ "ieee80211w": "1",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ for ocv in range(2):
+ dev[0].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1", disable_ht="1")
+ dev[1].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1", disable_vht="1")
+ dev[2].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1")
+ dev[0].wait_regdom(country_ie=True)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[1].wait_disconnected()
+ dev[2].wait_disconnected()
+ hapd.disable()
+
+@remote_compatible
+def test_wpa2_ocv_vht80plus80(dev, apdev):
+ """OCV with VHT80+80 channel"""
+ try:
+ run_wpa2_ocv_vht80plus80(dev, apdev)
+ finally:
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ dev[2].flush_scan_cache()
+
+def run_wpa2_ocv_vht80plus80(dev, apdev):
+ for channel, capab, freq in [("36", "[HT40+]", "5180"),
+ ("40", "[HT40-]", "5200")]:
+ params = {"hw_mode": "a",
+ "channel": channel,
+ "country_code": "US",
+ "ht_capab": capab,
+ "vht_capab": "[VHT160-80PLUS80]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "3",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "vht_oper_centr_freq_seg1_idx": "155",
+ "ieee80211w": "1",
+ "ieee80211d": "1",
+ "ieee80211h": "1",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ for ocv in range(2):
+ dev[0].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1", disable_ht="1")
+ dev[1].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1", disable_vht="1")
+ dev[2].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1")
+ dev[0].wait_regdom(country_ie=True)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[1].wait_disconnected()
+ dev[2].wait_disconnected()
+ for i in range(3):
+ dev[i].connect(ssid, psk=passphrase, scan_freq=freq, ocv=str(ocv),
+ ieee80211w="1")
+ if i == 0:
+ dev[i].wait_regdom(country_ie=True)
+ hapd.disable()
+ for i in range(3):
+ dev[i].request("DISCONNECT")
+ for i in range(3):
+ dev[i].disconnect_and_stop_scan()
+
+class APConnection:
+ def init_params(self):
+ # Static parameters
+ self.ssid = "test-wpa2-ocv"
+ self.passphrase = "qwertyuiop"
+ self.psk = "c2c6c255af836bed1b3f2f1ded98e052f5ad618bb554e2836757b55854a0eab7"
+
+ # Dynamic parameters
+ self.hapd = None
+ self.addr = None
+ self.rsne = None
+ self.kck = None
+ self.kek = None
+ self.msg = None
+ self.bssid = None
+ self.anonce = None
+ self.snonce = None
+
+ def __init__(self, apdev, dev, params):
+ self.init_params()
+
+ # By default, OCV is enabled for both the client and AP. The following
+ # parameters can be used to disable OCV for the client or AP.
+ ap_ocv = params.pop("ap_ocv", "1")
+ sta_ocv = params.pop("sta_ocv", "1")
+
+ freq = params.pop("freq")
+ params.update(hostapd.wpa2_params(ssid=self.ssid,
+ passphrase=self.passphrase))
+ params["wpa_pairwise_update_count"] = "10"
+ params["ocv"] = ap_ocv
+ try:
+ self.hapd = hostapd.add_ap(apdev, params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+ self.hapd.request("SET ext_eapol_frame_io 1")
+ dev.request("SET ext_eapol_frame_io 1")
+
+ self.bssid = apdev['bssid']
+ pmk = binascii.unhexlify("c2c6c255af836bed1b3f2f1ded98e052f5ad618bb554e2836757b55854a0eab7")
+
+ if sta_ocv != "0":
+ self.rsne = binascii.unhexlify("301a0100000fac040100000fac040100000fac0280400000000fac06")
+ else:
+ self.rsne = binascii.unhexlify("301a0100000fac040100000fac040100000fac0280000000000fac06")
+ self.snonce = binascii.unhexlify('1111111111111111111111111111111111111111111111111111111111111111')
+
+ dev.connect(self.ssid, raw_psk=self.psk, scan_freq=freq, ocv=sta_ocv,
+ ieee80211w="1", wait_connect=False)
+ if "country_code" in params:
+ dev.wait_regdom(country_ie=True)
+ self.addr = dev.p2p_interface_addr()
+
+ # Wait for EAPOL-Key msg 1/4 from hostapd to determine when associated
+ self.msg = recv_eapol(self.hapd)
+ self.anonce = self.msg['rsn_key_nonce']
+ (ptk, self.kck, self.kek) = pmk_to_ptk(pmk, self.addr, self.bssid,
+ self.snonce, self.anonce)
+
+ # hapd, addr, rsne, kck, msg, anonce, snonce
+ def test_bad_oci(self, logmsg, op_class, channel, seg1_idx):
+ logger.debug("Bad OCI element: " + logmsg)
+ if op_class is None:
+ ocikde = b''
+ else:
+ ocikde = make_ocikde(op_class, channel, seg1_idx)
+
+ reply_eapol("2/4", self.hapd, self.addr, self.msg, 0x010a, self.snonce,
+ self.rsne + ocikde, self.kck)
+ self.msg = recv_eapol(self.hapd)
+ if self.anonce != self.msg['rsn_key_nonce'] or self.msg["rsn_key_info"] != 138:
+ raise Exception("Didn't receive retransmitted 1/4")
+
+ def confirm_valid_oci(self, op_class, channel, seg1_idx):
+ logger.debug("Valid OCI element to complete handshake")
+ ocikde = make_ocikde(op_class, channel, seg1_idx)
+
+ reply_eapol("2/4", self.hapd, self.addr, self.msg, 0x010a, self.snonce,
+ self.rsne + ocikde, self.kck)
+ self.msg = recv_eapol(self.hapd)
+ if self.anonce != self.msg['rsn_key_nonce'] or self.msg["rsn_key_info"] != 5066:
+ raise Exception("Didn't receive 3/4 in response to valid 2/4")
+
+ reply_eapol("4/4", self.hapd, self.addr, self.msg, 0x030a, None, None,
+ self.kck)
+ self.hapd.wait_sta(timeout=15)
+
+@remote_compatible
+def test_wpa2_ocv_ap_mismatch(dev, apdev):
+ """OCV AP mismatch"""
+ params = {"channel": "1",
+ "ieee80211w": "1",
+ "freq": "2412"}
+ conn = APConnection(apdev[0], dev[0], params)
+ conn.test_bad_oci("element missing", None, 0, 0)
+ conn.test_bad_oci("wrong channel number", 81, 6, 0)
+ conn.test_bad_oci("invalid channel number", 81, 0, 0)
+ conn.test_bad_oci("wrong operating class", 80, 0, 0)
+ conn.test_bad_oci("invalid operating class", 0, 0, 0)
+ conn.confirm_valid_oci(81, 1, 0)
+
+@remote_compatible
+def test_wpa2_ocv_ap_ht_mismatch(dev, apdev):
+ """OCV AP mismatch (HT)"""
+ params = {"channel": "6",
+ "ht_capab": "[HT40-]",
+ "ieee80211w": "1",
+ "freq": "2437"}
+ conn = APConnection(apdev[0], dev[0], params)
+ conn.test_bad_oci("wrong primary channel", 84, 5, 0)
+ conn.test_bad_oci("lower bandwidth than negotiated", 81, 6, 0)
+ conn.test_bad_oci("bad upper/lower channel", 83, 6, 0)
+ conn.confirm_valid_oci(84, 6, 0)
+
+@remote_compatible
+def test_wpa2_ocv_ap_vht80_mismatch(dev, apdev):
+ """OCV AP mismatch (VHT80)"""
+ try:
+ run_wpa2_ocv_ap_vht80_mismatch(dev, apdev)
+ finally:
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].flush_scan_cache()
+
+def run_wpa2_ocv_ap_vht80_mismatch(dev, apdev):
+ params = {"hw_mode": "a",
+ "channel": "36",
+ "country_code": "US",
+ "ht_capab": "[HT40+]",
+ "ieee80211w": "1",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "freq": "5180",
+ "vht_oper_centr_freq_seg0_idx": "42"}
+ conn = APConnection(apdev[0], dev[0], params)
+ conn.test_bad_oci("wrong primary channel", 128, 38, 0)
+ conn.test_bad_oci("wrong primary channel", 128, 32, 0)
+ conn.test_bad_oci("smaller bandwidth than negotiated", 116, 36, 0)
+ conn.test_bad_oci("smaller bandwidth than negotiated", 115, 36, 0)
+ conn.confirm_valid_oci(128, 36, 0)
+
+ dev[0].dump_monitor()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+@remote_compatible
+def test_wpa2_ocv_ap_vht160_mismatch(dev, apdev):
+ """OCV AP mismatch (VHT160)"""
+ try:
+ run_wpa2_ocv_ap_vht160_mismatch(dev, apdev)
+ finally:
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def run_wpa2_ocv_ap_vht160_mismatch(dev, apdev):
+ params = {"hw_mode": "a",
+ "channel": "100",
+ "country_code": "ZA",
+ "ht_capab": "[HT40+]",
+ "ieee80211w": "1",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "2",
+ "freq": "5500",
+ "vht_oper_centr_freq_seg0_idx": "114",
+ "ieee80211d": "1",
+ "ieee80211h": "1"}
+ conn = APConnection(apdev[0], dev[0], params)
+ conn.test_bad_oci("wrong primary channel", 129, 36, 0)
+ conn.test_bad_oci("wrong primary channel", 129, 114, 0)
+ conn.test_bad_oci("smaller bandwidth (20 Mhz) than negotiated", 121, 100, 0)
+ conn.test_bad_oci("smaller bandwidth (40 Mhz) than negotiated", 122, 100, 0)
+ conn.test_bad_oci("smaller bandwidth (80 Mhz) than negotiated", 128, 100, 0)
+ conn.test_bad_oci("using 80+80 channel instead of 160", 130, 100, 155)
+ conn.confirm_valid_oci(129, 100, 0)
+
+ dev[0].dump_monitor()
+ if conn.hapd:
+ conn.hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+
+@remote_compatible
+def test_wpa2_ocv_ap_vht80plus80_mismatch(dev, apdev):
+ """OCV AP mismatch (VHT80+80)"""
+ try:
+ run_wpa2_ocv_ap_vht80plus80_mismatch(dev, apdev)
+ finally:
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def run_wpa2_ocv_ap_vht80plus80_mismatch(dev, apdev):
+ params = {"hw_mode": "a",
+ "channel": "36",
+ "country_code": "US",
+ "ht_capab": "[HT40+]",
+ "ieee80211w": "1",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "3",
+ "freq": "5180",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "ieee80211d": "1",
+ "vht_oper_centr_freq_seg1_idx": "155",
+ "ieee80211h": "1"}
+ conn = APConnection(apdev[0], dev[0], params)
+ conn.test_bad_oci("using 80 MHz operating class", 128, 36, 155)
+ conn.test_bad_oci("wrong frequency segment 1", 130, 36, 138)
+ conn.confirm_valid_oci(130, 36, 155)
+
+ dev[0].dump_monitor()
+ if conn.hapd:
+ conn.hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+
+@remote_compatible
+def test_wpa2_ocv_ap_unexpected1(dev, apdev):
+ """OCV and unexpected OCI KDE from station"""
+ params = {"channel": "1",
+ "ieee80211w": "1",
+ "ap_ocv": "0",
+ "sta_ocv": "1",
+ "freq": "2412"}
+ conn = APConnection(apdev[0], dev[0], params)
+ logger.debug("Client will send OCI KDE even if it was not negotiated")
+ conn.confirm_valid_oci(81, 1, 0)
+
+@remote_compatible
+def test_wpa2_ocv_ap_unexpected2(dev, apdev):
+ """OCV and unexpected OCI KDE from station"""
+ params = {"channel": "1",
+ "ieee80211w": "1",
+ "ap_ocv": "1",
+ "sta_ocv": "0",
+ "freq": "2412"}
+ conn = APConnection(apdev[0], dev[0], params)
+ logger.debug("Client will send OCI KDE even if it was not negotiated")
+ conn.confirm_valid_oci(81, 1, 0)
+
+@remote_compatible
+def test_wpa2_ocv_ap_retransmit_msg3(dev, apdev):
+ """Verify that manually retransmitted msg 3/4 contain a correct OCI"""
+ bssid = apdev[0]['bssid']
+ ssid = "test-wpa2-ocv"
+ passphrase = "qwertyuiop"
+ psk = "c2c6c255af836bed1b3f2f1ded98e052f5ad618bb554e2836757b55854a0eab7"
+ params = hostapd.wpa2_params(ssid=ssid)
+ params["wpa_psk"] = psk
+ params["ieee80211w"] = "1"
+ params["ocv"] = "1"
+ params['wpa_disable_eapol_key_retries'] = "1"
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+ hapd.request("SET ext_eapol_frame_io 1")
+ dev[0].request("SET ext_eapol_frame_io 1")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", wait_connect=False,
+ ocv="1", ieee80211w="1")
+ addr = dev[0].own_addr()
+
+ # EAPOL-Key msg 1/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ res = dev[0].request("EAPOL_RX " + bssid + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to wpa_supplicant failed")
+
+ # EAPOL-Key msg 2/4
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from wpa_supplicant")
+ res = hapd.request("EAPOL_RX " + addr + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX to hostapd failed")
+
+ # EAPOL-Key msg 3/4
+ ev = hapd.wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from hostapd")
+ logger.info("Drop the first EAPOL-Key msg 3/4")
+
+ # Use normal EAPOL TX/RX to handle retries.
+ hapd.request("SET ext_eapol_frame_io 0")
+ dev[0].request("SET ext_eapol_frame_io 0")
+
+ # Manually retransmit EAPOL-Key msg 3/4
+ if "OK" not in hapd.request("RESEND_M3 " + addr):
+ raise Exception("RESEND_M3 failed")
+
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_wpa2_ocv_ap_group_hs(dev, apdev):
+ """OCV group handshake (AP)"""
+ params = {"channel": "1",
+ "ieee80211w": "1",
+ "freq": "2412",
+ "wpa_strict_rekey": "1"}
+ conn = APConnection(apdev[0], dev[0], params)
+ conn.confirm_valid_oci(81, 1, 0)
+
+ conn.hapd.request("SET ext_eapol_frame_io 0")
+ dev[1].connect(conn.ssid, psk=conn.passphrase, scan_freq="2412", ocv="1",
+ ieee80211w="1")
+ conn.hapd.wait_sta()
+ conn.hapd.request("SET ext_eapol_frame_io 1")
+
+ # Trigger a group key handshake
+ dev[1].request("DISCONNECT")
+ dev[0].dump_monitor()
+
+ # Wait for EAPOL-Key msg 1/2
+ conn.msg = recv_eapol(conn.hapd)
+ if conn.msg["rsn_key_info"] != 4994:
+ raise Exception("Didn't receive 1/2 of group key handshake")
+
+ # Send a EAPOL-Key msg 2/2 with a bad OCI
+ logger.info("Bad OCI element")
+ ocikde = make_ocikde(1, 1, 1)
+ msg = build_eapol_key_2_2(conn.kck, ocikde, replay_counter=3)
+ conn.hapd.dump_monitor()
+ send_eapol(conn.hapd, conn.addr, build_eapol(msg))
+
+ # Wait for retransmitted EAPOL-Key msg 1/2
+ conn.msg = recv_eapol(conn.hapd)
+ if conn.msg["rsn_key_info"] != 4994:
+ raise Exception("Didn't receive 1/2 of group key handshake")
+
+ # Send a EAPOL-Key msg 2/2 with a good OCI
+ logger.info("Good OCI element")
+ ocikde = make_ocikde(81, 1, 0)
+ msg = build_eapol_key_2_2(conn.kck, ocikde, replay_counter=4)
+ conn.hapd.dump_monitor()
+ send_eapol(conn.hapd, conn.addr, build_eapol(msg))
+
+ # Verify that group key handshake has completed
+ ev = conn.hapd.wait_event(["EAPOL-TX"], timeout=1)
+ if ev is not None:
+ eapol = binascii.unhexlify(ev.split(' ')[2])
+ msg = parse_eapol(eapol)
+ if msg["rsn_key_info"] == 4994:
+ raise Exception("AP didn't accept 2/2 of group key handshake")
+
+class STAConnection:
+ def init_params(self):
+ # Static parameters
+ self.ssid = "test-wpa2-ocv"
+ self.passphrase = "qwertyuiop"
+ self.psk = "c2c6c255af836bed1b3f2f1ded98e052f5ad618bb554e2836757b55854a0eab7"
+
+ # Dynamic parameters
+ self.hapd = None
+ self.dev = None
+ self.addr = None
+ self.rsne = None
+ self.kck = None
+ self.kek = None
+ self.msg = None
+ self.bssid = None
+ self.anonce = None
+ self.snonce = None
+ self.gtkie = None
+ self.counter = None
+
+ def __init__(self, apdev, dev, params, sta_params=None):
+ self.init_params()
+ self.dev = dev
+ self.bssid = apdev['bssid']
+
+ freq = params.pop("freq")
+ if sta_params is None:
+ sta_params = dict()
+ if "ocv" not in sta_params:
+ sta_params["ocv"] = "1"
+ if "ieee80211w" not in sta_params:
+ sta_params["ieee80211w"] = "1"
+
+ params.update(hostapd.wpa2_params(ssid=self.ssid,
+ passphrase=self.passphrase))
+ params['wpa_pairwise_update_count'] = "10"
+
+ try:
+ self.hapd = hostapd.add_ap(apdev, params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+ self.hapd.request("SET ext_eapol_frame_io 1")
+ self.dev.request("SET ext_eapol_frame_io 1")
+ pmk = binascii.unhexlify("c2c6c255af836bed1b3f2f1ded98e052f5ad618bb554e2836757b55854a0eab7")
+
+ self.gtkie = binascii.unhexlify("dd16000fac010100dc11188831bf4aa4a8678d2b41498618")
+ if sta_params["ocv"] != "0":
+ self.rsne = binascii.unhexlify("30140100000fac040100000fac040100000fac028c40")
+ else:
+ self.rsne = binascii.unhexlify("30140100000fac040100000fac040100000fac028c00")
+
+ self.dev.connect(self.ssid, raw_psk=self.psk, scan_freq=freq,
+ wait_connect=False, **sta_params)
+ if "country_code" in params:
+ self.dev.wait_regdom(country_ie=True)
+ self.addr = dev.p2p_interface_addr()
+
+ # Forward msg 1/4 from AP to STA
+ self.msg = recv_eapol(self.hapd)
+ self.anonce = self.msg['rsn_key_nonce']
+ send_eapol(self.dev, self.bssid, build_eapol(self.msg))
+
+ # Capture msg 2/4 from the STA so we can derive the session keys
+ self.msg = recv_eapol(dev)
+ self.snonce = self.msg['rsn_key_nonce']
+ (ptk, self.kck, self.kek) = pmk_to_ptk(pmk, self.addr, self.bssid,
+ self.snonce, self.anonce)
+
+ self.counter = struct.unpack('>Q',
+ self.msg['rsn_replay_counter'])[0] + 1
+
+ def test_bad_oci(self, logmsg, op_class, channel, seg1_idx, errmsg):
+ logger.info("Bad OCI element: " + logmsg)
+ if op_class is None:
+ ocikde = b''
+ else:
+ ocikde = make_ocikde(op_class, channel, seg1_idx)
+
+ plain = self.rsne + self.gtkie + ocikde
+ wrapped = aes_wrap(self.kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(self.anonce, self.kck, wrapped,
+ replay_counter=self.counter)
+
+ self.dev.dump_monitor()
+ send_eapol(self.dev, self.bssid, build_eapol(msg))
+ self.counter += 1
+
+ ev = self.dev.wait_event([errmsg], timeout=5)
+ if ev is None:
+ raise Exception("Bad OCI not reported")
+
+ def confirm_valid_oci(self, op_class, channel, seg1_idx):
+ logger.debug("Valid OCI element to complete handshake")
+ ocikde = make_ocikde(op_class, channel, seg1_idx)
+
+ plain = self.rsne + self.gtkie + ocikde
+ wrapped = aes_wrap(self.kek, pad_key_data(plain))
+ msg = build_eapol_key_3_4(self.anonce, self.kck, wrapped,
+ replay_counter=self.counter)
+
+ self.dev.dump_monitor()
+ send_eapol(self.dev, self.bssid, build_eapol(msg))
+ self.counter += 1
+
+ self.dev.wait_connected(timeout=1)
+
+@remote_compatible
+def test_wpa2_ocv_mismatch_client(dev, apdev):
+ """OCV client mismatch"""
+ params = {"channel": "1",
+ "ieee80211w": "1",
+ "ocv": "1",
+ "freq": "2412"}
+ conn = STAConnection(apdev[0], dev[0], params)
+ conn.test_bad_oci("element missing", None, 0, 0,
+ "did not receive mandatory OCI")
+ conn.test_bad_oci("wrong channel number", 81, 6, 0,
+ "primary channel mismatch")
+ conn.test_bad_oci("invalid channel number", 81, 0, 0,
+ "unable to interpret received OCI")
+ conn.test_bad_oci("wrong operating class", 80, 0, 0,
+ "unable to interpret received OCI")
+ conn.test_bad_oci("invalid operating class", 0, 0, 0,
+ "unable to interpret received OCI")
+ conn.confirm_valid_oci(81, 1, 0)
+
+@remote_compatible
+def test_wpa2_ocv_vht160_mismatch_client(dev, apdev):
+ """OCV client mismatch (VHT160)"""
+ try:
+ run_wpa2_ocv_vht160_mismatch_client(dev, apdev)
+ finally:
+ set_world_reg(apdev[0], apdev[1], dev[0])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def run_wpa2_ocv_vht160_mismatch_client(dev, apdev):
+ params = {"hw_mode": "a",
+ "channel": "100",
+ "country_code": "ZA",
+ "ht_capab": "[HT40+]",
+ "ieee80211w": "1",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "2",
+ "ocv": "1",
+ "vht_oper_centr_freq_seg0_idx": "114",
+ "freq": "5500",
+ "ieee80211d": "1",
+ "ieee80211h": "1"}
+ sta_params = {"disable_vht": "1"}
+ conn = STAConnection(apdev[0], dev[0], params, sta_params)
+ conn.test_bad_oci("smaller bandwidth (20 Mhz) than negotiated",
+ 121, 100, 0, "channel bandwidth mismatch")
+ conn.test_bad_oci("wrong frequency, bandwith, and secondary channel",
+ 123, 104, 0, "primary channel mismatch")
+ conn.test_bad_oci("wrong upper/lower behaviour",
+ 129, 104, 0, "primary channel mismatch")
+ conn.confirm_valid_oci(122, 100, 0)
+
+ dev[0].dump_monitor()
+ if conn.hapd:
+ conn.hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+
+def test_wpa2_ocv_sta_group_hs(dev, apdev):
+ """OCV group handshake (STA)"""
+ params = {"channel": "1",
+ "ieee80211w": "1",
+ "ocv": "1",
+ "freq": "2412",
+ "wpa_strict_rekey": "1"}
+ conn = STAConnection(apdev[0], dev[0], params.copy())
+ conn.confirm_valid_oci(81, 1, 0)
+
+ # Send a EAPOL-Key msg 1/2 with a bad OCI
+ logger.info("Bad OCI element")
+ plain = conn.gtkie + make_ocikde(1, 1, 1)
+ wrapped = aes_wrap(conn.kek, pad_key_data(plain))
+ msg = build_eapol_key_1_2(conn.kck, wrapped, replay_counter=3)
+ send_eapol(dev[0], conn.bssid, build_eapol(msg))
+
+ # We shouldn't get a EAPOL-Key message back
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=1)
+ if ev is not None:
+ raise Exception("Received response to invalid EAPOL-Key 1/2")
+
+ # Reset AP to try with valid OCI
+ conn.hapd.disable()
+ conn = STAConnection(apdev[0], dev[0], params.copy())
+ conn.confirm_valid_oci(81, 1, 0)
+
+ # Send a EAPOL-Key msg 1/2 with a good OCI
+ logger.info("Good OCI element")
+ plain = conn.gtkie + make_ocikde(81, 1, 0)
+ wrapped = aes_wrap(conn.kek, pad_key_data(plain))
+ msg = build_eapol_key_1_2(conn.kck, wrapped, replay_counter=4)
+ send_eapol(dev[0], conn.bssid, build_eapol(msg))
+
+ # Wait for EAPOL-Key msg 2/2
+ conn.msg = recv_eapol(dev[0])
+ if conn.msg["rsn_key_info"] != 0x0302:
+ raise Exception("Didn't receive 2/2 of group key handshake")
+
+def test_wpa2_ocv_auto_enable_pmf(dev, apdev):
+ """OCV on 2.4 GHz with PMF getting enabled automatically"""
+ params = {"channel": "1",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ for ocv in range(2):
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv=str(ocv),
+ ieee80211w="2")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_wpa2_ocv_sta_override_eapol(dev, apdev):
+ """OCV on 2.4 GHz and STA override EAPOL-Key msg 2/4"""
+ params = {"channel": "1",
+ "ieee80211w": "2",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ dev[0].set("oci_freq_override_eapol", "2462")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
+ ieee80211w="2", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=15)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("No connection result reported")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ if "reason=15" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+ check_ocv_failure(hapd, "EAPOL-Key msg 2/4", "eapol-key-m2",
+ dev[0].own_addr())
+
+def test_wpa2_ocv_sta_override_sa_query_req(dev, apdev):
+ """OCV on 2.4 GHz and STA override SA Query Request"""
+ params = {"channel": "1",
+ "ieee80211w": "2",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
+ ieee80211w="2")
+ hapd.wait_sta()
+ dev[0].set("oci_freq_override_saquery_req", "2462")
+ if "OK" not in dev[0].request("UNPROT_DEAUTH"):
+ raise Exception("Triggering SA Query from the STA failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=3)
+ if ev is None:
+ raise Exception("Disconnection after failed SA Query not reported")
+ dev[0].set("oci_freq_override_saquery_req", "0")
+ dev[0].wait_connected()
+ if "OK" not in dev[0].request("UNPROT_DEAUTH"):
+ raise Exception("Triggering SA Query from the STA failed")
+ check_ocv_failure(hapd, "SA Query Request", "saqueryreq",
+ dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=3)
+ if ev is not None:
+ raise Exception("SA Query from the STA failed")
+
+def test_wpa2_ocv_sta_override_sa_query_resp(dev, apdev):
+ """OCV on 2.4 GHz and STA override SA Query Response"""
+ params = {"channel": "1",
+ "ieee80211w": "2",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
+ ieee80211w="2")
+ dev[0].set("oci_freq_override_saquery_resp", "2462")
+ hapd.wait_sta()
+ if "OK" not in hapd.request("SA_QUERY " + dev[0].own_addr()):
+ raise Exception("SA_QUERY failed")
+ check_ocv_failure(hapd, "SA Query Response", "saqueryresp",
+ dev[0].own_addr())
+
+def check_ocv_failure(dev, frame_txt, frame, addr):
+ ev = dev.wait_event(["OCV-FAILURE"], timeout=3)
+ if ev is None:
+ raise Exception("OCV failure for %s not reported" % frame_txt)
+ if "addr=" + addr not in ev:
+ raise Exception("Unexpected OCV failure addr: " + ev)
+ if "frame=" + frame not in ev:
+ raise Exception("Unexpected OCV failure frame: " + ev)
+ if "error=primary channel mismatch" not in ev:
+ raise Exception("Unexpected OCV failure error: " + ev)
+
+def test_wpa2_ocv_ap_override_eapol_m3(dev, apdev):
+ """OCV on 2.4 GHz and AP override EAPOL-Key msg 3/4"""
+ run_wpa2_ocv_ap_override_eapol_m3(dev, apdev)
+
+def test_wpa2_ocv_ap_override_eapol_m3_post_enable(dev, apdev):
+ """OCV on 2.4 GHz and AP override EAPOL-Key msg 3/4 (post enable)"""
+ run_wpa2_ocv_ap_override_eapol_m3(dev, apdev, True)
+
+def run_wpa2_ocv_ap_override_eapol_m3(dev, apdev, post_enable=False):
+ params = {"channel": "1",
+ "ieee80211w": "2",
+ "ocv": "1"}
+ if not post_enable:
+ params["oci_freq_override_eapol_m3"] = "2462"
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ if post_enable:
+ hapd.set("oci_freq_override_eapol_m3", "2462")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
+ ieee80211w="2", wait_connect=False)
+
+ check_ocv_failure(dev[0], "EAPOL-Key msg 3/4", "eapol-key-m3", bssid)
+
+ ev = dev[0].wait_disconnected()
+ if "reason=15" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+def test_wpa2_ocv_ap_override_eapol_g1(dev, apdev):
+ """OCV on 2.4 GHz and AP override EAPOL-Key group msg 1/2"""
+ run_wpa2_ocv_ap_override_eapol_g1(dev, apdev)
+
+def test_wpa2_ocv_ap_override_eapol_g1_post_enable(dev, apdev):
+ """OCV on 2.4 GHz and AP override EAPOL-Key group msg 1/2 (post enable)"""
+ run_wpa2_ocv_ap_override_eapol_g1(dev, apdev, True)
+
+def run_wpa2_ocv_ap_override_eapol_g1(dev, apdev, post_enable=False):
+ params = {"channel": "1",
+ "ieee80211w": "2",
+ "ocv": "1"}
+ if not post_enable:
+ params["oci_freq_override_eapol_g1"] = "2462"
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
+ ieee80211w="2")
+
+ if post_enable:
+ hapd.set("oci_freq_override_eapol_g1", "2462")
+ if "OK" not in hapd.request("REKEY_GTK"):
+ raise Exception("REKEY_GTK failed")
+ check_ocv_failure(dev[0], "EAPOL-Key group msg 1/2", "eapol-key-g1", bssid)
+
+def test_wpa2_ocv_ap_override_saquery_req(dev, apdev):
+ """OCV on 2.4 GHz and AP override SA Query Request"""
+ params = {"channel": "1",
+ "ieee80211w": "2",
+ "ocv": "1",
+ "oci_freq_override_saquery_req": "2462"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
+ ieee80211w="2")
+
+ if "OK" not in hapd.request("SA_QUERY " + dev[0].own_addr()):
+ raise Exception("SA_QUERY failed")
+ check_ocv_failure(dev[0], "SA Query Request", "saqueryreq", bssid)
+
+def test_wpa2_ocv_ap_override_saquery_resp(dev, apdev):
+ """OCV on 2.4 GHz and AP override SA Query Response"""
+ params = {"channel": "1",
+ "ieee80211w": "2",
+ "ocv": "1",
+ "oci_freq_override_saquery_resp": "2462"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="1",
+ ieee80211w="2")
+
+ if "OK" not in dev[0].request("UNPROT_DEAUTH"):
+ raise Exception("Triggering SA Query from the STA failed")
+ check_ocv_failure(dev[0], "SA Query Response", "saqueryresp", bssid)
+
+def test_wpa2_ocv_ap_override_fils_assoc(dev, apdev, params):
+ """OCV on 2.4 GHz and AP override FILS association"""
+ run_wpa2_ocv_ap_override_fils_assoc(dev, apdev, params)
+
+def test_wpa2_ocv_ap_override_fils_assoc_post_enable(dev, apdev, params):
+ """OCV on 2.4 GHz and AP override FILS association (post enable)"""
+ run_wpa2_ocv_ap_override_fils_assoc(dev, apdev, params, True)
+
+def run_wpa2_ocv_ap_override_fils_assoc(dev, apdev, params, post_enable=False):
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ ssid = "test-wpa2-ocv"
+ params = hostapd.wpa2_eap_params(ssid=ssid)
+ params['wpa_key_mgmt'] = "FILS-SHA256"
+ params['auth_server_port'] = "18128"
+ params['erp_send_reauth_start'] = '1'
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['wpa_group_rekey'] = '1'
+ params["ieee80211w"] = "2"
+ params["ocv"] = "1"
+ if not post_enable:
+ params["oci_freq_override_fils_assoc"] = "2462"
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+ bssid = hapd.own_addr()
+ if post_enable:
+ hapd.set("oci_freq_override_fils_assoc", "2462")
+ dev[0].request("ERP_FLUSH")
+ id = dev[0].connect(ssid, key_mgmt="FILS-SHA256",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412", ocv="1", ieee80211w="2")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+
+ check_ocv_failure(dev[0], "FILS Association Response", "fils-assoc", bssid)
+ dev[0].request("DISCONNECT")
+
+def test_wpa2_ocv_ap_override_ft_assoc(dev, apdev):
+ """OCV on 2.4 GHz and AP override FT reassociation"""
+ run_wpa2_ocv_ap_override_ft_assoc(dev, apdev)
+
+def test_wpa2_ocv_ap_override_ft_assoc_post_enable(dev, apdev):
+ """OCV on 2.4 GHz and AP override FT reassociation (post enable)"""
+ run_wpa2_ocv_ap_override_ft_assoc(dev, apdev, True)
+
+def run_wpa2_ocv_ap_override_ft_assoc(dev, apdev, post_enable=False):
+ ssid = "test-wpa2-ocv"
+ passphrase = "qwertyuiop"
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params["ocv"] = "1"
+ if not post_enable:
+ params["oci_freq_override_ft_assoc"] = "2462"
+ try:
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params["ocv"] = "1"
+ if not post_enable:
+ params["oci_freq_override_ft_assoc"] = "2462"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ if post_enable:
+ hapd0.set("oci_freq_override_ft_assoc", "2462")
+ hapd1.set("oci_freq_override_ft_assoc", "2462")
+
+ dev[0].connect(ssid, key_mgmt="FT-PSK", psk=passphrase,
+ scan_freq="2412", ocv="1", ieee80211w="2")
+
+ bssid = dev[0].get_status_field("bssid")
+ bssid0 = hapd0.own_addr()
+ bssid1 = hapd1.own_addr()
+ target = bssid0 if bssid == bssid1 else bssid1
+
+ dev[0].scan_for_bss(target, freq="2412")
+ if "OK" not in dev[0].request("ROAM " + target):
+ raise Exception("ROAM failed")
+
+ check_ocv_failure(dev[0], "FT Reassociation Response", "ft-assoc", target)
+ dev[0].request("DISCONNECT")
+
+@remote_compatible
+def test_wpa2_ocv_no_pmf(dev, apdev):
+ """OCV on 2.4 GHz and no PMF on STA"""
+ params = {"channel": "1",
+ "ieee80211w": "1",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ ie = "301a0100000fac040100000fac040100000fac0200400000000fac06"
+ if "OK" not in dev[0].request("TEST_ASSOC_IE " + ie):
+ raise Exception("Could not set TEST_ASSOC_IE")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="0",
+ ieee80211w="0", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-ASSOC-REJECT"],
+ timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("No connection result seen")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ if "status_code=31" not in ev:
+ raise Exception("Unexpected status code: " + ev)
+
+@remote_compatible
+def test_wpa2_ocv_no_pmf_workaround(dev, apdev):
+ """OCV on 2.4 GHz and no PMF on STA with workaround"""
+ params = {"channel": "1",
+ "ieee80211w": "1",
+ "ocv": "2"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ ie = "301a0100000fac040100000fac040100000fac0200400000000fac06"
+ if "OK" not in dev[0].request("TEST_ASSOC_IE " + ie):
+ raise Exception("Could not set TEST_ASSOC_IE")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="0",
+ ieee80211w="0")
+
+@remote_compatible
+def test_wpa2_ocv_no_oci(dev, apdev):
+ """OCV on 2.4 GHz and no OCI from STA"""
+ params = {"channel": "1",
+ "ieee80211w": "1",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ ie = "301a0100000fac040100000fac040100000fac0280400000000fac06"
+ if "OK" not in dev[0].request("TEST_ASSOC_IE " + ie):
+ raise Exception("Could not set TEST_ASSOC_IE")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="0",
+ ieee80211w="1", wait_connect=False)
+ ev = hapd.wait_event(["OCV-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("No OCV failure reported")
+ if "frame=eapol-key-m2 error=did not receive mandatory OCI" not in ev:
+ raise Exception("Unexpected error: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "WPA: 4-Way Handshake failed"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ if ev is None:
+ raise Exception("4-way handshake failure not reported")
+
+@remote_compatible
+def test_wpa2_ocv_no_oci_workaround(dev, apdev):
+ """OCV on 2.4 GHz and no OCI from STA with workaround"""
+ params = {"channel": "1",
+ "ieee80211w": "1",
+ "ocv": "2"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ ie = "301a0100000fac040100000fac040100000fac0280400000000fac06"
+ if "OK" not in dev[0].request("TEST_ASSOC_IE " + ie):
+ raise Exception("Could not set TEST_ASSOC_IE")
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412", ocv="0",
+ ieee80211w="1")
+
+def test_wpa2_ocv_without_pmf(dev, apdev):
+ """OCV without PMF"""
+ params = {"channel": "6",
+ "ieee80211n": "1",
+ "ieee80211w": "1",
+ "ocv": "1"}
+ hapd, ssid, passphrase = ocv_setup_ap(apdev[0], params)
+ hapd.disable()
+ hapd.set("ieee80211w", "0")
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("OCV without PMF accepted")
diff --git a/contrib/wpa/tests/hwsim/test_offchannel_tx.py b/contrib/wpa/tests/hwsim/test_offchannel_tx.py
new file mode 100644
index 000000000000..85308da26847
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_offchannel_tx.py
@@ -0,0 +1,50 @@
+# cfg80211 offchannel TX using remain-on-channel
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from test_gas import start_ap
+from test_gas import anqp_get
+from p2p_utils import *
+
+def test_offchannel_tx_roc_gas(dev, apdev):
+ """GAS using cfg80211 remain-on-channel for offchannel TX"""
+ start_ap(apdev[0])
+ bssid = apdev[0]['bssid']
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="no_offchannel_tx=1")
+ wpas.flush_scan_cache()
+ wpas.scan_for_bss(bssid, freq=2412)
+ anqp_get(wpas, bssid, 263)
+ ev = wpas.wait_event(["GAS-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("GAS query timed out")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Unexpected GAS query result")
+
+def test_offchannel_tx_roc_grpform(dev, apdev):
+ """P2P group formation using cfg80211 remain-on-channel for offchannel TX"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="no_offchannel_tx=1")
+
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_freq=2412,
+ r_dev=wpas, r_freq=2412)
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], wpas)
+
+def test_offchannel_tx_roc_grpform2(dev, apdev):
+ """P2P group formation(2) using cfg80211 remain-on-channel for offchannel TX"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="no_offchannel_tx=1")
+
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=wpas, i_freq=2412,
+ r_dev=dev[0], r_freq=2412)
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], wpas)
diff --git a/contrib/wpa/tests/hwsim/test_owe.py b/contrib/wpa/tests/hwsim/test_owe.py
new file mode 100644
index 000000000000..3f29913cf532
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_owe.py
@@ -0,0 +1,928 @@
+# Test cases for Opportunistic Wireless Encryption (OWE)
+# Copyright (c) 2017, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import logging
+logger = logging.getLogger()
+import time
+import os
+import struct
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+import hwsim_utils
+from tshark import run_tshark
+from utils import HwsimSkip, fail_test, alloc_fail, wait_fail_trigger
+from test_ap_acs import wait_acs
+
+def test_owe(dev, apdev):
+ """Opportunistic Wireless Encryption"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ conf = hapd.request("GET_CONFIG")
+ if "key_mgmt=OWE" not in conf.splitlines():
+ logger.info("GET_CONFIG:\n" + conf)
+ raise Exception("GET_CONFIG did not report correct key_mgmt")
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ bss = dev[0].get_bss(bssid)
+ if "[WPA2-OWE-CCMP]" not in bss['flags']:
+ raise Exception("OWE AKM not recognized: " + bss['flags'])
+
+ id = dev[0].connect("owe", key_mgmt="OWE", ieee80211w="2", scan_freq="2412")
+ hapd.wait_sta()
+ pmk_h = hapd.request("GET_PMK " + dev[0].own_addr())
+ pmk_w = dev[0].get_pmk(id)
+ if pmk_h != pmk_w:
+ raise Exception("Fetched PMK does not match: hostapd %s, wpa_supplicant %s" % (pmk_h, pmk_w))
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ val = dev[0].get_status_field("key_mgmt")
+ if val != "OWE":
+ raise Exception("Unexpected key_mgmt: " + val)
+
+def test_owe_groups(dev, apdev):
+ """Opportunistic Wireless Encryption - DH groups"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ for group in [19, 20, 21]:
+ dev[0].connect("owe", key_mgmt="OWE", owe_group=str(group))
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+
+def test_owe_pmksa_caching(dev, apdev):
+ """Opportunistic Wireless Encryption and PMKSA caching"""
+ try:
+ run_owe_pmksa_caching(dev, apdev)
+ finally:
+ dev[0].set("reassoc_same_bss_optim", "0")
+
+def test_owe_pmksa_caching_connect_cmd(dev, apdev):
+ """Opportunistic Wireless Encryption and PMKSA caching using cfg80211 connect command"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ try:
+ run_owe_pmksa_caching([wpas], apdev)
+ finally:
+ wpas.set("reassoc_same_bss_optim", "0")
+
+def run_owe_pmksa_caching(dev, apdev):
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].set("reassoc_same_bss_optim", "1")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ id = dev[0].connect("owe", key_mgmt="OWE")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ pmksa = dev[0].get_pmksa(bssid)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ dev[0].select_network(id, 2412)
+ dev[0].wait_connected()
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ pmksa2 = dev[0].get_pmksa(bssid)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ if "OK" not in hapd.request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+
+ dev[0].select_network(id, 2412)
+ dev[0].wait_connected()
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ pmksa3 = dev[0].get_pmksa(bssid)
+
+ if pmksa is None or pmksa2 is None or pmksa3 is None:
+ raise Exception("PMKSA entry missing")
+ if pmksa['pmkid'] != pmksa2['pmkid']:
+ raise Exception("Unexpected PMKID change when using PMKSA caching")
+ if pmksa['pmkid'] == pmksa3['pmkid']:
+ raise Exception("PMKID did not change after PMKSA cache flush")
+
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected()
+ pmksa4 = dev[0].get_pmksa(bssid)
+ if pmksa3['pmkid'] != pmksa4['pmkid']:
+ raise Exception("Unexpected PMKID change when using PMKSA caching [2]")
+
+def test_owe_and_psk(dev, apdev):
+ """Opportunistic Wireless Encryption and WPA2-PSK enabled"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ params = {"ssid": "owe+psk",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE WPA-PSK",
+ "rsn_pairwise": "CCMP",
+ "wpa_passphrase": "12345678"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("owe+psk", psk="12345678")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[1].scan_for_bss(bssid, freq="2412")
+ dev[1].connect("owe+psk", key_mgmt="OWE")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[1], hapd)
+
+def test_owe_transition_mode(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode"""
+ run_owe_transition_mode(dev, apdev)
+
+def test_owe_transition_mode_connect_cmd(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode using cfg80211 connect command"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ run_owe_transition_mode([wpas], apdev)
+
+def test_owe_transition_mode_mismatch1(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode (mismatch 1)"""
+ run_owe_transition_mode(dev, apdev, adv_bssid0="02:11:22:33:44:55")
+
+def test_owe_transition_mode_mismatch2(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode (mismatch 2)"""
+ run_owe_transition_mode(dev, apdev, adv_bssid1="02:11:22:33:44:66")
+
+def test_owe_transition_mode_mismatch3(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode (mismatch 3)"""
+ run_owe_transition_mode(dev, apdev, adv_bssid0="02:11:22:33:44:55",
+ adv_bssid1="02:11:22:33:44:66")
+
+def run_owe_transition_mode(dev, apdev, adv_bssid0=None, adv_bssid1=None):
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ dev[0].flush_scan_cache()
+ adv_bssid = adv_bssid0 if adv_bssid0 else apdev[1]['bssid']
+ params = {"ssid": "owe-random",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP",
+ "ieee80211w": "2",
+ "owe_transition_bssid": adv_bssid,
+ "owe_transition_ssid": '"owe-test"',
+ "ignore_broadcast_ssid": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ adv_bssid = adv_bssid1 if adv_bssid1 else apdev[0]['bssid']
+ params = {"ssid": "owe-test",
+ "owe_transition_bssid": adv_bssid,
+ "owe_transition_ssid": '"owe-random"'}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+
+ bss = dev[0].get_bss(bssid)
+ if "[WPA2-OWE-CCMP]" not in bss['flags']:
+ raise Exception("OWE AKM not recognized: " + bss['flags'])
+ if "[OWE-TRANS]" not in bss['flags']:
+ raise Exception("OWE transition not recognized: " + bss['flags'])
+
+ bss = dev[0].get_bss(bssid2)
+ if "[OWE-TRANS-OPEN]" not in bss['flags']:
+ raise Exception("OWE transition (open) not recognized: " + bss['flags'])
+
+ id = dev[0].connect("owe-test", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ val = dev[0].get_status_field("key_mgmt")
+ if val != "OWE":
+ raise Exception("Unexpected key_mgmt: " + val)
+
+ logger.info("Move to OWE only mode (disable transition mode)")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ hapd2.disable()
+ hapd.disable()
+ dev[0].flush_scan_cache()
+ hapd.set("owe_transition_bssid", "00:00:00:00:00:00")
+ hapd.set("ignore_broadcast_ssid", '0')
+ hapd.set("ssid", 'owe-test')
+ hapd.enable()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].select_network(id, 2412)
+ dev[0].wait_connected()
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_owe_transition_mode_ifname(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode (ifname)"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ dev[0].flush_scan_cache()
+ params = {"ssid": "owe-random",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP",
+ "ieee80211w": "2",
+ "owe_transition_ifname": apdev[1]['ifname'],
+ "ignore_broadcast_ssid": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ params = {"ssid": "owe-test",
+ "owe_transition_ifname": apdev[0]['ifname']}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+
+ id = dev[0].connect("owe-test", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="2412")
+ val = dev[0].get_status_field("key_mgmt")
+ if val != "OWE":
+ raise Exception("Unexpected key_mgmt: " + val)
+
+def test_owe_transition_mode_ifname_acs(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode (ifname, ACS)"""
+ run_owe_transition_mode_ifname_acs(dev, apdev, wait_first=False)
+
+def test_owe_transition_mode_ifname_acs2(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode (ifname, ACS)"""
+ run_owe_transition_mode_ifname_acs(dev, apdev, wait_first=True)
+
+def run_owe_transition_mode_ifname_acs(dev, apdev, wait_first):
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ dev[0].flush_scan_cache()
+ params = {"ssid": "owe-random",
+ "channel": "0",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP",
+ "ieee80211w": "2",
+ "owe_transition_ifname": apdev[1]['ifname'],
+ "ignore_broadcast_ssid": "1"}
+ hapd = hostapd.add_ap(apdev[0], params, wait_enabled=False)
+ bssid = hapd.own_addr()
+
+ if wait_first:
+ wait_acs(hapd)
+
+ params = {"ssid": "owe-test",
+ "channel": "0",
+ "owe_transition_ifname": apdev[0]['ifname']}
+ hapd2 = hostapd.add_ap(apdev[1], params, wait_enabled=False)
+ bssid2 = hapd2.own_addr()
+
+ wait_acs(hapd2)
+ if not wait_first:
+ state = hapd.get_status_field("state")
+ if state == "ACS-STARTED":
+ time.sleep(5)
+ state = hapd.get_status_field("state")
+ if state != "ENABLED":
+ raise Exception("AP1 startup did not succeed")
+
+ freq = hapd.get_status_field("freq")
+ freq2 = hapd2.get_status_field("freq")
+
+ dev[0].scan_for_bss(bssid, freq=freq)
+ dev[0].scan_for_bss(bssid2, freq=freq2)
+
+ id = dev[0].connect("owe-test", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="%s %s" % (freq, freq2))
+ val = dev[0].get_status_field("key_mgmt")
+ if val != "OWE":
+ raise Exception("Unexpected key_mgmt: " + val)
+
+def test_owe_transition_mode_open_only_ap(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode connect to open-only AP"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ dev[0].flush_scan_cache()
+ params = {"ssid": "owe-test-open"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ bss = dev[0].get_bss(bssid)
+
+ id = dev[0].connect("owe-test-open", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ val = dev[0].get_status_field("key_mgmt")
+ if val != "NONE":
+ raise Exception("Unexpected key_mgmt: " + val)
+
+def test_owe_only_sta(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode disabled on STA"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ dev[0].flush_scan_cache()
+ params = {"ssid": "owe-test-open"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ id = dev[0].connect("owe-test-open", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="2412", owe_only="1", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if not ev:
+ raise Exception("Unknown result for the connection attempt")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection to open network")
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+
+ params = {"ssid": "owe-test-open",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+def test_owe_transition_mode_open_multiple_scans(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode and need for multiple scans"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ dev[0].flush_scan_cache()
+ params = {"ssid": "owe-test",
+ "owe_transition_bssid": apdev[0]['bssid'],
+ "owe_transition_ssid": '"owe-random"'}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid2, freq="2412")
+
+ dev[0].dump_monitor()
+ id = dev[0].connect("owe-test", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=1)
+
+ params = {"ssid": "owe-random",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP",
+ "ieee80211w": "2",
+ "owe_transition_bssid": apdev[1]['bssid'],
+ "owe_transition_ssid": '"owe-test"',
+ "ignore_broadcast_ssid": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].wait_connected()
+
+ val = dev[0].get_status_field("key_mgmt")
+ if val != "OWE":
+ raise Exception("Unexpected key_mgmt: " + val)
+
+def test_owe_transition_mode_multi_bss(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode (multi BSS)"""
+ try:
+ run_owe_transition_mode_multi_bss(dev, apdev)
+ finally:
+ dev[0].request("SCAN_INTERVAL 5")
+
+def run_owe_transition_mode_multi_bss(dev, apdev):
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ ifname1 = apdev[0]['ifname']
+ ifname2 = apdev[0]['ifname'] + '-2'
+ hapd1 = hostapd.add_bss(apdev[0], ifname1, 'owe-bss-1.conf')
+ hapd2 = hostapd.add_bss(apdev[0], ifname2, 'owe-bss-2.conf')
+ hapd2.bssidx = 1
+
+ bssid = hapd1.own_addr()
+ bssid2 = hapd2.own_addr()
+
+ # Beaconing with the OWE Transition Mode element can start only once both
+ # BSSs are enabled, so the very first Beacon frame may go out without this
+ # element. Wait a bit to avoid getting incomplete scan results.
+ time.sleep(0.1)
+
+ dev[0].request("SCAN_INTERVAL 1")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("transition-mode-open", key_mgmt="OWE")
+ val = dev[0].get_status_field("bssid")
+ if val != bssid2:
+ raise Exception("Unexpected bssid: " + val)
+ val = dev[0].get_status_field("key_mgmt")
+ if val != "OWE":
+ raise Exception("Unexpected key_mgmt: " + val)
+ hwsim_utils.test_connectivity(dev[0], hapd2)
+
+def test_owe_transition_mode_rsne_mismatch(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode and RSNE mismatch"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ dev[0].flush_scan_cache()
+ params = {"ssid": "owe-random",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP",
+ "ieee80211w": "2",
+ "rsne_override_eapol": "30140100000fac040100000fac040100000fac020c00",
+ "owe_transition_bssid": apdev[1]['bssid'],
+ "owe_transition_ssid": '"owe-test"',
+ "ignore_broadcast_ssid": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ params = {"ssid": "owe-test",
+ "owe_transition_bssid": apdev[0]['bssid'],
+ "owe_transition_ssid": '"owe-random"'}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+
+ id = dev[0].connect("owe-test", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["PMKSA-CACHE-ADDED"], timeout=5)
+ if ev is None:
+ raise Exception("OWE PMKSA not created")
+ ev = dev[0].wait_event(["WPA: IE in 3/4 msg does not match with IE in Beacon/ProbeResp"],
+ timeout=5)
+ if ev is None:
+ raise Exception("RSNE mismatch not reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=5)
+ dev[0].request("REMOVE_NETWORK all")
+ if ev is None:
+ raise Exception("No disconnection seen")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection")
+ if "reason=17 locally_generated=1" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+def test_owe_unsupported_group(dev, apdev):
+ """Opportunistic Wireless Encryption and unsupported group"""
+ try:
+ run_owe_unsupported_group(dev, apdev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def test_owe_unsupported_group_connect_cmd(dev, apdev):
+ """Opportunistic Wireless Encryption and unsupported group using cfg80211 connect command"""
+ try:
+ wpas = None
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ run_owe_unsupported_group([wpas], apdev)
+ finally:
+ if wpas:
+ wpas.request("VENDOR_ELEM_REMOVE 13 *")
+
+def run_owe_unsupported_group(dev, apdev):
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ # Override OWE Dh Parameters element with a payload that uses invalid group
+ # 0 (and actual group 19 data) to make the AP reject this with the specific
+ # status code 77.
+ dev[0].request("VENDOR_ELEM_ADD 13 ff23200000783590fb7440e03d5b3b33911f86affdcc6b4411b707846ac4ff08ddc8831ccd")
+
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("owe", key_mgmt="OWE", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Association not rejected")
+ if "status_code=77" not in ev:
+ raise Exception("Unexpected rejection reason: " + ev)
+
+def test_owe_limited_group_set(dev, apdev):
+ """Opportunistic Wireless Encryption and limited group set"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP",
+ "owe_groups": "20 21"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("owe", key_mgmt="OWE", owe_group="19", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Association not rejected")
+ if "status_code=77" not in ev:
+ raise Exception("Unexpected rejection reason: " + ev)
+ dev[0].dump_monitor()
+
+ for group in [20, 21]:
+ dev[0].connect("owe", key_mgmt="OWE", owe_group=str(group))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def test_owe_limited_group_set_pmf(dev, apdev, params):
+ """Opportunistic Wireless Encryption and limited group set (PMF)"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ pcapng = os.path.join(params['logdir'], "hwsim0.pcapng")
+
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP",
+ "owe_groups": "21"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("owe", key_mgmt="OWE", owe_group="19", ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Association not rejected")
+ if "status_code=77" not in ev:
+ raise Exception("Unexpected rejection reason: " + ev)
+ dev[0].dump_monitor()
+
+ dev[0].connect("owe", key_mgmt="OWE", owe_group="20", ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Association not rejected (2)")
+ if "status_code=77" not in ev:
+ raise Exception("Unexpected rejection reason (2): " + ev)
+ dev[0].dump_monitor()
+
+ dev[0].connect("owe", key_mgmt="OWE", owe_group="21", ieee80211w="2",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ out = run_tshark(pcapng,
+ "wlan.fc.type_subtype == 1",
+ display=['wlan_mgt.fixed.status_code'])
+ status = out.splitlines()
+ logger.info("Association Response frame status codes: " + str(status))
+ if len(status) != 3:
+ raise Exception("Unexpected number of Association Response frames")
+ if (int(status[0], base=0) != 77 or int(status[1], base=0) != 77 or
+ int(status[2], base=0) != 0):
+ raise Exception("Unexpected Association Response frame status code")
+
+def test_owe_group_negotiation(dev, apdev):
+ """Opportunistic Wireless Encryption and group negotiation"""
+ run_owe_group_negotiation(dev[0], apdev)
+
+def test_owe_group_negotiation_connect_cmd(dev, apdev):
+ """Opportunistic Wireless Encryption and group negotiation (connect command)"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ run_owe_group_negotiation(wpas, apdev)
+
+def run_owe_group_negotiation(dev, apdev):
+ if "OWE" not in dev.get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP",
+ "owe_groups": "21"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev.scan_for_bss(bssid, freq="2412")
+ dev.connect("owe", key_mgmt="OWE")
+
+def test_owe_assoc_reject(dev, apdev):
+ """Opportunistic Wireless Encryption association rejection handling"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ params = {"ssid": "owe",
+ "require_ht": "1",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP",
+ "owe_groups": "19"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ # First, reject two associations with HT-required (i.e., not OWE related)
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("owe", key_mgmt="OWE", ieee80211w="2",
+ disable_ht="1", scan_freq="2412", wait_connect=False)
+ for i in range(0, 2):
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("Association rejection not reported")
+
+ # Then, verify that STA tries OWE with the default group (19) on the next
+ # attempt instead of having moved to testing another group.
+ hapd.set("require_ht", "0")
+ for i in range(0, 2):
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Association result not reported")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ break
+ if "status_code=77" in ev:
+ raise Exception("Unexpected unsupport group rejection")
+ if "CTRL-EVENT-CONNECTED" not in ev:
+ raise Exception("Did not connect successfully")
+
+def test_owe_local_errors(dev, apdev):
+ """Opportunistic Wireless Encryption - local errors on supplicant"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ tests = [(1, "crypto_ecdh_init;owe_build_assoc_req"),
+ (1, "crypto_ecdh_get_pubkey;owe_build_assoc_req"),
+ (1, "wpabuf_alloc;owe_build_assoc_req")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("owe", key_mgmt="OWE", owe_group="20",
+ ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = [(1, "crypto_ecdh_set_peerkey;owe_process_assoc_resp"),
+ (1, "crypto_ecdh_get_pubkey;owe_process_assoc_resp"),
+ (1, "wpabuf_alloc;=owe_process_assoc_resp")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("owe", key_mgmt="OWE", owe_group="20",
+ ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ tests = [(1, "hmac_sha256;owe_process_assoc_resp", 19),
+ (1, "hmac_sha256_kdf;owe_process_assoc_resp", 19),
+ (1, "hmac_sha384;owe_process_assoc_resp", 20),
+ (1, "hmac_sha384_kdf;owe_process_assoc_resp", 20),
+ (1, "hmac_sha512;owe_process_assoc_resp", 21),
+ (1, "hmac_sha512_kdf;owe_process_assoc_resp", 21)]
+ for count, func, group in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("owe", key_mgmt="OWE", owe_group=str(group),
+ ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ dev[0].connect("owe", key_mgmt="OWE", owe_group="18",
+ ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["SME: Trying to authenticate"], timeout=5)
+ if ev is None:
+ raise Exception("No authentication attempt")
+ time.sleep(0.5)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def hapd_auth(hapd):
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame not received")
+
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = struct.pack('<HHH', 0, 2, 0)
+ hapd.mgmt_tx(resp)
+
+def hapd_assoc(hapd, extra):
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out")
+ if req['subtype'] == 0:
+ break
+ req = None
+ if not req:
+ raise Exception("Association Request frame not received")
+
+ resp = {}
+ resp['fc'] = 0x0010
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ payload = struct.pack('<HHH', 0x0411, 0, 0xc001)
+ payload += binascii.unhexlify("010882848b960c121824")
+ resp['payload'] = payload + extra
+ hapd.mgmt_tx(resp)
+
+def test_owe_invalid_assoc_resp(dev, apdev):
+ """Opportunistic Wireless Encryption - invalid Association Response frame"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ # OWE: No Diffie-Hellman Parameter element found in Association Response frame
+ tests = [b'']
+ # No room for group --> no DH Params
+ tests += [binascii.unhexlify('ff0120')]
+ # OWE: Unexpected Diffie-Hellman group in response: 18
+ tests += [binascii.unhexlify('ff03201200')]
+ # OWE: Invalid peer DH public key
+ tests += [binascii.unhexlify('ff23201300' + 31*'00' + '01')]
+ # OWE: Invalid peer DH public key
+ tests += [binascii.unhexlify('ff24201300' + 33*'ee')]
+ for extra in tests:
+ dev[0].connect("owe", key_mgmt="OWE", owe_group="19", ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+ hapd_auth(hapd)
+ hapd_assoc(hapd, extra)
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ # OWE: Empty public key (this ends up getting padded to a valid point)
+ dev[0].connect("owe", key_mgmt="OWE", owe_group="19", ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+ hapd_auth(hapd)
+ hapd_assoc(hapd, binascii.unhexlify('ff03201300'))
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED", "PMKSA-CACHE-ADDED"],
+ timeout=5)
+ if ev is None:
+ raise Exception("No result reported for empty public key")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def start_owe(dev, apdev, workaround=0):
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "OWE",
+ "owe_ptk_workaround": str(workaround),
+ "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].scan_for_bss(hapd.own_addr(), freq="2412")
+ return hapd
+
+def owe_check_ok(dev, hapd, owe_group, owe_ptk_workaround):
+ dev.connect("owe", key_mgmt="OWE", ieee80211w="2",
+ owe_group=owe_group, owe_ptk_workaround=owe_ptk_workaround,
+ scan_freq="2412")
+ hapd.wait_sta()
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_owe_ptk_workaround_ap(dev, apdev):
+ """Opportunistic Wireless Encryption - AP using PTK workaround"""
+ hapd = start_owe(dev, apdev, workaround=1)
+ for group, workaround in [(19, 0), (20, 0), (21, 0),
+ (19, 1), (20, 1), (21, 1)]:
+ owe_check_ok(dev[0], hapd, str(group), str(workaround))
+
+def test_owe_ptk_hash(dev, apdev):
+ """Opportunistic Wireless Encryption - PTK derivation hash alg"""
+ hapd = start_owe(dev, apdev)
+ for group, workaround in [(19, 0), (20, 0), (21, 0), (19, 1)]:
+ owe_check_ok(dev[0], hapd, str(group), str(workaround))
+
+ for group in [20, 21]:
+ dev[0].connect("owe", key_mgmt="OWE", ieee80211w="2",
+ owe_group=str(group), owe_ptk_workaround="1",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["PMKSA-CACHE-ADDED"], timeout=10)
+ if ev is None:
+ raise Exception("Could not complete OWE association")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("Unknown connection result")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ dev[0].request("REMOVE_NETWORK all")
+ ev = dev[0].wait_event(["PMKSA-CACHE-REMOVED"], timeout=5)
+ if ev is None:
+ raise Exception("No PMKSA cache removal event seen")
+ dev[0].dump_monitor()
+
+def test_owe_transition_mode_disable(dev, apdev):
+ """Opportunistic Wireless Encryption transition mode disable"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ dev[0].flush_scan_cache()
+ params = {"ssid": "owe-random",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "rsn_pairwise": "CCMP",
+ "ieee80211w": "2",
+ "transition_disable": '0x08',
+ "owe_transition_bssid": apdev[1]['bssid'],
+ "owe_transition_ssid": '"owe-test"',
+ "ignore_broadcast_ssid": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ params = {"ssid": "owe-test",
+ "owe_transition_bssid": apdev[0]['bssid'],
+ "owe_transition_ssid": '"owe-random"'}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+
+ id = dev[0].connect("owe-test", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="2412")
+
+ ev = dev[0].wait_event(["TRANSITION-DISABLE"], timeout=1)
+ if ev is None:
+ raise Exception("Transition disable not indicated")
+ if ev.split(' ')[1] != "08":
+ raise Exception("Unexpected transition disable bitmap: " + ev)
+
+ val = dev[0].get_network(id, "owe_only")
+ if val != "1":
+ raise Exception("Unexpected owe_only value: " + val)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
diff --git a/contrib/wpa/tests/hwsim/test_p2p_autogo.py b/contrib/wpa/tests/hwsim/test_p2p_autogo.py
new file mode 100644
index 000000000000..91d68eaf2836
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_autogo.py
@@ -0,0 +1,936 @@
+# P2P autonomous GO test cases
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+import hostapd
+import hwsim_utils
+import utils
+from utils import HwsimSkip
+from wlantest import Wlantest
+from wpasupplicant import WpaSupplicant
+from p2p_utils import *
+from test_p2p_messages import mgmt_tx, parse_p2p_public_action
+
+def test_autogo(dev):
+ """P2P autonomous GO and client joining group"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ res = autogo(dev[0])
+ if "p2p-wlan" in res['ifname']:
+ raise Exception("Unexpected group interface name on GO")
+ res = connect_cli(dev[0], dev[1])
+ if "p2p-wlan" in res['ifname']:
+ raise Exception("Unexpected group interface name on client")
+ bss = dev[1].get_bss("p2p_dev_addr=" + addr0, res['ifname'])
+ if not bss or bss['bssid'] != dev[0].p2p_interface_addr():
+ raise Exception("Unexpected BSSID in the BSS entry for the GO")
+ id = bss['id']
+ bss = dev[1].get_bss("ID-" + id, res['ifname'])
+ if not bss or bss['id'] != id:
+ raise Exception("Could not find BSS entry based on id")
+ res = dev[1].group_request("BSS RANGE=" + id + "- MASK=0x1")
+ if "id=" + id not in res:
+ raise Exception("Could not find BSS entry based on id range")
+
+ res = dev[1].request("SCAN_RESULTS")
+ if "[P2P]" not in res:
+ raise Exception("P2P flag missing from scan results: " + res)
+
+ # Presence request to increase testing coverage
+ if "FAIL" not in dev[1].group_request("P2P_PRESENCE_REQ 30000"):
+ raise Exception("Invald P2P_PRESENCE_REQ accepted")
+ if "FAIL" not in dev[1].group_request("P2P_PRESENCE_REQ 30000 102400 30001"):
+ raise Exception("Invald P2P_PRESENCE_REQ accepted")
+ if "FAIL" in dev[1].group_request("P2P_PRESENCE_REQ 30000 102400"):
+ raise Exception("Could not send presence request")
+ ev = dev[1].wait_group_event(["P2P-PRESENCE-RESPONSE"], 10)
+ if ev is None:
+ raise Exception("Timeout while waiting for Presence Response")
+ if "FAIL" in dev[1].group_request("P2P_PRESENCE_REQ 30000 102400 20000 102400"):
+ raise Exception("Could not send presence request")
+ ev = dev[1].wait_group_event(["P2P-PRESENCE-RESPONSE"])
+ if ev is None:
+ raise Exception("Timeout while waiting for Presence Response")
+ if "FAIL" in dev[1].group_request("P2P_PRESENCE_REQ"):
+ raise Exception("Could not send presence request")
+ ev = dev[1].wait_group_event(["P2P-PRESENCE-RESPONSE"])
+ if ev is None:
+ raise Exception("Timeout while waiting for Presence Response")
+
+ if not dev[2].discover_peer(addr0):
+ raise Exception("Could not discover GO")
+ dev[0].dump_monitor()
+ dev[2].global_request("P2P_PROV_DISC " + addr0 + " display join")
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=10)
+ if ev is None:
+ raise Exception("GO did not report P2P-PROV-DISC-SHOW-PIN")
+ if "p2p_dev_addr=" + addr2 not in ev:
+ raise Exception("Unexpected P2P Device Address in event: " + ev)
+ if "group=" + dev[0].group_ifname not in ev:
+ raise Exception("Unexpected group interface in event: " + ev)
+ ev = dev[2].wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DISC-ENTER-PIN not reported")
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+def test_autogo2(dev):
+ """P2P autonomous GO with a separate group interface and client joining group"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ res = autogo(dev[0], freq=2437)
+ if "p2p-wlan" not in res['ifname']:
+ raise Exception("Unexpected group interface name on GO")
+ if res['ifname'] not in utils.get_ifnames():
+ raise Exception("Could not find group interface netdev")
+ connect_cli(dev[0], dev[1], social=True, freq=2437)
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ if res['ifname'] in utils.get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+
+def test_autogo3(dev):
+ """P2P autonomous GO and client with a separate group interface joining group"""
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ autogo(dev[0], freq=2462)
+ res = connect_cli(dev[0], dev[1], social=True, freq=2462)
+ if "p2p-wlan" not in res['ifname']:
+ raise Exception("Unexpected group interface name on client")
+ if res['ifname'] not in utils.get_ifnames():
+ raise Exception("Could not find group interface netdev")
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ dev[1].ping()
+ if res['ifname'] in utils.get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+
+def test_autogo4(dev):
+ """P2P autonomous GO and client joining group (both with a separate group interface)"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ res1 = autogo(dev[0], freq=2412)
+ res2 = connect_cli(dev[0], dev[1], social=True, freq=2412)
+ if "p2p-wlan" not in res1['ifname']:
+ raise Exception("Unexpected group interface name on GO")
+ if "p2p-wlan" not in res2['ifname']:
+ raise Exception("Unexpected group interface name on client")
+ ifnames = utils.get_ifnames()
+ if res1['ifname'] not in ifnames:
+ raise Exception("Could not find GO group interface netdev")
+ if res2['ifname'] not in ifnames:
+ raise Exception("Could not find client group interface netdev")
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ dev[1].ping()
+ ifnames = utils.get_ifnames()
+ if res1['ifname'] in ifnames:
+ raise Exception("GO group interface netdev was not removed")
+ if res2['ifname'] in ifnames:
+ raise Exception("Client group interface netdev was not removed")
+
+def test_autogo_m2d(dev):
+ """P2P autonomous GO and clients not authorized"""
+ autogo(dev[0], freq=2412)
+ go_addr = dev[0].p2p_dev_addr()
+
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ if not dev[1].discover_peer(go_addr, social=True):
+ raise Exception("GO " + go_addr + " not found")
+ dev[1].dump_monitor()
+
+ if not dev[2].discover_peer(go_addr, social=True):
+ raise Exception("GO " + go_addr + " not found")
+ dev[2].dump_monitor()
+
+ logger.info("Trying to join the group when GO has not authorized the client")
+ pin = dev[1].wps_read_pin()
+ cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
+ if "OK" not in dev[1].global_request(cmd):
+ raise Exception("P2P_CONNECT join failed")
+
+ pin = dev[2].wps_read_pin()
+ cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
+ if "OK" not in dev[2].global_request(cmd):
+ raise Exception("P2P_CONNECT join failed")
+
+ ev = dev[1].wait_global_event(["WPS-M2D"], timeout=16)
+ if ev is None:
+ raise Exception("No global M2D event")
+ ifaces = dev[1].request("INTERFACES").splitlines()
+ iface = ifaces[0] if "p2p-wlan" in ifaces[0] else ifaces[1]
+ wpas = WpaSupplicant(ifname=iface)
+ ev = wpas.wait_event(["WPS-M2D"], timeout=10)
+ if ev is None:
+ raise Exception("No M2D event on group interface")
+
+ ev = dev[2].wait_global_event(["WPS-M2D"], timeout=10)
+ if ev is None:
+ raise Exception("No global M2D event (2)")
+ ev = dev[2].wait_event(["WPS-M2D"], timeout=10)
+ if ev is None:
+ raise Exception("No M2D event on group interface (2)")
+
+@remote_compatible
+def test_autogo_fail(dev):
+ """P2P autonomous GO and incorrect PIN"""
+ autogo(dev[0], freq=2412)
+ go_addr = dev[0].p2p_dev_addr()
+ dev[0].p2p_go_authorize_client("00000000")
+
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ if not dev[1].discover_peer(go_addr, social=True):
+ raise Exception("GO " + go_addr + " not found")
+ dev[1].dump_monitor()
+
+ logger.info("Trying to join the group when GO has not authorized the client")
+ pin = dev[1].wps_read_pin()
+ cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
+ if "OK" not in dev[1].global_request(cmd):
+ raise Exception("P2P_CONNECT join failed")
+
+ ev = dev[1].wait_global_event(["WPS-FAIL"], timeout=10)
+ if ev is None:
+ raise Exception("No global WPS-FAIL event")
+
+def test_autogo_2cli(dev):
+ """P2P autonomous GO and two clients joining group"""
+ autogo(dev[0], freq=2412)
+ connect_cli(dev[0], dev[1], social=True, freq=2412)
+ connect_cli(dev[0], dev[2], social=True, freq=2412)
+ hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+ dev[0].global_request("P2P_REMOVE_CLIENT " + dev[1].p2p_dev_addr())
+ dev[1].wait_go_ending_session()
+ dev[0].global_request("P2P_REMOVE_CLIENT iface=" + dev[2].p2p_interface_addr())
+ dev[2].wait_go_ending_session()
+ if "FAIL" not in dev[0].global_request("P2P_REMOVE_CLIENT foo"):
+ raise Exception("Invalid P2P_REMOVE_CLIENT command accepted")
+ dev[0].remove_group()
+
+def test_autogo_pbc(dev):
+ """P2P autonomous GO and PBC"""
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ autogo(dev[0], freq=2412)
+ if "FAIL" not in dev[0].group_request("WPS_PBC p2p_dev_addr=00:11:22:33:44"):
+ raise Exception("Invalid WPS_PBC succeeded")
+ if "OK" not in dev[0].group_request("WPS_PBC p2p_dev_addr=" + dev[1].p2p_dev_addr()):
+ raise Exception("WPS_PBC failed")
+ dev[2].p2p_connect_group(dev[0].p2p_dev_addr(), "pbc", timeout=0,
+ social=True)
+ ev = dev[2].wait_global_event(["WPS-M2D"], timeout=15)
+ if ev is None:
+ raise Exception("WPS-M2D not reported")
+ if "config_error=12" not in ev:
+ raise Exception("Unexpected config_error: " + ev)
+ dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), "pbc", timeout=15,
+ social=True)
+
+def test_autogo_pbc_session_overlap(dev, apdev):
+ """P2P autonomous GO and PBC session overlap"""
+ params = {"ssid": "wps", "eap_server": "1", "wps_state": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("WPS_PBC")
+ bssid = hapd.own_addr()
+ time.sleep(0.1)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[1].scan_for_bss(bssid, freq=2412)
+
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ autogo(dev[0], freq=2412)
+ if "OK" not in dev[0].group_request("WPS_PBC p2p_dev_addr=" + dev[1].p2p_dev_addr()):
+ raise Exception("WPS_PBC failed")
+ dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), "pbc", timeout=15,
+ social=True)
+ hapd.disable()
+ remove_group(dev[0], dev[1])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_autogo_tdls(dev):
+ """P2P autonomous GO and two clients using TDLS"""
+ go = dev[0]
+ logger.info("Start autonomous GO with fixed parameters " + go.ifname)
+ id = go.add_network()
+ go.set_network_quoted(id, "ssid", "DIRECT-tdls")
+ go.set_network_quoted(id, "psk", "12345678")
+ go.set_network(id, "mode", "3")
+ go.set_network(id, "disabled", "2")
+ res = go.p2p_start_go(persistent=id, freq="2462")
+ logger.debug("res: " + str(res))
+ Wlantest.setup(go, True)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ connect_cli(go, dev[1], social=True, freq=2462)
+ connect_cli(go, dev[2], social=True, freq=2462)
+ hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+ bssid = dev[0].p2p_interface_addr()
+ addr1 = dev[1].p2p_interface_addr()
+ addr2 = dev[2].p2p_interface_addr()
+ dev[1].tdls_setup(addr2)
+ time.sleep(1)
+ hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+ conf = wt.get_tdls_counter("setup_conf_ok", bssid, addr1, addr2)
+ if conf == 0:
+ raise Exception("No TDLS Setup Confirm (success) seen")
+ dl = wt.get_tdls_counter("valid_direct_link", bssid, addr1, addr2)
+ if dl == 0:
+ raise Exception("No valid frames through direct link")
+ wt.tdls_clear(bssid, addr1, addr2)
+ dev[1].tdls_teardown(addr2)
+ time.sleep(1)
+ teardown = wt.get_tdls_counter("teardown", bssid, addr1, addr2)
+ if teardown == 0:
+ raise Exception("No TDLS Setup Teardown seen")
+ wt.tdls_clear(bssid, addr1, addr2)
+ hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+ ap_path = wt.get_tdls_counter("valid_ap_path", bssid, addr1, addr2)
+ if ap_path == 0:
+ raise Exception("No valid frames via AP path")
+ direct_link = wt.get_tdls_counter("valid_direct_link", bssid, addr1, addr2)
+ if direct_link > 0:
+ raise Exception("Unexpected frames through direct link")
+ idirect_link = wt.get_tdls_counter("invalid_direct_link", bssid, addr1,
+ addr2)
+ if idirect_link > 0:
+ raise Exception("Unexpected frames through direct link (invalid)")
+ dev[2].remove_group()
+ dev[1].remove_group()
+ dev[0].remove_group()
+
+def test_autogo_legacy(dev):
+ """P2P autonomous GO and legacy clients"""
+ res = autogo(dev[0], freq=2462)
+ if dev[0].get_group_status_field("passphrase", extra="WPS") != res['passphrase']:
+ raise Exception("passphrase mismatch")
+ if dev[0].group_request("P2P_GET_PASSPHRASE") != res['passphrase']:
+ raise Exception("passphrase mismatch(2)")
+
+ logger.info("Connect P2P client")
+ connect_cli(dev[0], dev[1], social=True, freq=2462)
+
+ if "FAIL" not in dev[1].request("P2P_GET_PASSPHRASE"):
+ raise Exception("P2P_GET_PASSPHRASE succeeded on P2P Client")
+
+ logger.info("Connect legacy WPS client")
+ pin = dev[2].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[2].request("P2P_SET disabled 1")
+ dev[2].dump_monitor()
+ dev[2].request("WPS_PIN any " + pin)
+ dev[2].wait_connected(timeout=30)
+ status = dev[2].get_status()
+ if status['wpa_state'] != 'COMPLETED':
+ raise Exception("Not fully connected")
+ hwsim_utils.test_connectivity_p2p_sta(dev[1], dev[2])
+ dev[2].request("DISCONNECT")
+
+ logger.info("Connect legacy non-WPS client")
+ dev[2].request("FLUSH")
+ dev[2].request("P2P_SET disabled 1")
+ dev[2].connect(ssid=res['ssid'], psk=res['passphrase'], proto='RSN',
+ key_mgmt='WPA-PSK', pairwise='CCMP', group='CCMP',
+ scan_freq=res['freq'])
+ hwsim_utils.test_connectivity_p2p_sta(dev[1], dev[2])
+ dev[2].request("DISCONNECT")
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+def test_autogo_chan_switch(dev):
+ """P2P autonomous GO switching channels"""
+ run_autogo_chan_switch(dev)
+
+def run_autogo_chan_switch(dev):
+ autogo(dev[0], freq=2417)
+ connect_cli(dev[0], dev[1], freq=2417)
+ res = dev[0].group_request("CHAN_SWITCH 5 2422")
+ if "FAIL" in res:
+ # for now, skip test since mac80211_hwsim support is not yet widely
+ # deployed
+ raise HwsimSkip("Assume mac80211_hwsim did not support channel switching")
+ ev = dev[0].wait_group_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=2422" not in ev:
+ raise Exception("Unexpected cahnnel in CSA finished event")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+def test_autogo_chan_switch_group_iface(dev):
+ """P2P autonomous GO switching channels (separate group interface)"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ run_autogo_chan_switch(dev)
+
+@remote_compatible
+def test_autogo_extra_cred(dev):
+ """P2P autonomous GO sending two WPS credentials"""
+ if "FAIL" in dev[0].request("SET wps_testing_dummy_cred 1"):
+ raise Exception("Failed to enable test mode")
+ autogo(dev[0], freq=2412)
+ connect_cli(dev[0], dev[1], social=True, freq=2412)
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+def test_autogo_ifdown(dev):
+ """P2P autonomous GO and external ifdown"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ res = autogo(wpas)
+ wpas.dump_monitor()
+ wpas.interface_remove("wlan5")
+ wpas.interface_add("wlan5")
+ res = autogo(wpas)
+ wpas.dump_monitor()
+ subprocess.call(['ifconfig', res['ifname'], 'down'])
+ ev = wpas.wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+ if ev is None:
+ raise Exception("Group removal not reported")
+ if res['ifname'] not in ev:
+ raise Exception("Unexpected group removal event: " + ev)
+
+@remote_compatible
+def test_autogo_start_during_scan(dev):
+ """P2P autonomous GO started during ongoing manual scan"""
+ try:
+ # use autoscan to set scan_req = MANUAL_SCAN_REQ
+ if "OK" not in dev[0].request("AUTOSCAN periodic:1"):
+ raise Exception("Failed to set autoscan")
+ autogo(dev[0], freq=2462)
+ connect_cli(dev[0], dev[1], social=True, freq=2462)
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ finally:
+ dev[0].request("AUTOSCAN ")
+
+def test_autogo_passphrase_len(dev):
+ """P2P autonomous GO and longer passphrase"""
+ try:
+ if "OK" not in dev[0].request("SET p2p_passphrase_len 13"):
+ raise Exception("Failed to set passphrase length")
+ res = autogo(dev[0], freq=2412)
+ if len(res['passphrase']) != 13:
+ raise Exception("Unexpected passphrase length")
+ if dev[0].get_group_status_field("passphrase", extra="WPS") != res['passphrase']:
+ raise Exception("passphrase mismatch")
+
+ logger.info("Connect P2P client")
+ connect_cli(dev[0], dev[1], social=True, freq=2412)
+
+ logger.info("Connect legacy WPS client")
+ pin = dev[2].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[2].request("P2P_SET disabled 1")
+ dev[2].dump_monitor()
+ dev[2].request("WPS_PIN any " + pin)
+ dev[2].wait_connected(timeout=30)
+ status = dev[2].get_status()
+ if status['wpa_state'] != 'COMPLETED':
+ raise Exception("Not fully connected")
+ dev[2].request("DISCONNECT")
+
+ logger.info("Connect legacy non-WPS client")
+ dev[2].request("FLUSH")
+ dev[2].request("P2P_SET disabled 1")
+ dev[2].connect(ssid=res['ssid'], psk=res['passphrase'], proto='RSN',
+ key_mgmt='WPA-PSK', pairwise='CCMP', group='CCMP',
+ scan_freq=res['freq'])
+ hwsim_utils.test_connectivity_p2p_sta(dev[1], dev[2])
+ dev[2].request("DISCONNECT")
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ finally:
+ dev[0].request("SET p2p_passphrase_len 8")
+
+@remote_compatible
+def test_autogo_bridge(dev):
+ """P2P autonomous GO in a bridge"""
+ try:
+ # use autoscan to set scan_req = MANUAL_SCAN_REQ
+ if "OK" not in dev[0].request("AUTOSCAN periodic:1"):
+ raise Exception("Failed to set autoscan")
+ autogo(dev[0])
+ ifname = dev[0].get_group_ifname()
+ dev[0].cmd_execute(['brctl', 'addbr', 'p2p-br0'])
+ dev[0].cmd_execute(['brctl', 'setfd', 'p2p-br0', '0'])
+ dev[0].cmd_execute(['brctl', 'addif', 'p2p-br0', ifname])
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'p2p-br0', 'up'])
+ time.sleep(0.1)
+ dev[0].cmd_execute(['brctl', 'delif', 'p2p-br0', ifname])
+ time.sleep(0.1)
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'p2p-br0', 'down'])
+ time.sleep(0.1)
+ dev[0].cmd_execute(['brctl', 'delbr', 'p2p-br0'])
+ ev = dev[0].wait_global_event(["P2P-GROUP-REMOVED"], timeout=1)
+ if ev is not None:
+ raise Exception("P2P group removed unexpectedly")
+ if dev[0].get_group_status_field('wpa_state') != "COMPLETED":
+ raise Exception("Unexpected wpa_state")
+ dev[0].remove_group()
+ finally:
+ dev[0].request("AUTOSCAN ")
+ dev[0].cmd_execute(['brctl', 'delif', 'p2p-br0', ifname,
+ '2>', '/dev/null'], shell=True)
+ dev[0].cmd_execute(['ip', 'link', 'set', 'dev', 'p2p-br0', 'down',
+ '2>', '/dev/null'], shell=True)
+ dev[0].cmd_execute(['brctl', 'delbr', 'p2p-br0', '2>', '/dev/null'],
+ shell=True)
+
+@remote_compatible
+def test_presence_req_on_group_interface(dev):
+ """P2P_PRESENCE_REQ on group interface"""
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ res = autogo(dev[0], freq=2437)
+ res = connect_cli(dev[0], dev[1], social=True, freq=2437)
+ if "FAIL" in dev[1].group_request("P2P_PRESENCE_REQ 30000 102400"):
+ raise Exception("Could not send presence request")
+ ev = dev[1].wait_group_event(["P2P-PRESENCE-RESPONSE"])
+ if ev is None:
+ raise Exception("Timeout while waiting for Presence Response")
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+def test_autogo_join_auto_go_not_found(dev):
+ """P2P_CONNECT-auto not finding GO"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.request("P2P_SET listen_channel 1")
+ wpas.global_request("SET p2p_no_group_iface 0")
+ autogo(wpas, freq=2412)
+ addr = wpas.p2p_dev_addr()
+ bssid = wpas.p2p_interface_addr()
+ wpas.dump_monitor()
+
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ dev[1].scan_for_bss(bssid, freq=2412)
+ # This makes the GO not show up in the scan iteration following the
+ # P2P_CONNECT command by stopping beaconing and handling Probe Request
+ # frames externally (but not really replying to them). P2P listen mode is
+ # needed to keep the GO listening on the operating channel for the PD
+ # exchange.
+ if "OK" not in wpas.group_request("STOP_AP"):
+ raise Exception("STOP_AP failed")
+ wpas.dump_monitor()
+ wpas.group_request("SET ext_mgmt_frame_handling 1")
+ wpas.p2p_listen()
+ wpas.dump_monitor()
+ time.sleep(0.02)
+ dev[1].global_request("P2P_CONNECT " + addr + " pbc auto")
+
+ ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG-ENABLED"], 15)
+ wpas.dump_monitor()
+ if ev is None:
+ raise Exception("Could not trigger old-scan-only case")
+ return
+
+ ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG"], 15)
+ wpas.remove_group()
+ if ev is None:
+ raise Exception("Fallback to GO Negotiation not seen")
+ if "reason=GO-not-found" not in ev:
+ raise Exception("Unexpected reason for fallback: " + ev)
+ wpas.dump_monitor()
+
+def test_autogo_join_auto(dev):
+ """P2P_CONNECT-auto joining a group"""
+ autogo(dev[0])
+ addr = dev[0].p2p_dev_addr()
+ if "OK" not in dev[1].global_request("P2P_CONNECT " + addr + " pbc auto"):
+ raise Exception("P2P_CONNECT failed")
+
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-PBC-REQ"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on P2P-PROV-DISC-PBC-REQ")
+ if "group=" + dev[0].group_ifname not in ev:
+ raise Exception("Unexpected PD event contents: " + ev)
+ dev[0].group_request("WPS_PBC")
+
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Joining the group timed out")
+ dev[1].group_form_result(ev)
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ dev[1].flush_scan_cache()
+
+@remote_compatible
+def test_autogo_join_auto_go_neg(dev):
+ """P2P_CONNECT-auto fallback to GO Neg"""
+ dev[1].flush_scan_cache()
+ dev[0].p2p_listen()
+ addr = dev[0].p2p_dev_addr()
+ if not dev[1].discover_peer(addr, social=True):
+ raise Exception("Peer not found")
+ dev[1].p2p_stop_find()
+ if "OK" not in dev[1].global_request("P2P_CONNECT " + addr + " pbc auto"):
+ raise Exception("P2P_CONNECT failed")
+
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on P2P-GO-NEG-REQUEST")
+ peer = ev.split(' ')[1]
+ dev[0].p2p_go_neg_init(peer, None, "pbc", timeout=15, go_intent=15)
+
+ ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG"], timeout=1)
+ if ev is None:
+ raise Exception("No P2P-FALLBACK-TO-GO-NEG event seen")
+ if "P2P-FALLBACK-TO-GO-NEG-ENABLED" in ev:
+ ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG"], timeout=1)
+ if ev is None:
+ raise Exception("No P2P-FALLBACK-TO-GO-NEG event seen")
+ if "reason=peer-not-running-GO" not in ev:
+ raise Exception("Unexpected reason: " + ev)
+
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Joining the group timed out")
+ dev[1].group_form_result(ev)
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ dev[1].flush_scan_cache()
+
+@remote_compatible
+def test_autogo_join_auto_go_neg_after_seeing_go(dev):
+ """P2P_CONNECT-auto fallback to GO Neg after seeing GO"""
+ autogo(dev[0], freq=2412)
+ addr = dev[0].p2p_dev_addr()
+ bssid = dev[0].p2p_interface_addr()
+ dev[1].scan_for_bss(bssid, freq=2412)
+ dev[0].remove_group()
+ dev[0].p2p_listen()
+
+ if "OK" not in dev[1].global_request("P2P_CONNECT " + addr + " pbc auto"):
+ raise Exception("P2P_CONNECT failed")
+
+ ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG-ENABLED"],
+ timeout=15)
+ if ev is None:
+ raise Exception("No P2P-FALLBACK-TO-GO-NEG-ENABLED event seen")
+
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on P2P-GO-NEG-REQUEST")
+ peer = ev.split(' ')[1]
+ dev[0].p2p_go_neg_init(peer, None, "pbc", timeout=15, go_intent=15)
+
+ ev = dev[1].wait_global_event(["P2P-FALLBACK-TO-GO-NEG"], timeout=1)
+ if ev is None:
+ raise Exception("No P2P-FALLBACK-TO-GO-NEG event seen")
+ if "reason=no-ACK-to-PD-Req" not in ev and "reason=PD-failed" not in ev:
+ raise Exception("Unexpected reason: " + ev)
+
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Joining the group timed out")
+ dev[1].group_form_result(ev)
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ dev[1].flush_scan_cache()
+
+def test_go_search_non_social(dev):
+ """P2P_FIND with freq parameter to scan a single channel"""
+ addr0 = dev[0].p2p_dev_addr()
+ autogo(dev[0], freq=2422)
+ dev[1].p2p_find(freq=2422)
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=3.5)
+ if ev is None:
+ dev[1].p2p_stop_find()
+ dev[1].p2p_find(freq=2422)
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=3.5)
+ if ev is None:
+ raise Exception("Did not find GO quickly enough")
+ dev[2].p2p_listen()
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Did not find peer")
+ dev[2].p2p_stop_find()
+ dev[1].p2p_stop_find()
+ dev[0].remove_group()
+
+def test_go_search_non_social2(dev):
+ """P2P_FIND with freq parameter to scan a single channel (2)"""
+ addr0 = dev[0].p2p_dev_addr()
+ dev[1].p2p_find(freq=2422)
+ # Wait for the first p2p_find scan round to complete before starting GO
+ time.sleep(1)
+ autogo(dev[0], freq=2422)
+ # Verify that p2p_find is still scanning the specified frequency
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ dev[1].p2p_stop_find()
+ raise Exception("Did not find GO quickly enough")
+ # Verify that p2p_find is scanning the social channels
+ dev[2].p2p_listen()
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Did not find peer")
+ dev[2].p2p_stop_find()
+ dev[1].p2p_stop_find()
+ dev[0].remove_group()
+ dev[1].dump_monitor()
+
+ # Verify that social channel as the specific channel works
+ dev[1].p2p_find(freq=2412)
+ time.sleep(0.5)
+ dev[2].p2p_listen()
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Did not find peer (2)")
+
+def test_autogo_many(dev):
+ """P2P autonomous GO with large number of GO instances"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ for i in range(100):
+ if "OK" not in dev[0].global_request("P2P_GROUP_ADD freq=2412"):
+ logger.info("Was able to add %d groups" % i)
+ if i < 5:
+ raise Exception("P2P_GROUP_ADD failed")
+ stop_ev = dev[0].wait_global_event(["P2P-GROUP-REMOVE"], timeout=1)
+ if stop_ev is not None:
+ raise Exception("Unexpected P2P-GROUP-REMOVE event")
+ break
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("GO start up timed out")
+ dev[0].group_form_result(ev)
+
+ for i in dev[0].global_request("INTERFACES").splitlines():
+ dev[0].request("P2P_GROUP_REMOVE " + i)
+ dev[0].dump_monitor()
+ dev[0].request("P2P_GROUP_REMOVE *")
+
+def test_autogo_many_clients(dev):
+ """P2P autonomous GO and many clients (P2P IE fragmentation)"""
+ try:
+ _test_autogo_many_clients(dev)
+ finally:
+ dev[0].global_request("SET device_name Device A")
+ dev[1].global_request("SET device_name Device B")
+ dev[2].global_request("SET device_name Device C")
+
+def _test_autogo_many_clients(dev):
+ # These long device names will push the P2P IE contents beyond the limit
+ # that requires fragmentation.
+ name0 = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+ name1 = "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+ name2 = "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC"
+ name3 = "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD"
+ dev[0].global_request("SET device_name " + name0)
+ dev[1].global_request("SET device_name " + name1)
+ dev[2].global_request("SET device_name " + name2)
+
+ addr0 = dev[0].p2p_dev_addr()
+ res = autogo(dev[0], freq=2412)
+ bssid = dev[0].p2p_interface_addr()
+
+ connect_cli(dev[0], dev[1], social=True, freq=2412)
+ dev[0].dump_monitor()
+ connect_cli(dev[0], dev[2], social=True, freq=2412)
+ dev[0].dump_monitor()
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.global_request("SET device_name " + name3)
+ wpas.global_request("SET sec_device_type 1-11111111-1")
+ wpas.global_request("SET sec_device_type 2-22222222-2")
+ wpas.global_request("SET sec_device_type 3-33333333-3")
+ wpas.global_request("SET sec_device_type 4-44444444-4")
+ wpas.global_request("SET sec_device_type 5-55555555-5")
+ connect_cli(dev[0], wpas, social=True, freq=2412)
+ dev[0].dump_monitor()
+
+ dev[1].dump_monitor()
+ dev[1].p2p_find(freq=2412)
+ ev1 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev1 is None:
+ raise Exception("Could not find peer (1)")
+ ev2 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev2 is None:
+ raise Exception("Could not find peer (2)")
+ ev3 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev3 is None:
+ raise Exception("Could not find peer (3)")
+ dev[1].p2p_stop_find()
+
+ for i in [name0, name2, name3]:
+ if i not in ev1 and i not in ev2 and i not in ev3:
+ raise Exception('name "%s" not found' % i)
+
+def rx_pd_req(dev):
+ msg = dev.mgmt_rx()
+ if msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_PROV_DISC_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+ p2p['freq'] = msg['freq']
+ return p2p
+
+@remote_compatible
+def test_autogo_scan(dev):
+ """P2P autonomous GO and no P2P IE in Probe Response scan results"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[0].p2p_start_go(freq=2412, persistent=True)
+ bssid = dev[0].p2p_interface_addr()
+
+ dev[1].discover_peer(addr0)
+ dev[1].p2p_stop_find()
+ ev = dev[1].wait_global_event(["P2P-FIND-STOPPED"], timeout=2)
+ time.sleep(0.1)
+ dev[1].flush_scan_cache()
+
+ pin = dev[1].wps_read_pin()
+ dev[0].group_request("WPS_PIN any " + pin)
+
+ try:
+ dev[1].request("SET p2p_disabled 1")
+ dev[1].request("SCAN freq=2412")
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Active scan did not complete")
+ finally:
+ dev[1].request("SET p2p_disabled 0")
+
+ for i in range(2):
+ dev[1].request("SCAN freq=2412 passive=1")
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+
+ # Disable management frame processing for a moment to skip Probe Response
+ # frame with P2P IE.
+ dev[0].group_request("SET ext_mgmt_frame_handling 1")
+
+ dev[1].global_request("P2P_CONNECT " + bssid + " " + pin + " freq=2412 join")
+
+ # Skip the first Probe Request frame
+ ev = dev[0].wait_group_event(["MGMT-RX"], timeout=10)
+ if ev is None:
+ raise Exception("No Probe Request frame seen")
+ if not ev.split(' ')[4].startswith("40"):
+ raise Exception("Not a Probe Request frame")
+
+ # If a P2P Device is not used, the PD Request will be received on the group
+ # interface (which is actually wlan0, since a separate interface is not
+ # used), which was set to external management frame handling, so need to
+ # reply to it manually.
+ res = dev[0].get_driver_status()
+ if not (int(res['capa.flags'], 0) & 0x20000000):
+ # Reply to PD Request while still filtering Probe Request frames
+ msg = rx_pd_req(dev[0])
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=10 no_cck=1 action={}".format(addr1, addr0, 2412, "0409506f9a0908%02xdd0a0050f204100800020008" % msg['dialog_token']))
+
+ # Skip Probe Request frames until something else is received
+ for i in range(10):
+ ev = dev[0].wait_group_event(["MGMT-RX"], timeout=10)
+ if ev is None:
+ raise Exception("No frame seen")
+ if not ev.split(' ')[4].startswith("40"):
+ break
+
+ # Allow wpa_supplicant to process authentication and association
+ dev[0].group_request("SET ext_mgmt_frame_handling 0")
+
+ # Joining the group should succeed and indicate persistent group based on
+ # Beacon frame P2P IE.
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("Failed to join group")
+ if "[PERSISTENT]" not in ev:
+ raise Exception("Did not recognize group as persistent")
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+@remote_compatible
+def test_autogo_join_before_found(dev):
+ """P2P client joining a group before having found GO Device Address"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ res = autogo(dev[0], freq=2412)
+ if "p2p-wlan" not in res['ifname']:
+ raise Exception("Unexpected group interface name on GO")
+ status = dev[0].get_group_status()
+ bssid = status['bssid']
+
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ cmd = "P2P_CONNECT " + bssid + " " + pin + " join freq=2412"
+ if "OK" not in dev[1].global_request(cmd):
+ raise Exception("P2P_CONNECT join failed")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Joining the group timed out")
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+def test_autogo_noa(dev):
+ """P2P autonomous GO and NoA"""
+ res = autogo(dev[0])
+ dev[0].group_request("P2P_SET noa 1,5,20")
+ dev[0].group_request("P2P_SET noa 255,10,50")
+
+ # Connect and disconnect legacy STA to check NoA special cases
+ try:
+ dev[1].request("SET p2p_disabled 1")
+ dev[1].connect(ssid=res['ssid'], psk=res['passphrase'], proto='RSN',
+ key_mgmt='WPA-PSK', pairwise='CCMP', group='CCMP',
+ scan_freq=res['freq'])
+ dev[0].group_request("P2P_SET noa 255,15,55")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ finally:
+ dev[1].request("SET p2p_disabled 0")
+
+ dev[0].group_request("P2P_SET noa 0,0,0")
+
+def test_autogo_interworking(dev):
+ """P2P autonomous GO and Interworking"""
+ try:
+ run_autogo_interworking(dev)
+ finally:
+ dev[0].set("go_interworking", "0")
+
+def run_autogo_interworking(dev):
+ dev[0].global_request("SET go_interworking 1")
+ dev[0].global_request("SET go_access_network_type 1")
+ dev[0].global_request("SET go_internet 1")
+ dev[0].global_request("SET go_venue_group 2")
+ dev[0].global_request("SET go_venue_type 3")
+ res = autogo(dev[0])
+ bssid = dev[0].p2p_interface_addr()
+ dev[1].scan_for_bss(bssid, freq=res['freq'])
+ bss = dev[1].get_bss(bssid)
+ dev[0].remove_group()
+ if '6b03110203' not in bss['ie']:
+ raise Exception("Interworking element not seen")
+
+def test_autogo_remove_iface(dev):
+ """P2P autonomous GO and interface being removed"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.global_request("SET p2p_no_group_iface 1")
+ wpas.set("p2p_group_idle", "1")
+ autogo(wpas)
+ wpas.global_request("P2P_SET disallow_freq 5000")
+ time.sleep(0.1)
+ wpas.global_request("INTERFACE_REMOVE " + wpas.ifname)
+ time.sleep(1)
diff --git a/contrib/wpa/tests/hwsim/test_p2p_channel.py b/contrib/wpa/tests/hwsim/test_p2p_channel.py
new file mode 100644
index 000000000000..d57234dadb64
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_channel.py
@@ -0,0 +1,1384 @@
+# P2P channel selection test cases
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import os
+import subprocess
+import time
+
+import hostapd
+import hwsim_utils
+from tshark import run_tshark
+from wpasupplicant import WpaSupplicant
+from hwsim import HWSimRadio
+from p2p_utils import *
+from utils import *
+
+def set_country(country, dev=None):
+ subprocess.call(['iw', 'reg', 'set', country])
+ time.sleep(0.1)
+ if dev:
+ for i in range(10):
+ ev = dev.wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=15)
+ if ev is None:
+ raise Exception("No regdom change event seen")
+ if "type=COUNTRY alpha2=" + country in ev:
+ return
+ raise Exception("No matching regdom event seen for set_country(%s)" % country)
+
+def test_p2p_channel_5ghz(dev):
+ """P2P group formation with 5 GHz preference"""
+ try:
+ set_country("US", dev[0])
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq < 5000:
+ raise Exception("Unexpected channel %d MHz - did not follow 5 GHz preference" % freq)
+ remove_group(dev[0], dev[1])
+ finally:
+ set_country("00")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_5ghz_no_vht(dev):
+ """P2P group formation with 5 GHz preference when VHT channels are disallowed"""
+ try:
+ set_country("US", dev[0])
+ dev[0].global_request("P2P_SET disallow_freq 5180-5240")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq < 5000:
+ raise Exception("Unexpected channel %d MHz - did not follow 5 GHz preference" % freq)
+ remove_group(dev[0], dev[1])
+ finally:
+ set_country("00")
+ dev[0].global_request("P2P_SET disallow_freq ")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_random_social(dev):
+ """P2P group formation with 5 GHz preference but all 5 GHz channels disabled"""
+ try:
+ set_country("US", dev[0])
+ dev[0].global_request("SET p2p_oper_channel 11")
+ dev[0].global_request("P2P_SET disallow_freq 5000-6000,2462")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq not in [2412, 2437, 2462]:
+ raise Exception("Unexpected channel %d MHz - did not pick random social channel" % freq)
+ remove_group(dev[0], dev[1])
+ finally:
+ set_country("00")
+ dev[0].global_request("P2P_SET disallow_freq ")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_random(dev):
+ """P2P group formation with 5 GHz preference but all 5 GHz channels and all social channels disabled"""
+ try:
+ set_country("US", dev[0])
+ dev[0].global_request("SET p2p_oper_channel 11")
+ dev[0].global_request("P2P_SET disallow_freq 5000-6000,2412,2437,2462")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq > 2500 or freq in [2412, 2437, 2462]:
+ raise Exception("Unexpected channel %d MHz" % freq)
+ remove_group(dev[0], dev[1])
+ finally:
+ set_country("00")
+ dev[0].global_request("P2P_SET disallow_freq ")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_random_social_with_op_class_change(dev, apdev, params):
+ """P2P group formation using random social channel with oper class change needed"""
+ try:
+ set_country("US", dev[0])
+ logger.info("Start group on 5 GHz")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq < 5000:
+ raise Exception("Unexpected channel %d MHz - did not pick 5 GHz preference" % freq)
+ remove_group(dev[0], dev[1])
+
+ logger.info("Disable 5 GHz and try to re-start group based on 5 GHz preference")
+ dev[0].global_request("SET p2p_oper_reg_class 115")
+ dev[0].global_request("SET p2p_oper_channel 36")
+ dev[0].global_request("P2P_SET disallow_freq 5000-6000")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq not in [2412, 2437, 2462]:
+ raise Exception("Unexpected channel %d MHz - did not pick random social channel" % freq)
+ remove_group(dev[0], dev[1])
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wifi_p2p.public_action.subtype == 0")
+ if out is not None:
+ last = None
+ for l in out.splitlines():
+ if "Operating Channel:" not in l:
+ continue
+ last = l
+ if last is None:
+ raise Exception("Could not find GO Negotiation Request")
+ if "Operating Class 81" not in last:
+ raise Exception("Unexpected operating class: " + last.strip())
+ finally:
+ set_country("00")
+ dev[0].global_request("P2P_SET disallow_freq ")
+ dev[0].global_request("SET p2p_oper_reg_class 0")
+ dev[0].global_request("SET p2p_oper_channel 0")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_avoid(dev):
+ """P2P and avoid frequencies driver event"""
+ try:
+ set_country("US", dev[0])
+ if "OK" not in dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES 5000-6000,2412,2437,2462"):
+ raise Exception("Could not simulate driver event")
+ ev = dev[0].wait_event(["CTRL-EVENT-AVOID-FREQ"], timeout=10)
+ if ev is None:
+ raise Exception("No CTRL-EVENT-AVOID-FREQ event")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq > 2500 or freq in [2412, 2437, 2462]:
+ raise Exception("Unexpected channel %d MHz" % freq)
+
+ if "OK" not in dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES"):
+ raise Exception("Could not simulate driver event(2)")
+ ev = dev[0].wait_event(["CTRL-EVENT-AVOID-FREQ"], timeout=10)
+ if ev is None:
+ raise Exception("No CTRL-EVENT-AVOID-FREQ event")
+ ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP",
+ "AP-CSA-FINISHED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected + " + ev + " event")
+
+ if "OK" not in dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES " + str(freq)):
+ raise Exception("Could not simulate driver event(3)")
+ ev = dev[0].wait_event(["CTRL-EVENT-AVOID-FREQ"], timeout=10)
+ if ev is None:
+ raise Exception("No CTRL-EVENT-AVOID-FREQ event")
+ ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP",
+ "AP-CSA-FINISHED"],
+ timeout=10)
+ if ev is None:
+ raise Exception("No P2P-REMOVE-AND-REFORM-GROUP or AP-CSA-FINISHED event")
+ finally:
+ set_country("00")
+ dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_avoid2(dev):
+ """P2P and avoid frequencies driver event on 5 GHz"""
+ try:
+ set_country("US", dev[0])
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False,
+ i_max_oper_chwidth=80,
+ i_ht40=True, i_vht=True)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq < 5000:
+ raise Exception("Unexpected channel %d MHz" % freq)
+
+ if "OK" not in dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES " + str(freq)):
+ raise Exception("Could not simulate driver event(2)")
+ ev = dev[0].wait_event(["CTRL-EVENT-AVOID-FREQ"], timeout=10)
+ if ev is None:
+ raise Exception("No CTRL-EVENT-AVOID-FREQ event")
+ ev = dev[0].wait_group_event(["CTRL-EVENT-CHANNEL-SWITCH"], timeout=10)
+ if ev is None:
+ raise Exception("No channel switch event seen")
+ if "ch_width=80 MHz" not in ev:
+ raise Exception("Could not move to a VHT80 channel")
+ ev = dev[0].wait_group_event(["AP-CSA-FINISHED"], timeout=1)
+ if ev is None:
+ raise Exception("No AP-CSA-FINISHED event seen")
+ finally:
+ set_country("00")
+ dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_avoid3(dev):
+ """P2P and avoid frequencies driver event on 5 GHz"""
+ try:
+ dev[0].global_request("SET p2p_pref_chan 128:44")
+ set_country("CN", dev[0])
+ form(dev[0], dev[1])
+ set_country("CN", dev[0])
+ [i_res, r_res] = invite_from_go(dev[0], dev[1], terminate=False,
+ extra="ht40 vht")
+ freq = int(i_res['freq'])
+ if freq < 5000:
+ raise Exception("Unexpected channel %d MHz" % freq)
+
+ if "OK" not in dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES 5180-5320,5500-5640"):
+ raise Exception("Could not simulate driver event(2)")
+ ev = dev[0].wait_event(["CTRL-EVENT-AVOID-FREQ"], timeout=10)
+ if ev is None:
+ raise Exception("No CTRL-EVENT-AVOID-FREQ event")
+ ev = dev[0].wait_group_event(["CTRL-EVENT-CHANNEL-SWITCH"], timeout=10)
+ if ev is None:
+ raise Exception("No channel switch event seen")
+ if "ch_width=80 MHz" not in ev:
+ raise Exception("Could not move to a VHT80 channel")
+ ev = dev[0].wait_group_event(["AP-CSA-FINISHED"], timeout=1)
+ if ev is None:
+ raise Exception("No AP-CSA-FINISHED event seen")
+ finally:
+ set_country("00")
+ dev[0].request("DRIVER_EVENT AVOID_FREQUENCIES")
+ dev[0].global_request("SET p2p_pref_chan ")
+ dev[1].flush_scan_cache()
+
+@remote_compatible
+def test_autogo_following_bss(dev, apdev):
+ """P2P autonomous GO operate on the same channel as station interface"""
+ if dev[0].get_mcc() > 1:
+ logger.info("test mode: MCC")
+
+ dev[0].global_request("SET p2p_no_group_iface 0")
+
+ channels = {3: "2422", 5: "2432", 9: "2452"}
+ for key in channels:
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'ap-test',
+ "channel": str(key)})
+ dev[0].connect("ap-test", key_mgmt="NONE",
+ scan_freq=str(channels[key]))
+ res_go = autogo(dev[0])
+ if res_go['freq'] != channels[key]:
+ raise Exception("Group operation channel is not the same as on connected station interface")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[0].remove_group(res_go['ifname'])
+
+@remote_compatible
+def test_go_neg_with_bss_connected(dev, apdev):
+ """P2P channel selection: GO negotiation when station interface is connected"""
+
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ dev[0].global_request("SET p2p_no_group_iface 0")
+
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-2.4ghz', "channel": '5'})
+ dev[0].connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2432")
+ #dev[0] as GO
+ [i_res, r_res] = go_neg_pbc(i_dev=dev[0], i_intent=10, r_dev=dev[1],
+ r_intent=1)
+ check_grpform_results(i_res, r_res)
+ if i_res['role'] != "GO":
+ raise Exception("GO not selected according to go_intent")
+ if i_res['freq'] != "2432":
+ raise Exception("Group formed on a different frequency than BSS")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[0].remove_group(i_res['ifname'])
+ dev[1].wait_go_ending_session()
+
+ if dev[0].get_mcc() > 1:
+ logger.info("Skip as-client case due to MCC being enabled")
+ return
+
+ #dev[0] as client
+ [i_res2, r_res2] = go_neg_pbc(i_dev=dev[0], i_intent=1, r_dev=dev[1],
+ r_intent=10)
+ check_grpform_results(i_res2, r_res2)
+ if i_res2['role'] != "client":
+ raise Exception("GO not selected according to go_intent")
+ if i_res2['freq'] != "2432":
+ raise Exception("Group formed on a different frequency than BSS")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[1].remove_group(r_res2['ifname'])
+ dev[0].wait_go_ending_session()
+ dev[0].request("DISCONNECT")
+ hapd.disable()
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_autogo_with_bss_on_disallowed_chan(dev, apdev):
+ """P2P channel selection: Autonomous GO with BSS on a disallowed channel"""
+
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ wpas.global_request("SET p2p_no_group_iface 0")
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ try:
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'bss-2.4ghz',
+ "channel": '1'})
+ wpas.global_request("P2P_SET disallow_freq 2412")
+ wpas.connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2412")
+ res = autogo(wpas)
+ if res['freq'] == "2412":
+ raise Exception("GO set on a disallowed channel")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ finally:
+ wpas.global_request("P2P_SET disallow_freq ")
+
+def test_go_neg_with_bss_on_disallowed_chan(dev, apdev):
+ """P2P channel selection: GO negotiation with station interface on a disallowed channel"""
+
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ wpas.global_request("SET p2p_no_group_iface 0")
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ try:
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-2.4ghz', "channel": '1'})
+ # make sure PBC overlap from old test cases is not maintained
+ dev[1].flush_scan_cache()
+ wpas.connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2412")
+ wpas.global_request("P2P_SET disallow_freq 2412")
+
+ #wpas as GO
+ [i_res, r_res] = go_neg_pbc(i_dev=wpas, i_intent=10, r_dev=dev[1],
+ r_intent=1)
+ check_grpform_results(i_res, r_res)
+ if i_res['role'] != "GO":
+ raise Exception("GO not selected according to go_intent")
+ if i_res['freq'] == "2412":
+ raise Exception("Group formed on a disallowed channel")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ wpas.remove_group(i_res['ifname'])
+ dev[1].wait_go_ending_session()
+ dev[1].flush_scan_cache()
+
+ wpas.dump_monitor()
+ dev[1].dump_monitor()
+
+ #wpas as client
+ [i_res2, r_res2] = go_neg_pbc(i_dev=wpas, i_intent=1, r_dev=dev[1],
+ r_intent=10)
+ check_grpform_results(i_res2, r_res2)
+ if i_res2['role'] != "client":
+ raise Exception("GO not selected according to go_intent")
+ if i_res2['freq'] == "2412":
+ raise Exception("Group formed on a disallowed channel")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ dev[1].remove_group(r_res2['ifname'])
+ wpas.wait_go_ending_session()
+ ev = dev[1].wait_global_event(["P2P-GROUP-REMOVED"], timeout=5)
+ if ev is None:
+ raise Exception("Group removal not indicated")
+ wpas.request("DISCONNECT")
+ hapd.disable()
+ finally:
+ wpas.global_request("P2P_SET disallow_freq ")
+
+def test_autogo_force_diff_channel(dev, apdev):
+ """P2P autonomous GO and station interface operate on different channels"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ wpas.global_request("SET p2p_no_group_iface 0")
+
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": 'ap-test', "channel": '1'})
+ wpas.connect("ap-test", key_mgmt="NONE", scan_freq="2412")
+ wpas.dump_monitor()
+ channels = {2: 2417, 5: 2432, 9: 2452}
+ for key in channels:
+ res_go = autogo(wpas, channels[key])
+ wpas.dump_monitor()
+ hwsim_utils.test_connectivity(wpas, hapd)
+ if int(res_go['freq']) == 2412:
+ raise Exception("Group operation channel is: 2412 excepted: " + res_go['freq'])
+ wpas.remove_group(res_go['ifname'])
+ wpas.dump_monitor()
+
+def test_go_neg_forced_freq_diff_than_bss_freq(dev, apdev):
+ """P2P channel selection: GO negotiation with forced freq different than station interface"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ # Clear possible PBC session overlap from previous test case
+ dev[1].flush_scan_cache()
+
+ wpas.global_request("SET p2p_no_group_iface 0")
+
+ hapd = hostapd.add_ap(apdev[0],
+ {"country_code": 'US',
+ "ssid": 'bss-5ghz', "hw_mode": 'a',
+ "channel": '40'})
+ wpas.connect("bss-5ghz", key_mgmt="NONE", scan_freq="5200")
+
+ # GO and peer force the same freq, different than BSS freq,
+ # wpas to become GO
+ [i_res, r_res] = go_neg_pbc(i_dev=dev[1], i_intent=1, i_freq=5180,
+ r_dev=wpas, r_intent=14, r_freq=5180)
+ check_grpform_results(i_res, r_res)
+ if i_res['freq'] != "5180":
+ raise Exception("P2P group formed on unexpected frequency: " + i_res['freq'])
+ if r_res['role'] != "GO":
+ raise Exception("GO not selected according to go_intent")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ wpas.remove_group(r_res['ifname'])
+ dev[1].wait_go_ending_session()
+ dev[1].flush_scan_cache()
+
+ # GO and peer force the same freq, different than BSS freq, wpas to
+ # become client
+ [i_res2, r_res2] = go_neg_pbc(i_dev=dev[1], i_intent=14, i_freq=2422,
+ r_dev=wpas, r_intent=1, r_freq=2422)
+ check_grpform_results(i_res2, r_res2)
+ if i_res2['freq'] != "2422":
+ raise Exception("P2P group formed on unexpected frequency: " + i_res2['freq'])
+ if r_res2['role'] != "client":
+ raise Exception("GO not selected according to go_intent")
+ hwsim_utils.test_connectivity(wpas, hapd)
+
+ hapd.request("DISABLE")
+ wpas.request("DISCONNECT")
+ wpas.request("ABORT_SCAN")
+ wpas.wait_disconnected()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ wpas.flush_scan_cache()
+
+@remote_compatible
+def test_go_pref_chan_bss_on_diff_chan(dev, apdev):
+ """P2P channel selection: Station on different channel than GO configured pref channel"""
+
+ dev[0].global_request("SET p2p_no_group_iface 0")
+
+ try:
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'bss-2.4ghz',
+ "channel": '1'})
+ dev[0].global_request("SET p2p_pref_chan 81:2")
+ dev[0].connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2412")
+ res = autogo(dev[0])
+ if res['freq'] != "2412":
+ raise Exception("GO channel did not follow BSS")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ dev[0].global_request("SET p2p_pref_chan ")
+
+def test_go_pref_chan_bss_on_disallowed_chan(dev, apdev):
+ """P2P channel selection: Station interface on different channel than GO configured pref channel, and station channel is disallowed"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ wpas.global_request("SET p2p_no_group_iface 0")
+
+ try:
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'bss-2.4ghz',
+ "channel": '1'})
+ wpas.global_request("P2P_SET disallow_freq 2412")
+ wpas.global_request("SET p2p_pref_chan 81:2")
+ wpas.connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2412")
+ res2 = autogo(wpas)
+ if res2['freq'] != "2417":
+ raise Exception("GO channel did not follow pref_chan configuration")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ finally:
+ wpas.global_request("P2P_SET disallow_freq ")
+ wpas.global_request("SET p2p_pref_chan ")
+
+@remote_compatible
+def test_no_go_freq(dev, apdev):
+ """P2P channel selection: no GO freq"""
+ try:
+ dev[0].global_request("SET p2p_no_go_freq 2412")
+ # dev[0] as client, channel 1 is ok
+ [i_res, r_res] = go_neg_pbc(i_dev=dev[0], i_intent=1,
+ r_dev=dev[1], r_intent=14, r_freq=2412)
+ check_grpform_results(i_res, r_res)
+ if i_res['freq'] != "2412":
+ raise Exception("P2P group not formed on forced freq")
+
+ dev[1].remove_group(r_res['ifname'])
+ dev[0].wait_go_ending_session()
+ dev[0].flush_scan_cache()
+
+ fail = False
+ # dev[0] as GO, channel 1 is not allowed
+ try:
+ dev[0].global_request("SET p2p_no_go_freq 2412")
+ [i_res2, r_res2] = go_neg_pbc(i_dev=dev[0], i_intent=14,
+ r_dev=dev[1], r_intent=1, r_freq=2412)
+ check_grpform_results(i_res2, r_res2)
+ fail = True
+ except:
+ pass
+ if fail:
+ raise Exception("GO set on a disallowed freq")
+ finally:
+ dev[0].global_request("SET p2p_no_go_freq ")
+
+@remote_compatible
+def test_go_neg_peers_force_diff_freq(dev, apdev):
+ """P2P channel selection when peers for different frequency"""
+ try:
+ [i_res2, r_res2] = go_neg_pbc(i_dev=dev[0], i_intent=14, i_freq=5180,
+ r_dev=dev[1], r_intent=0, r_freq=5200)
+ except Exception as e:
+ return
+ raise Exception("Unexpected group formation success")
+
+@remote_compatible
+def test_autogo_random_channel(dev, apdev):
+ """P2P channel selection: GO instantiated on random channel 1, 6, 11"""
+ freqs = []
+ go_freqs = ["2412", "2437", "2462"]
+ for i in range(0, 20):
+ result = autogo(dev[0])
+ if result['freq'] not in go_freqs:
+ raise Exception("Unexpected frequency selected: " + result['freq'])
+ if result['freq'] not in freqs:
+ freqs.append(result['freq'])
+ if len(freqs) == 3:
+ break
+ dev[0].remove_group(result['ifname'])
+ if i == 20:
+ raise Exception("GO created 20 times and not all social channels were selected. freqs not selected: " + str(list(set(go_freqs) - set(freqs))))
+
+@remote_compatible
+def test_p2p_autogo_pref_chan_disallowed(dev, apdev):
+ """P2P channel selection: GO preferred channels are disallowed"""
+ try:
+ dev[0].global_request("SET p2p_pref_chan 81:1,81:3,81:6,81:9,81:11")
+ dev[0].global_request("P2P_SET disallow_freq 2412,2422,2437,2452,2462")
+ for i in range(0, 5):
+ res = autogo(dev[0])
+ if res['freq'] in ["2412", "2422", "2437", "2452", "2462"]:
+ raise Exception("GO channel is disallowed")
+ dev[0].remove_group(res['ifname'])
+ finally:
+ dev[0].global_request("P2P_SET disallow_freq ")
+ dev[0].global_request("SET p2p_pref_chan ")
+
+def test_p2p_autogo_pref_chan_not_in_regulatory(dev, apdev):
+ """P2P channel selection: GO preferred channel not allowed in the regulatory rules"""
+ try:
+ set_country("US", dev[0])
+ dev[0].global_request("SET p2p_pref_chan 124:149")
+ res = autogo(dev[0], persistent=True)
+ if res['freq'] != "5745":
+ raise Exception("Unexpected channel selected: " + res['freq'])
+ dev[0].remove_group(res['ifname'])
+
+ netw = dev[0].list_networks(p2p=True)
+ if len(netw) != 1:
+ raise Exception("Unexpected number of network blocks: " + str(netw))
+ id = netw[0]['id']
+
+ set_country("JP", dev[0])
+ res = autogo(dev[0], persistent=id)
+ if res['freq'] == "5745":
+ raise Exception("Unexpected channel selected(2): " + res['freq'])
+ dev[0].remove_group(res['ifname'])
+ finally:
+ dev[0].global_request("SET p2p_pref_chan ")
+ clear_regdom_dev(dev)
+
+def run_autogo(dev, param):
+ if "OK" not in dev.global_request("P2P_GROUP_ADD " + param):
+ raise Exception("P2P_GROUP_ADD failed: " + param)
+ ev = dev.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("GO start up timed out")
+ res = dev.group_form_result(ev)
+ dev.remove_group()
+ return res
+
+def _test_autogo_ht_vht(dev):
+ res = run_autogo(dev[0], "ht40")
+
+ res = run_autogo(dev[0], "vht")
+
+ res = run_autogo(dev[0], "freq=2")
+ freq = int(res['freq'])
+ if freq < 2412 or freq > 2462:
+ raise Exception("Unexpected freq=2 channel: " + str(freq))
+
+ res = run_autogo(dev[0], "freq=5")
+ freq = int(res['freq'])
+ if freq < 5000 or freq >= 6000:
+ raise Exception("Unexpected freq=5 channel: " + str(freq))
+
+ res = run_autogo(dev[0], "freq=5 ht40 vht")
+ logger.info(str(res))
+ freq = int(res['freq'])
+ if freq < 5000 or freq >= 6000:
+ raise Exception("Unexpected freq=5 ht40 vht channel: " + str(freq))
+
+def test_autogo_ht_vht(dev):
+ """P2P autonomous GO with HT/VHT parameters"""
+ try:
+ set_country("US", dev[0])
+ _test_autogo_ht_vht(dev)
+ finally:
+ clear_regdom_dev(dev)
+
+def test_p2p_listen_chan_optimize(dev, apdev):
+ """P2P listen channel optimization"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ addr5 = wpas.p2p_dev_addr()
+ try:
+ if "OK" not in wpas.global_request("SET p2p_optimize_listen_chan 1"):
+ raise Exception("Failed to set p2p_optimize_listen_chan")
+ wpas.p2p_listen()
+ if not dev[0].discover_peer(addr5):
+ raise Exception("Could not discover peer")
+ peer = dev[0].get_peer(addr5)
+ lfreq = peer['listen_freq']
+ wpas.p2p_stop_find()
+ dev[0].p2p_stop_find()
+
+ channel = "1" if lfreq != '2412' else "6"
+ freq = "2412" if lfreq != '2412' else "2437"
+ params = {"ssid": "test-open", "channel": channel}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ id = wpas.connect("test-open", key_mgmt="NONE", scan_freq=freq)
+ wpas.p2p_listen()
+
+ if "OK" not in dev[0].global_request("P2P_FLUSH"):
+ raise Exception("P2P_FLUSH failed")
+ if not dev[0].discover_peer(addr5):
+ raise Exception("Could not discover peer")
+ peer = dev[0].get_peer(addr5)
+ lfreq2 = peer['listen_freq']
+ if lfreq == lfreq2:
+ raise Exception("Listen channel did not change")
+ if lfreq2 != freq:
+ raise Exception("Listen channel not on AP's operating channel")
+ wpas.p2p_stop_find()
+ dev[0].p2p_stop_find()
+
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+
+ # for larger coverage, cover case of current channel matching
+ wpas.select_network(id)
+ wpas.wait_connected()
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+
+ lchannel = "1" if channel != "1" else "6"
+ lfreq3 = "2412" if channel != "1" else "2437"
+ if "OK" not in wpas.global_request("P2P_SET listen_channel " + lchannel):
+ raise Exception("Failed to set listen channel")
+
+ wpas.select_network(id)
+ wpas.wait_connected()
+ wpas.p2p_listen()
+
+ if "OK" not in dev[0].global_request("P2P_FLUSH"):
+ raise Exception("P2P_FLUSH failed")
+ if not dev[0].discover_peer(addr5):
+ raise Exception("Could not discover peer")
+ peer = dev[0].get_peer(addr5)
+ lfreq4 = peer['listen_freq']
+ if lfreq4 != lfreq3:
+ raise Exception("Unexpected Listen channel after configuration")
+ wpas.p2p_stop_find()
+ dev[0].p2p_stop_find()
+ finally:
+ wpas.global_request("SET p2p_optimize_listen_chan 0")
+
+def test_p2p_channel_5ghz_only(dev):
+ """P2P GO start with only 5 GHz band allowed"""
+ try:
+ set_country("US", dev[0])
+ dev[0].global_request("P2P_SET disallow_freq 2400-2500")
+ res = autogo(dev[0])
+ freq = int(res['freq'])
+ if freq < 5000:
+ raise Exception("Unexpected channel %d MHz" % freq)
+ dev[0].remove_group()
+ finally:
+ dev[0].global_request("P2P_SET disallow_freq ")
+ clear_regdom_dev(dev)
+
+def test_p2p_channel_5ghz_165_169_us(dev):
+ """P2P GO and 5 GHz channels 165 (allowed) and 169 (disallowed) in US"""
+ try:
+ set_country("US", dev[0])
+ res = dev[0].p2p_start_go(freq=5825)
+ if res['freq'] != "5825":
+ raise Exception("Unexpected frequency: " + res['freq'])
+ dev[0].remove_group()
+
+ res = dev[0].global_request("P2P_GROUP_ADD freq=5845")
+ if "FAIL" not in res:
+ raise Exception("GO on channel 169 allowed unexpectedly")
+ finally:
+ clear_regdom_dev(dev)
+
+def wait_go_down_up(dev):
+ ev = dev.wait_group_event(["AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("AP-DISABLED not seen after P2P-REMOVE-AND-REFORM-GROUP")
+ ev = dev.wait_group_event(["AP-ENABLED"], timeout=5)
+ if ev is None:
+ raise Exception("AP-ENABLED not seen after P2P-REMOVE-AND-REFORM-GROUP")
+
+def test_p2p_go_move_reg_change(dev, apdev):
+ """P2P GO move due to regulatory change"""
+ try:
+ set_country("US")
+ dev[0].global_request("P2P_SET disallow_freq 2400-5000,5700-6000")
+ res = autogo(dev[0])
+ freq1 = int(res['freq'])
+ if freq1 < 5000:
+ raise Exception("Unexpected channel %d MHz" % freq1)
+ dev[0].dump_monitor()
+
+ dev[0].global_request("P2P_SET disallow_freq ")
+
+ # GO move is not allowed while waiting for initial client connection
+ connect_cli(dev[0], dev[1], freq=freq1)
+ dev[1].remove_group()
+ ev = dev[1].wait_global_event(["P2P-GROUP-REMOVED"], timeout=5)
+ if ev is None:
+ raise Exception("P2P-GROUP-REMOVED not reported on client")
+ dev[1].dump_monitor()
+ dev[0].dump_monitor()
+
+ freq = dev[0].get_group_status_field('freq')
+ if int(freq) < 5000:
+ raise Exception("Unexpected freq after initial client: " + freq)
+ dev[0].dump_monitor()
+
+ dev[0].request("NOTE Setting country=BD")
+ set_country("BD")
+ dev[0].request("NOTE Waiting for GO channel change")
+ ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP",
+ "AP-CSA-FINISHED"],
+ timeout=10)
+ if ev is None:
+ raise Exception("P2P-REMOVE-AND-REFORM-GROUP or AP-CSA-FINISHED not seen")
+ if "P2P-REMOVE-AND-REFORM-GROUP" in ev:
+ wait_go_down_up(dev[0])
+
+ freq2 = dev[0].get_group_status_field('freq')
+ if freq1 == freq2:
+ raise Exception("Unexpected freq after group reform=" + freq2)
+
+ dev[0].remove_group()
+ finally:
+ dev[0].global_request("P2P_SET disallow_freq ")
+ set_country("00")
+
+def test_p2p_go_move_active(dev, apdev):
+ """P2P GO stays in freq although SCM is possible"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ ndev = [wpas, dev[1]]
+ _test_p2p_go_move_active(ndev, apdev)
+
+def _test_p2p_go_move_active(dev, apdev):
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ try:
+ dev[0].global_request("P2P_SET disallow_freq 2430-6000")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'ap-test',
+ "channel": '11'})
+ dev[0].connect("ap-test", key_mgmt="NONE",
+ scan_freq="2462")
+
+ res = autogo(dev[0])
+ freq = int(res['freq'])
+ if freq > 2430:
+ raise Exception("Unexpected channel %d MHz" % freq)
+
+ # GO move is not allowed while waiting for initial client connection
+ connect_cli(dev[0], dev[1], freq=freq)
+ dev[1].remove_group()
+
+ freq = dev[0].get_group_status_field('freq')
+ if int(freq) > 2430:
+ raise Exception("Unexpected freq after initial client: " + freq)
+
+ dev[0].dump_monitor()
+ dev[0].global_request("P2P_SET disallow_freq ")
+
+ ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP",
+ "AP-CSA-FINISHED"],
+ timeout=10)
+ if ev is not None:
+ raise Exception("Unexpected P2P-REMOVE-AND-REFORM-GROUP or AP-CSA-FINISHED seen")
+
+ dev[0].remove_group()
+ finally:
+ dev[0].global_request("P2P_SET disallow_freq ")
+
+def test_p2p_go_move_scm(dev, apdev):
+ """P2P GO move due to SCM operation preference"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ ndev = [wpas, dev[1]]
+ _test_p2p_go_move_scm(ndev, apdev)
+
+def _test_p2p_go_move_scm(dev, apdev):
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ try:
+ dev[0].global_request("P2P_SET disallow_freq 2430-6000")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'ap-test',
+ "channel": '11'})
+ dev[0].connect("ap-test", key_mgmt="NONE",
+ scan_freq="2462")
+
+ dev[0].global_request("SET p2p_go_freq_change_policy 0")
+ res = autogo(dev[0])
+ freq = int(res['freq'])
+ if freq > 2430:
+ raise Exception("Unexpected channel %d MHz" % freq)
+
+ # GO move is not allowed while waiting for initial client connection
+ connect_cli(dev[0], dev[1], freq=freq)
+ dev[1].remove_group()
+
+ freq = dev[0].get_group_status_field('freq')
+ if int(freq) > 2430:
+ raise Exception("Unexpected freq after initial client: " + freq)
+
+ dev[0].dump_monitor()
+ dev[0].global_request("P2P_SET disallow_freq ")
+
+ ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP",
+ "AP-CSA-FINISHED"], timeout=3)
+ if ev is None:
+ raise Exception("P2P-REMOVE-AND-REFORM-GROUP or AP-CSA-FINISHED not seen")
+ if "P2P-REMOVE-AND-REFORM-GROUP" in ev:
+ wait_go_down_up(dev[0])
+
+ freq = dev[0].get_group_status_field('freq')
+ if freq != '2462':
+ raise Exception("Unexpected freq after group reform=" + freq)
+
+ dev[0].remove_group()
+ finally:
+ dev[0].global_request("P2P_SET disallow_freq ")
+ dev[0].global_request("SET p2p_go_freq_change_policy 2")
+
+def test_p2p_go_move_scm_peer_supports(dev, apdev):
+ """P2P GO move due to SCM operation preference (peer supports)"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ ndev = [wpas, dev[1]]
+ _test_p2p_go_move_scm_peer_supports(ndev, apdev)
+
+def _test_p2p_go_move_scm_peer_supports(dev, apdev):
+ try:
+ dev[0].global_request("SET p2p_go_freq_change_policy 1")
+ set_country("US", dev[0])
+
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq < 5000:
+ raise Exception("Unexpected channel %d MHz - did not follow 5 GHz preference" % freq)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'ap-test',
+ "channel": '11'})
+ logger.info('Connecting client to to an AP on channel 11')
+ dev[0].connect("ap-test", key_mgmt="NONE",
+ scan_freq="2462")
+
+ ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP",
+ "AP-CSA-FINISHED"], timeout=3)
+ if ev is None:
+ raise Exception("P2P-REMOVE-AND-REFORM-GROUP or AP-CSA-FINISHED not seen")
+ if "P2P-REMOVE-AND-REFORM-GROUP" in ev:
+ wait_go_down_up(dev[0])
+
+ freq = dev[0].get_group_status_field('freq')
+ if freq != '2462':
+ raise Exception("Unexpected freq after group reform=" + freq)
+
+ dev[0].remove_group()
+ finally:
+ dev[0].global_request("SET p2p_go_freq_change_policy 2")
+ disable_hapd(hapd)
+ clear_regdom_dev(dev, 1)
+
+def test_p2p_go_move_scm_peer_does_not_support(dev, apdev):
+ """No P2P GO move due to SCM operation (peer does not supports)"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ ndev = [wpas, dev[1]]
+ _test_p2p_go_move_scm_peer_does_not_support(ndev, apdev)
+
+def _test_p2p_go_move_scm_peer_does_not_support(dev, apdev):
+ try:
+ dev[0].global_request("SET p2p_go_freq_change_policy 1")
+ set_country("US", dev[0])
+
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ if "OK" not in dev[1].request("DRIVER_EVENT AVOID_FREQUENCIES 2400-2500"):
+ raise Exception("Could not simulate driver event")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq < 5000:
+ raise Exception("Unexpected channel %d MHz - did not follow 5 GHz preference" % freq)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'ap-test',
+ "channel": '11'})
+ logger.info('Connecting client to to an AP on channel 11')
+ dev[0].connect("ap-test", key_mgmt="NONE",
+ scan_freq="2462")
+
+ ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP",
+ "AP-CSA-FINISHED"],
+ timeout=10)
+ if ev is not None:
+ raise Exception("Unexpected P2P-REMOVE-AND-REFORM-GROUP or AP-CSA-FINISHED seen")
+
+ dev[0].remove_group()
+ finally:
+ dev[0].global_request("SET p2p_go_freq_change_policy 2")
+ dev[1].request("DRIVER_EVENT AVOID_FREQUENCIES")
+ disable_hapd(hapd)
+ clear_regdom_dev(dev, 2)
+
+def test_p2p_go_move_scm_multi(dev, apdev):
+ """P2P GO move due to SCM operation preference multiple times"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ ndev = [wpas, dev[1]]
+ _test_p2p_go_move_scm_multi(ndev, apdev)
+
+def _test_p2p_go_move_scm_multi(dev, apdev):
+ dev[0].request("SET p2p_no_group_iface 0")
+ try:
+ dev[0].global_request("P2P_SET disallow_freq 2430-6000")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'ap-test-1',
+ "channel": '11'})
+ dev[0].connect("ap-test-1", key_mgmt="NONE",
+ scan_freq="2462")
+
+ dev[0].global_request("SET p2p_go_freq_change_policy 0")
+ res = autogo(dev[0])
+ freq = int(res['freq'])
+ if freq > 2430:
+ raise Exception("Unexpected channel %d MHz" % freq)
+
+ # GO move is not allowed while waiting for initial client connection
+ connect_cli(dev[0], dev[1], freq=freq)
+ dev[1].remove_group()
+
+ freq = dev[0].get_group_status_field('freq')
+ if int(freq) > 2430:
+ raise Exception("Unexpected freq after initial client: " + freq)
+
+ dev[0].dump_monitor()
+ dev[0].global_request("P2P_SET disallow_freq ")
+
+ ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP",
+ "AP-CSA-FINISHED"], timeout=3)
+ if ev is None:
+ raise Exception("P2P-REMOVE-AND-REFORM-GROUP or AP-CSA-FINISHED not seen")
+ if "P2P-REMOVE-AND-REFORM-GROUP" in ev:
+ wait_go_down_up(dev[0])
+
+ freq = dev[0].get_group_status_field('freq')
+ if freq != '2462':
+ raise Exception("Unexpected freq after group reform=" + freq)
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'ap-test-2',
+ "channel": '6'})
+ dev[0].connect("ap-test-2", key_mgmt="NONE",
+ scan_freq="2437")
+
+ ev = dev[0].wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP",
+ "AP-CSA-FINISHED"], timeout=5)
+ if ev is None:
+ raise Exception("(2) P2P-REMOVE-AND-REFORM-GROUP or AP-CSA-FINISHED not seen")
+ if "P2P-REMOVE-AND-REFORM-GROUP" in ev:
+ wait_go_down_up(dev[0])
+
+ freq = dev[0].get_group_status_field('freq')
+ if freq != '2437':
+ raise Exception("(2) Unexpected freq after group reform=" + freq)
+
+ dev[0].remove_group()
+ finally:
+ dev[0].global_request("P2P_SET disallow_freq ")
+ dev[0].global_request("SET p2p_go_freq_change_policy 2")
+
+def test_p2p_delay_go_csa(dev, apdev, params):
+ """P2P GO CSA delayed when inviting a P2P Device to an active P2P Group"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ wpas.global_request("SET p2p_no_group_iface 0")
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ addr0 = wpas.p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ try:
+ dev[1].p2p_listen()
+ if not wpas.discover_peer(addr1, social=True):
+ raise Exception("Peer " + addr1 + " not found")
+ wpas.p2p_stop_find()
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'bss-2.4ghz',
+ "channel": '1'})
+
+ wpas.connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2412")
+
+ wpas.global_request("SET p2p_go_freq_change_policy 0")
+ wpas.dump_monitor()
+
+ logger.info("Start GO on channel 6")
+ res = autogo(wpas, freq=2437)
+ if res['freq'] != "2437":
+ raise Exception("GO set on a freq=%s instead of 2437" % res['freq'])
+
+ # Start find on dev[1] to run scans with dev[2] in parallel
+ dev[1].p2p_find(social=True)
+
+ # Use another client device to stop the initial client connection
+ # timeout on the GO
+ if not dev[2].discover_peer(addr0, social=True):
+ raise Exception("Peer2 did not find the GO")
+ dev[2].p2p_stop_find()
+ pin = dev[2].wps_read_pin()
+ wpas.p2p_go_authorize_client(pin)
+ dev[2].global_request("P2P_CONNECT " + addr0 + " " + pin + " join freq=2437")
+ ev = dev[2].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("Peer2 did not get connected")
+
+ if not dev[1].discover_peer(addr0, social=True):
+ raise Exception("Peer did not find the GO")
+
+ pin = dev[1].wps_read_pin()
+ dev[1].global_request("P2P_CONNECT " + addr0 + " " + pin + " join auth")
+ dev[1].p2p_listen()
+
+ # Force P2P GO channel switch on successful invitation signaling
+ wpas.group_request("SET p2p_go_csa_on_inv 1")
+
+ logger.info("Starting invitation")
+ wpas.p2p_go_authorize_client(pin)
+ wpas.global_request("P2P_INVITE group=" + wpas.group_ifname + " peer=" + addr1)
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED",
+ "P2P-GROUP-STARTED"], timeout=10)
+
+ if ev is None:
+ raise Exception("Timeout on invitation on peer")
+ if "P2P-INVITATION-RECEIVED" in ev:
+ raise Exception("Unexpected request to accept pre-authorized invitation")
+
+ # A P2P GO move is not expected at this stage, as during the
+ # invitation signaling, the P2P GO includes only its current
+ # operating channel in the channel list, and as the invitation
+ # response can only include channels that were also in the
+ # invitation request channel list, the group common channels
+ # includes only the current P2P GO operating channel.
+ ev = wpas.wait_group_event(["P2P-REMOVE-AND-REFORM-GROUP",
+ "AP-CSA-FINISHED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected + " + ev + " event")
+
+ finally:
+ wpas.global_request("SET p2p_go_freq_change_policy 2")
+
+def test_p2p_channel_vht80(dev):
+ """P2P group formation with VHT 80 MHz"""
+ try:
+ set_country("FI", dev[0])
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ i_freq=5180,
+ i_max_oper_chwidth=80,
+ i_ht40=True, i_vht=True,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq < 5000:
+ raise Exception("Unexpected channel %d MHz - did not follow 5 GHz preference" % freq)
+ sig = dev[1].group_request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ remove_group(dev[0], dev[1])
+ finally:
+ set_country("00")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_vht80p80(dev):
+ """P2P group formation and VHT 80+80 MHz channel"""
+ try:
+ set_country("US", dev[0])
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ i_freq=5180,
+ i_freq2=5775,
+ i_max_oper_chwidth=160,
+ i_ht40=True, i_vht=True,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq < 5000:
+ raise Exception("Unexpected channel %d MHz - did not follow 5 GHz preference" % freq)
+ sig = dev[1].group_request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80+80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "CENTER_FRQ1=5210" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+ if "CENTER_FRQ2=5775" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(4): " + str(sig))
+ remove_group(dev[0], dev[1])
+ finally:
+ set_country("00")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_vht80p80_autogo(dev):
+ """P2P autonomous GO and VHT 80+80 MHz channel"""
+ addr0 = dev[0].p2p_dev_addr()
+
+ try:
+ set_country("US", dev[0])
+ if "OK" not in dev[0].global_request("P2P_GROUP_ADD vht freq=5180 freq2=5775"):
+ raise Exception("Could not start GO")
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("GO start up timed out")
+ dev[0].group_form_result(ev)
+
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+
+ dev[1].global_request("P2P_CONNECT " + addr0 + " " + pin + " join freq=5180")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("Peer did not get connected")
+
+ dev[1].group_form_result(ev)
+ sig = dev[1].group_request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80+80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "CENTER_FRQ1=5210" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+ if "CENTER_FRQ2=5775" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(4): " + str(sig))
+ remove_group(dev[0], dev[1])
+ finally:
+ set_country("00")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_vht80_autogo(dev):
+ """P2P autonomous GO and VHT 80 MHz channel"""
+ addr0 = dev[0].p2p_dev_addr()
+
+ try:
+ set_country("US", dev[0])
+ if "OK" not in dev[0].global_request("P2P_GROUP_ADD vht freq=5180 max_oper_chwidth=80"):
+ raise Exception("Could not start GO")
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("GO start up timed out")
+ dev[0].group_form_result(ev)
+
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+
+ dev[1].global_request("P2P_CONNECT " + addr0 + " " + pin + " join freq=5180")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("Peer did not get connected")
+
+ dev[1].group_form_result(ev)
+ sig = dev[1].group_request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ remove_group(dev[0], dev[1])
+ finally:
+ set_country("00")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_vht80p80_persistent(dev):
+ """P2P persistent group re-invocation and VHT 80+80 MHz channel"""
+ addr0 = dev[0].p2p_dev_addr()
+ form(dev[0], dev[1])
+
+ try:
+ set_country("US", dev[0])
+ invite(dev[0], dev[1], extra="vht freq=5745 freq2=5210")
+ [go_res, cli_res] = check_result(dev[0], dev[1])
+
+ sig = dev[1].group_request("SIGNAL_POLL").splitlines()
+ if "FREQUENCY=5745" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80+80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "CENTER_FRQ1=5775" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+ if "CENTER_FRQ2=5210" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(4): " + str(sig))
+ remove_group(dev[0], dev[1])
+ finally:
+ set_country("00")
+ dev[1].flush_scan_cache()
+
+def test_p2p_channel_drv_pref_go_neg(dev):
+ """P2P GO Negotiation with GO device channel preference"""
+ dev[0].global_request("SET get_pref_freq_list_override 3:2417 4:2422")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq != 2417:
+ raise Exception("Unexpected channel selected: %d" % freq)
+ remove_group(dev[0], dev[1])
+
+def test_p2p_channel_drv_pref_go_neg2(dev):
+ """P2P GO Negotiation with P2P client device channel preference"""
+ dev[0].global_request("SET get_pref_freq_list_override 3:2417,2422")
+ dev[1].global_request("SET get_pref_freq_list_override 4:2422")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq != 2422:
+ raise Exception("Unexpected channel selected: %d" % freq)
+ remove_group(dev[0], dev[1])
+
+def test_p2p_channel_drv_pref_go_neg3(dev):
+ """P2P GO Negotiation with GO device channel preference"""
+ dev[1].global_request("SET get_pref_freq_list_override 3:2417,2427 4:2422")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+ r_dev=dev[1], r_intent=15,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq != 2417:
+ raise Exception("Unexpected channel selected: %d" % freq)
+ remove_group(dev[0], dev[1])
+
+def test_p2p_channel_drv_pref_go_neg4(dev):
+ """P2P GO Negotiation with P2P client device channel preference"""
+ dev[0].global_request("SET get_pref_freq_list_override 3:2417,2422,5180")
+ dev[1].global_request("P2P_SET override_pref_op_chan 115:36")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq != 2417:
+ raise Exception("Unexpected channel selected: %d" % freq)
+ remove_group(dev[0], dev[1])
+
+def test_p2p_channel_drv_pref_go_neg5(dev):
+ """P2P GO Negotiation with P2P client device channel preference"""
+ dev[0].global_request("SET get_pref_freq_list_override 3:2417")
+ dev[1].global_request("SET get_pref_freq_list_override 4:2422")
+ dev[1].global_request("P2P_SET override_pref_op_chan 115:36")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ freq = int(i_res['freq'])
+ if freq != 2417:
+ raise Exception("Unexpected channel selected: %d" % freq)
+ remove_group(dev[0], dev[1])
+
+def test_p2p_channel_drv_pref_autogo(dev):
+ """P2P autonomous GO with driver channel preference"""
+ dev[0].global_request("SET get_pref_freq_list_override 3:2417,2422,5180")
+ res_go = autogo(dev[0])
+ if res_go['freq'] != "2417":
+ raise Exception("Unexpected operating frequency: " + res_go['freq'])
+
+def test_p2p_channel_disable_6ghz(dev):
+ """P2P with 6 GHz disabled"""
+ try:
+ dev[0].global_request("SET p2p_6ghz_disable 1")
+ dev[1].p2p_listen()
+ dev[0].discover_peer(dev[1].p2p_dev_addr(), social=False)
+
+ autogo(dev[1])
+ connect_cli(dev[1], dev[0])
+ finally:
+ dev[0].global_request("SET p2p_6ghz_disable 0")
diff --git a/contrib/wpa/tests/hwsim/test_p2p_concurrency.py b/contrib/wpa/tests/hwsim/test_p2p_concurrency.py
new file mode 100644
index 000000000000..8fb2bb9294ab
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_concurrency.py
@@ -0,0 +1,286 @@
+# P2P concurrency test cases
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+
+import hwsim_utils
+import hostapd
+from p2p_utils import *
+from utils import *
+
+@remote_compatible
+def test_concurrent_autogo(dev, apdev):
+ """Concurrent P2P autonomous GO"""
+ logger.info("Connect to an infrastructure AP")
+ dev[0].request("P2P_SET cross_connect 0")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ logger.info("Start a P2P group while associated to an AP")
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[0].p2p_start_go()
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60,
+ social=True)
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+ logger.info("Confirm AP connection after P2P group removal")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_concurrent_autogo_5ghz_ht40(dev, apdev):
+ """Concurrent P2P autonomous GO on 5 GHz and HT40 co-ex"""
+ clear_scan_cache(apdev[1])
+ try:
+ hapd = None
+ hapd2 = None
+ params = {"ssid": "ht40",
+ "hw_mode": "a",
+ "channel": "153",
+ "country_code": "US",
+ "ht_capab": "[HT40-]"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-open-5",
+ "hw_mode": "a",
+ "channel": "149",
+ "country_code": "US"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("P2P_SET cross_connect 0")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=5745)
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq=5765)
+ dev[0].connect("test-open-5", key_mgmt="NONE", scan_freq="5745")
+
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ if "OK" not in dev[0].global_request("P2P_GROUP_ADD ht40"):
+ raise Exception("P2P_GROUP_ADD failed")
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("GO start up timed out")
+ dev[0].group_form_result(ev)
+
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_find(freq=5745)
+ addr0 = dev[0].p2p_dev_addr()
+ count = 0
+ while count < 10:
+ time.sleep(0.25)
+ count += 1
+ if dev[1].peer_known(addr0):
+ break
+ dev[1].p2p_connect_group(addr0, pin, timeout=60)
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ finally:
+ dev[0].request("REMOVE_NETWORK all")
+ if hapd:
+ hapd.request("DISABLE")
+ if hapd2:
+ hapd2.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_concurrent_autogo_crossconnect(dev, apdev):
+ """Concurrent P2P autonomous GO"""
+ dev[0].global_request("P2P_SET cross_connect 1")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[0].p2p_start_go(no_event_clear=True)
+ ev = dev[0].wait_global_event(["P2P-CROSS-CONNECT-ENABLE"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on cross connection enabled event")
+ if dev[0].group_ifname + " " + dev[0].ifname not in ev:
+ raise Exception("Unexpected interfaces: " + ev)
+ dev[0].dump_monitor()
+
+ dev[0].global_request("P2P_SET cross_connect 0")
+ ev = dev[0].wait_global_event(["P2P-CROSS-CONNECT-DISABLE"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on cross connection disabled event")
+ if dev[0].group_ifname + " " + dev[0].ifname not in ev:
+ raise Exception("Unexpected interfaces: " + ev)
+ dev[0].remove_group()
+
+ dev[0].global_request("P2P_SET cross_connect 1")
+ dev[0].p2p_start_go(no_event_clear=True)
+ ev = dev[0].wait_global_event(["P2P-CROSS-CONNECT-ENABLE"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on cross connection enabled event")
+ if dev[0].group_ifname + " " + dev[0].ifname not in ev:
+ raise Exception("Unexpected interfaces: " + ev)
+ dev[0].dump_monitor()
+ dev[0].remove_group()
+ ev = dev[0].wait_global_event(["P2P-CROSS-CONNECT-DISABLE"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on cross connection disabled event")
+ dev[0].global_request("P2P_SET cross_connect 0")
+
+@remote_compatible
+def test_concurrent_p2pcli(dev, apdev):
+ """Concurrent P2P client join"""
+ logger.info("Connect to an infrastructure AP")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ logger.info("Join a P2P group while associated to an AP")
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[1].p2p_start_go(freq=2412)
+ pin = dev[0].wps_read_pin()
+ dev[1].p2p_go_authorize_client(pin)
+ dev[0].p2p_connect_group(dev[1].p2p_dev_addr(), pin, timeout=60,
+ social=True)
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ dev[1].remove_group()
+ dev[0].wait_go_ending_session()
+
+ logger.info("Confirm AP connection after P2P group removal")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_concurrent_grpform_go(dev, apdev):
+ """Concurrent P2P group formation to become GO"""
+ logger.info("Connect to an infrastructure AP")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ logger.info("Form a P2P group while associated to an AP")
+ dev[0].global_request("SET p2p_no_group_iface 0")
+
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], dev[1])
+
+ logger.info("Confirm AP connection after P2P group removal")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_concurrent_grpform_cli(dev, apdev):
+ """Concurrent P2P group formation to become P2P Client"""
+ logger.info("Connect to an infrastructure AP")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ logger.info("Form a P2P group while associated to an AP")
+ dev[0].global_request("SET p2p_no_group_iface 0")
+
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+ r_dev=dev[1], r_intent=15)
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], dev[1])
+
+ logger.info("Confirm AP connection after P2P group removal")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_concurrent_grpform_while_connecting(dev, apdev):
+ """Concurrent P2P group formation while connecting to an AP"""
+ logger.info("Start connection to an infrastructure AP")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE", wait_connect=False)
+
+ logger.info("Form a P2P group while connecting to an AP")
+ dev[0].global_request("SET p2p_no_group_iface 0")
+
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_freq=2412,
+ r_dev=dev[1], r_freq=2412)
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], dev[1])
+
+ logger.info("Confirm AP connection after P2P group removal")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_concurrent_grpform_while_connecting2(dev, apdev):
+ """Concurrent P2P group formation while connecting to an AP (2)"""
+ logger.info("Start connection to an infrastructure AP")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE", wait_connect=False)
+ dev[1].flush_scan_cache()
+
+ logger.info("Form a P2P group while connecting to an AP")
+ dev[0].global_request("SET p2p_no_group_iface 0")
+
+ [i_res, r_res] = go_neg_pbc(i_dev=dev[0], i_intent=15, i_freq=2412,
+ r_dev=dev[1], r_intent=0, r_freq=2412)
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], dev[1])
+
+ logger.info("Confirm AP connection after P2P group removal")
+ dev[0].wait_completed()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_concurrent_grpform_while_connecting3(dev, apdev):
+ """Concurrent P2P group formation while connecting to an AP (3)"""
+ logger.info("Start connection to an infrastructure AP")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-open"})
+ dev[0].connect("test-open", key_mgmt="NONE", wait_connect=False)
+
+ logger.info("Form a P2P group while connecting to an AP")
+ dev[0].global_request("SET p2p_no_group_iface 0")
+
+ [i_res, r_res] = go_neg_pbc(i_dev=dev[1], i_intent=15, i_freq=2412,
+ r_dev=dev[0], r_intent=0, r_freq=2412)
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], dev[1])
+
+ logger.info("Confirm AP connection after P2P group removal")
+ dev[0].wait_completed()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+@remote_compatible
+def test_concurrent_persistent_group(dev, apdev):
+ """Concurrent P2P persistent group"""
+ logger.info("Connect to an infrastructure AP")
+ hostapd.add_ap(apdev[0], {"ssid": "test-open", "channel": "2"})
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2417")
+
+ logger.info("Run persistent group test while associated to an AP")
+ form(dev[0], dev[1])
+ [go_res, cli_res] = invite_from_cli(dev[0], dev[1])
+ if go_res['freq'] != '2417':
+ raise Exception("Unexpected channel selected: " + go_res['freq'])
+ [go_res, cli_res] = invite_from_go(dev[0], dev[1])
+ if go_res['freq'] != '2417':
+ raise Exception("Unexpected channel selected: " + go_res['freq'])
+
+def test_concurrent_invitation_channel_mismatch(dev, apdev):
+ """P2P persistent group invitation and channel mismatch"""
+ if dev[0].get_mcc() > 1:
+ raise HwsimSkip("Skip due to MCC being enabled")
+
+ form(dev[0], dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.info("Connect to an infrastructure AP")
+ hostapd.add_ap(apdev[0], {"ssid": "test-open", "channel": "2"})
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[0].connect("test-open", key_mgmt="NONE", scan_freq="2417")
+ invite(dev[1], dev[0], extra="freq=2412")
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+ if ev is None:
+ raise Exception("P2P invitation result not received")
+ if "status=7" not in ev:
+ raise Exception("Unexpected P2P invitation result: " + ev)
diff --git a/contrib/wpa/tests/hwsim/test_p2p_device.py b/contrib/wpa/tests/hwsim/test_p2p_device.py
new file mode 100644
index 000000000000..ed781d5c50fc
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_device.py
@@ -0,0 +1,552 @@
+# cfg80211 P2P Device
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+
+from wpasupplicant import WpaSupplicant
+from p2p_utils import *
+from test_nfc_p2p import set_ip_addr_info, check_ip_addr, grpform_events
+from hwsim import HWSimRadio
+from utils import HwsimSkip
+import hostapd
+import hwsim_utils
+
+def test_p2p_device_grpform(dev, apdev):
+ """P2P group formation with driver using cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=wpas, r_intent=0)
+ check_grpform_results(i_res, r_res)
+ wpas.dump_monitor()
+ remove_group(dev[0], wpas)
+ wpas.dump_monitor()
+ if not r_res['ifname'].startswith('p2p-' + iface):
+ raise Exception("Unexpected group ifname: " + r_res['ifname'])
+
+ res = wpas.global_request("IFNAME=p2p-dev-" + iface + " STATUS-DRIVER")
+ lines = res.splitlines()
+ found = False
+ for l in lines:
+ try:
+ [name, value] = l.split('=', 1)
+ if name == "wdev_id":
+ found = True
+ break
+ except ValueError:
+ pass
+ if not found:
+ raise Exception("wdev_id not found")
+
+def test_p2p_device_grpform2(dev, apdev):
+ """P2P group formation with driver using cfg80211 P2P Device (reverse)"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=wpas, i_intent=15,
+ r_dev=dev[0], r_intent=0)
+ check_grpform_results(i_res, r_res)
+ wpas.dump_monitor()
+ remove_group(wpas, dev[0])
+ wpas.dump_monitor()
+ if not i_res['ifname'].startswith('p2p-' + iface):
+ raise Exception("Unexpected group ifname: " + i_res['ifname'])
+
+def test_p2p_device_grpform_no_group_iface(dev, apdev):
+ """P2P group formation with driver using cfg80211 P2P Device but no separate group interface"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 1")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=wpas, r_intent=0)
+ check_grpform_results(i_res, r_res)
+ wpas.dump_monitor()
+ remove_group(dev[0], wpas)
+ wpas.dump_monitor()
+ if r_res['ifname'] != iface:
+ raise Exception("Unexpected group ifname: " + r_res['ifname'])
+
+def test_p2p_device_grpform_no_group_iface2(dev, apdev):
+ """P2P group formation with driver using cfg80211 P2P Device but no separate group interface (reverse)"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 1")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=wpas, i_intent=15,
+ r_dev=dev[0], r_intent=0)
+ check_grpform_results(i_res, r_res)
+ wpas.dump_monitor()
+ remove_group(dev[0], wpas)
+ wpas.dump_monitor()
+ if i_res['ifname'] != iface:
+ raise Exception("Unexpected group ifname: " + i_res['ifname'])
+
+def test_p2p_device_group_remove(dev, apdev):
+ """P2P group removal via the P2P ctrl interface with driver using cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=wpas, r_intent=0)
+ check_grpform_results(i_res, r_res)
+ # Issue the remove request on the interface which will be removed
+ p2p_iface_wpas = WpaSupplicant(ifname=r_res['ifname'])
+ res = p2p_iface_wpas.request("P2P_GROUP_REMOVE *")
+ if "OK" not in res:
+ raise Exception("Failed to remove P2P group")
+ ev = wpas.wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+ if ev is None:
+ raise Exception("Group removal event not received")
+ if not wpas.global_ping():
+ raise Exception("Could not ping global ctrl_iface after group removal")
+
+def test_p2p_device_concurrent_scan(dev, apdev):
+ """Concurrent P2P and station mode scans with driver using cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.p2p_find()
+ time.sleep(0.1)
+ wpas.request("SCAN")
+ ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Station mode scan did not start")
+
+def test_p2p_device_nfc_invite(dev, apdev):
+ """P2P NFC invitation with driver using cfg80211 P2P Device"""
+ run_p2p_device_nfc_invite(dev, apdev, 0)
+
+def test_p2p_device_nfc_invite_no_group_iface(dev, apdev):
+ """P2P NFC invitation with driver using cfg80211 P2P Device (no separate group interface)"""
+ run_p2p_device_nfc_invite(dev, apdev, 1)
+
+def run_p2p_device_nfc_invite(dev, apdev, no_group_iface):
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface %d" % no_group_iface)
+
+ set_ip_addr_info(dev[0])
+ logger.info("Start autonomous GO")
+ dev[0].p2p_start_go()
+
+ logger.info("Write NFC Tag on the P2P Client")
+ res = wpas.global_request("P2P_LISTEN")
+ if "FAIL" in res:
+ raise Exception("Failed to start Listen mode")
+ wpas.dump_monitor()
+ pw = wpas.global_request("WPS_NFC_TOKEN NDEF").rstrip()
+ if "FAIL" in pw:
+ raise Exception("Failed to generate password token")
+ res = wpas.global_request("P2P_SET nfc_tag 1").rstrip()
+ if "FAIL" in res:
+ raise Exception("Failed to enable NFC Tag for P2P static handover")
+ sel = wpas.global_request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+ if "FAIL" in sel:
+ raise Exception("Failed to generate NFC connection handover select")
+ wpas.dump_monitor()
+
+ logger.info("Read NFC Tag on the GO to trigger invitation")
+ res = dev[0].global_request("WPS_NFC_TAG_READ " + sel)
+ if "FAIL" in res:
+ raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
+
+ ev = wpas.wait_global_event(grpform_events, timeout=20)
+ if ev is None:
+ raise Exception("Joining the group timed out")
+ res = wpas.group_form_result(ev)
+ wpas.dump_monitor()
+ hwsim_utils.test_connectivity_p2p(dev[0], wpas)
+ check_ip_addr(res)
+ wpas.dump_monitor()
+
+def test_p2p_device_misuses(dev, apdev):
+ """cfg80211 P2P Device misuses"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ # Add a normal network profile to the P2P Device management only
+ # interface to verify that it does not get used.
+ id = int(wpas.global_request('IFNAME=p2p-dev-%s ADD_NETWORK' % iface).strip())
+ wpas.global_request('IFNAME=p2p-dev-%s SET_NETWORK %d ssid "open"' % (iface, id))
+ wpas.global_request('IFNAME=p2p-dev-%s SET_NETWORK %d key_mgmt NONE' % (iface, id))
+ wpas.global_request('IFNAME=p2p-dev-%s ENABLE_NETWORK %d' % (iface, id))
+
+ # Scan requests get ignored on p2p-dev
+ wpas.global_request('IFNAME=p2p-dev-%s SCAN' % iface)
+
+ dev[0].p2p_start_go(freq=2412)
+ addr = dev[0].p2p_interface_addr()
+ wpas.scan_for_bss(addr, freq=2412)
+ wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(wpas, hapd)
+
+ pin = wpas.wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ res = wpas.p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60,
+ social=True, freq=2412)
+ hwsim_utils.test_connectivity_p2p(dev[0], wpas)
+
+ # Optimize scan-after-disconnect
+ wpas.group_request("SET_NETWORK 0 scan_freq 2412")
+
+ dev[0].group_request("DISASSOCIATE " + wpas.p2p_interface_addr())
+ ev = wpas.wait_group_event(["CTRL-EVENT-DISCONNECT"])
+ if ev is None:
+ raise Exception("Did not see disconnect event on P2P group interface")
+ dev[0].remove_group()
+
+ ev = wpas.wait_group_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Scan not started")
+ ev = wpas.wait_group_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=15)
+ if ev is None:
+ raise Exception("Scan not completed")
+ time.sleep(1)
+ hwsim_utils.test_connectivity(wpas, hapd)
+
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection event received from hostapd")
+ ev = wpas.wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection event received from wpa_supplicant")
+
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+
+def test_p2p_device_incorrect_command_interface(dev, apdev):
+ """cfg80211 P2P Device and P2P_* command on incorrect interface"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ dev[0].p2p_listen()
+ wpas.request('P2P_FIND type=social')
+ ev = wpas.wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("Peer not found")
+ ev = wpas.wait_event(["P2P-DEVICE-FOUND"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected P2P-DEVICE-FOUND event on station interface")
+ wpas.dump_monitor()
+
+ pin = wpas.wps_read_pin()
+ dev[0].p2p_go_neg_auth(wpas.p2p_dev_addr(), pin, "enter", go_intent=14,
+ freq=2412)
+ wpas.request('P2P_STOP_FIND')
+ wpas.dump_monitor()
+ if "OK" not in wpas.request('P2P_CONNECT ' + dev[0].p2p_dev_addr() + ' ' + pin + ' display go_intent=1'):
+ raise Exception("P2P_CONNECT failed")
+
+ ev = wpas.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ wpas.group_form_result(ev)
+ wpas.dump_monitor()
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out(2)")
+ dev[0].group_form_result(ev)
+
+ dev[0].remove_group()
+ wpas.wait_go_ending_session()
+ wpas.dump_monitor()
+
+def test_p2p_device_incorrect_command_interface2(dev, apdev):
+ """cfg80211 P2P Device and P2P_GROUP_ADD command on incorrect interface"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if "OK" not in wpas.request('P2P_GROUP_ADD'):
+ raise Exception("P2P_GROUP_ADD failed")
+ ev = wpas.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ res = wpas.group_form_result(ev)
+ wpas.dump_monitor()
+ logger.info("Group results: " + str(res))
+ wpas.remove_group()
+ if not res['ifname'].startswith('p2p-' + iface + '-'):
+ raise Exception("Unexpected group ifname: " + res['ifname'])
+ wpas.dump_monitor()
+
+def test_p2p_device_grpform_timeout_client(dev, apdev):
+ """P2P group formation timeout on client with cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ addr0 = dev[0].p2p_dev_addr()
+ addr5 = wpas.p2p_dev_addr()
+ wpas.p2p_listen()
+ dev[0].discover_peer(addr5)
+ dev[0].p2p_listen()
+ wpas.discover_peer(addr0)
+ wpas.p2p_ext_listen(100, 150)
+ dev[0].global_request("P2P_CONNECT " + addr5 + " 12345670 enter go_intent=15 auth")
+ wpas.global_request("P2P_CONNECT " + addr0 + " 12345670 display go_intent=0")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("GO Negotiation did not succeed")
+ ev = dev[0].wait_global_event(["WPS-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("WPS did not succeed (GO)")
+ if "OK" not in dev[0].global_request("P2P_CANCEL"):
+ wpas.global_request("P2P_CANCEL")
+ del wpas
+ raise HwsimSkip("Did not manage to cancel group formation")
+ dev[0].dump_monitor()
+ ev = wpas.wait_global_event(["WPS-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("WPS did not succeed (Client)")
+ dev[0].dump_monitor()
+ ev = wpas.wait_global_event(["P2P-GROUP-FORMATION-FAILURE"], timeout=20)
+ if ev is None:
+ raise Exception("Group formation timeout not seen on client")
+ ev = wpas.wait_global_event(["P2P-GROUP-REMOVED"], timeout=5)
+ if ev is None:
+ raise Exception("Group removal not seen on client")
+ wpas.p2p_cancel_ext_listen()
+ time.sleep(0.1)
+ ifaces = wpas.global_request("INTERFACES")
+ logger.info("Remaining interfaces: " + ifaces)
+ del wpas
+ if "p2p-" + iface + "-" in ifaces:
+ raise Exception("Group interface still present after failure")
+
+def test_p2p_device_grpform_timeout_go(dev, apdev):
+ """P2P group formation timeout on GO with cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ addr0 = dev[0].p2p_dev_addr()
+ addr5 = wpas.p2p_dev_addr()
+ wpas.p2p_listen()
+ dev[0].discover_peer(addr5)
+ dev[0].p2p_listen()
+ wpas.discover_peer(addr0)
+ wpas.p2p_ext_listen(100, 150)
+ dev[0].global_request("P2P_CONNECT " + addr5 + " 12345670 enter go_intent=0 auth")
+ wpas.global_request("P2P_CONNECT " + addr0 + " 12345670 display go_intent=15")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("GO Negotiation did not succeed")
+ ev = dev[0].wait_global_event(["WPS-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("WPS did not succeed (Client)")
+ if "OK" not in dev[0].global_request("P2P_CANCEL"):
+ if "OK" not in dev[0].global_request("P2P_GROUP_REMOVE *"):
+ wpas.global_request("P2P_CANCEL")
+ del wpas
+ raise HwsimSkip("Did not manage to cancel group formation")
+ dev[0].dump_monitor()
+ ev = wpas.wait_global_event(["WPS-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("WPS did not succeed (GO)")
+ dev[0].dump_monitor()
+ ev = wpas.wait_global_event(["P2P-GROUP-FORMATION-FAILURE"], timeout=20)
+ if ev is None:
+ raise Exception("Group formation timeout not seen on GO")
+ ev = wpas.wait_global_event(["P2P-GROUP-REMOVED"], timeout=5)
+ if ev is None:
+ raise Exception("Group removal not seen on GO")
+ wpas.p2p_cancel_ext_listen()
+ time.sleep(0.1)
+ ifaces = wpas.global_request("INTERFACES")
+ logger.info("Remaining interfaces: " + ifaces)
+ del wpas
+ if "p2p-" + iface + "-" in ifaces:
+ raise Exception("Group interface still present after failure")
+
+def test_p2p_device_autogo(dev, apdev):
+ """P2P autogo using cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ res = wpas.p2p_start_go()
+ if not res['ifname'].startswith('p2p-' + iface):
+ raise Exception("Unexpected group ifname: " + res['ifname'])
+ bssid = wpas.get_group_status_field('bssid')
+
+ dev[0].scan_for_bss(bssid, res['freq'])
+ connect_cli(wpas, dev[0], freq=res['freq'])
+ terminate_group(wpas, dev[0])
+
+def test_p2p_device_autogo_no_group_iface(dev, apdev):
+ """P2P autogo using cfg80211 P2P Device (no separate group interface)"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 1")
+
+ res = wpas.p2p_start_go()
+ if res['ifname'] != iface:
+ raise Exception("Unexpected group ifname: " + res['ifname'])
+ bssid = wpas.get_group_status_field('bssid')
+
+ dev[0].scan_for_bss(bssid, res['freq'])
+ connect_cli(wpas, dev[0], freq=res['freq'])
+ terminate_group(wpas, dev[0])
+
+def test_p2p_device_join(dev, apdev):
+ """P2P join-group using cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ res = dev[0].p2p_start_go()
+ bssid = dev[0].get_group_status_field('bssid')
+
+ wpas.scan_for_bss(bssid, res['freq'])
+ res2 = connect_cli(dev[0], wpas, freq=res['freq'])
+ if not res2['ifname'].startswith('p2p-' + iface):
+ raise Exception("Unexpected group ifname: " + res2['ifname'])
+
+ terminate_group(dev[0], wpas)
+
+def test_p2p_device_join_no_group_iface(dev, apdev):
+ """P2P join-group using cfg80211 P2P Device (no separate group interface)"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 1")
+
+ res = dev[0].p2p_start_go()
+ bssid = dev[0].get_group_status_field('bssid')
+
+ wpas.scan_for_bss(bssid, res['freq'])
+ res2 = connect_cli(dev[0], wpas, freq=res['freq'])
+ if res2['ifname'] != iface:
+ raise Exception("Unexpected group ifname: " + res2['ifname'])
+
+ terminate_group(dev[0], wpas)
+
+def test_p2p_device_join_no_group_iface_cancel(dev, apdev):
+ """P2P cancel join-group using cfg80211 P2P Device (no separate group interface)"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 1")
+
+ res = dev[0].p2p_start_go()
+ bssid = dev[0].get_group_status_field('bssid')
+
+ wpas.scan_for_bss(bssid, res['freq'])
+ pin = wpas.wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ cmd = "P2P_CONNECT %s %s join freq=%s" % (dev[0].p2p_dev_addr(), pin,
+ res['freq'])
+ if "OK" not in wpas.request(cmd):
+ raise Exception("P2P_CONNECT(join) failed")
+ ev = wpas.wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=1)
+ if "OK" not in wpas.request("P2P_CANCEL"):
+ raise Exception("P2P_CANCEL failed")
+
+ dev[0].remove_group()
+
+def test_p2p_device_persistent_group(dev):
+ """P2P persistent group formation and re-invocation with cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 0")
+
+ form(dev[0], wpas)
+ invite_from_cli(dev[0], wpas)
+ invite_from_go(dev[0], wpas)
+
+def test_p2p_device_persistent_group_no_group_iface(dev):
+ """P2P persistent group formation and re-invocation with cfg80211 P2P Device (no separate group interface)"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 1")
+
+ form(dev[0], wpas)
+ invite_from_cli(dev[0], wpas)
+ invite_from_go(dev[0], wpas)
+
+def test_p2p_device_persistent_group2(dev):
+ """P2P persistent group formation and re-invocation (reverse) with cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 0")
+
+ form(wpas, dev[0])
+ invite_from_cli(wpas, dev[0])
+ invite_from_go(wpas, dev[0])
+
+def test_p2p_device_persistent_group2_no_group_iface(dev):
+ """P2P persistent group formation and re-invocation (reverse) with cfg80211 P2P Device (no separate group interface)"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 1")
+
+ form(wpas, dev[0])
+ invite_from_cli(wpas, dev[0])
+ invite_from_go(wpas, dev[0])
+
+def p2p_device_group_conf(dev1, dev2):
+ dev1.global_request("SET p2p_group_idle 12")
+ dev1.global_request("SET p2p_go_freq_change_policy 2")
+ dev1.global_request("SET p2p_go_ctwindow 7")
+
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev1, i_intent=15,
+ r_dev=dev2, r_intent=0)
+ check_grpform_results(i_res, r_res)
+
+ if (dev1.group_request("GET p2p_group_idle") != "12" or
+ dev1.group_request("GET p2p_go_freq_change_policy") != "2" or
+ dev1.group_request("GET p2p_go_ctwindow") != "7"):
+ raise Exception("Unexpected configuration value")
+
+ remove_group(dev1, dev2)
+ dev1.global_request("P2P_FLUSH")
+ dev2.global_request("P2P_FLUSH")
+
+def test_p2p_device_conf(dev, apdev):
+ """P2P configuration with cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 1")
+ p2p_device_group_conf(wpas, dev[0])
+ wpas.global_request("SET p2p_no_group_iface 0")
+ p2p_device_group_conf(wpas, dev[0])
+
+def test_p2p_device_autogo_chan_switch(dev):
+ """P2P autonomous GO switching channels with cfg80211 P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface 1")
+ autogo(wpas, freq=2417)
+ connect_cli(wpas, dev[1])
+ res = wpas.group_request("CHAN_SWITCH 5 2422")
+ if "FAIL" in res:
+ # for now, skip test since mac80211_hwsim support is not yet widely
+ # deployed
+ raise HwsimSkip("Assume mac80211_hwsim did not support channel switching")
+ ev = wpas.wait_group_event(["AP-CSA-FINISHED"], timeout=10)
+ if ev is None:
+ raise Exception("CSA finished event timed out")
+ if "freq=2422" not in ev:
+ raise Exception("Unexpected cahnnel in CSA finished event")
+ wpas.dump_monitor()
+ dev[1].dump_monitor()
+ time.sleep(0.1)
+ hwsim_utils.test_connectivity_p2p(wpas, dev[1])
diff --git a/contrib/wpa/tests/hwsim/test_p2p_discovery.py b/contrib/wpa/tests/hwsim/test_p2p_discovery.py
new file mode 100644
index 000000000000..0537f02e9e5b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_discovery.py
@@ -0,0 +1,871 @@
+# P2P device discovery test cases
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import binascii
+import os
+import struct
+import time
+
+import hostapd
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+from p2p_utils import *
+from hwsim import HWSimRadio
+from tshark import run_tshark
+from test_gas import start_ap
+from test_cfg80211 import nl80211_remain_on_channel
+from test_p2p_channel import set_country
+
+@remote_compatible
+def test_discovery(dev):
+ """P2P device discovery and provision discovery"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ logger.info("Start device discovery")
+ dev[0].p2p_find(social=True, delay=1)
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Device discovery timed out")
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+
+ logger.info("Test provision discovery for display")
+ dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
+ ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
+ if ev1 is None:
+ raise Exception("Provision discovery timed out (display/dev1)")
+ if addr0 not in ev1:
+ raise Exception("Dev0 not in provision discovery event")
+ ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN",
+ "P2P-PROV-DISC-FAILURE"], timeout=15)
+ if ev0 is None:
+ raise Exception("Provision discovery timed out (display/dev0)")
+ if "P2P-PROV-DISC-FAILURE" in ev0:
+ raise Exception("Provision discovery failed (display/dev0)")
+ if addr1 not in ev0:
+ raise Exception("Dev1 not in provision discovery event")
+
+ logger.info("Test provision discovery for keypad")
+ dev[0].global_request("P2P_PROV_DISC " + addr1 + " keypad")
+ ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=15)
+ if ev1 is None:
+ raise Exception("Provision discovery timed out (keypad/dev1)")
+ if addr0 not in ev1:
+ raise Exception("Dev0 not in provision discovery event")
+ ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN",
+ "P2P-PROV-DISC-FAILURE"],
+ timeout=15)
+ if ev0 is None:
+ raise Exception("Provision discovery timed out (keypad/dev0)")
+ if "P2P-PROV-DISC-FAILURE" in ev0:
+ raise Exception("Provision discovery failed (keypad/dev0)")
+ if addr1 not in ev0:
+ raise Exception("Dev1 not in provision discovery event")
+
+ logger.info("Test provision discovery for push button")
+ dev[0].global_request("P2P_PROV_DISC " + addr1 + " pbc")
+ ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-PBC-REQ"], timeout=15)
+ if ev1 is None:
+ raise Exception("Provision discovery timed out (pbc/dev1)")
+ if addr0 not in ev1:
+ raise Exception("Dev0 not in provision discovery event")
+ ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-PBC-RESP",
+ "P2P-PROV-DISC-FAILURE"],
+ timeout=15)
+ if ev0 is None:
+ raise Exception("Provision discovery timed out (pbc/dev0)")
+ if "P2P-PROV-DISC-FAILURE" in ev0:
+ raise Exception("Provision discovery failed (pbc/dev0)")
+ if addr1 not in ev0:
+ raise Exception("Dev1 not in provision discovery event")
+
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+
+ if "FAIL" not in dev[0].p2p_find(dev_id="foo"):
+ raise Exception("P2P_FIND with invalid dev_id accepted")
+ if "FAIL" not in dev[0].p2p_find(dev_type="foo"):
+ raise Exception("P2P_FIND with invalid dev_type accepted")
+ if "FAIL" not in dev[0].p2p_find(dev_type="1-foo-2"):
+ raise Exception("P2P_FIND with invalid dev_type accepted")
+ if "FAIL" not in dev[0].p2p_find(dev_type="1-11223344"):
+ raise Exception("P2P_FIND with invalid dev_type accepted")
+
+ if "FAIL" not in dev[0].global_request("P2P_PROV_DISC foo pbc"):
+ raise Exception("Invalid P2P_PROV_DISC accepted")
+ if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 00:11:22:33:44:55"):
+ raise Exception("Invalid P2P_PROV_DISC accepted")
+ if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 00:11:22:33:44:55 pbc join"):
+ raise Exception("Invalid P2P_PROV_DISC accepted")
+ if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 00:11:22:33:44:55 foo"):
+ raise Exception("Invalid P2P_PROV_DISC accepted")
+
+@remote_compatible
+def test_discovery_pd_retries(dev):
+ """P2P device discovery and provision discovery retries"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_stop_find()
+ dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=60)
+ if ev is None:
+ raise Exception("No PD failure reported")
+
+def test_discovery_group_client(dev):
+ """P2P device discovery for a client in a group"""
+ logger.info("Start autonomous GO " + dev[0].ifname)
+ res = dev[0].p2p_start_go(freq="2422")
+ logger.debug("res: " + str(res))
+ logger.info("Connect a client to the GO")
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, freq=int(res['freq']),
+ timeout=60)
+ logger.info("Client connected")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ logger.info("Try to discover a P2P client in a group")
+ if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
+ stop_p2p_find_and_wait(dev[2])
+ if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
+ stop_p2p_find_and_wait(dev[2])
+ if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
+ raise Exception("Could not discover group client")
+
+ # This is not really perfect, but something to get a bit more testing
+ # coverage.. For proper discoverability mechanism validation, the P2P
+ # client would need to go to sleep to avoid acknowledging the GO Negotiation
+ # Request frame. Offchannel Listen mode operation on the P2P Client with
+ # mac80211_hwsim is apparently not enough to avoid the acknowledgement on
+ # the operating channel, so need to disconnect from the group which removes
+ # the GO-to-P2P Client part of the discoverability exchange in practice.
+
+ pin = dev[2].wps_read_pin()
+ # make group client non-responsive on operating channel
+ dev[1].dump_monitor()
+ dev[1].group_request("DISCONNECT")
+ ev = dev[1].wait_group_event(["CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on waiting disconnection")
+ dev[2].request("P2P_CONNECT {} {} display".format(dev[1].p2p_dev_addr(),
+ pin))
+ ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=2)
+ if ev:
+ raise Exception("Unexpected frame RX on P2P client")
+ # make group client available on operating channe
+ dev[1].group_request("REASSOCIATE")
+ ev = dev[1].wait_global_event(["CTRL-EVENT-CONNECTED",
+ "P2P-GO-NEG-REQUEST"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on reconnection to group")
+ if "P2P-GO-NEG-REQUEST" not in ev:
+ ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on waiting for GO Negotiation Request")
+
+def stop_p2p_find_and_wait(dev):
+ dev.request("P2P_STOP_FIND")
+ for i in range(10):
+ res = dev.get_driver_status_field("scan_state")
+ if "SCAN_STARTED" not in res and "SCAN_REQUESTED" not in res:
+ break
+ logger.debug("Waiting for final P2P_FIND scan to complete")
+ time.sleep(0.02)
+
+def test_discovery_ctrl_char_in_devname(dev):
+ """P2P device discovery and control character in Device Name"""
+ try:
+ _test_discovery_ctrl_char_in_devname(dev)
+ finally:
+ dev[1].global_request("SET device_name Device B")
+
+def _test_discovery_ctrl_char_in_devname(dev):
+ dev[1].global_request("SET device_name Device\tB")
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ res = dev[0].p2p_start_go(freq=2422)
+ bssid = dev[0].p2p_interface_addr()
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].scan_for_bss(bssid, freq=2422)
+ dev[1].p2p_connect_group(addr0, pin, timeout=60, freq=2422)
+ if not dev[2].discover_peer(addr1, social=False, freq=2422, timeout=5):
+ stop_p2p_find_and_wait(dev[2])
+ if not dev[2].discover_peer(addr1, social=False, freq=2422, timeout=5):
+ stop_p2p_find_and_wait(dev[2])
+ if not dev[2].discover_peer(addr1, social=False, freq=2422,
+ timeout=5):
+ raise Exception("Could not discover group client")
+ devname = dev[2].get_peer(addr1)['device_name']
+ dev[2].p2p_stop_find()
+ if devname != "Device_B":
+ raise Exception("Unexpected device_name from group client: " + devname)
+
+ terminate_group(dev[0], dev[1])
+ dev[2].request("P2P_FLUSH")
+
+ dev[1].p2p_listen()
+ if not dev[2].discover_peer(addr1, social=True, timeout=10):
+ raise Exception("Could not discover peer")
+ devname = dev[2].get_peer(addr1)['device_name']
+ dev[2].p2p_stop_find()
+ if devname != "Device_B":
+ raise Exception("Unexpected device_name from peer: " + devname)
+
+@remote_compatible
+def test_discovery_dev_type(dev):
+ """P2P device discovery with Device Type filter"""
+ dev[1].request("SET sec_device_type 1-0050F204-2")
+ dev[1].p2p_listen()
+ dev[0].p2p_find(social=True, dev_type="5-0050F204-1")
+ ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
+ if ev:
+ raise Exception("Unexpected P2P device found")
+ dev[0].p2p_find(social=True, dev_type="1-0050F204-2")
+ ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=2)
+ if ev is None:
+ raise Exception("P2P device not found")
+ peer = dev[0].get_peer(dev[1].p2p_dev_addr())
+ if "1-0050F204-2" not in peer['sec_dev_type']:
+ raise Exception("sec_device_type not reported properly")
+
+def test_discovery_dev_type_go(dev):
+ """P2P device discovery with Device Type filter on GO"""
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].request("SET sec_device_type 1-0050F204-2")
+ res = dev[0].p2p_start_go(freq="2412")
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
+
+ dev[2].p2p_find(social=True, dev_type="5-0050F204-1")
+ ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
+ if ev:
+ raise Exception("Unexpected P2P device found")
+ dev[2].p2p_find(social=True, dev_type="1-0050F204-2")
+ ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND ' + addr1], timeout=2)
+ if ev is None:
+ raise Exception("P2P device not found")
+
+def test_discovery_dev_id(dev):
+ """P2P device discovery with Device ID filter"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.request("P2P_LISTEN 1")
+ status = wpas.global_request("STATUS")
+ if "p2p_state=LISTEN_ONLY" not in status:
+ raise Exception("Unexpected status: " + status)
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].p2p_listen()
+ dev[0].p2p_find(social=True, dev_id="02:03:04:05:06:07")
+ ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
+ if ev:
+ raise Exception("Unexpected P2P device found")
+ dev[0].p2p_find(social=True, dev_id=addr1)
+ ev = dev[0].wait_global_event(['P2P-DEVICE-FOUND'], timeout=5)
+ if ev is None:
+ raise Exception("P2P device not found")
+ if addr1 not in ev:
+ raise Exception("Unexpected P2P peer found")
+ status = wpas.global_request("STATUS")
+ for i in range(0, 2):
+ if "p2p_state=IDLE" in status:
+ break
+ time.sleep(0.5)
+ status = wpas.global_request("STATUS")
+ if "p2p_state=IDLE" not in status:
+ raise Exception("Unexpected status: " + status)
+
+def test_discovery_dev_id_go(dev):
+ """P2P device discovery with Device ID filter on GO"""
+ addr1 = dev[1].p2p_dev_addr()
+ res = dev[0].p2p_start_go(freq="2412")
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
+
+ dev[2].p2p_find(social=True, dev_id="02:03:04:05:06:07")
+ ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND'], timeout=1)
+ if ev:
+ raise Exception("Unexpected P2P device found")
+ dev[2].p2p_find(social=True, dev_id=addr1)
+ ev = dev[2].wait_global_event(['P2P-DEVICE-FOUND ' + addr1], timeout=2)
+ if ev is None:
+ raise Exception("P2P device not found")
+
+def test_discovery_social_plus_one(dev):
+ """P2P device discovery with social-plus-one"""
+ logger.info("Start autonomous GO " + dev[0].ifname)
+ dev[1].p2p_find(social=True)
+ dev[0].p2p_find(progressive=True)
+ logger.info("Wait for initial progressive find phases")
+ dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ go = dev[2].p2p_dev_addr()
+ dev[2].p2p_start_go(freq="2422")
+ logger.info("Verify whether the GO on non-social channel can be found")
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+ if ev is None:
+ raise Exception("Peer not found")
+ if go not in ev:
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+ if ev is None:
+ raise Exception("Peer not found")
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+ if ev is None:
+ raise Exception("Peer not found")
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+ if not dev[0].peer_known(go):
+ raise Exception("GO not found in progressive scan")
+ if dev[1].peer_known(go):
+ raise Exception("GO found in social-only scan")
+
+def _test_discovery_and_interface_disabled(dev, delay=1):
+ try:
+ if "OK" not in dev[0].p2p_find():
+ raise Exception("Failed to start P2P find")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ if ev is None:
+ raise Exception("Scan did not start")
+ dev[0].request("DRIVER_EVENT INTERFACE_DISABLED")
+ time.sleep(delay)
+
+ # verify that P2P_FIND is rejected
+ if "FAIL" not in dev[0].p2p_find():
+ raise Exception("New P2P_FIND request was accepted unexpectedly")
+
+ dev[0].request("DRIVER_EVENT INTERFACE_ENABLED")
+ time.sleep(3)
+ dev[0].scan(freq="2412")
+ if "OK" not in dev[0].p2p_find():
+ raise Exception("Failed to start P2P find")
+ dev[0].dump_monitor()
+ dev[1].p2p_listen()
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+ if ev is None:
+ raise Exception("Peer not found")
+ finally:
+ dev[0].request("DRIVER_EVENT INTERFACE_ENABLED")
+
+def test_discovery_and_interface_disabled(dev):
+ """P2P device discovery with interface getting disabled"""
+ _test_discovery_and_interface_disabled(dev, delay=1)
+ _test_discovery_and_interface_disabled(dev, delay=5)
+
+def test_discovery_auto(dev):
+ """P2P device discovery and provision discovery with auto GO/dev selection"""
+ dev[0].flush_scan_cache()
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ dev[2].p2p_start_go(freq="2412")
+ logger.info("Start device discovery")
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Device discovery timed out")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+ if not dev[0].discover_peer(addr2):
+ raise Exception("Device discovery timed out")
+
+ logger.info("Test provision discovery for display (device)")
+ dev[0].global_request("P2P_PROV_DISC " + addr1 + " display auto")
+ ev1 = dev[1].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
+ if ev1 is None:
+ raise Exception("Provision discovery timed out (display/dev1)")
+ if addr0 not in ev1:
+ raise Exception("Dev0 not in provision discovery event")
+ if " group=" in ev1:
+ raise Exception("Unexpected group parameter from non-GO")
+ ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN",
+ "P2P-PROV-DISC-FAILURE"], timeout=15)
+ if ev0 is None:
+ raise Exception("Provision discovery timed out (display/dev0)")
+ if "P2P-PROV-DISC-FAILURE" in ev0:
+ raise Exception("Provision discovery failed (display/dev0)")
+ if addr1 not in ev0:
+ raise Exception("Dev1 not in provision discovery event")
+ if "peer_go=0" not in ev0:
+ raise Exception("peer_go incorrect in PD response from non-GO")
+
+ logger.info("Test provision discovery for display (GO)")
+ dev[0].global_request("P2P_PROV_DISC " + addr2 + " display auto")
+ ev2 = dev[2].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
+ if ev2 is None:
+ raise Exception("Provision discovery timed out (display/dev2)")
+ if addr0 not in ev2:
+ raise Exception("Dev0 not in provision discovery event")
+ if " group=" not in ev2:
+ raise Exception("Group parameter missing from GO")
+ ev0 = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN",
+ "P2P-PROV-DISC-FAILURE"], timeout=15)
+ if ev0 is None:
+ raise Exception("Provision discovery timed out (display/dev0)")
+ if "P2P-PROV-DISC-FAILURE" in ev0:
+ raise Exception("Provision discovery failed (display/dev0)")
+ if addr2 not in ev0:
+ raise Exception("Dev1 not in provision discovery event")
+ if "peer_go=1" not in ev0:
+ raise Exception("peer_go incorrect in PD response from GO")
+
+def test_discovery_stop(dev):
+ """P2P device discovery and p2p_stop_find"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].p2p_listen()
+ dev[2].p2p_listen()
+
+ dev[0].p2p_find(social=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.5)
+ if ev is None:
+ logger.info("No CTRL-EVENT-SCAN-STARTED event")
+ dev[0].p2p_stop_find()
+ ev = dev[0].wait_global_event(["P2P-FIND-STOPPED"], timeout=1)
+ if ev is None:
+ raise Exception("P2P_STOP not reported")
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is not None:
+ raise Exception("Peer found unexpectedly: " + ev)
+
+ dev[0].p2p_find(social=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.5)
+ if ev is None:
+ logger.info("No CTRL-EVENT-SCAN-STARTED event")
+ dev[0].global_request("P2P_FLUSH")
+ ev = dev[0].wait_global_event(["P2P-FIND-STOPPED"], timeout=1)
+ if ev is None:
+ raise Exception("P2P_STOP not reported")
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is not None:
+ raise Exception("Peer found unexpectedly: " + ev)
+
+def test_discovery_restart(dev):
+ """P2P device discovery and p2p_find restart"""
+ autogo(dev[1], freq=2457)
+ dev[0].p2p_find(social=True)
+ dev[0].p2p_stop_find()
+ dev[0].p2p_find(social=False)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=7)
+ if ev is None:
+ dev[0].p2p_find(social=False)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=7)
+ if ev is None:
+ raise Exception("Peer not found")
+
+def test_discovery_restart_progressive(dev):
+ """P2P device discovery and p2p_find type=progressive restart"""
+ try:
+ set_country("US", dev[1])
+ autogo(dev[1], freq=5805)
+ dev[0].p2p_find(social=True)
+ dev[0].p2p_stop_find()
+ dev[0].p2p_find(progressive=True)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=20)
+ dev[1].remove_group()
+ if ev is None:
+ raise Exception("Peer not found")
+ finally:
+ set_country("00")
+ dev[1].flush_scan_cache()
+
+def test_p2p_peer_command(dev):
+ """P2P_PEER command"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ dev[1].p2p_listen()
+ dev[2].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+ if not dev[0].discover_peer(addr2):
+ raise Exception("Device discovery timed out")
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+ dev[2].p2p_stop_find()
+
+ res0 = dev[0].request("P2P_PEER FIRST")
+ peer = res0.splitlines()[0]
+ if peer not in [addr1, addr2]:
+ raise Exception("Unexpected P2P_PEER FIRST address")
+ res1 = dev[0].request("P2P_PEER NEXT-" + peer)
+ peer2 = res1.splitlines()[0]
+ if peer2 not in [addr1, addr2] or peer == peer2:
+ raise Exception("Unexpected P2P_PEER NEXT address")
+
+ if "FAIL" not in dev[0].request("P2P_PEER NEXT-foo"):
+ raise Exception("Invalid P2P_PEER command accepted")
+ if "FAIL" not in dev[0].request("P2P_PEER foo"):
+ raise Exception("Invalid P2P_PEER command accepted")
+ if "FAIL" not in dev[0].request("P2P_PEER 00:11:22:33:44:55"):
+ raise Exception("P2P_PEER command for unknown peer accepted")
+
+def test_p2p_listen_and_offchannel_tx(dev):
+ """P2P_LISTEN behavior with offchannel TX"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+
+ dev[0].p2p_listen()
+ dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=15)
+ if ev is None:
+ raise Exception("No PD result reported")
+ dev[1].p2p_stop_find()
+
+ if not dev[2].discover_peer(addr0):
+ raise Exception("Device discovery timed out after PD exchange")
+ dev[2].p2p_stop_find()
+ dev[0].p2p_stop_find()
+
+@remote_compatible
+def test_p2p_listen_and_scan(dev):
+ """P2P_LISTEN and scan"""
+ dev[0].p2p_listen()
+ if "OK" not in dev[0].request("SCAN freq=2412"):
+ raise Exception("Failed to request a scan")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 3)
+ if ev is not None:
+ raise Exception("Unexpected scan results")
+ dev[0].p2p_stop_find()
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+ if ev is None:
+ raise Exception("Scan timed out")
+
+def test_p2p_config_methods(dev):
+ """P2P and WPS config method update"""
+ addr0 = dev[0].p2p_dev_addr()
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ addr1 = wpas.p2p_dev_addr()
+
+ if "OK" not in wpas.request("SET config_methods keypad virtual_push_button"):
+ raise Exception("Failed to set config_methods")
+
+ wpas.p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+ dev[0].p2p_stop_find()
+ peer = dev[0].get_peer(addr1)
+ if peer['config_methods'] != '0x180':
+ raise Exception("Unexpected peer config methods(1): " + peer['config_methods'])
+ dev[0].global_request("P2P_FLUSH")
+
+ if "OK" not in wpas.request("SET config_methods virtual_display"):
+ raise Exception("Failed to set config_methods")
+
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+ dev[0].p2p_stop_find()
+ peer = dev[0].get_peer(addr1)
+ if peer['config_methods'] != '0x8':
+ raise Exception("Unexpected peer config methods(2): " + peer['config_methods'])
+
+ wpas.p2p_stop_find()
+
+@remote_compatible
+def test_discovery_after_gas(dev, apdev):
+ """P2P device discovery after GAS/ANQP exchange"""
+ hapd = start_ap(apdev[0])
+ hapd.set("gas_frag_limit", "50")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ dev[0].request("FETCH_ANQP")
+ ev = dev[0].wait_event(["ANQP-QUERY-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("No ANQP-QUERY-DONE event")
+ dev[0].dump_monitor()
+
+ start = os.times()[4]
+ dev[0].p2p_listen()
+ dev[1].p2p_find(social=True)
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Peer not discovered")
+ end = os.times()[4]
+ dev[0].dump_monitor()
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+ logger.info("Device discovery after fragmented GAS took %f seconds" % (end - start))
+ if end - start > 1.3:
+ raise Exception("Device discovery took unexpectedly long time")
+
+@remote_compatible
+def test_discovery_listen_find(dev):
+ """P2P_LISTEN immediately followed by P2P_FIND"""
+ # Request an external remain-on-channel operation to delay start of the ROC
+ # for the following p2p_listen() enough to get p2p_find() processed before
+ # the ROC started event shows up. This is done to test a code path where the
+ # p2p_find() needs to clear the wait for the pending listen operation
+ # (p2p->pending_listen_freq).
+ ifindex = int(dev[0].get_driver_status_field("ifindex"))
+ nl80211_remain_on_channel(dev[0], ifindex, 2417, 200)
+
+ addr0 = dev[0].p2p_dev_addr()
+ dev[0].p2p_listen()
+ dev[0].p2p_find(social=True)
+ time.sleep(0.4)
+ dev[1].p2p_listen()
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=1.2)
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Device discovery timed out")
+ if ev is None:
+ raise Exception("Did not find peer quickly enough after stopped P2P_LISTEN")
+
+def test_discovery_long_listen(dev):
+ """Long P2P_LISTEN and offchannel TX"""
+ addr0 = dev[0].p2p_dev_addr()
+ dev[0].p2p_listen()
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ addr = wpas.p2p_dev_addr()
+ if not wpas.discover_peer(addr0):
+ raise Exception("Device discovery timed out")
+ peer = wpas.get_peer(addr0)
+ chan = '1' if peer['listen_freq'] == '2462' else '11'
+
+ wpas.request("P2P_SET listen_channel " + chan)
+ wpas.request("P2P_LISTEN 10")
+ if not dev[0].discover_peer(addr):
+ raise Exception("Device discovery timed out (2)")
+
+ time.sleep(0.1)
+ wpas.global_request("P2P_PROV_DISC " + addr0 + " display")
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=15)
+ if ev is None:
+ raise Exception("Provision discovery timed out")
+ dev[0].p2p_stop_find()
+
+ # Verify that the long listen period is still continuing after off-channel
+ # TX of Provision Discovery frames.
+ if not dev[1].discover_peer(addr):
+ raise Exception("Device discovery timed out (3)")
+
+ dev[1].p2p_stop_find()
+ wpas.p2p_stop_find()
+
+def test_discovery_long_listen2(dev):
+ """Long P2P_LISTEN longer than remain-on-channel time"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ addr = wpas.p2p_dev_addr()
+ wpas.request("P2P_LISTEN 15")
+
+ # Wait for remain maximum remain-on-channel time to pass
+ time.sleep(7)
+
+ if not dev[0].discover_peer(addr):
+ raise Exception("Device discovery timed out")
+ dev[0].p2p_stop_find()
+ wpas.p2p_stop_find()
+
+def pd_test(dev, addr):
+ if not dev.discover_peer(addr, freq=2412):
+ raise Exception("Device discovery timed out")
+ dev.global_request("P2P_PROV_DISC " + addr + " display")
+ ev0 = dev.wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=15)
+ if ev0 is None:
+ raise Exception("Provision discovery timed out (display)")
+ dev.p2p_stop_find()
+
+def run_discovery_while_go(wpas, dev, params):
+ wpas.request("P2P_SET listen_channel 1")
+ wpas.p2p_start_go(freq="2412")
+ addr = wpas.p2p_dev_addr()
+ pin = dev[0].wps_read_pin()
+ wpas.p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(addr, pin, freq=2412, timeout=30)
+
+ pd_test(dev[0], addr)
+ wpas.p2p_listen()
+ pd_test(dev[2], addr)
+
+ wpas.p2p_stop_find()
+ terminate_group(wpas, dev[1])
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wifi_p2p.public_action.subtype == 8", ["wlan.da"])
+ da = out.splitlines()
+ logger.info("PD Response DAs: " + str(da))
+ if len(da) != 3:
+ raise Exception("Unexpected DA count for PD Response")
+
+def test_discovery_while_go(dev, apdev, params):
+ """P2P provision discovery from GO"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ run_discovery_while_go(wpas, dev, params)
+
+def test_discovery_while_go_p2p_dev(dev, apdev, params):
+ """P2P provision discovery from GO (using P2P Device interface)"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ run_discovery_while_go(wpas, dev, params)
+
+def run_discovery_while_cli(wpas, dev, params):
+ wpas.request("P2P_SET listen_channel 1")
+ dev[1].p2p_start_go(freq="2412")
+ addr = wpas.p2p_dev_addr()
+ pin = wpas.wps_read_pin()
+ dev[1].p2p_go_authorize_client(pin)
+ wpas.p2p_connect_group(dev[1].p2p_dev_addr(), pin, freq=2412, timeout=30)
+
+ pd_test(dev[0], addr)
+ wpas.p2p_listen()
+ pd_test(dev[2], addr)
+
+ wpas.p2p_stop_find()
+ terminate_group(dev[1], wpas)
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wifi_p2p.public_action.subtype == 8", ["wlan.da"])
+ da = out.splitlines()
+ logger.info("PD Response DAs: " + str(da))
+ if len(da) != 3:
+ raise Exception("Unexpected DA count for PD Response")
+
+def test_discovery_while_cli(dev, apdev, params):
+ """P2P provision discovery from CLI"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ run_discovery_while_cli(wpas, dev, params)
+
+def test_discovery_while_cli_p2p_dev(dev, apdev, params):
+ """P2P provision discovery from CLI (using P2P Device interface)"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ run_discovery_while_cli(wpas, dev, params)
+
+def test_discovery_device_name_change(dev):
+ """P2P device discovery and peer changing device name"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.set("device_name", "test-a")
+ wpas.p2p_listen()
+ dev[0].p2p_find(social=True)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+ if ev is None:
+ raise Exception("Peer not found")
+ if "new=1" not in ev:
+ raise Exception("Incorrect new event: " + ev)
+ if "name='test-a'" not in ev:
+ raise Exception("Unexpected device name(1): " + ev)
+
+ # Verify that new P2P-DEVICE-FOUND event is indicated when the peer changes
+ # its device name.
+ wpas.set("device_name", "test-b")
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+ if ev is None:
+ raise Exception("Peer update not seen")
+ if "new=0" not in ev:
+ raise Exception("Incorrect update event: " + ev)
+ if "name='test-b'" not in ev:
+ raise Exception("Unexpected device name(2): " + ev)
+ wpas.p2p_stop_find()
+ dev[0].p2p_stop_find()
+
+def test_p2p_group_cli_invalid(dev, apdev):
+ """P2P device discovery with invalid group client info"""
+ attr = struct.pack('<BHBB', 2, 2, 0x25, 0x09)
+
+ attr += struct.pack('<BH', 3, 6) + "\x02\x02\x02\x02\x02\x00".encode()
+
+ cli = bytes()
+ cli += "\x02\x02\x02\x02\x02\x03".encode()
+ cli += "\x02\x02\x02\x02\x02\x04".encode()
+ cli += struct.pack('>BH', 0, 0x3148)
+ dev_type = "\x00\x00\x00\x00\x00\x00\x00\x01".encode()
+ cli += dev_type
+ num_sec = 25
+ cli += struct.pack('B', num_sec)
+ cli += num_sec * dev_type
+ name = "TEST".encode()
+ cli += struct.pack('>HH', 0x1011, len(name)) + name
+ desc = struct.pack('B', len(cli)) + cli
+ attr += struct.pack('<BH', 14, len(desc)) + desc
+
+ p2p_ie = struct.pack('>BBL', 0xdd, 4 + len(attr), 0x506f9a09) + attr
+ ie = binascii.hexlify(p2p_ie).decode()
+
+ params = {"ssid": "DIRECT-test",
+ "eap_server": "1",
+ "wps_state": "2",
+ "wpa_passphrase": "12345678",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK",
+ "rsn_pairwise": "CCMP",
+ "vendor_elements": ie}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ for i in range(2):
+ dev[i].p2p_find(social=True)
+ ev = dev[i].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if not ev:
+ raise Exception("P2P device not found")
+
+def test_discovery_max_peers(dev):
+ """P2P device discovery and maximum peer limit exceeded"""
+ dev[0].p2p_listen()
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ probereq1 = "40000000ffffffffffff"
+ probereq2 = "ffffffffffff000000074449524543542d01080c1218243048606c0301012d1afe131bffff000000000000000000000100000000000000000000ff16230178c812400000bfce0000000000000000fafffaffdd730050f204104a000110103a00010110080002314810470010572cf82fc95756539b16b5cfb298abf1105400080000000000000000103c0001031002000200001009000200001012000200001021000120102300012010240001201011000844657669636520421049000900372a000120030101dd11506f9a0902020025000605005858045106"
+
+ # Fill the P2P peer table with max+1 entries based on Probe Request frames
+ # to verify correct behavior on# removing the oldest entry when running out
+ # of room.
+ for i in range(101):
+ addr = "0202020202%02x" % i
+ probereq = probereq1 + addr + probereq2
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=60 ssi_signal=-30 frame=" + probereq):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ res = dev[0].global_request("P2P_PEER FIRST")
+ addr = res.splitlines()[0]
+ peers = [addr]
+ limit = 200
+ while limit > 0:
+ res = dev[0].global_request("P2P_PEER NEXT-" + addr)
+ addr = res.splitlines()[0]
+ if addr == "FAIL":
+ break
+ peers.append(addr)
+ limit -= 1
+ logger.info("Peers: " + str(peers))
+
+ if len(peers) != 100:
+ raise Exception("Unexpected number of peer entries")
+ oldest = "02:02:02:02:02:00"
+ if oldest in peers:
+ raise Exception("Oldest entry is still present")
+ for i in range(101):
+ addr = "02:02:02:02:02:%02x" % i
+ if addr == oldest:
+ continue
+ if addr not in peers:
+ raise Exception("Peer missing from table: " + addr)
+
+ # Provision Discovery Request from the oldest peer (SA) using internally
+ # different P2P Device Address as a regression test for incorrect processing
+ # for this corner case.
+ dst = dev[0].own_addr().replace(':', '')
+ src = peers[99].replace(':', '')
+ devaddr = "0202020202ff"
+ pdreq = "d0004000" + dst + src + dst + "d0000409506f9a090701dd29506f9a0902020025000d1d00" + devaddr + "1108000000000000000000101100084465766963652041dd0a0050f204100800020008"
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=60 ssi_signal=-30 frame=" + pdreq):
+ raise Exception("MGMT_RX_PROCESS failed")
diff --git a/contrib/wpa/tests/hwsim/test_p2p_ext.py b/contrib/wpa/tests/hwsim/test_p2p_ext.py
new file mode 100644
index 000000000000..2c23ee9a0b78
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_ext.py
@@ -0,0 +1,384 @@
+# P2P vendor specific extension tests
+# Copyright (c) 2014-2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import os
+
+from tshark import run_tshark
+from p2p_utils import *
+
+@remote_compatible
+def test_p2p_ext_discovery(dev):
+ """P2P device discovery with vendor specific extensions"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ try:
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 1 dd050011223344"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ res = dev[0].request("VENDOR_ELEM_GET 1")
+ if res != "dd050011223344":
+ raise Exception("Unexpected VENDOR_ELEM_GET result: " + res)
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 1 dd06001122335566"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ res = dev[0].request("VENDOR_ELEM_GET 1")
+ if res != "dd050011223344dd06001122335566":
+ raise Exception("Unexpected VENDOR_ELEM_GET result(2): " + res)
+ res = dev[0].request("VENDOR_ELEM_GET 2")
+ if res != "":
+ raise Exception("Unexpected VENDOR_ELEM_GET result(3): " + res)
+ if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 dd050011223344"):
+ raise Exception("VENDOR_ELEM_REMOVE failed")
+ res = dev[0].request("VENDOR_ELEM_GET 1")
+ if res != "dd06001122335566":
+ raise Exception("Unexpected VENDOR_ELEM_GET result(4): " + res)
+ if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 dd06001122335566"):
+ raise Exception("VENDOR_ELEM_REMOVE failed")
+ res = dev[0].request("VENDOR_ELEM_GET 1")
+ if res != "":
+ raise Exception("Unexpected VENDOR_ELEM_GET result(5): " + res)
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 1 dd050011223344dd06001122335566"):
+ raise Exception("VENDOR_ELEM_ADD failed(2)")
+
+ if "FAIL" not in dev[0].request("VENDOR_ELEM_REMOVE 1 dd051122334455"):
+ raise Exception("Unexpected VENDOR_ELEM_REMOVE success")
+ if "FAIL" not in dev[0].request("VENDOR_ELEM_REMOVE 1 dd"):
+ raise Exception("Unexpected VENDOR_ELEM_REMOVE success(2)")
+ if "FAIL" not in dev[0].request("VENDOR_ELEM_ADD 1 ddff"):
+ raise Exception("Unexpected VENDOR_ELEM_ADD success(3)")
+
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Device discovery timed out")
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+
+ peer = dev[1].get_peer(addr0)
+ if peer['vendor_elems'] != "dd050011223344dd06001122335566":
+ raise Exception("Vendor elements not reported correctly")
+
+ res = dev[0].request("VENDOR_ELEM_GET 1")
+ if res != "dd050011223344dd06001122335566":
+ raise Exception("Unexpected VENDOR_ELEM_GET result(6): " + res)
+ if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 dd06001122335566"):
+ raise Exception("VENDOR_ELEM_REMOVE failed")
+ res = dev[0].request("VENDOR_ELEM_GET 1")
+ if res != "dd050011223344":
+ raise Exception("Unexpected VENDOR_ELEM_GET result(7): " + res)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 1 *")
+
+@remote_compatible
+def test_p2p_ext_discovery_go(dev):
+ """P2P device discovery with vendor specific extensions for GO"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ try:
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 2 dd050011223344dd06001122335566"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 3 dd050011223344dd06001122335566"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 12 dd050011223344dd06001122335566"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+
+ dev[0].p2p_start_go(freq="2412")
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Device discovery timed out")
+ peer = dev[1].get_peer(addr0)
+ if peer['vendor_elems'] != "dd050011223344dd06001122335566":
+ logger.info("Peer vendor_elems: " + peer['vendor_elems'])
+ raise Exception("Vendor elements not reported correctly")
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 2 *")
+ dev[0].request("VENDOR_ELEM_REMOVE 3 *")
+ dev[0].request("VENDOR_ELEM_REMOVE 12 *")
+
+def test_p2p_ext_vendor_elem_probe_req(dev):
+ """VENDOR_ELEM in P2P Probe Request frames"""
+ try:
+ _test_p2p_ext_vendor_elem_probe_req(dev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 0 *")
+
+def _test_p2p_ext_vendor_elem_probe_req(dev):
+ addr1 = dev[1].p2p_dev_addr()
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 0 dd050011223300"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT-RX timeout")
+ if " 40" not in ev:
+ raise Exception("Not a Probe Request frame")
+ if "dd050011223300" not in ev:
+ raise Exception("Vendor element not found from Probe Request frame")
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_pd_req(dev):
+ """VENDOR_ELEM in PD Request frames"""
+ try:
+ _test_p2p_ext_vendor_elem_pd_req(dev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 4 *")
+
+def _test_p2p_ext_vendor_elem_pd_req(dev):
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 4 dd050011223301"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+ dev[0].p2p_stop_find()
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ dev[0].global_request("P2P_PROV_DISC " + addr1 + " display")
+ for i in range(5):
+ ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT-RX timeout")
+ if " d0" in ev:
+ break
+ if "dd050011223301" not in ev:
+ raise Exception("Vendor element not found from PD Request frame")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_pd_resp(dev):
+ """VENDOR_ELEM in PD Response frames"""
+ try:
+ _test_p2p_ext_vendor_elem_pd_resp(dev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 5 *")
+
+def _test_p2p_ext_vendor_elem_pd_resp(dev):
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 5 dd050011223302"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Device discovery timed out")
+ dev[1].p2p_stop_find()
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ dev[1].global_request("P2P_PROV_DISC " + addr0 + " display")
+ for i in range(5):
+ ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT-RX timeout")
+ if " d0" in ev:
+ break
+ if "dd050011223302" not in ev:
+ raise Exception("Vendor element not found from PD Response frame")
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_go_neg_req(dev):
+ """VENDOR_ELEM in GO Negotiation Request frames"""
+ try:
+ _test_p2p_ext_vendor_elem_go_neg_req(dev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 6 *")
+
+def _test_p2p_ext_vendor_elem_go_neg_req(dev):
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 6 dd050011223303"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+ dev[0].p2p_stop_find()
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ dev[0].global_request("P2P_CONNECT " + addr1 + " 12345670 display")
+ for i in range(5):
+ ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT-RX timeout")
+ if " d0" in ev:
+ break
+ if "dd050011223303" not in ev:
+ raise Exception("Vendor element not found from GO Negotiation Request frame")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_go_neg_resp(dev):
+ """VENDOR_ELEM in GO Negotiation Response frames"""
+ try:
+ _test_p2p_ext_vendor_elem_go_neg_resp(dev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 7 *")
+
+def _test_p2p_ext_vendor_elem_go_neg_resp(dev):
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 7 dd050011223304"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Device discovery timed out")
+ dev[1].p2p_stop_find()
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ dev[1].global_request("P2P_CONNECT " + addr0 + " 12345670 display")
+ for i in range(5):
+ ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT-RX timeout")
+ if " d0" in ev:
+ break
+ if "dd050011223304" not in ev:
+ raise Exception("Vendor element not found from GO Negotiation Response frame")
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_go_neg_conf(dev, apdev, params):
+ """VENDOR_ELEM in GO Negotiation Confirm frames"""
+ try:
+ _test_p2p_ext_vendor_elem_go_neg_conf(dev, apdev, params)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 8 *")
+
+def _test_p2p_ext_vendor_elem_go_neg_conf(dev, apdev, params):
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 8 dd050011223305"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[0].p2p_listen()
+ dev[1].p2p_go_neg_auth(addr0, "12345670", "enter")
+ dev[1].p2p_listen()
+ dev[0].p2p_go_neg_init(addr1, "12345678", "display")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("GO negotiation timed out")
+ ev = dev[0].wait_global_event(["P2P-GROUP-FORMATION-FAILURE"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation failure not indicated")
+ dev[0].dump_monitor()
+ dev[1].p2p_go_neg_auth_result(expect_failure=True)
+ dev[1].dump_monitor()
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wifi_p2p.public_action.subtype == 2")
+ if "Vendor Specific Data: 3305" not in out:
+ raise Exception("Vendor element not found from GO Negotiation Confirm frame")
+
+def test_p2p_ext_vendor_elem_invitation(dev):
+ """VENDOR_ELEM in Invitation frames"""
+ try:
+ _test_p2p_ext_vendor_elem_invitation(dev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 9 *")
+ dev[0].request("VENDOR_ELEM_REMOVE 10 *")
+
+def _test_p2p_ext_vendor_elem_invitation(dev):
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ form(dev[0], dev[1])
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 9 dd050011223306"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 10 dd050011223307"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Device discovery timed out")
+ peer = dev[0].get_peer(addr1)
+ dev[0].p2p_stop_find()
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ dev[0].global_request("P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr1)
+ for i in range(5):
+ ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT-RX timeout")
+ if " d0" in ev:
+ break
+ if "dd050011223306" not in ev:
+ raise Exception("Vendor element not found from Invitation Request frame")
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 0"):
+ raise Exception("Failed to disable external management frame handling")
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Device discovery timed out")
+ peer = dev[1].get_peer(addr0)
+ dev[1].p2p_stop_find()
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ dev[1].global_request("P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr0)
+ for i in range(5):
+ ev = dev[1].wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT-RX timeout")
+ if " d0" in ev:
+ break
+ if "dd050011223307" not in ev:
+ raise Exception("Vendor element not found from Invitation Response frame")
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Group start not reported")
+ dev[0].group_form_result(ev)
+ dev[0].remove_group()
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+
+def test_p2p_ext_vendor_elem_assoc(dev, apdev, params):
+ """VENDOR_ELEM in Association frames"""
+ try:
+ _test_p2p_ext_vendor_elem_assoc(dev, apdev, params)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 11 *")
+ dev[1].request("VENDOR_ELEM_REMOVE 12 *")
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def _test_p2p_ext_vendor_elem_assoc(dev, apdev, params):
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ res = dev[0].get_driver_status()
+ p2p_device = True if (int(res['capa.flags'], 0) & 0x20000000) else False
+
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 11 dd050011223308"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ if "OK" not in dev[1].request("VENDOR_ELEM_ADD 12 dd050011223309"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ if not p2p_device and "OK" not in dev[0].request("VENDOR_ELEM_ADD 13 dd05001122330a"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[0].p2p_listen()
+ dev[1].p2p_listen()
+ dev[1].p2p_go_neg_auth(addr0, "12345670", "enter", go_intent=15)
+ dev[0].p2p_go_neg_init(addr1, "12345670", "display", go_intent=0,
+ timeout=15)
+ dev[1].p2p_go_neg_auth_result()
+ dev[1].remove_group()
+ dev[0].wait_go_ending_session()
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan.fc.type_subtype == 0x00", wait=False)
+ if "Vendor Specific Data: 3308" not in out:
+ raise Exception("Vendor element (P2P) not found from Association Request frame")
+ if not p2p_device and "Vendor Specific Data: 330a" not in out:
+ raise Exception("Vendor element (non-P2P) not found from Association Request frame")
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan.fc.type_subtype == 0x01", wait=False)
+ if "Vendor Specific Data: 3309" not in out:
+ raise Exception("Vendor element not found from Association Response frame")
diff --git a/contrib/wpa/tests/hwsim/test_p2p_grpform.py b/contrib/wpa/tests/hwsim/test_p2p_grpform.py
new file mode 100644
index 000000000000..88e253c0b085
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_grpform.py
@@ -0,0 +1,1185 @@
+# P2P group formation test cases
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import logging
+logger = logging.getLogger()
+import struct
+import time
+import os
+
+import hostapd
+import hwsim_utils
+from utils import *
+from wpasupplicant import WpaSupplicant
+from p2p_utils import *
+from test_p2p_messages import parse_p2p_public_action, p2p_hdr, p2p_attr_capability, p2p_attr_go_intent, p2p_attr_config_timeout, p2p_attr_listen_channel, p2p_attr_intended_interface_addr, p2p_attr_channel_list, p2p_attr_device_info, p2p_attr_operating_channel, ie_p2p, ie_wsc, mgmt_tx, P2P_GO_NEG_REQ
+
+@remote_compatible
+def test_grpform(dev):
+ """P2P group formation using PIN and authorized connection (init -> GO)"""
+ try:
+ dev[0].global_request("SET p2p_group_idle 2")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ check_grpform_results(i_res, r_res)
+ dev[1].remove_group()
+ ev = dev[0].wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+ if ev is None:
+ raise Exception("GO did not remove group on idle timeout")
+ if "GO reason=IDLE" not in ev:
+ raise Exception("Unexpected group removal event: " + ev)
+ finally:
+ dev[0].global_request("SET p2p_group_idle 0")
+
+def test_grpform_a(dev):
+ """P2P group formation using PIN and authorized connection (init -> GO) (init: group iface)"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ if "p2p-wlan" not in i_res['ifname']:
+ raise Exception("Unexpected group interface name")
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], dev[1])
+ if i_res['ifname'] in get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+
+def test_grpform_b(dev):
+ """P2P group formation using PIN and authorized connection (init -> GO) (resp: group iface)"""
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ if "p2p-wlan" not in r_res['ifname']:
+ raise Exception("Unexpected group interface name")
+ check_grpform_results(i_res, r_res)
+ addr = dev[0].group_request("P2P_GROUP_MEMBER " + dev[1].p2p_dev_addr())
+ if "FAIL" in addr:
+ raise Exception("P2P_GROUP_MEMBER failed")
+ if addr != dev[1].p2p_interface_addr():
+ raise Exception("Unexpected P2P_GROUP_MEMBER result: " + addr)
+ if "FAIL" not in dev[0].group_request("P2P_GROUP_MEMBER a"):
+ raise Exception("Invalid P2P_GROUP_MEMBER command accepted")
+ if "FAIL" not in dev[0].group_request("P2P_GROUP_MEMBER 00:11:22:33:44:55"):
+ raise Exception("P2P_GROUP_MEMBER for non-member accepted")
+ remove_group(dev[0], dev[1])
+ if r_res['ifname'] in get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+
+def test_grpform_c(dev):
+ """P2P group formation using PIN and authorized connection (init -> GO) (group iface)"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ if "p2p-wlan" not in i_res['ifname']:
+ raise Exception("Unexpected group interface name")
+ if "p2p-wlan" not in r_res['ifname']:
+ raise Exception("Unexpected group interface name")
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], dev[1])
+ if i_res['ifname'] in get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+ if r_res['ifname'] in get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+
+@remote_compatible
+def test_grpform2(dev):
+ """P2P group formation using PIN and authorized connection (resp -> GO)"""
+ go_neg_pin_authorized(i_dev=dev[0], i_intent=0, r_dev=dev[1], r_intent=15)
+ remove_group(dev[0], dev[1])
+
+def test_grpform2_c(dev):
+ """P2P group formation using PIN and authorized connection (resp -> GO) (group iface)"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0, r_dev=dev[1], r_intent=15)
+ remove_group(dev[0], dev[1])
+ if i_res['ifname'] in get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+ if r_res['ifname'] in get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+
+@remote_compatible
+def test_grpform3(dev):
+ """P2P group formation using PIN and re-init GO Negotiation"""
+ go_neg_pin(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
+ remove_group(dev[0], dev[1])
+
+def test_grpform3_c(dev):
+ """P2P group formation using PIN and re-init GO Negotiation (group iface)"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ [i_res, r_res] = go_neg_pin(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
+ remove_group(dev[0], dev[1])
+ if i_res['ifname'] in get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+ if r_res['ifname'] in get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+
+@remote_compatible
+def test_grpform4(dev):
+ """P2P group formation response during p2p_find"""
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].p2p_listen()
+ dev[0].discover_peer(addr1)
+ dev[1].p2p_find(social=True)
+ time.sleep(0.4)
+ dev[0].global_request("P2P_CONNECT " + addr1 + " 12345670 display")
+ ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+ if ev is None:
+ raise Exception("GO Negotiation RX timed out")
+ time.sleep(0.5)
+ dev[1].p2p_stop_find()
+ dev[0].p2p_stop_find()
+
+@remote_compatible
+def test_grpform_pbc(dev):
+ """P2P group formation using PBC and re-init GO Negotiation"""
+ [i_res, r_res] = go_neg_pbc(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
+ check_grpform_results(i_res, r_res)
+ if i_res['role'] != 'GO' or r_res['role'] != 'client':
+ raise Exception("Unexpected device roles")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_grpform_pd(dev):
+ """P2P group formation with PD-before-GO-Neg workaround"""
+ [i_res, r_res] = go_neg_pbc(i_dev=dev[0], provdisc=True, r_dev=dev[1], r_listen=True)
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], dev[1])
+
+def test_grpform_ext_listen(dev):
+ """P2P group formation with extended listen timing enabled"""
+ addr0 = dev[0].p2p_dev_addr()
+ try:
+ if "FAIL" not in dev[0].global_request("P2P_EXT_LISTEN 100"):
+ raise Exception("Invalid P2P_EXT_LISTEN accepted")
+ if "OK" not in dev[0].global_request("P2P_EXT_LISTEN 300 1000"):
+ raise Exception("Failed to set extended listen timing")
+ if "OK" not in dev[1].global_request("P2P_EXT_LISTEN 200 40000"):
+ raise Exception("Failed to set extended listen timing")
+ [i_res, r_res] = go_neg_pbc(i_dev=dev[0], provdisc=True, r_dev=dev[1],
+ r_listen=True, i_freq="2417", r_freq="2417",
+ i_intent=1, r_intent=15)
+ check_grpform_results(i_res, r_res)
+ peer1 = dev[0].get_peer(dev[1].p2p_dev_addr())
+ if peer1['ext_listen_interval'] != "40000":
+ raise Exception("Extended listen interval not discovered correctly")
+ if peer1['ext_listen_period'] != "200":
+ raise Exception("Extended listen period not discovered correctly")
+ peer0 = dev[1].get_peer(dev[0].p2p_dev_addr())
+ if peer0['ext_listen_interval'] != "1000":
+ raise Exception("Extended listen interval not discovered correctly")
+ if peer0['ext_listen_period'] != "300":
+ raise Exception("Extended listen period not discovered correctly")
+ if not dev[2].discover_peer(addr0):
+ raise Exception("Could not discover peer during ext listen")
+ remove_group(dev[0], dev[1])
+ finally:
+ if "OK" not in dev[0].global_request("P2P_EXT_LISTEN"):
+ raise Exception("Failed to clear extended listen timing")
+ if "OK" not in dev[1].global_request("P2P_EXT_LISTEN"):
+ raise Exception("Failed to clear extended listen timing")
+
+def test_grpform_ext_listen_oper(dev):
+ """P2P extended listen timing operations"""
+ try:
+ _test_grpform_ext_listen_oper(dev)
+ finally:
+ dev[0].global_request("P2P_EXT_LISTEN")
+
+def _test_grpform_ext_listen_oper(dev):
+ addr0 = dev[0].p2p_dev_addr()
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ addr1 = wpas.p2p_dev_addr()
+ wpas.request("P2P_SET listen_channel 1")
+ wpas.global_request("SET p2p_no_group_iface 0")
+ wpas.request("P2P_LISTEN")
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Could not discover peer")
+ dev[0].request("P2P_LISTEN")
+ if not wpas.discover_peer(addr0):
+ raise Exception("Could not discover peer (2)")
+
+ dev[0].global_request("P2P_EXT_LISTEN 300 500")
+ dev[0].global_request("P2P_CONNECT " + addr1 + " 12345670 display auth go_intent=0 freq=2417")
+ wpas.global_request("P2P_CONNECT " + addr0 + " 12345670 enter go_intent=15 freq=2417")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("GO Negotiation failed")
+ ifaces = wpas.request("INTERFACES").splitlines()
+ iface = ifaces[0] if "p2p-wlan" in ifaces[0] else ifaces[1]
+ wpas.group_ifname = iface
+ if "OK" not in wpas.group_request("STOP_AP"):
+ raise Exception("STOP_AP failed")
+ wpas.group_request("SET ext_mgmt_frame_handling 1")
+ dev[1].p2p_find(social=True)
+ time.sleep(1)
+ if dev[1].peer_known(addr0):
+ raise Exception("Unexpected peer discovery")
+ ifaces = dev[0].request("INTERFACES").splitlines()
+ iface = ifaces[0] if "p2p-wlan" in ifaces[0] else ifaces[1]
+ if "OK" not in dev[0].global_request("P2P_GROUP_REMOVE " + iface):
+ raise Exception("Failed to request group removal")
+ wpas.remove_group()
+
+ count = 0
+ timeout = 15
+ found = False
+ while count < timeout * 4:
+ time.sleep(0.25)
+ count = count + 1
+ if dev[1].peer_known(addr0):
+ found = True
+ break
+ dev[1].p2p_stop_find()
+ if not found:
+ raise Exception("Could not discover peer that was supposed to use extended listen")
+
+@remote_compatible
+def test_both_go_intent_15(dev):
+ """P2P GO Negotiation with both devices using GO intent 15"""
+ go_neg_pin_authorized(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=15, expect_failure=True, i_go_neg_status=9)
+
+@remote_compatible
+def test_both_go_neg_display(dev):
+ """P2P GO Negotiation with both devices trying to display PIN"""
+ go_neg_pin_authorized(i_dev=dev[0], r_dev=dev[1], expect_failure=True, i_go_neg_status=10, i_method='display', r_method='display')
+
+@remote_compatible
+def test_both_go_neg_enter(dev):
+ """P2P GO Negotiation with both devices trying to enter PIN"""
+ go_neg_pin_authorized(i_dev=dev[0], r_dev=dev[1], expect_failure=True, i_go_neg_status=10, i_method='enter', r_method='enter')
+
+@remote_compatible
+def test_go_neg_pbc_vs_pin(dev):
+ """P2P GO Negotiation with one device using PBC and the other PIN"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Could not discover peer")
+ dev[0].p2p_listen()
+ if "OK" not in dev[0].request("P2P_CONNECT " + addr1 + " pbc auth"):
+ raise Exception("Failed to authorize GO Neg")
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Could not discover peer")
+ if "OK" not in dev[1].request("P2P_CONNECT " + addr0 + " 12345670 display"):
+ raise Exception("Failed to initiate GO Neg")
+ ev = dev[1].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("GO Negotiation failure timed out")
+ if "status=10" not in ev:
+ raise Exception("Unexpected failure reason: " + ev)
+
+@remote_compatible
+def test_go_neg_pin_vs_pbc(dev):
+ """P2P GO Negotiation with one device using PIN and the other PBC"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Could not discover peer")
+ dev[0].p2p_listen()
+ if "OK" not in dev[0].request("P2P_CONNECT " + addr1 + " 12345670 display auth"):
+ raise Exception("Failed to authorize GO Neg")
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Could not discover peer")
+ if "OK" not in dev[1].request("P2P_CONNECT " + addr0 + " pbc"):
+ raise Exception("Failed to initiate GO Neg")
+ ev = dev[1].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("GO Negotiation failure timed out")
+ if "status=10" not in ev:
+ raise Exception("Unexpected failure reason: " + ev)
+
+def test_grpform_per_sta_psk(dev):
+ """P2P group formation with per-STA PSKs"""
+ dev[0].global_request("P2P_SET per_sta_psk 1")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
+ check_grpform_results(i_res, r_res)
+
+ pin = dev[2].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ c_res = dev[2].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60)
+ check_grpform_results(i_res, c_res)
+
+ if r_res['psk'] == c_res['psk']:
+ raise Exception("Same PSK assigned for both clients")
+
+ hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ dev[2].wait_go_ending_session()
+
+def test_grpform_per_sta_psk_wps(dev):
+ """P2P group formation with per-STA PSKs with non-P2P WPS STA"""
+ dev[0].global_request("P2P_SET per_sta_psk 1")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
+ check_grpform_results(i_res, r_res)
+
+ dev[0].p2p_go_authorize_client_pbc()
+ dev[2].request("WPS_PBC")
+ dev[2].wait_connected(timeout=30)
+
+ hwsim_utils.test_connectivity_p2p_sta(dev[1], dev[2])
+
+ dev[0].remove_group()
+ dev[2].request("DISCONNECT")
+ dev[1].wait_go_ending_session()
+
+@remote_compatible
+def test_grpform_force_chan_go(dev):
+ """P2P group formation forced channel selection by GO"""
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ i_freq=2432,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ if i_res['freq'] != "2432":
+ raise Exception("Unexpected channel - did not follow GO's forced channel")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_grpform_force_chan_cli(dev):
+ """P2P group formation forced channel selection by client"""
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+ i_freq=2417,
+ r_dev=dev[1], r_intent=15,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ if i_res['freq'] != "2417":
+ raise Exception("Unexpected channel - did not follow GO's forced channel")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_grpform_force_chan_conflict(dev):
+ """P2P group formation fails due to forced channel mismatch"""
+ go_neg_pin_authorized(i_dev=dev[0], i_intent=0, i_freq=2422,
+ r_dev=dev[1], r_intent=15, r_freq=2427,
+ expect_failure=True, i_go_neg_status=7)
+
+@remote_compatible
+def test_grpform_pref_chan_go(dev):
+ """P2P group formation preferred channel selection by GO"""
+ try:
+ dev[0].request("SET p2p_pref_chan 81:7")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ if i_res['freq'] != "2442":
+ raise Exception("Unexpected channel - did not follow GO's p2p_pref_chan")
+ remove_group(dev[0], dev[1])
+ finally:
+ dev[0].request("SET p2p_pref_chan ")
+
+@remote_compatible
+def test_grpform_pref_chan_go_overridden(dev):
+ """P2P group formation preferred channel selection by GO overridden by client"""
+ try:
+ dev[1].request("SET p2p_pref_chan 81:7")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+ i_freq=2422,
+ r_dev=dev[1], r_intent=15,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ if i_res['freq'] != "2422":
+ raise Exception("Unexpected channel - did not follow client's forced channel")
+ remove_group(dev[0], dev[1])
+ finally:
+ dev[1].request("SET p2p_pref_chan ")
+
+@remote_compatible
+def test_grpform_no_go_freq_forcing_chan(dev):
+ """P2P group formation with no-GO freq forcing channel"""
+ try:
+ dev[1].request("SET p2p_no_go_freq 100-200,300,4000-6000")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+ r_dev=dev[1], r_intent=15,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ if int(i_res['freq']) > 4000:
+ raise Exception("Unexpected channel - did not follow no-GO freq")
+ remove_group(dev[0], dev[1])
+ finally:
+ dev[1].request("SET p2p_no_go_freq ")
+
+@remote_compatible
+def test_grpform_no_go_freq_conflict(dev):
+ """P2P group formation fails due to no-GO range forced by client"""
+ try:
+ dev[1].request("SET p2p_no_go_freq 2000-3000")
+ go_neg_pin_authorized(i_dev=dev[0], i_intent=0, i_freq=2422,
+ r_dev=dev[1], r_intent=15,
+ expect_failure=True, i_go_neg_status=7)
+ finally:
+ dev[1].request("SET p2p_no_go_freq ")
+
+@remote_compatible
+def test_grpform_no_5ghz_world_roaming(dev):
+ """P2P group formation with world roaming regulatory"""
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+ r_dev=dev[1], r_intent=15,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ if int(i_res['freq']) > 4000:
+ raise Exception("Unexpected channel - did not follow world roaming rules")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_grpform_no_5ghz_add_cli(dev):
+ """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1"""
+ try:
+ dev[0].request("SET p2p_add_cli_chan 1")
+ dev[1].request("SET p2p_add_cli_chan 1")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+ r_dev=dev[1], r_intent=14,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ if int(i_res['freq']) > 4000:
+ raise Exception("Unexpected channel - did not follow world roaming rules")
+ remove_group(dev[0], dev[1])
+ finally:
+ dev[0].request("SET p2p_add_cli_chan 0")
+ dev[1].request("SET p2p_add_cli_chan 0")
+
+@remote_compatible
+def test_grpform_no_5ghz_add_cli2(dev):
+ """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1 (reverse)"""
+ try:
+ dev[0].request("SET p2p_add_cli_chan 1")
+ dev[1].request("SET p2p_add_cli_chan 1")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=14,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ if int(i_res['freq']) > 4000:
+ raise Exception("Unexpected channel - did not follow world roaming rules")
+ remove_group(dev[0], dev[1])
+ finally:
+ dev[0].request("SET p2p_add_cli_chan 0")
+ dev[1].request("SET p2p_add_cli_chan 0")
+
+@remote_compatible
+def test_grpform_no_5ghz_add_cli3(dev):
+ """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1 (intent 15)"""
+ try:
+ dev[0].request("SET p2p_add_cli_chan 1")
+ dev[1].request("SET p2p_add_cli_chan 1")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=0,
+ r_dev=dev[1], r_intent=15,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ if int(i_res['freq']) > 4000:
+ raise Exception("Unexpected channel - did not follow world roaming rules")
+ remove_group(dev[0], dev[1])
+ finally:
+ dev[0].request("SET p2p_add_cli_chan 0")
+ dev[1].request("SET p2p_add_cli_chan 0")
+
+@remote_compatible
+def test_grpform_no_5ghz_add_cli4(dev):
+ """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1 (reverse; intent 15)"""
+ try:
+ dev[0].request("SET p2p_add_cli_chan 1")
+ dev[1].request("SET p2p_add_cli_chan 1")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0,
+ test_data=False)
+ check_grpform_results(i_res, r_res)
+ if int(i_res['freq']) > 4000:
+ raise Exception("Unexpected channel - did not follow world roaming rules")
+ remove_group(dev[0], dev[1])
+ finally:
+ dev[0].request("SET p2p_add_cli_chan 0")
+ dev[1].request("SET p2p_add_cli_chan 0")
+
+@remote_compatible
+def test_grpform_incorrect_pin(dev):
+ """P2P GO Negotiation with incorrect PIN"""
+ dev[1].p2p_listen()
+ addr1 = dev[1].p2p_dev_addr()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Peer not found")
+ res = dev[1].global_request("P2P_CONNECT " + dev[0].p2p_dev_addr() + " pin auth go_intent=0")
+ if "FAIL" in res:
+ raise Exception("P2P_CONNECT failed to generate PIN")
+ logger.info("PIN from P2P_CONNECT: " + res)
+ dev[0].global_request("P2P_CONNECT " + addr1 + " 00000000 enter go_intent=15")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("GO Negotiation did not complete successfully(0)")
+ ev = dev[1].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=15)
+ if ev is None:
+ raise Exception("GO Negotiation did not complete successfully(1)")
+ ev = dev[1].wait_global_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS failure not reported(1)")
+ if "msg=8 config_error=18" not in ev:
+ raise Exception("Unexpected WPS failure(1): " + ev)
+ ev = dev[0].wait_global_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS failure not reported")
+ if "msg=8 config_error=18" not in ev:
+ raise Exception("Unexpected WPS failure: " + ev)
+ ev = dev[1].wait_global_event(["P2P-GROUP-FORMATION-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("Group formation failure timed out")
+ ev = dev[0].wait_global_event(["P2P-GROUP-FORMATION-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("Group formation failure timed out")
+
+@remote_compatible
+def test_grpform_reject(dev):
+ """User rejecting group formation attempt by a P2P peer"""
+ addr0 = dev[0].p2p_dev_addr()
+ dev[0].p2p_listen()
+ dev[1].p2p_go_neg_init(addr0, None, "pbc")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=15)
+ if ev is None:
+ raise Exception("GO Negotiation timed out")
+ if "OK" in dev[0].global_request("P2P_REJECT foo"):
+ raise Exception("Invalid P2P_REJECT accepted")
+ if "FAIL" in dev[0].global_request("P2P_REJECT " + ev.split(' ')[1]):
+ raise Exception("P2P_REJECT failed")
+ dev[1].request("P2P_STOP_FIND")
+ dev[1].p2p_go_neg_init(addr0, None, "pbc")
+ ev = dev[1].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("Rejection not reported")
+ if "status=11" not in ev:
+ raise Exception("Unexpected status code in rejection")
+
+@remote_compatible
+def test_grpform_pd_no_probe_resp(dev):
+ """GO Negotiation after PD, but no Probe Response"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Peer not found")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_stop_find()
+ peer = dev[0].get_peer(addr1)
+ if peer['listen_freq'] == '0':
+ raise Exception("Peer listen frequency not learned from Probe Request")
+ time.sleep(0.3)
+ dev[0].request("P2P_FLUSH")
+ dev[0].p2p_listen()
+ dev[1].global_request("P2P_PROV_DISC " + addr0 + " display")
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=5)
+ if ev is None:
+ raise Exception("PD Request timed out")
+ ev = dev[1].wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=5)
+ if ev is None:
+ raise Exception("PD Response timed out")
+ peer = dev[0].get_peer(addr1)
+ if peer['listen_freq'] != '0':
+ raise Exception("Peer listen frequency learned unexpectedly from PD Request")
+
+ pin = dev[0].wps_read_pin()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " " + pin + " enter"):
+ raise Exception("P2P_CONNECT on initiator failed")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=5)
+ if ev is None:
+ raise Exception("GO Negotiation start timed out")
+ peer = dev[0].get_peer(addr1)
+ if peer['listen_freq'] == '0':
+ raise Exception("Peer listen frequency not learned from PD followed by GO Neg Req")
+ if "FAIL" in dev[0].global_request("P2P_CONNECT " + addr1 + " " + pin + " display"):
+ raise Exception("P2P_CONNECT on responder failed")
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+
+def test_go_neg_two_peers(dev):
+ """P2P GO Negotiation rejected due to already started negotiation with another peer"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ dev[1].p2p_listen()
+ dev[2].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Could not discover peer")
+ if not dev[0].discover_peer(addr2):
+ raise Exception("Could not discover peer")
+ if "OK" not in dev[0].request("P2P_CONNECT " + addr2 + " pbc auth"):
+ raise Exception("Failed to authorize GO Neg")
+ dev[0].p2p_listen()
+ if not dev[2].discover_peer(addr0):
+ raise Exception("Could not discover peer")
+ if "OK" not in dev[0].request("P2P_CONNECT " + addr1 + " pbc"):
+ raise Exception("Failed to initiate GO Neg")
+ ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=5)
+ if ev is None:
+ raise Exception("timeout on GO Neg RX event")
+ dev[2].request("P2P_CONNECT " + addr0 + " pbc")
+ ev = dev[2].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("Rejection not reported")
+ if "status=5" not in ev:
+ raise Exception("Unexpected status code in rejection: " + ev)
+
+def clear_pbc_overlap(dev, ap):
+ hostapd.remove_bss(ap)
+ dev[0].request("P2P_CANCEL")
+ dev[1].request("P2P_CANCEL")
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+ remove_group(dev[0], dev[1], allow_failure=True)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ time.sleep(0.1)
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ time.sleep(0.1)
+
+@remote_compatible
+def test_grpform_pbc_overlap(dev, apdev):
+ """P2P group formation during PBC overlap"""
+ params = {"ssid": "wps", "eap_server": "1", "wps_state": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("WPS_PBC")
+ time.sleep(0.1)
+
+ # Since P2P Client scan case is now optimized to use a specific SSID, the
+ # WPS AP will not reply to that and the scan after GO Negotiation can quite
+ # likely miss the AP due to dwell time being short enough to miss the Beacon
+ # frame. This has made the test case somewhat pointless, but keep it here
+ # for now with an additional scan to confirm that PBC detection works if
+ # there is a BSS entry for a overlapping AP.
+ for i in range(0, 5):
+ dev[0].scan(freq="2412")
+ if dev[0].get_bss(apdev[0]['bssid']) is not None:
+ break
+
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Could not discover peer")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Could not discover peer")
+ dev[0].p2p_listen()
+ if "OK" not in dev[0].global_request("P2P_CONNECT " + addr1 + " pbc auth go_intent=0"):
+ raise Exception("Failed to authorize GO Neg")
+ if "OK" not in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=15 freq=2412"):
+ raise Exception("Failed to initiate GO Neg")
+ ev = dev[0].wait_global_event(["WPS-OVERLAP-DETECTED",
+ "P2P-GROUP-FORMATION-SUCCESS"], timeout=15)
+ clear_pbc_overlap(dev, apdev[0])
+ if ev is None or "P2P-GROUP-FORMATION-SUCCESS" not in ev:
+ raise Exception("P2P group formation did not complete")
+
+@remote_compatible
+def test_grpform_pbc_overlap_group_iface(dev, apdev):
+ """P2P group formation during PBC overlap using group interfaces"""
+ # Note: Need to include P2P IE from the AP to get the P2P interface BSS
+ # update use this information.
+ params = {"ssid": "wps", "eap_server": "1", "wps_state": "1",
+ "beacon_int": "15", 'manage_p2p': '1'}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.request("WPS_PBC")
+
+ dev[0].request("SET p2p_no_group_iface 0")
+ dev[1].request("SET p2p_no_group_iface 0")
+
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Could not discover peer")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Could not discover peer")
+ dev[0].p2p_stop_find()
+ dev[0].scan(freq="2412")
+ dev[0].p2p_listen()
+ if "OK" not in dev[0].global_request("P2P_CONNECT " + addr1 + " pbc auth go_intent=0"):
+ raise Exception("Failed to authorize GO Neg")
+ if "OK" not in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=15 freq=2412"):
+ raise Exception("Failed to initiate GO Neg")
+ ev = dev[0].wait_global_event(["WPS-OVERLAP-DETECTED",
+ "P2P-GROUP-FORMATION-SUCCESS"], timeout=15)
+ clear_pbc_overlap(dev, apdev[0])
+ if ev is None or "P2P-GROUP-FORMATION-SUCCESS" not in ev:
+ raise Exception("P2P group formation did not complete")
+
+@remote_compatible
+def test_grpform_goneg_fail_with_group_iface(dev):
+ """P2P group formation fails while using group interface"""
+ dev[0].request("SET p2p_no_group_iface 0")
+ dev[1].p2p_listen()
+ peer = dev[1].p2p_dev_addr()
+ if not dev[0].discover_peer(peer):
+ raise Exception("Peer " + peer + " not found")
+ if "OK" not in dev[1].request("P2P_REJECT " + dev[0].p2p_dev_addr()):
+ raise Exception("P2P_REJECT failed")
+ if "OK" not in dev[0].request("P2P_CONNECT " + peer + " pbc"):
+ raise Exception("P2P_CONNECT failed")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("GO Negotiation failure timed out")
+
+@long_duration_test
+def test_grpform_cred_ready_timeout(dev):
+ """P2P GO Negotiation wait for credentials to become ready"""
+ dev[1].p2p_listen()
+ addr1 = dev[1].p2p_dev_addr()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Peer " + addr1 + " not found")
+ if not dev[2].discover_peer(addr1):
+ raise Exception("Peer " + addr1 + " not found(2)")
+
+ start = os.times()[4]
+
+ cmd = "P2P_CONNECT " + addr1 + " 12345670 display"
+ if "OK" not in dev[0].global_request(cmd):
+ raise Exception("Failed to initiate GO Neg")
+
+ if "OK" not in dev[2].global_request(cmd):
+ raise Exception("Failed to initiate GO Neg(2)")
+
+ # First, check with p2p_find
+ ev = dev[2].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=30)
+ if ev is not None:
+ raise Exception("Too early GO Negotiation timeout reported(2)")
+ dev[2].dump_monitor()
+ logger.info("Starting p2p_find to change state")
+ dev[2].p2p_find()
+ for i in range(10):
+ ev = dev[2].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=10)
+ if ev:
+ break
+ dev[2].dump_monitor(global_mon=False)
+ if ev is None:
+ raise Exception("GO Negotiation failure timed out(2)")
+ dev[2].dump_monitor()
+ end = os.times()[4]
+ logger.info("GO Negotiation wait time: {} seconds(2)".format(end - start))
+ if end - start < 120:
+ raise Exception("Too short GO Negotiation wait time(2): {}".format(end - start))
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+
+ wpas.p2p_listen()
+ ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("Did not discover new device after GO Negotiation failure")
+ if wpas.p2p_dev_addr() not in ev:
+ raise Exception("Unexpected device found: " + ev)
+ dev[2].p2p_stop_find()
+ dev[2].dump_monitor()
+ wpas.p2p_stop_find()
+ wpas.close_monitor()
+ del wpas
+
+ # Finally, verify without p2p_find
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=120)
+ if ev is None:
+ raise Exception("GO Negotiation failure timed out")
+ end = os.times()[4]
+ logger.info("GO Negotiation wait time: {} seconds".format(end - start))
+ if end - start < 120:
+ raise Exception("Too short GO Negotiation wait time: {}".format(end - start))
+
+def test_grpform_no_wsc_done(dev):
+ """P2P group formation with WSC-Done not sent"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ for i in range(0, 2):
+ dev[0].request("SET ext_eapol_frame_io 1")
+ dev[1].request("SET ext_eapol_frame_io 1")
+ dev[0].p2p_listen()
+ dev[1].p2p_go_neg_auth(addr0, "12345670", "display", 0)
+ dev[1].p2p_listen()
+ dev[0].p2p_go_neg_init(addr1, "12345670", "enter", timeout=20,
+ go_intent=15, wait_group=False)
+
+ mode = None
+ while True:
+ ev = dev[0].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from GO")
+ if not mode:
+ mode = dev[0].get_status_field("mode")
+ res = dev[1].request("EAPOL_RX " + addr0 + " " + ev.split(' ')[2])
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+ ev = dev[1].wait_event(["EAPOL-TX"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAPOL-TX from P2P Client")
+ msg = ev.split(' ')[2]
+ if msg[46:56] == "102200010f":
+ logger.info("Drop WSC_Done")
+ dev[0].request("SET ext_eapol_frame_io 0")
+ dev[1].request("SET ext_eapol_frame_io 0")
+ # Fake EAP-Failure to complete session on the client
+ id = msg[10:12]
+ dev[1].request("EAPOL_RX " + addr0 + " 0300000404" + id + "0004")
+ break
+ res = dev[0].request("EAPOL_RX " + addr1 + " " + msg)
+ if "OK" not in res:
+ raise Exception("EAPOL_RX failed")
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out on GO")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out on P2P Client")
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+ if mode != "P2P GO - group formation":
+ raise Exception("Unexpected mode on GO during group formation: " + mode)
+
+@remote_compatible
+def test_grpform_wait_peer(dev):
+ """P2P group formation wait for peer to become ready"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Peer " + addr1 + " not found")
+ dev[0].request("SET extra_roc_dur 500")
+ if "OK" not in dev[0].request("P2P_CONNECT " + addr1 + " 12345670 display go_intent=15"):
+ raise Exception("Failed to initiate GO Neg")
+ time.sleep(3)
+ dev[1].request("P2P_CONNECT " + addr0 + " 12345670 enter go_intent=0")
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ dev[0].group_form_result(ev)
+
+ dev[0].request("SET extra_roc_dur 0")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ dev[0].remove_group()
+
+@remote_compatible
+def test_invalid_p2p_connect_command(dev):
+ """P2P_CONNECT error cases"""
+ id = dev[0].add_network()
+ for cmd in ["foo",
+ "00:11:22:33:44:55",
+ "00:11:22:33:44:55 pbc persistent=123",
+ "00:11:22:33:44:55 pbc persistent=%d" % id,
+ "00:11:22:33:44:55 pbc go_intent=-1",
+ "00:11:22:33:44:55 pbc go_intent=16",
+ "00:11:22:33:44:55 pin",
+ "00:11:22:33:44:55 pbc freq=0"]:
+ if "FAIL" not in dev[0].request("P2P_CONNECT " + cmd):
+ raise Exception("Invalid P2P_CONNECT command accepted: " + cmd)
+
+ if "FAIL-INVALID-PIN" not in dev[0].request("P2P_CONNECT 00:11:22:33:44:55 1234567"):
+ raise Exception("Invalid PIN was not rejected")
+ if "FAIL-INVALID-PIN" not in dev[0].request("P2P_CONNECT 00:11:22:33:44:55 12345678a"):
+ raise Exception("Invalid PIN was not rejected")
+
+ if "FAIL-CHANNEL-UNSUPPORTED" not in dev[0].request("P2P_CONNECT 00:11:22:33:44:55 pin freq=3000"):
+ raise Exception("Unsupported channel not reported")
+
+@remote_compatible
+def test_p2p_unauthorize(dev):
+ """P2P_UNAUTHORIZE to unauthorize a peer"""
+ if "FAIL" not in dev[0].request("P2P_UNAUTHORIZE foo"):
+ raise Exception("Invalid P2P_UNAUTHORIZE accepted")
+ if "FAIL" not in dev[0].request("P2P_UNAUTHORIZE 00:11:22:33:44:55"):
+ raise Exception("P2P_UNAUTHORIZE for unknown peer accepted")
+
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].p2p_listen()
+ pin = dev[0].wps_read_pin()
+ dev[0].p2p_go_neg_auth(addr1, pin, "display")
+ dev[0].p2p_listen()
+ if "OK" not in dev[0].request("P2P_UNAUTHORIZE " + addr1):
+ raise Exception("P2P_UNAUTHORIZE failed")
+ dev[1].p2p_go_neg_init(addr0, pin, "keypad", timeout=0)
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
+ if ev is None:
+ raise Exception("No GO Negotiation Request RX reported")
+
+@remote_compatible
+def test_grpform_pbc_multiple(dev):
+ """P2P group formation using PBC multiple times in a row"""
+ try:
+ dev[1].request("SET passive_scan 1")
+ for i in range(5):
+ [i_res, r_res] = go_neg_pbc_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ remove_group(dev[0], dev[1])
+ finally:
+ dev[1].request("SET passive_scan 0")
+ dev[1].flush_scan_cache()
+
+def test_grpform_not_ready(dev):
+ """Not ready for GO Negotiation (listen)"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Could not discover peer")
+ dev[1].global_request("P2P_CONNECT " + addr0 + " pbc")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=5)
+ if ev is None:
+ raise Exception("No P2P-GO-NEG-REQUEST event")
+ dev[0].dump_monitor()
+ time.sleep(5)
+ if not dev[2].discover_peer(addr0):
+ raise Exception("Could not discover peer(2)")
+ for i in range(3):
+ dev[i].p2p_stop_find()
+
+def test_grpform_not_ready2(dev):
+ """Not ready for GO Negotiation (search)"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ dev[0].p2p_find(social=True)
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Could not discover peer")
+ dev[1].global_request("P2P_CONNECT " + addr0 + " pbc")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=5)
+ if ev is None:
+ raise Exception("No P2P-GO-NEG-REQUEST event")
+ dev[0].dump_monitor()
+ time.sleep(1)
+ dev[2].p2p_listen()
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("Peer not discovered after GO Neg Resp(status=1) TX")
+ if addr2 not in ev:
+ raise Exception("Unexpected peer discovered: " + ev)
+ for i in range(3):
+ dev[i].p2p_stop_find()
+
+@remote_compatible
+def test_grpform_and_scan(dev):
+ """GO Negotiation and scan operations"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1):
+ raise Exception("Could not discover peer")
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+
+ if "OK" not in dev[0].request("SCAN TYPE=ONLY freq=2412-2472"):
+ raise Exception("Could not start scan")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Scan did not start")
+ time.sleep(0.1)
+ # Request PD while the previously started scan is still in progress
+ if "OK" not in dev[0].request("P2P_PROV_DISC %s pbc" % addr1):
+ raise Exception("Could not request PD")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ time.sleep(0.3)
+
+ dev[1].p2p_listen()
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-PBC-RESP"], timeout=5)
+ if ev is None:
+ raise Exception("PD Response not received")
+
+ if "OK" not in dev[0].request("SCAN TYPE=ONLY freq=2412-2472"):
+ raise Exception("Could not start scan")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Scan did not start")
+ time.sleep(0.1)
+ # Request GO Neg while the previously started scan is still in progress
+ if "OK" not in dev[0].request("P2P_CONNECT %s pbc" % addr1):
+ raise Exception("Could not request GO Negotiation")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+
+ ev = dev[1].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
+ if ev is None:
+ raise Exception("GO Neg Req RX not reported")
+
+ dev[1].p2p_stop_find()
+
+ if "OK" not in dev[1].request("SCAN TYPE=ONLY freq=2412-2472"):
+ raise Exception("Could not start scan")
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Scan did not start")
+ time.sleep(0.1)
+ dev[1].global_request("P2P_CONNECT " + addr0 + " pbc")
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+
+ ev0 = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev0 is None:
+ raise Exception("Group formation timed out on dev0")
+ dev[0].group_form_result(ev0)
+
+ ev1 = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev1 is None:
+ raise Exception("Group formation timed out on dev1")
+ dev[1].group_form_result(ev1)
+
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ remove_group(dev[0], dev[1])
+
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_grpform_go_neg_dup_on_restart(dev):
+ """Duplicated GO Negotiation Request after GO Neg restart"""
+ if dev[0].p2p_dev_addr() > dev[1].p2p_dev_addr():
+ higher = dev[0]
+ lower = dev[1]
+ else:
+ higher = dev[1]
+ lower = dev[0]
+ addr_low = lower.p2p_dev_addr()
+ addr_high = higher.p2p_dev_addr()
+ higher.p2p_listen()
+ if not lower.discover_peer(addr_high):
+ raise Exception("Could not discover peer")
+ lower.p2p_stop_find()
+
+ if "OK" not in lower.request("P2P_CONNECT %s pbc" % addr_high):
+ raise Exception("Could not request GO Negotiation")
+ ev = higher.wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
+ if ev is None:
+ raise Exception("GO Neg Req RX not reported")
+
+ # Wait for GO Negotiation Response (Status=1) to go through
+ time.sleep(0.2)
+
+ if "FAIL" in lower.request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+
+ higher.p2p_stop_find()
+ higher.global_request("P2P_CONNECT " + addr_low + " pbc")
+
+ # Wait for the GO Negotiation Request frame of the restarted GO Negotiation
+ rx_msg = lower.mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame")
+ if p2p['subtype'] != 0:
+ raise Exception("Unexpected P2P Public Action subtype %d" % p2p['subtype'])
+
+ # Send duplicate GO Negotiation Request from the prior instance of GO
+ # Negotiation
+ lower.p2p_stop_find()
+ peer = higher.get_peer(addr_low)
+
+ msg = p2p_hdr(addr_high, addr_low, type=P2P_GO_NEG_REQ, dialog_token=123)
+ attrs = p2p_attr_capability(dev_capab=0x25, group_capab=0x08)
+ attrs += p2p_attr_go_intent(go_intent=7, tie_breaker=1)
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel(chan=(int(peer['listen_freq']) - 2407) // 5)
+ attrs += p2p_attr_intended_interface_addr(lower.p2p_dev_addr())
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr_low, config_methods=0x80, name="Device A")
+ attrs += p2p_attr_operating_channel()
+ wsc_attrs = struct.pack(">HHH", 0x1012, 2, 4)
+ msg['payload'] += ie_p2p(attrs) + ie_wsc(wsc_attrs)
+ mgmt_tx(lower, "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr_high, addr_high, peer['listen_freq'], binascii.hexlify(msg['payload']).decode()))
+
+ # Wait for the GO Negotiation Response frame which would have been sent in
+ # this case previously, but not anymore after the check for
+ # dev->go_neg_req_sent and dev->flags & P2P_DEV_PEER_WAITING_RESPONSE.
+ rx_msg = lower.mgmt_rx(timeout=0.2)
+ if rx_msg is not None:
+ raise Exception("Unexpected management frame")
+
+ if "FAIL" in lower.request("SET ext_mgmt_frame_handling 0"):
+ raise Exception("Failed to disable external management frame handling")
+ lower.p2p_listen()
+
+ ev = lower.wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("GO Negotiation did not succeed on dev0")
+
+ ev = higher.wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("GO Negotiation did not succeed on dev1")
+
+ ev0 = lower.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev0 is None:
+ raise Exception("Group formation timed out on dev0")
+ lower.group_form_result(ev0)
+
+ ev1 = higher.wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev1 is None:
+ raise Exception("Group formation timed out on dev1")
+ higher.group_form_result(ev1)
+
+ lower.dump_monitor()
+ higher.dump_monitor()
+
+ remove_group(lower, higher)
+
+ lower.dump_monitor()
+ higher.dump_monitor()
+
+@remote_compatible
+def test_grpform_go_neg_stopped(dev):
+ """GO Negotiation stopped after TX start"""
+ addr0 = dev[0].p2p_dev_addr()
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Could not discover peer")
+ dev[0].p2p_stop_find()
+ if "OK" not in dev[1].request("P2P_CONNECT %s pbc" % addr0):
+ raise Exception("Could not request GO Negotiation")
+ dev[1].p2p_stop_find()
+ dev[1].p2p_listen()
+ dev[0].p2p_find(social=True)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=1.2)
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+ if ev is None:
+ raise Exception("Did not find peer quickly enough after stopped P2P_CONNECT")
+
+def test_grpform_random_addr(dev):
+ """P2P group formation with random interface addresses"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ try:
+ if "OK" not in dev[0].global_request("SET p2p_interface_random_mac_addr 1"):
+ raise Exception("Failed to set p2p_interface_random_mac_addr")
+ if "OK" not in dev[1].global_request("SET p2p_interface_random_mac_addr 1"):
+ raise Exception("Failed to set p2p_interface_random_mac_addr")
+ [i_res, r_res] = go_neg_pin_authorized(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ if "p2p-wlan" not in i_res['ifname']:
+ raise Exception("Unexpected group interface name")
+ check_grpform_results(i_res, r_res)
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+ remove_group(dev[0], dev[1])
+ if i_res['ifname'] in get_ifnames():
+ raise Exception("Group interface netdev was not removed")
+ finally:
+ dev[0].global_request("SET p2p_interface_random_mac_addr 0")
+ dev[1].global_request("SET p2p_interface_random_mac_addr 0")
diff --git a/contrib/wpa/tests/hwsim/test_p2p_invitation.py b/contrib/wpa/tests/hwsim/test_p2p_invitation.py
new file mode 100644
index 000000000000..1e84af29dfb9
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_invitation.py
@@ -0,0 +1,195 @@
+# P2P invitation test cases
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+
+@remote_compatible
+def test_p2p_go_invite(dev):
+ """P2P GO inviting a client to join"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ logger.info("Generate BSS table entry for old group")
+ # this adds more coverage to testing by forcing the GO to be found with an
+ # older entry in the BSS table and with that entry having a different
+ # operating channel.
+ dev[0].p2p_start_go(freq=2422)
+ dev[1].scan()
+ dev[0].remove_group()
+
+ logger.info("Discover peer")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1, social=True):
+ raise Exception("Peer " + addr1 + " not found")
+
+ logger.info("Start GO on non-social channel")
+ res = dev[0].p2p_start_go(freq=2417)
+ logger.debug("res: " + str(res))
+
+ logger.info("Invite peer to join the group")
+ dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on invitation on peer")
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on invitation on GO")
+ if "status=1" not in ev:
+ raise Exception("Unexpected invitation result")
+
+ logger.info("Join the group")
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(addr0, pin, timeout=60)
+ logger.info("Client connected")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+ logger.info("Terminate group")
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+@remote_compatible
+def test_p2p_go_invite_auth(dev):
+ """P2P GO inviting a client to join (authorized invitation)"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ logger.info("Generate BSS table entry for old group")
+ # this adds more coverage to testing by forcing the GO to be found with an
+ # older entry in the BSS table and with that entry having a different
+ # operating channel.
+ dev[0].p2p_start_go(freq=2432)
+ dev[1].scan()
+ dev[0].remove_group()
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.info("Discover peer")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1, social=True):
+ raise Exception("Peer " + addr1 + " not found")
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0, social=True):
+ raise Exception("Peer " + addr0 + " not found")
+ dev[1].p2p_listen()
+
+ logger.info("Authorize invitation")
+ pin = dev[1].wps_read_pin()
+ dev[1].global_request("P2P_CONNECT " + addr0 + " " + pin + " join auth")
+
+ logger.info("Start GO on non-social channel")
+ res = dev[0].p2p_start_go(freq=2427)
+ logger.debug("res: " + str(res))
+
+ logger.info("Invite peer to join the group")
+ dev[0].p2p_go_authorize_client(pin)
+ dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED",
+ "P2P-GROUP-STARTED"], timeout=20)
+ if ev is None:
+ raise Exception("Timeout on invitation on peer")
+ if "P2P-INVITATION-RECEIVED" in ev:
+ raise Exception("Unexpected request to accept pre-authorized invitaton")
+ dev[1].group_form_result(ev)
+ dev[0].dump_monitor()
+
+ logger.info("Client connected")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+ logger.info("Terminate group")
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+@remote_compatible
+def test_p2p_go_invite_unknown(dev):
+ """P2P GO inviting a client that has not discovered the GO"""
+ try:
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1, social=True):
+ raise Exception("Peer " + addr1 + " not found")
+ dev[1].global_request("P2P_FLUSH")
+ dev[1].p2p_listen()
+
+ dev[0].p2p_start_go(freq=2412)
+
+ logger.info("Invite peer to join the group")
+ # Prevent peer entry from being added for testing coverage
+ if "OK" not in dev[1].global_request("P2P_SET peer_filter 00:11:22:33:44:55"):
+ raise Exception("Failed to set peer_filter")
+ dev[0].p2p_go_authorize_client("12345670")
+ dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=15)
+ if ev is None:
+ raise Exception("Invitation Request not received")
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+ if ev is None:
+ raise Exception("Invitation Response not received")
+ if "status=1" not in ev:
+ raise Exception("Unexpected invitation result: " + ev)
+ finally:
+ dev[1].global_request("P2P_SET peer_filter 00:00:00:00:00:00")
+
+def test_p2p_cli_invite(dev):
+ """P2P Client inviting a device to join"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+
+ dev[0].p2p_start_go(freq=2412)
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(addr0, pin, timeout=60)
+
+ dev[2].p2p_listen()
+ if not dev[1].discover_peer(addr2, social=True):
+ raise Exception("Peer " + addr2 + " not found")
+
+ if "OK" not in dev[1].global_request("P2P_INVITE group=" + dev[1].group_ifname + " peer=" + addr2):
+ raise Exception("Unexpected failure of P2P_INVITE to known peer")
+ ev = dev[2].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on invitation invited peer")
+ if "sa=" + addr1 not in ev:
+ raise Exception("Incorrect source address")
+ if "go_dev_addr=" + addr0 not in ev:
+ raise Exception("Incorrect GO address")
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on invitation on inviting client")
+ if "status=1" not in ev:
+ raise Exception("Unexpected invitation result")
+
+ pin = dev[2].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[2].p2p_connect_group(addr0, pin, timeout=60)
+
+ if "FAIL" not in dev[1].global_request("P2P_INVITE group=" + dev[1].group_ifname + " peer=00:11:22:33:44:55"):
+ raise Exception("Unexpected success of P2P_INVITE to unknown peer")
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ dev[2].wait_go_ending_session()
+
+@remote_compatible
+def test_p2p_invite_invalid(dev):
+ """Invalid parameters to P2P_INVITE"""
+ id = dev[0].add_network()
+ for cmd in ["foo=bar",
+ "persistent=123 peer=foo",
+ "persistent=123",
+ "persistent=%d" % id,
+ "group=foo",
+ "group=foo peer=foo",
+ "group=foo peer=00:11:22:33:44:55 go_dev_addr=foo"]:
+ if "FAIL" not in dev[0].request("P2P_INVITE " + cmd):
+ raise Exception("Invalid P2P_INVITE accepted: " + cmd)
diff --git a/contrib/wpa/tests/hwsim/test_p2p_messages.py b/contrib/wpa/tests/hwsim/test_p2p_messages.py
new file mode 100644
index 000000000000..a4cac698b2b2
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_messages.py
@@ -0,0 +1,2143 @@
+# P2P protocol tests for various messages
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import struct
+import time
+import logging
+logger = logging.getLogger()
+
+import hostapd
+from p2p_utils import *
+from test_gas import anqp_adv_proto
+
+def ie_ssid(ssid):
+ return struct.pack("<BB", WLAN_EID_SSID, len(ssid)) + ssid.encode()
+
+def ie_supp_rates():
+ return struct.pack("<BBBBBBBBBB", WLAN_EID_SUPP_RATES, 8,
+ 2*6, 2*9, 2*12, 2*18, 2*24, 2*36, 2*48, 2*54)
+
+def ie_p2p(attrs):
+ return struct.pack("<BBBBBB", WLAN_EID_VENDOR_SPECIFIC, 4 + len(attrs),
+ 0x50, 0x6f, 0x9a, 9) + attrs
+
+def ie_wsc(attrs):
+ return struct.pack("<BBBBBB", WLAN_EID_VENDOR_SPECIFIC, 4 + len(attrs),
+ 0x00, 0x50, 0xf2, 4) + attrs
+
+def wsc_attr_config_methods(methods=0):
+ return struct.pack(">HHH", WSC_ATTR_CONFIG_METHODS, 2, methods)
+
+def p2p_attr_status(status=P2P_SC_SUCCESS):
+ return struct.pack("<BHB", P2P_ATTR_STATUS, 1, status)
+
+def p2p_attr_minor_reason_code(code=0):
+ return struct.pack("<BHB", P2P_ATTR_MINOR_REASON_CODE, 1, code)
+
+def p2p_attr_capability(dev_capab=0, group_capab=0):
+ return struct.pack("<BHBB", P2P_ATTR_CAPABILITY, 2, dev_capab, group_capab)
+
+def p2p_attr_device_id(addr):
+ val = struct.unpack('6B', binascii.unhexlify(addr.replace(':', '')))
+ t = (P2P_ATTR_DEVICE_ID, 6) + val
+ return struct.pack('<BH6B', *t)
+
+def p2p_attr_go_intent(go_intent=0, tie_breaker=0):
+ return struct.pack("<BHB", P2P_ATTR_GROUP_OWNER_INTENT, 1,
+ (go_intent << 1) | (tie_breaker & 0x01))
+
+def p2p_attr_config_timeout(go_config_timeout=0, client_config_timeout=0):
+ return struct.pack("<BHBB", P2P_ATTR_CONFIGURATION_TIMEOUT, 2,
+ go_config_timeout, client_config_timeout)
+
+def p2p_attr_listen_channel(op_class=81, chan=1):
+ return struct.pack("<BHBBBBB", P2P_ATTR_LISTEN_CHANNEL, 5,
+ 0x58, 0x58, 0x04, op_class, chan)
+
+def p2p_attr_group_bssid(addr):
+ val = struct.unpack('6B', binascii.unhexlify(addr.replace(':', '')))
+ t = (P2P_ATTR_GROUP_BSSID, 6) + val
+ return struct.pack('<BH6B', *t)
+
+def p2p_attr_ext_listen_timing(period=0, interval=0):
+ return struct.pack("<BHHH", P2P_ATTR_EXT_LISTEN_TIMING, 4, period, interval)
+
+def p2p_attr_intended_interface_addr(addr):
+ val = struct.unpack('6B', binascii.unhexlify(addr.replace(':', '')))
+ t = (P2P_ATTR_INTENDED_INTERFACE_ADDR, 6) + val
+ return struct.pack('<BH6B', *t)
+
+def p2p_attr_manageability(bitmap=0):
+ return struct.pack("<BHB", P2P_ATTR_MANAGEABILITY, 1, bitmap)
+
+def p2p_attr_channel_list():
+ return struct.pack("<BH3BBB11B", P2P_ATTR_CHANNEL_LIST, 16,
+ 0x58, 0x58, 0x04,
+ 81, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
+
+def p2p_attr_device_info(addr, name="Test", config_methods=0, dev_type="00010050F2040001"):
+ val = struct.unpack('6B', binascii.unhexlify(addr.replace(':', '')))
+ val2 = struct.unpack('8B', binascii.unhexlify(dev_type))
+ t = (P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 4 + len(name)) + val
+ t2 = val2 + (0,)
+ return struct.pack("<BH6B", *t) + struct.pack(">H", config_methods) + struct.pack("8BB", *t2) + struct.pack('>HH', 0x1011, len(name)) + name.encode()
+
+def p2p_attr_group_id(addr, ssid):
+ val = struct.unpack('6B', binascii.unhexlify(addr.replace(':', '')))
+ t = (P2P_ATTR_GROUP_ID, 6 + len(ssid)) + val
+ return struct.pack('<BH6B', *t) + ssid.encode()
+
+def p2p_attr_operating_channel(op_class=81, chan=1):
+ return struct.pack("<BHBBBBB", P2P_ATTR_OPERATING_CHANNEL, 5,
+ 0x58, 0x58, 0x04, op_class, chan)
+
+def p2p_attr_invitation_flags(bitmap=0):
+ return struct.pack("<BHB", P2P_ATTR_INVITATION_FLAGS, 1, bitmap)
+
+def p2p_hdr_helper(dst, src, type=None, dialog_token=1, req=True):
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = dst
+ msg['sa'] = src
+ if req:
+ msg['bssid'] = dst
+ else:
+ msg['bssid'] = src
+ msg['payload'] = struct.pack("<BBBBBB",
+ ACTION_CATEG_PUBLIC, 9, 0x50, 0x6f, 0x9a, 9)
+ if type is not None:
+ msg['payload'] += struct.pack("<B", type)
+ if dialog_token:
+ msg['payload'] += struct.pack("<B", dialog_token)
+ return msg
+
+def p2p_hdr(dst, src, type=None, dialog_token=1):
+ return p2p_hdr_helper(dst, src, type, dialog_token, True)
+
+def p2p_hdr_resp(dst, src, type=None, dialog_token=1):
+ return p2p_hdr_helper(dst, src, type, dialog_token, False)
+
+def start_p2p(dev, apdev):
+ addr0 = dev[0].p2p_dev_addr()
+ dev[0].p2p_listen()
+ dev[1].p2p_find(social=True)
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Device discovery timed out")
+ dev[1].p2p_stop_find()
+ peer = dev[1].get_peer(addr0)
+
+ bssid = apdev[0]['bssid']
+ params = {'ssid': "test", 'beacon_int': "2000"}
+ if peer['listen_freq'] == "2412":
+ params['channel'] = '1'
+ elif peer['listen_freq'] == "2437":
+ params['channel'] = '6'
+ elif peer['listen_freq'] == "2462":
+ params['channel'] = '11'
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ return addr0, bssid, hapd, int(params['channel'])
+
+def p2p_probe(hapd, src, chan=1):
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_PROBE_REQ << 4
+ msg['da'] = "ff:ff:ff:ff:ff:ff"
+ msg['sa'] = src
+ msg['bssid'] = "ff:ff:ff:ff:ff:ff"
+ attrs = p2p_attr_listen_channel(chan=chan)
+ msg['payload'] = ie_ssid("DIRECT-") + ie_supp_rates() + ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+def parse_p2p_public_action(payload):
+ pos = payload
+ (category, action) = struct.unpack('BB', pos[0:2])
+ if category != ACTION_CATEG_PUBLIC:
+ return None
+ if action != 9:
+ return None
+ pos = pos[2:]
+ (oui1, oui2, oui3, subtype) = struct.unpack('BBBB', pos[0:4])
+ if oui1 != 0x50 or oui2 != 0x6f or oui3 != 0x9a or subtype != 9:
+ return None
+ pos = pos[4:]
+ (subtype, dialog_token) = struct.unpack('BB', pos[0:2])
+ p2p = {}
+ p2p['subtype'] = subtype
+ p2p['dialog_token'] = dialog_token
+ pos = pos[2:]
+ p2p['elements'] = pos
+ while len(pos) > 2:
+ (id, elen) = struct.unpack('BB', pos[0:2])
+ pos = pos[2:]
+ if elen > len(pos):
+ raise Exception("Truncated IE in P2P Public Action frame (elen=%d left=%d)" % (elen, len(pos)))
+ if id == WLAN_EID_VENDOR_SPECIFIC:
+ if elen < 4:
+ raise Exception("Too short vendor specific IE in P2P Public Action frame (elen=%d)" % elen)
+ (oui1, oui2, oui3, subtype) = struct.unpack('BBBB', pos[0:4])
+ if oui1 == 0x50 and oui2 == 0x6f and oui3 == 0x9a and subtype == 9:
+ if 'p2p' in p2p:
+ p2p['p2p'] += pos[4:elen]
+ else:
+ p2p['p2p'] = pos[4:elen]
+ if oui1 == 0x00 and oui2 == 0x50 and oui3 == 0xf2 and subtype == 4:
+ p2p['wsc'] = pos[4:elen]
+ pos = pos[elen:]
+ if len(pos) > 0:
+ raise Exception("Invalid element in P2P Public Action frame")
+
+ if 'p2p' in p2p:
+ p2p['p2p_attrs'] = {}
+ pos = p2p['p2p']
+ while len(pos) >= 3:
+ (id, alen) = struct.unpack('<BH', pos[0:3])
+ pos = pos[3:]
+ if alen > len(pos):
+ logger.info("P2P payload: " + binascii.hexlify(p2p['p2p']))
+ raise Exception("Truncated P2P attribute in P2P Public Action frame (alen=%d left=%d p2p-payload=%d)" % (alen, len(pos), len(p2p['p2p'])))
+ p2p['p2p_attrs'][id] = pos[0:alen]
+ pos = pos[alen:]
+ if P2P_ATTR_STATUS in p2p['p2p_attrs']:
+ p2p['p2p_status'] = struct.unpack('B', p2p['p2p_attrs'][P2P_ATTR_STATUS])[0]
+
+ if 'wsc' in p2p:
+ p2p['wsc_attrs'] = {}
+ pos = p2p['wsc']
+ while len(pos) >= 4:
+ (id, alen) = struct.unpack('>HH', pos[0:4])
+ pos = pos[4:]
+ if alen > len(pos):
+ logger.info("WSC payload: " + binascii.hexlify(p2p['wsc']))
+ raise Exception("Truncated WSC attribute in P2P Public Action frame (alen=%d left=%d wsc-payload=%d)" % (alen, len(pos), len(p2p['wsc'])))
+ p2p['wsc_attrs'][id] = pos[0:alen]
+ pos = pos[alen:]
+
+ return p2p
+
+@remote_compatible
+def test_p2p_msg_empty(dev, apdev):
+ """P2P protocol test: empty P2P Public Action frame"""
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+ msg = p2p_hdr(dst, src)
+ hapd.mgmt_tx(msg)
+
+@remote_compatible
+def test_p2p_msg_long_ssid(dev, apdev):
+ """P2P protocol test: Too long SSID in P2P Public Action frame"""
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=1)
+ attrs = p2p_attr_config_timeout()
+ attrs += p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, 'DIRECT-foo')
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ msg['payload'] += ie_ssid(255 * 'A')
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on device found event")
+
+@remote_compatible
+def test_p2p_msg_long_dev_name(dev, apdev):
+ """P2P protocol test: Too long Device Name in P2P Public Action frame"""
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=1)
+ attrs = p2p_attr_config_timeout()
+ attrs += p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, 'DIRECT-foo')
+ attrs += p2p_attr_device_info(src, config_methods=0x0108,
+ name="123456789012345678901234567890123")
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_event(["P2P-DEVICE-FOUND"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected device found event")
+
+def test_p2p_msg_invitation_req(dev, apdev):
+ """P2P protocol tests for invitation request processing"""
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+
+ # Empty P2P Invitation Request (missing dialog token)
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=None)
+ hapd.mgmt_tx(msg)
+ dialog_token = 0
+
+ # Various p2p_parse() failure cases due to invalid attributes
+
+ # Too short attribute header
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BB", P2P_ATTR_CAPABILITY, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Minimal attribute underflow
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH", P2P_ATTR_CAPABILITY, 1)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Large attribute underflow
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BHB", P2P_ATTR_CAPABILITY, 0xffff, 1)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Capability attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BHB", P2P_ATTR_CAPABILITY, 1, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Device ID attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ val = struct.unpack('5B', binascii.unhexlify("1122334455"))
+ t = (P2P_ATTR_DEVICE_ID, 5) + val
+ attrs = struct.pack('<BH5B', *t)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short GO Intent attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH", P2P_ATTR_GROUP_OWNER_INTENT, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Status attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH", P2P_ATTR_STATUS, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # null Listen channel and too short Listen Channel attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH", P2P_ATTR_LISTEN_CHANNEL, 0)
+ attrs += struct.pack("<BHB", P2P_ATTR_LISTEN_CHANNEL, 1, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # null Operating channel and too short Operating Channel attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH", P2P_ATTR_OPERATING_CHANNEL, 0)
+ attrs += struct.pack("<BHB", P2P_ATTR_OPERATING_CHANNEL, 1, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Channel List attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BHBB", P2P_ATTR_CHANNEL_LIST, 2, 1, 2)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Device Info attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BHBB", P2P_ATTR_DEVICE_INFO, 2, 1, 2)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Truncated Secondary Device Types in Device Info attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH6BH8BB", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1,
+ 0, 0, 0, 0, 0, 0,
+ 0,
+ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x11, 0x22,
+ 255)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Missing Device Name in Device Info attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH6BH8BB8B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8,
+ 0, 0, 0, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1,
+ 1, 2, 3, 4, 5, 6, 7, 8)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Invalid Device Name header in Device Info attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH6BH8BB8B4B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8 + 4,
+ 0, 0, 0, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 0x11, 0x12, 0, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Invalid Device Name header length in Device Info attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH6BH8BB8B4B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8 + 4,
+ 0, 0, 0, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 0x10, 0x11, 0xff, 0xff)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Invalid Device Name header length in Device Info attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ devname = b'A'
+ attrs = struct.pack("<BH6BH8BB8B4B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8 + 4 + len(devname),
+ 0, 0, 0, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 0x10, 0x11, 0, len(devname) + 1) + devname
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Device Name filtering and too long Device Name in Device Info attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH6BH8BB8B4B4B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8 + 4 + 4,
+ 0, 0, 0, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 0x10, 0x11, 0, 4,
+ 64, 9, 0, 64)
+ devname = b'123456789012345678901234567890123'
+ attrs += struct.pack("<BH6BH8BB8B4B", P2P_ATTR_DEVICE_INFO, 6 + 2 + 8 + 1 + 8 + 4 + len(devname),
+ 0, 0, 0, 0, 0, 0,
+ 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 1,
+ 1, 2, 3, 4, 5, 6, 7, 8,
+ 0x10, 0x11, 0, len(devname)) + devname
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Configuration Timeout attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BHB", P2P_ATTR_CONFIGURATION_TIMEOUT, 1, 1)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Intended P2P Interface Address attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BHB", P2P_ATTR_INTENDED_INTERFACE_ADDR, 1, 1)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short P2P Group BSSID attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BHB", P2P_ATTR_GROUP_BSSID, 1, 1)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short P2P Group ID attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BHB", P2P_ATTR_GROUP_ID, 1, 1)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too long P2P Group ID attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH6B", P2P_ATTR_GROUP_ID, 6 + 33, 0, 0, 0, 0, 0, 0) + b"123456789012345678901234567890123"
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Invitation Flags attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH", P2P_ATTR_INVITATION_FLAGS, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Valid and too short Manageability attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_manageability()
+ attrs += struct.pack("<BH", P2P_ATTR_MANAGEABILITY, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short NoA attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BHB", P2P_ATTR_NOTICE_OF_ABSENCE, 1, 1)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Valid and too short Extended Listen Timing attributes
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_ext_listen_timing(period=100, interval=50)
+ attrs += struct.pack("<BHBBB", P2P_ATTR_EXT_LISTEN_TIMING, 3, 0, 0, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Valid and too short Minor Reason Code attributes
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_minor_reason_code(code=2)
+ attrs += struct.pack("<BH", P2P_ATTR_MINOR_REASON_CODE, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Unknown attribute and too short OOB GO Negotiation Channel attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BHB", 99, 1, 1)
+ attrs += struct.pack("<BHB", P2P_ATTR_OOB_GO_NEG_CHANNEL, 1, 1)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Service Hash attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH5B", P2P_ATTR_SERVICE_HASH, 5, 1, 2, 3, 4, 5)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Connection Capability attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH", P2P_ATTR_CONNECTION_CAPABILITY, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Advertisement ID attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH9B", P2P_ATTR_ADVERTISEMENT_ID, 9, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Truncated and too short Service Instance attributes
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH8B", P2P_ATTR_ADVERTISED_SERVICE, 8, 1, 2, 3, 4, 5,
+ 6, 2, 8)
+ attrs += struct.pack("<BH7B", P2P_ATTR_ADVERTISED_SERVICE, 7, 1, 2, 3, 4, 5,
+ 6, 7)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Session ID attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH4B", P2P_ATTR_SESSION_ID, 4, 1, 2, 3, 4)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Feature Capability attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH", P2P_ATTR_FEATURE_CAPABILITY, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too short Persistent Group attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH5B", P2P_ATTR_PERSISTENT_GROUP, 5, 1, 2, 3, 4, 5)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ # Too long Persistent Group attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BH9L3B", P2P_ATTR_PERSISTENT_GROUP, 6 + 32 + 1,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ if hapd.mgmt_rx(timeout=0.5) is not None:
+ raise Exception("Unexpected management frame received")
+
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs += p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on device found event")
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on invitation event " + str(dialog_token))
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs += p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on invitation event " + str(dialog_token))
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ #attrs = p2p_attr_config_timeout()
+ attrs = p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ #attrs = p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs = p2p_attr_invitation_flags()
+ #attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs = p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ #attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs = p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ #attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs = p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ #attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs = p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ #attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+ # Unusable peer operating channel preference
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs = p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel(chan=15)
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+def test_p2p_msg_invitation_req_to_go(dev, apdev):
+ """P2P protocol tests for invitation request processing on GO device"""
+ res = form(dev[0], dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ peer = dev[1].get_peer(addr0)
+ listen_freq = peer['listen_freq']
+
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+
+ networks = dev[0].list_networks()
+ if len(networks) != 1:
+ raise Exception("Unexpected number of networks")
+ if "[P2P-PERSISTENT]" not in networks[0]['flags']:
+ raise Exception("Not the persistent group data")
+ dev[0].p2p_start_go(persistent=networks[0]['id'], freq=listen_freq)
+
+ dialog_token = 0
+
+ # Unusable peer operating channel preference
+ dialog_token += 1
+ msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_REQ,
+ dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs = p2p_attr_invitation_flags(bitmap=1)
+ attrs += p2p_attr_operating_channel(chan=15)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_group_id(res['go_dev_addr'], res['ssid'])
+ attrs += p2p_attr_device_info(addr1, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, peer['listen_freq'], binascii.hexlify(msg['payload']).decode()))
+
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_RESP:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+ if p2p['p2p_status'] != 0:
+ raise Exception("Unexpected status %d" % p2p['p2p_status'])
+
+ # Forced channel re-selection due to channel list
+ dialog_token += 1
+ msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_REQ,
+ dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs = p2p_attr_invitation_flags(bitmap=1)
+ attrs += struct.pack("<BH3BBBB", P2P_ATTR_CHANNEL_LIST, 6,
+ 0x58, 0x58, 0x04,
+ 81, 1, 3)
+ attrs += p2p_attr_group_id(res['go_dev_addr'], res['ssid'])
+ attrs += p2p_attr_device_info(addr1, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(addr0, addr0, peer['listen_freq'], binascii.hexlify(msg['payload']).decode()))
+
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_RESP:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+ if p2p['p2p_status'] != 7 and dev[1].get_mcc() <= 1:
+ raise Exception("Unexpected status %d" % p2p['p2p_status'])
+
+@remote_compatible
+def test_p2p_msg_invitation_req_unknown(dev, apdev):
+ """P2P protocol tests for invitation request from unknown peer"""
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+ dialog_token = 0
+
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs += p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += p2p_attr_channel_list()
+ #attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ #attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on invitation event " + str(dialog_token))
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+
+@remote_compatible
+def test_p2p_msg_invitation_no_common_channels(dev, apdev):
+ """P2P protocol tests for invitation request without common channels"""
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+ dialog_token = 0
+
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_config_timeout()
+ attrs += p2p_attr_invitation_flags()
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_bssid(src)
+ attrs += struct.pack("<BH3BBB", P2P_ATTR_CHANNEL_LIST, 5,
+ 0x58, 0x58, 0x04,
+ 81, 0)
+ attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No invitation response " + str(dialog_token))
+ ev = dev[0].wait_event(["P2P-INVITATION-RECEIVED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected invitation event")
+
+def test_p2p_msg_invitation_resp(dev, apdev):
+ """P2P protocol tests for invitation response processing"""
+ form(dev[0], dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ peer = dev[1].get_peer(addr0)
+
+ # P2P Invitation Response from unknown peer
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=1)
+ hapd.mgmt_tx(msg)
+
+ # P2P Invitation Response from peer that is not in invitation
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=2)
+ attrs = p2p_attr_status()
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, peer['listen_freq'], binascii.hexlify(msg['payload']).decode()))
+ time.sleep(0.25)
+
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+
+ invite(dev[0], dev[1])
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+ # Invalid attribute to cause p2p_parse() failure
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=p2p['dialog_token'])
+ attrs = struct.pack("<BB", P2P_ATTR_CAPABILITY, 0)
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']).decode()))
+
+ invite(dev[0], dev[1])
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+ # missing mandatory Status attribute
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=p2p['dialog_token'])
+ attrs = p2p_attr_channel_list()
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']).decode()))
+
+ invite(dev[0], dev[1])
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+ # no channel match (no common channel found at all)
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=p2p['dialog_token'])
+ attrs = p2p_attr_status()
+ attrs += struct.pack("<BH3BBBB", P2P_ATTR_CHANNEL_LIST, 6,
+ 0x58, 0x58, 0x04,
+ 81, 1, 15)
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']).decode()))
+
+ invite(dev[0], dev[1])
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+ # no channel match (no acceptable P2P channel)
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=p2p['dialog_token'])
+ attrs = p2p_attr_status()
+ attrs += struct.pack("<BH3BBBB", P2P_ATTR_CHANNEL_LIST, 6,
+ 0x58, 0x58, 0x04,
+ 81, 1, 12)
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']).decode()))
+
+ invite(dev[0], dev[1])
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+ # missing mandatory Channel List attribute (ignored as a workaround)
+ msg = p2p_hdr(dst, src, type=P2P_INVITATION_RESP, dialog_token=p2p['dialog_token'])
+ attrs = p2p_attr_status()
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']).decode()))
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group was not started")
+
+def test_p2p_msg_invitation_resend(dev, apdev):
+ """P2P protocol tests for invitation resending on no-common-channels"""
+ form(dev[0], dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+
+ logger.info("Forced channel in invitation")
+ invite(dev[0], dev[1], extra="freq=2422")
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+ msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_RESP,
+ dialog_token=p2p['dialog_token'])
+ attrs = p2p_attr_status(status=P2P_SC_FAIL_NO_COMMON_CHANNELS)
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']).decode()))
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on invitation result")
+ if "status=7" not in ev:
+ raise Exception("Unexpected invitation result: " + ev)
+
+ logger.info("Any channel allowed, only preference provided in invitation")
+ invite(dev[0], dev[1], extra="pref=2422")
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+ msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_RESP,
+ dialog_token=p2p['dialog_token'])
+ attrs = p2p_attr_status(status=P2P_SC_FAIL_NO_COMMON_CHANNELS)
+ msg['payload'] += ie_p2p(attrs)
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 0"):
+ raise Exception("Failed to disable external management frame handling")
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']).decode()))
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on invitation result")
+ if "status=0" not in ev:
+ raise Exception("Unexpected invitation result: " + ev)
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group was not started on dev0")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Group was not started on dev1")
+
+def test_p2p_msg_invitation_resend_duplicate(dev, apdev):
+ """P2P protocol tests for invitation resending on no-common-channels and duplicated response"""
+ form(dev[0], dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+
+ logger.info("Any channel allowed, only preference provided in invitation")
+ invite(dev[0], dev[1], extra="pref=2422")
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+ msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_RESP,
+ dialog_token=p2p['dialog_token'])
+ attrs = p2p_attr_status(status=P2P_SC_FAIL_NO_COMMON_CHANNELS)
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']).decode()))
+
+ rx_msg = dev[1].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(rx_msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_INVITATION_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+
+ logger.info("Retransmit duplicate of previous response")
+ mgmt_tx(dev[1], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']).decode()))
+
+ logger.info("Transmit real response")
+ msg = p2p_hdr(addr0, addr1, type=P2P_INVITATION_RESP,
+ dialog_token=p2p['dialog_token'])
+ attrs = p2p_attr_status(status=P2P_SC_SUCCESS)
+ attrs += p2p_attr_channel_list()
+ msg['payload'] += ie_p2p(attrs)
+ if "FAIL" in dev[1].request("MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr0, addr0, rx_msg['freq'], binascii.hexlify(msg['payload']).decode())):
+ raise Exception("Failed to transmit real response")
+ dev[1].request("SET ext_mgmt_frame_handling 0")
+
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on invitation result")
+ if "status=0" not in ev:
+ raise Exception("Unexpected invitation result: " + ev)
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("Group formation timed out")
+ dev[0].group_form_result(ev)
+ dev[0].remove_group()
+
+@remote_compatible
+def test_p2p_msg_pd_req(dev, apdev):
+ """P2P protocol tests for provision discovery request processing"""
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+ dialog_token = 0
+
+ # Too short attribute header
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_PROV_DISC_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BB", P2P_ATTR_CAPABILITY, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+
+ if hapd.mgmt_rx(timeout=0.5) is not None:
+ raise Exception("Unexpected management frame received")
+
+ # No attributes
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_PROV_DISC_REQ, dialog_token=dialog_token)
+ attrs = b''
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No PD response " + str(dialog_token))
+
+ # Valid request
+ time.sleep(0.1)
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_PROV_DISC_REQ, dialog_token=dialog_token)
+ attrs = wsc_attr_config_methods(methods=0x1008)
+ msg['payload'] += ie_wsc(attrs)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on device found event")
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on PD event")
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No PD response " + str(dialog_token))
+
+ # Unknown group
+ time.sleep(0.1)
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_PROV_DISC_REQ, dialog_token=dialog_token)
+ attrs = wsc_attr_config_methods(methods=0x1008)
+ msg['payload'] += ie_wsc(attrs)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_group_id("02:02:02:02:02:02", "DIRECT-foo")
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No PD response " + str(dialog_token))
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected PD event")
+
+ # Listen channel is not yet known
+ if "FAIL" not in dev[0].global_request("P2P_PROV_DISC " + src + " display"):
+ raise Exception("Unexpected P2P_PROV_DISC success")
+
+ # Unknown peer
+ if "FAIL" not in dev[0].global_request("P2P_PROV_DISC 02:03:04:05:06:07 display"):
+ raise Exception("Unexpected P2P_PROV_DISC success (2)")
+
+def test_p2p_msg_pd(dev, apdev):
+ """P2P protocol tests for provision discovery request processing (known)"""
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+ dialog_token = 0
+
+ p2p_probe(hapd, src, chan=channel)
+ time.sleep(0.1)
+
+ # Valid request
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_PROV_DISC_REQ, dialog_token=dialog_token)
+ attrs = wsc_attr_config_methods(methods=0x1008)
+ msg['payload'] += ie_wsc(attrs)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on device found event")
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-SHOW-PIN"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on PD event")
+ if hapd.mgmt_rx(timeout=1) is None:
+ raise Exception("No PD response " + str(dialog_token))
+
+ if "FAIL" in dev[0].global_request("P2P_PROV_DISC " + src + " display"):
+ raise Exception("Unexpected P2P_PROV_DISC failure")
+ frame = hapd.mgmt_rx(timeout=1)
+ if frame is None:
+ raise Exception("No PD request " + str(dialog_token))
+ p2p = parse_p2p_public_action(frame['payload'])
+ if p2p is None:
+ raise Exception("Failed to parse PD request")
+
+ # invalid dialog token
+ msg = p2p_hdr_resp(dst, src, type=P2P_PROV_DISC_RESP,
+ dialog_token=p2p['dialog_token'] + 1)
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected PD result event")
+
+ # valid dialog token
+ msg = p2p_hdr_resp(dst, src, type=P2P_PROV_DISC_RESP,
+ dialog_token=p2p['dialog_token'])
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("Timeout on PD result event")
+
+ # valid dialog token
+ msg = p2p_hdr_resp(dst, src, type=P2P_PROV_DISC_RESP,
+ dialog_token=p2p['dialog_token'])
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected PD result event")
+
+def check_p2p_response(hapd, dialog_token, status):
+ resp = hapd.mgmt_rx(timeout=2)
+ if resp is None:
+ raise Exception("No GO Neg Response " + str(dialog_token))
+ p2p = parse_p2p_public_action(resp['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if dialog_token != p2p['dialog_token']:
+ raise Exception("Unexpected dialog token in response")
+ if p2p['p2p_status'] != status:
+ raise Exception("Unexpected status code %s in response (expected %d)" % (p2p['p2p_status'], status))
+
+def test_p2p_msg_go_neg_both_start(dev, apdev):
+ """P2P protocol test for simultaneous GO Neg initiation"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[0].p2p_listen()
+ dev[1].discover_peer(addr0)
+ dev[1].p2p_listen()
+ dev[0].discover_peer(addr1)
+ dev[0].p2p_listen()
+ if "FAIL" in dev[0].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ dev[0].request("P2P_CONNECT {} pbc".format(addr1))
+ dev[1].request("P2P_CONNECT {} pbc".format(addr0))
+ msg = dev[0].mgmt_rx()
+ if msg is None:
+ raise Exception("MGMT-RX timeout")
+ msg = dev[1].mgmt_rx()
+ if msg is None:
+ raise Exception("MGMT-RX timeout(2)")
+ if "FAIL" in dev[0].request("SET ext_mgmt_frame_handling 0"):
+ raise Exception("Failed to disable external management frame handling")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=2)
+ if ev is not None:
+ raise Exception("Unexpected GO Neg success")
+ if "FAIL" in dev[1].request("SET ext_mgmt_frame_handling 0"):
+ raise Exception("Failed to disable external management frame handling")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("GO Neg did not succeed")
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Group formation not succeed")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Group formation not succeed")
+
+def test_p2p_msg_go_neg_req(dev, apdev):
+ """P2P protocol tests for invitation request from unknown peer"""
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+ dialog_token = 0
+
+ # invalid attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ attrs = struct.pack("<BB", P2P_ATTR_CAPABILITY, 0)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ frame = hapd.mgmt_rx(timeout=0.1)
+ if frame is not None:
+ print(frame)
+ raise Exception("Unexpected GO Neg Response")
+
+ # missing atributes
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ #attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=2) is None:
+ raise Exception("No GO Neg Response " + str(dialog_token))
+ time.sleep(0.1)
+
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ #attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=2) is None:
+ raise Exception("No GO Neg Response " + str(dialog_token))
+ time.sleep(0.1)
+
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ #attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=2) is None:
+ raise Exception("No GO Neg Response " + str(dialog_token))
+ time.sleep(0.1)
+
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ #attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=2) is None:
+ raise Exception("No GO Neg Response " + str(dialog_token))
+ time.sleep(0.1)
+
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ attrs += p2p_attr_channel_list()
+ #attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=2) is None:
+ raise Exception("No GO Neg Response " + str(dialog_token))
+ time.sleep(0.1)
+
+ # SA != P2P Device address
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info("02:02:02:02:02:02", config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=2) is None:
+ raise Exception("No GO Neg Response " + str(dialog_token))
+ time.sleep(0.1)
+
+ # unexpected Status attribute
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_status(status=P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=2) is None:
+ raise Exception("No GO Neg Response(1) " + str(dialog_token))
+ time.sleep(0.1)
+
+ # valid (with workarounds) GO Neg Req
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ #attrs = p2p_attr_capability()
+ #attrs += p2p_attr_go_intent()
+ #attrs += p2p_attr_config_timeout()
+ attrs = p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ check_p2p_response(hapd, dialog_token,
+ P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=2)
+ if ev is None:
+ raise Exception("Timeout on GO Neg event " + str(dialog_token))
+
+ dev[0].request("P2P_CONNECT " + src + " 12345670 display auth")
+
+ # ready - missing attributes (with workarounds) GO Neg Req
+ time.sleep(0.1)
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ #attrs = p2p_attr_capability()
+ #attrs += p2p_attr_go_intent()
+ #attrs += p2p_attr_config_timeout()
+ attrs = p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ if hapd.mgmt_rx(timeout=2) is None:
+ raise Exception("No GO Neg Response " + str(dialog_token))
+
+ # ready - invalid GO Intent GO Neg Req
+ time.sleep(0.1)
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ #attrs = p2p_attr_capability()
+ attrs = p2p_attr_go_intent(go_intent=16)
+ #attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ check_p2p_response(hapd, dialog_token, P2P_SC_FAIL_INVALID_PARAMS)
+
+ # ready - invalid Channel List
+ time.sleep(0.1)
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ attrs += struct.pack("<BH3BBB11B", P2P_ATTR_CHANNEL_LIST, 16,
+ 0x58, 0x58, 0x04,
+ 81, 12, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ check_p2p_response(hapd, dialog_token, P2P_SC_FAIL_NO_COMMON_CHANNELS)
+
+ # ready - invalid GO Neg Req (unsupported Device Password ID)
+ time.sleep(0.1)
+ dialog_token += 1
+ msg = p2p_hdr(dst, src, type=P2P_GO_NEG_REQ, dialog_token=dialog_token)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr("02:02:02:02:02:02")
+ # very long channel list
+ attrs += struct.pack("<BH3BBB11B30B", P2P_ATTR_CHANNEL_LIST, 46,
+ 0x58, 0x58, 0x04,
+ 81, 11, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 1, 1, 1, 2, 1, 2, 3, 1, 3, 4, 1, 4, 5, 1, 5,
+ 6, 1, 6, 7, 1, 7, 8, 1, 8, 9, 1, 9, 10, 1, 10)
+ attrs += p2p_attr_device_info(src, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ hapd.mgmt_tx(msg)
+ check_p2p_response(hapd, dialog_token, P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD)
+
+def mgmt_tx(dev, msg):
+ for i in range(0, 20):
+ if "FAIL" in dev.request(msg):
+ raise Exception("Failed to send Action frame")
+ ev = dev.wait_event(["MGMT-TX-STATUS"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on MGMT-TX-STATUS")
+ if "result=SUCCESS" in ev:
+ break
+ time.sleep(0.01)
+ if "result=SUCCESS" not in ev:
+ raise Exception("Peer did not ack Action frame")
+
+def rx_go_neg_req(dev):
+ msg = dev.mgmt_rx()
+ if msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_GO_NEG_REQ:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+ p2p['freq'] = msg['freq']
+ return p2p
+
+def rx_go_neg_conf(dev, status=None, dialog_token=None):
+ msg = dev.mgmt_rx()
+ if msg is None:
+ raise Exception("MGMT-RX timeout")
+ p2p = parse_p2p_public_action(msg['payload'])
+ if p2p is None:
+ raise Exception("Not a P2P Public Action frame " + str(dialog_token))
+ if p2p['subtype'] != P2P_GO_NEG_CONF:
+ raise Exception("Unexpected subtype %d" % p2p['subtype'])
+ if dialog_token is not None and dialog_token != p2p['dialog_token']:
+ raise Exception("Unexpected dialog token")
+ if status is not None and p2p['p2p_status'] != status:
+ raise Exception("Unexpected status %d" % p2p['p2p_status'])
+
+def check_p2p_go_neg_fail_event(dev, status):
+ ev = dev.wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("GO Negotiation failure not reported")
+ if "status=%d" % status not in ev:
+ raise Exception("Unexpected failure reason: " + ev)
+
+def test_p2p_msg_go_neg_req_reject(dev, apdev):
+ """P2P protocol tests for user reject incorrectly in GO Neg Req"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[0].p2p_listen()
+ dev[1].discover_peer(addr0)
+ dev[1].group_request("P2P_CONNECT " + addr0 + " pbc")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on GO Neg Req")
+
+ peer = dev[0].get_peer(addr1)
+ dev[0].p2p_stop_find()
+
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_REQ, dialog_token=123)
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_status(status=P2P_SC_FAIL_REJECTED_BY_USER)
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_listen_channel()
+ attrs += p2p_attr_ext_listen_timing()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=10 no_cck=1 action={}".format(
+ addr1, addr1, peer['listen_freq'], binascii.hexlify(msg['payload']).decode()))
+
+ ev = dev[1].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=5)
+ if ev is None:
+ raise Exception("GO Negotiation failure not reported")
+ if "status=%d" % P2P_SC_FAIL_REJECTED_BY_USER not in ev:
+ raise Exception("Unexpected failure reason: " + ev)
+
+def test_p2p_msg_unexpected_go_neg_resp(dev, apdev):
+ """P2P protocol tests for unexpected GO Neg Resp"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[1].p2p_listen()
+ dev[0].discover_peer(addr1)
+ dev[0].p2p_stop_find()
+ dev[0].dump_monitor()
+
+ peer = dev[0].get_peer(addr1)
+
+ logger.debug("GO Neg Resp without GO Neg session")
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=123)
+ attrs = p2p_attr_status()
+ attrs += p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=10 no_cck=1 action={}".format(
+ addr1, addr1, peer['listen_freq'], binascii.hexlify(msg['payload']).decode()))
+
+ dev[0].p2p_listen()
+ dev[1].discover_peer(addr0)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("Unexpected GO Neg Resp while waiting for new GO Neg session")
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+ raise Exception("P2P_CONNECT failed")
+ ev = dev[0].wait_global_event(["P2P-GO-NEG-REQUEST"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on GO Neg Req")
+ dev[0].p2p_stop_find()
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=10 no_cck=1 action={}".format(
+ addr1, addr1, peer['listen_freq'], binascii.hexlify(msg['payload']).decode()))
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("Invalid attribute in GO Neg Response")
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=197)
+ attrs = struct.pack("<BB", P2P_ATTR_CAPABILITY, 0)
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=10 no_cck=1 action={}".format(
+ addr1, addr1, peer['listen_freq'], binascii.hexlify(msg['payload']).decode()))
+ frame = dev[0].mgmt_rx(timeout=0.1)
+ if frame is not None:
+ raise Exception("Unexpected GO Neg Confirm")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("GO Neg Resp with unexpected dialog token")
+ dev[1].p2p_stop_find()
+ if "FAIL" in dev[0].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+ raise Exception("P2P_CONNECT failed(2)")
+ p2p = rx_go_neg_req(dev[0])
+ dev[0].p2p_stop_find()
+ dialog_token = p2p['dialog_token']
+ if dialog_token < 255:
+ dialog_token += 1
+ else:
+ dialog_token = 1
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+ attrs = p2p_attr_status()
+ attrs += p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload']).decode()))
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("GO Neg Resp without Status")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+ raise Exception("P2P_CONNECT failed(2)")
+ p2p = rx_go_neg_req(dev[0])
+ dev[0].p2p_stop_find()
+ dialog_token = p2p['dialog_token']
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+ #attrs = p2p_attr_status()
+ attrs = p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload']).decode()))
+ check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+ rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("GO Neg Resp without Intended Address")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+ raise Exception("P2P_CONNECT failed(2)")
+ p2p = rx_go_neg_req(dev[0])
+ dev[0].p2p_stop_find()
+ dialog_token = p2p['dialog_token']
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+ attrs = p2p_attr_status()
+ #attrs += p2p_attr_capability()
+ attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ #attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += p2p_attr_channel_list()
+ #attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload']).decode()))
+ check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+ rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("GO Neg Resp without GO Intent")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+ raise Exception("P2P_CONNECT failed(2)")
+ p2p = rx_go_neg_req(dev[0])
+ dev[0].p2p_stop_find()
+ dialog_token = p2p['dialog_token']
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+ attrs = p2p_attr_status()
+ attrs += p2p_attr_capability()
+ #attrs += p2p_attr_go_intent()
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload']).decode()))
+ check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+ rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("GO Neg Resp with invalid GO Intent")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc"):
+ raise Exception("P2P_CONNECT failed(2)")
+ p2p = rx_go_neg_req(dev[0])
+ dev[0].p2p_stop_find()
+ dialog_token = p2p['dialog_token']
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+ attrs = p2p_attr_status()
+ attrs += p2p_attr_capability()
+ attrs += p2p_attr_go_intent(go_intent=16)
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload']).decode()))
+ check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+ rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("GO Neg Resp with incompatible GO Intent")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=15"):
+ raise Exception("P2P_CONNECT failed(2)")
+ p2p = rx_go_neg_req(dev[0])
+ dev[0].p2p_stop_find()
+ dialog_token = p2p['dialog_token']
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+ attrs = p2p_attr_status()
+ attrs += p2p_attr_capability()
+ attrs += p2p_attr_go_intent(go_intent=15)
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload']).decode()))
+ check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INCOMPATIBLE_PARAMS)
+ rx_go_neg_conf(dev[0], P2P_SC_FAIL_INCOMPATIBLE_PARAMS, dialog_token)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("GO Neg Resp without P2P Group ID")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=0"):
+ raise Exception("P2P_CONNECT failed(2)")
+ p2p = rx_go_neg_req(dev[0])
+ dev[0].p2p_stop_find()
+ dialog_token = p2p['dialog_token']
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+ attrs = p2p_attr_status()
+ attrs += p2p_attr_capability()
+ attrs += p2p_attr_go_intent(go_intent=15)
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ #attrs += p2p_attr_group_id(src, "DIRECT-foo")
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload']).decode()))
+ check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+ rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("GO Neg Resp without Operating Channel")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=0"):
+ raise Exception("P2P_CONNECT failed(2)")
+ p2p = rx_go_neg_req(dev[0])
+ dev[0].p2p_stop_find()
+ dialog_token = p2p['dialog_token']
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+ attrs = p2p_attr_status()
+ attrs += p2p_attr_capability()
+ attrs += p2p_attr_go_intent(go_intent=15)
+ #attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ #attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_id(addr0, "DIRECT-foo")
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload']).decode()))
+ check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+ rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("GO Neg Resp without Channel List")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=0"):
+ raise Exception("P2P_CONNECT failed(2)")
+ p2p = rx_go_neg_req(dev[0])
+ dev[0].p2p_stop_find()
+ dialog_token = p2p['dialog_token']
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+ attrs = p2p_attr_status()
+ attrs += p2p_attr_capability()
+ attrs += p2p_attr_go_intent(go_intent=15)
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ #attrs += p2p_attr_channel_list()
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_id(addr0, "DIRECT-foo")
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload']).decode()))
+ check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_INVALID_PARAMS)
+ rx_go_neg_conf(dev[0], P2P_SC_FAIL_INVALID_PARAMS, dialog_token)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.debug("GO Neg Resp without common channels")
+ dev[1].p2p_stop_find()
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_CONNECT " + addr0 + " pbc go_intent=0"):
+ raise Exception("P2P_CONNECT failed(2)")
+ p2p = rx_go_neg_req(dev[0])
+ dev[0].p2p_stop_find()
+ dialog_token = p2p['dialog_token']
+ msg = p2p_hdr(addr1, addr0, type=P2P_GO_NEG_RESP, dialog_token=dialog_token)
+ attrs = p2p_attr_status()
+ attrs += p2p_attr_capability()
+ attrs += p2p_attr_go_intent(go_intent=15)
+ attrs += p2p_attr_config_timeout()
+ attrs += p2p_attr_intended_interface_addr(addr0)
+ attrs += struct.pack("<BH3BBB", P2P_ATTR_CHANNEL_LIST, 5,
+ 0x58, 0x58, 0x04,
+ 81, 0)
+ attrs += p2p_attr_device_info(addr0, config_methods=0x0108)
+ attrs += p2p_attr_operating_channel()
+ attrs += p2p_attr_group_id(addr0, "DIRECT-foo")
+ msg['payload'] += ie_p2p(attrs)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq={} wait_time=200 no_cck=1 action={}".format(
+ addr1, addr1, p2p['freq'], binascii.hexlify(msg['payload']).decode()))
+ check_p2p_go_neg_fail_event(dev[1], P2P_SC_FAIL_NO_COMMON_CHANNELS)
+ rx_go_neg_conf(dev[0], P2P_SC_FAIL_NO_COMMON_CHANNELS, dialog_token)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_p2p_msg_group_info(dev):
+ """P2P protocol tests for Group Info parsing"""
+ try:
+ _test_p2p_msg_group_info(dev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 2 *")
+
+def _test_p2p_msg_group_info(dev):
+ tests = ["dd08506f9a090e010001",
+ "dd08506f9a090e010000",
+ "dd20506f9a090e190018" + "112233445566" + "aabbccddeeff" + "00" + "0000" + "0000000000000000" + "ff",
+ "dd20506f9a090e190018" + "112233445566" + "aabbccddeeff" + "00" + "0000" + "0000000000000000" + "00",
+ "dd24506f9a090e1d001c" + "112233445566" + "aabbccddeeff" + "00" + "0000" + "0000000000000000" + "00" + "00000000",
+ "dd24506f9a090e1d001c" + "112233445566" + "aabbccddeeff" + "00" + "0000" + "0000000000000000" + "00" + "10110001",
+ "dd24506f9a090e1d001c" + "112233445566" + "aabbccddeeff" + "00" + "0000" + "0000000000000000" + "00" + "1011ffff"]
+ for t in tests:
+ dev[0].request("VENDOR_ELEM_REMOVE 2 *")
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 2 " + t):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ dev[0].p2p_start_go(freq=2412)
+ bssid = dev[0].get_group_status_field('bssid')
+ dev[2].request("BSS_FLUSH 0")
+ dev[2].scan_for_bss(bssid, freq=2412, force_scan=True)
+ bss = dev[2].request("BSS " + bssid)
+ if 'p2p_group_client' in bss:
+ raise Exception("Unexpected p2p_group_client")
+ dev[0].remove_group()
+
+MGMT_SUBTYPE_ACTION = 13
+ACTION_CATEG_PUBLIC = 4
+
+GAS_INITIAL_REQUEST = 10
+GAS_INITIAL_RESPONSE = 11
+GAS_COMEBACK_REQUEST = 12
+GAS_COMEBACK_RESPONSE = 13
+
+def gas_hdr(dst, src, type, req=True, dialog_token=0):
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = dst
+ msg['sa'] = src
+ if req:
+ msg['bssid'] = dst
+ else:
+ msg['bssid'] = src
+ if dialog_token is None:
+ msg['payload'] = struct.pack("<BB", ACTION_CATEG_PUBLIC, type)
+ else:
+ msg['payload'] = struct.pack("<BBB", ACTION_CATEG_PUBLIC, type,
+ dialog_token)
+ return msg
+
+@remote_compatible
+def test_p2p_msg_sd(dev, apdev):
+ """P2P protocol tests for service discovery messages"""
+ dst, src, hapd, channel = start_p2p(dev, apdev)
+
+ logger.debug("Truncated GAS Initial Request - no Dialog Token field")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST, dialog_token=None)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - no Advertisement Protocol element")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - no Advertisement Protocol element length")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += struct.pack('B', 108)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Invalid GAS Initial Request - unexpected IE")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += struct.pack('BB', 0, 0)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - too short Advertisement Protocol element")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += struct.pack('BB', 108, 0)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - too short Advertisement Protocol element 2")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += struct.pack('BBB', 108, 1, 127)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Invalid GAS Initial Request - unsupported GAS advertisement protocol id 255")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += struct.pack('BBBB', 108, 2, 127, 255)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - no Query Request length field")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - too short Query Request length field")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ msg['payload'] += struct.pack('<B', 0)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - too short Query Request field (minimum underflow)")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ msg['payload'] += struct.pack('<H', 1)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - too short Query Request field (maximum underflow)")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ msg['payload'] += struct.pack('<H', 65535)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - too short Query Request field")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ msg['payload'] += struct.pack('<H', 0)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Invalid GAS Initial Request - unsupported ANQP Info ID 65535")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ msg['payload'] += struct.pack('<HHH', 4, 65535, 0)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Invalid GAS Initial Request - invalid ANQP Query Request length (truncated frame)")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ msg['payload'] += struct.pack('<HHH', 4, 56797, 65535)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Invalid GAS Initial Request - invalid ANQP Query Request length (too short Query Request to contain OUI + OUI-type)")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ msg['payload'] += struct.pack('<HHH', 4, 56797, 0)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Invalid GAS Initial Request - unsupported ANQP vendor OUI-type")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ req = struct.pack('<HH', 56797, 4) + struct.pack('>L', 0x506f9a00)
+ msg['payload'] += struct.pack('<H', len(req)) + req
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - no Service Update Indicator")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ req = struct.pack('<HH', 56797, 4) + struct.pack('>L', 0x506f9a09)
+ msg['payload'] += struct.pack('<H', len(req)) + req
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Initial Request - truncated Service Update Indicator")
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ req = struct.pack('<HH', 56797, 4) + struct.pack('>L', 0x506f9a09)
+ req += struct.pack('<B', 0)
+ msg['payload'] += struct.pack('<H', len(req)) + req
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Unexpected GAS Initial Response")
+ hapd.dump_monitor()
+ msg = gas_hdr(dst, src, GAS_INITIAL_RESPONSE)
+ msg['payload'] += struct.pack('<HH', 0, 0)
+ msg['payload'] += anqp_adv_proto()
+ msg['payload'] += struct.pack('<H', 0)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Truncated GAS Comeback Request - no Dialog Token field")
+ msg = gas_hdr(dst, src, GAS_COMEBACK_REQUEST, dialog_token=None)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("GAS Comeback Request - no pending SD response fragment available")
+ msg = gas_hdr(dst, src, GAS_COMEBACK_REQUEST)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Unexpected GAS Comeback Response")
+ hapd.dump_monitor()
+ msg = gas_hdr(dst, src, GAS_COMEBACK_RESPONSE)
+ msg['payload'] += struct.pack('<HBH', 0, 0, 0)
+ msg['payload'] += anqp_adv_proto()
+ msg['payload'] += struct.pack('<H', 0)
+ hapd.mgmt_tx(msg)
+
+ logger.debug("Minimal GAS Initial Request")
+ hapd.dump_monitor()
+ msg = gas_hdr(dst, src, GAS_INITIAL_REQUEST)
+ msg['payload'] += anqp_adv_proto()
+ req = struct.pack('<HH', 56797, 4) + struct.pack('>L', 0x506f9a09)
+ req += struct.pack('<H', 0)
+ msg['payload'] += struct.pack('<H', len(req)) + req
+ hapd.mgmt_tx(msg)
+ resp = hapd.mgmt_rx()
+ if resp is None:
+ raise Exception("No response to minimal GAS Initial Request")
diff --git a/contrib/wpa/tests/hwsim/test_p2p_persistent.py b/contrib/wpa/tests/hwsim/test_p2p_persistent.py
new file mode 100644
index 000000000000..93a0c6826e19
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_persistent.py
@@ -0,0 +1,676 @@
+# P2P persistent group test cases
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import re
+import time
+
+import hwsim_utils
+from p2p_utils import *
+
+@remote_compatible
+def test_persistent_group(dev):
+ """P2P persistent group formation and re-invocation"""
+ form(dev[0], dev[1])
+ invite_from_cli(dev[0], dev[1])
+ invite_from_go(dev[0], dev[1])
+
+ logger.info("Remove group on the client and try to invite from GO")
+ id = None
+ for n in dev[0].list_networks(p2p=True):
+ if "[P2P-PERSISTENT]" in n['flags']:
+ id = n['id']
+ break
+ if id is None:
+ raise Exception("Could not find persistent group entry")
+ clients = dev[0].global_request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+ if dev[1].p2p_dev_addr() not in clients:
+ raise Exception("Peer missing from client list")
+ if "FAIL" not in dev[1].request("SELECT_NETWORK " + str(id)):
+ raise Exception("SELECT_NETWORK succeeded unexpectedly")
+ if "FAIL" not in dev[1].request("SELECT_NETWORK 1234567"):
+ raise Exception("SELECT_NETWORK succeeded unexpectedly(2)")
+ if "FAIL" not in dev[1].request("ENABLE_NETWORK " + str(id)):
+ raise Exception("ENABLE_NETWORK succeeded unexpectedly")
+ if "FAIL" not in dev[1].request("ENABLE_NETWORK 1234567"):
+ raise Exception("ENABLE_NETWORK succeeded unexpectedly(2)")
+ if "FAIL" not in dev[1].request("DISABLE_NETWORK " + str(id)):
+ raise Exception("DISABLE_NETWORK succeeded unexpectedly")
+ if "FAIL" not in dev[1].request("DISABLE_NETWORK 1234567"):
+ raise Exception("DISABLE_NETWORK succeeded unexpectedly(2)")
+ if "FAIL" not in dev[1].request("REMOVE_NETWORK 1234567"):
+ raise Exception("REMOVE_NETWORK succeeded unexpectedly")
+ dev[1].global_request("REMOVE_NETWORK all")
+ if len(dev[1].list_networks(p2p=True)) > 0:
+ raise Exception("Unexpected network block remaining")
+ invite(dev[0], dev[1])
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+ if ev is None:
+ raise Exception("No invitation result seen")
+ if "status=8" not in ev:
+ raise Exception("Unexpected invitation result: " + ev)
+ clients = dev[0].request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+ if dev[1].p2p_dev_addr() in clients:
+ raise Exception("Peer was still in client list")
+
+@remote_compatible
+def test_persistent_group2(dev):
+ """P2P persistent group formation with reverse roles"""
+ form(dev[0], dev[1], reverse_init=True)
+ invite_from_cli(dev[0], dev[1])
+ invite_from_go(dev[0], dev[1])
+
+@remote_compatible
+def test_persistent_group3(dev):
+ """P2P persistent group formation and re-invocation with empty BSS table"""
+ form(dev[0], dev[1])
+ dev[1].request("BSS_FLUSH 0")
+ invite_from_cli(dev[0], dev[1])
+ dev[1].request("BSS_FLUSH 0")
+ invite_from_go(dev[0], dev[1])
+
+def test_persistent_group_per_sta_psk(dev):
+ """P2P persistent group formation and re-invocation using per-client PSK"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+ dev[0].global_request("P2P_SET per_sta_psk 1")
+ logger.info("Form a persistent group")
+ [i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ if not i_res['persistent'] or not r_res['persistent']:
+ raise Exception("Formed group was not persistent")
+
+ logger.info("Join another client to the group")
+ pin = dev[2].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ social = int(i_res['freq']) in [2412, 2437, 2462]
+ c_res = dev[2].p2p_connect_group(addr0, pin, timeout=60, social=social,
+ freq=i_res['freq'])
+ if not c_res['persistent']:
+ raise Exception("Joining client did not recognize persistent group")
+ if r_res['psk'] == c_res['psk']:
+ raise Exception("Same PSK assigned for both clients")
+ hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+
+ logger.info("Remove persistent group and re-start it manually")
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ dev[2].wait_go_ending_session()
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+
+ for i in range(0, 3):
+ networks = dev[i].list_networks(p2p=True)
+ if len(networks) != 1:
+ raise Exception("Unexpected number of networks")
+ if "[P2P-PERSISTENT]" not in networks[0]['flags']:
+ raise Exception("Not the persistent group data")
+ if i > 0:
+ # speed up testing by avoiding use of the old BSS entry since the
+ # GO may have changed channels
+ dev[i].request("BSS_FLUSH 0")
+ dev[i].scan(freq="2412", only_new=True)
+ if "OK" not in dev[i].global_request("P2P_GROUP_ADD persistent=" + networks[0]['id'] + " freq=2412"):
+ raise Exception("Could not re-start persistent group")
+ ev = dev[i].wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on group restart")
+ dev[i].group_form_result(ev)
+
+ logger.info("Leave persistent group and rejoin it")
+ dev[2].remove_group()
+ ev = dev[2].wait_global_event(["P2P-GROUP-REMOVED"], timeout=3)
+ if ev is None:
+ raise Exception("Group removal event timed out")
+ if not dev[2].discover_peer(addr0, social=True):
+ raise Exception("Peer " + addr0 + " not found")
+ dev[2].dump_monitor()
+ peer = dev[2].get_peer(addr0)
+ dev[2].global_request("P2P_GROUP_ADD persistent=" + peer['persistent'] + " freq=2412")
+ ev = dev[2].wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on group restart (on client)")
+ cli_res = dev[2].group_form_result(ev)
+ if not cli_res['persistent']:
+ raise Exception("Persistent group not restarted as persistent (cli)")
+ hwsim_utils.test_connectivity_p2p(dev[1], dev[2])
+
+ logger.info("Remove one of the clients from the group without removing persistent group information for the client")
+ dev[0].global_request("P2P_REMOVE_CLIENT iface=" + dev[2].p2p_interface_addr())
+ dev[2].wait_go_ending_session()
+
+ logger.info("Try to reconnect after having been removed from group (but persistent group info still present)")
+ if not dev[2].discover_peer(addr0, social=True):
+ raise Exception("Peer " + peer + " not found")
+ dev[2].dump_monitor()
+ peer = dev[2].get_peer(addr0)
+ dev[2].global_request("P2P_GROUP_ADD persistent=" + peer['persistent'] + " freq=2412")
+ ev = dev[2].wait_global_event(["P2P-GROUP-STARTED",
+ "WPA: 4-Way Handshake failed"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on group restart (on client)")
+ if "P2P-GROUP-STARTED" not in ev:
+ raise Exception("Connection failed")
+
+ logger.info("Remove one of the clients from the group")
+ dev[0].global_request("P2P_REMOVE_CLIENT " + addr2)
+ dev[2].wait_go_ending_session()
+
+ logger.info("Try to reconnect after having been removed from group")
+ if not dev[2].discover_peer(addr0, social=True):
+ raise Exception("Peer " + peer + " not found")
+ dev[2].dump_monitor()
+ peer = dev[2].get_peer(addr0)
+ dev[2].global_request("P2P_GROUP_ADD persistent=" + peer['persistent'] + " freq=2412")
+ ev = dev[2].wait_global_event(["P2P-GROUP-STARTED",
+ "WPA: 4-Way Handshake failed"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on group restart (on client)")
+ if "P2P-GROUP-STARTED" in ev:
+ raise Exception("Client managed to connect after being removed")
+
+ logger.info("Remove the remaining client from the group")
+ dev[0].global_request("P2P_REMOVE_CLIENT " + addr1)
+ dev[1].wait_go_ending_session()
+
+ logger.info("Terminate persistent group")
+ dev[0].remove_group()
+ dev[0].dump_monitor()
+
+ logger.info("Try to re-invoke persistent group from client")
+ dev[0].global_request("SET persistent_reconnect 1")
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0, social=True):
+ raise Exception("Peer " + peer + " not found")
+ dev[1].dump_monitor()
+ peer = dev[1].get_peer(addr0)
+ dev[1].global_request("P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr0)
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+ dev[0].group_form_result(ev)
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+ "WPA: 4-Way Handshake failed"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on group restart (on client)")
+ if "P2P-GROUP-STARTED" in ev:
+ raise Exception("Client managed to re-invoke after being removed")
+ dev[0].dump_monitor()
+
+ logger.info("Terminate persistent group")
+ dev[0].remove_group()
+ dev[0].dump_monitor()
+
+def test_persistent_group_invite_removed_client(dev):
+ """P2P persistent group client removal and re-invitation"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ dev[0].request("P2P_SET per_sta_psk 1")
+ logger.info("Form a persistent group")
+ [i_res, r_res] = go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ if not i_res['persistent'] or not r_res['persistent']:
+ raise Exception("Formed group was not persistent")
+
+ logger.info("Remove client from the group")
+ dev[0].global_request("P2P_REMOVE_CLIENT " + addr1)
+ dev[1].wait_go_ending_session()
+
+ logger.info("Re-invite the removed client to join the group")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1, social=True):
+ raise Exception("Peer " + peer + " not found")
+ dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on invitation")
+ if "sa=" + addr0 + " persistent=" not in ev:
+ raise Exception("Unexpected invitation event")
+ [event, addr, persistent] = ev.split(' ', 2)
+ dev[1].global_request("P2P_GROUP_ADD " + persistent)
+ ev = dev[1].wait_global_event(["P2P-PERSISTENT-PSK-FAIL"], timeout=30)
+ if ev is None:
+ raise Exception("Did not receive PSK failure report")
+ [tmp, id] = ev.split('=', 1)
+ ev = dev[1].wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+ if ev is None:
+ raise Exception("Group removal event timed out")
+ if "reason=PSK_FAILURE" not in ev:
+ raise Exception("Unexpected group removal reason")
+ dev[1].global_request("REMOVE_NETWORK " + id)
+
+ logger.info("Re-invite after client removed persistent group info")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1, social=True):
+ raise Exception("Peer " + peer + " not found")
+ dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on invitation")
+ if " persistent=" in ev:
+ raise Exception("Unexpected invitation event")
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ c_res = dev[1].p2p_connect_group(addr0, pin, timeout=60, social=True,
+ freq=i_res['freq'])
+ if not c_res['persistent']:
+ raise Exception("Joining client did not recognize persistent group")
+ if r_res['psk'] == c_res['psk']:
+ raise Exception("Same PSK assigned on both times")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+ terminate_group(dev[0], dev[1])
+
+@remote_compatible
+def test_persistent_group_channel(dev):
+ """P2P persistent group re-invocation with channel selection"""
+ form(dev[0], dev[1], test_data=False)
+
+ logger.info("Re-invoke persistent group from client with forced channel")
+ invite(dev[1], dev[0], "freq=2427")
+ [go_res, cli_res] = check_result(dev[0], dev[1])
+ if go_res['freq'] != "2427":
+ raise Exception("Persistent group client forced channel not followed")
+ terminate_group(dev[0], dev[1])
+
+ logger.info("Re-invoke persistent group from GO with forced channel")
+ invite(dev[0], dev[1], "freq=2432")
+ [go_res, cli_res] = check_result(dev[0], dev[1])
+ if go_res['freq'] != "2432":
+ raise Exception("Persistent group GO channel preference not followed")
+ terminate_group(dev[0], dev[1])
+
+ logger.info("Re-invoke persistent group from client with channel preference")
+ invite(dev[1], dev[0], "pref=2417")
+ [go_res, cli_res] = check_result(dev[0], dev[1])
+ if go_res['freq'] != "2417":
+ raise Exception("Persistent group client channel preference not followed")
+ terminate_group(dev[0], dev[1])
+
+@remote_compatible
+def test_persistent_group_and_role_change(dev):
+ """P2P persistent group, auto GO in another role, and re-invocation"""
+ form(dev[0], dev[1])
+
+ logger.info("Start and stop autonomous GO on previous P2P client device")
+ dev[1].p2p_start_go()
+ dev[1].remove_group()
+ dev[1].dump_monitor()
+
+ logger.info("Re-invoke the persistent group")
+ invite_from_go(dev[0], dev[1])
+
+def test_persistent_go_client_list(dev):
+ """P2P GO and list of clients in persistent group"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ addr2 = dev[2].p2p_dev_addr()
+
+ res = dev[0].p2p_start_go(persistent=True)
+ id = None
+ for n in dev[0].list_networks(p2p=True):
+ if "[P2P-PERSISTENT]" in n['flags']:
+ id = n['id']
+ break
+ if id is None:
+ raise Exception("Could not find persistent group entry")
+
+ connect_cli(dev[0], dev[1], social=True, freq=res['freq'])
+ clients = dev[0].global_request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+ if clients != addr1:
+ raise Exception("Unexpected p2p_client_list entry(2): " + clients)
+ connect_cli(dev[0], dev[2], social=True, freq=res['freq'])
+ clients = dev[0].global_request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+ if clients != addr2 + " " + addr1:
+ raise Exception("Unexpected p2p_client_list entry(3): " + clients)
+
+ peer = dev[1].get_peer(res['go_dev_addr'])
+ dev[1].remove_group()
+ dev[1].global_request("P2P_GROUP_ADD persistent=" + peer['persistent'])
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on group restart (on client)")
+ dev[1].group_form_result(ev)
+ clients = dev[0].global_request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+ if clients != addr1 + " " + addr2:
+ raise Exception("Unexpected p2p_client_list entry(4): " + clients)
+
+ dev[2].remove_group()
+ dev[1].remove_group()
+ dev[0].remove_group()
+
+ clients = dev[0].global_request("GET_NETWORK " + id + " p2p_client_list").rstrip()
+ if clients != addr1 + " " + addr2:
+ raise Exception("Unexpected p2p_client_list entry(5): " + clients)
+
+ dev[1].p2p_listen()
+ dev[2].p2p_listen()
+ dev[0].request("P2P_FLUSH")
+ dev[0].discover_peer(addr1, social=True)
+ peer = dev[0].get_peer(addr1)
+ if 'persistent' not in peer or peer['persistent'] != id:
+ raise Exception("Persistent group client not recognized(1)")
+
+ dev[0].discover_peer(addr2, social=True)
+ peer = dev[0].get_peer(addr2)
+ if 'persistent' not in peer or peer['persistent'] != id:
+ raise Exception("Persistent group client not recognized(2)")
+
+@remote_compatible
+def test_persistent_group_in_grpform(dev):
+ """P2P persistent group parameters re-used in group formation"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ form(dev[0], dev[1])
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1, social=True):
+ raise Exception("Could not discover peer")
+ peer = dev[0].get_peer(addr1)
+ if "persistent" not in peer:
+ raise Exception("Could not map peer to a persistent group")
+
+ pin = dev[1].wps_read_pin()
+ dev[1].p2p_go_neg_auth(addr0, pin, "display", go_intent=0)
+ i_res = dev[0].p2p_go_neg_init(addr1, pin, "enter", timeout=20,
+ go_intent=15,
+ persistent_id=peer['persistent'])
+ r_res = dev[1].p2p_go_neg_auth_result()
+ logger.debug("i_res: " + str(i_res))
+ logger.debug("r_res: " + str(r_res))
+
+@remote_compatible
+def test_persistent_group_without_persistent_reconnect(dev):
+ """P2P persistent group re-invocation without persistent reconnect"""
+ form(dev[0], dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.info("Re-invoke persistent group from client")
+ invite(dev[1], dev[0], persistent_reconnect=False)
+
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=15)
+ if ev is None:
+ raise Exception("No invitation request reported")
+ if "persistent=" not in ev:
+ raise Exception("Invalid invitation type reported: " + ev)
+
+ ev2 = dev[1].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+ if ev2 is None:
+ raise Exception("No invitation response reported")
+ if "status=1" not in ev2:
+ raise Exception("Unexpected status: " + ev2)
+ dev[1].p2p_listen()
+
+ exp = r'<.>(P2P-INVITATION-RECEIVED) sa=([0-9a-f:]*) persistent=([0-9]*) freq=([0-9]*)'
+ s = re.split(exp, ev)
+ if len(s) < 5:
+ raise Exception("Could not parse invitation event")
+ sa = s[2]
+ id = s[3]
+ freq = s[4]
+ logger.info("Invalid P2P_INVITE test coverage")
+ if "FAIL" not in dev[0].global_request("P2P_INVITE persistent=" + id + " peer=" + sa + " freq=0"):
+ raise Exception("Invalid P2P_INVITE accepted")
+ if "FAIL" not in dev[0].global_request("P2P_INVITE persistent=" + id + " peer=" + sa + " pref=0"):
+ raise Exception("Invalid P2P_INVITE accepted")
+ logger.info("Re-initiate invitation based on upper layer acceptance")
+ if "OK" not in dev[0].global_request("P2P_INVITE persistent=" + id + " peer=" + sa + " freq=" + freq):
+ raise Exception("Invitation command failed")
+ [go_res, cli_res] = check_result(dev[0], dev[1])
+ if go_res['freq'] != freq:
+ raise Exception("Unexpected channel on GO: {} MHz, expected {} MHz".format(go_res['freq'], freq))
+ if cli_res['freq'] != freq:
+ raise Exception("Unexpected channel on CLI: {} MHz, expected {} MHz".format(cli_res['freq'], freq))
+ terminate_group(dev[0], dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ logger.info("Re-invoke persistent group from GO")
+ invite(dev[0], dev[1], persistent_reconnect=False)
+
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED"], timeout=15)
+ if ev is None:
+ raise Exception("No invitation request reported")
+ if "persistent=" not in ev:
+ raise Exception("Invalid invitation type reported: " + ev)
+
+ ev2 = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+ if ev2 is None:
+ raise Exception("No invitation response reported")
+ if "status=1" not in ev2:
+ raise Exception("Unexpected status: " + ev2)
+ dev[0].p2p_listen()
+
+ exp = r'<.>(P2P-INVITATION-RECEIVED) sa=([0-9a-f:]*) persistent=([0-9]*)'
+ s = re.split(exp, ev)
+ if len(s) < 4:
+ raise Exception("Could not parse invitation event")
+ sa = s[2]
+ id = s[3]
+ logger.info("Re-initiate invitation based on upper layer acceptance")
+ if "OK" not in dev[1].global_request("P2P_INVITE persistent=" + id + " peer=" + sa + " freq=" + freq):
+ raise Exception("Invitation command failed")
+ [go_res, cli_res] = check_result(dev[0], dev[1])
+ terminate_group(dev[0], dev[1])
+
+@remote_compatible
+def test_persistent_group_already_running(dev):
+ """P2P persistent group formation and invitation while GO already running"""
+ form(dev[0], dev[1])
+ peer = dev[1].get_peer(dev[0].p2p_dev_addr())
+ listen_freq = peer['listen_freq']
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ networks = dev[0].list_networks(p2p=True)
+ if len(networks) != 1:
+ raise Exception("Unexpected number of networks")
+ if "[P2P-PERSISTENT]" not in networks[0]['flags']:
+ raise Exception("Not the persistent group data")
+ if "OK" not in dev[0].global_request("P2P_GROUP_ADD persistent=" + networks[0]['id'] + " freq=" + listen_freq):
+ raise Exception("Could not state GO")
+ invite_from_cli(dev[0], dev[1])
+
+@remote_compatible
+def test_persistent_group_add_cli_chan(dev):
+ """P2P persistent group formation and re-invocation with p2p_add_cli_chan=1"""
+ try:
+ dev[0].request("SET p2p_add_cli_chan 1")
+ dev[1].request("SET p2p_add_cli_chan 1")
+ form(dev[0], dev[1])
+ dev[1].request("BSS_FLUSH 0")
+ dev[1].scan(freq="2412", only_new=True)
+ dev[1].scan(freq="2437", only_new=True)
+ dev[1].scan(freq="2462", only_new=True)
+ dev[1].request("BSS_FLUSH 0")
+ invite_from_cli(dev[0], dev[1])
+ invite_from_go(dev[0], dev[1])
+ finally:
+ dev[0].request("SET p2p_add_cli_chan 0")
+ dev[1].request("SET p2p_add_cli_chan 0")
+
+@remote_compatible
+def test_persistent_invalid_group_add(dev):
+ """Invalid P2P_GROUP_ADD command"""
+ id = dev[0].add_network()
+ if "FAIL" not in dev[0].global_request("P2P_GROUP_ADD persistent=12345"):
+ raise Exception("Invalid P2P_GROUP_ADD accepted")
+ if "FAIL" not in dev[0].global_request("P2P_GROUP_ADD persistent=%d" % id):
+ raise Exception("Invalid P2P_GROUP_ADD accepted")
+ if "FAIL" not in dev[0].global_request("P2P_GROUP_ADD foo"):
+ raise Exception("Invalid P2P_GROUP_ADD accepted")
+
+def test_persistent_group_missed_inv_resp(dev):
+ """P2P persistent group re-invocation with invitation response getting lost"""
+ form(dev[0], dev[1])
+ addr = dev[1].p2p_dev_addr()
+ dev[1].global_request("SET persistent_reconnect 1")
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr, social=True):
+ raise Exception("Peer " + addr + " not found")
+ dev[0].dump_monitor()
+ peer = dev[0].get_peer(addr)
+ # Drop the first Invitation Response frame
+ if "FAIL" in dev[0].request("SET ext_mgmt_frame_handling 1"):
+ raise Exception("Failed to enable external management frame handling")
+ cmd = "P2P_INVITE persistent=" + peer['persistent'] + " peer=" + addr
+ dev[0].global_request(cmd)
+ rx_msg = dev[0].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout (no Invitation Response)")
+ time.sleep(2)
+ # Allow following Invitation Response frame to go through
+ if "FAIL" in dev[0].request("SET ext_mgmt_frame_handling 0"):
+ raise Exception("Failed to disable external management frame handling")
+ time.sleep(1)
+ # Force the P2P Client side to be on its Listen channel for retry
+ dev[1].p2p_listen()
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=15)
+ if ev is None:
+ raise Exception("Invitation result timed out")
+ # Allow P2P Client side to continue connection-to-GO attempts
+ dev[1].p2p_stop_find()
+
+ # Verify that group re-invocation goes through
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GROUP-FORMATION-FAILURE"],
+ timeout=20)
+ if ev is None:
+ raise Exception("Group start event timed out")
+ if "P2P-GROUP-STARTED" not in ev:
+ raise Exception("Group re-invocation failed")
+ dev[0].group_form_result(ev)
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Group start event timed out on GO")
+ dev[0].group_form_result(ev)
+
+ terminate_group(dev[0], dev[1])
+
+@remote_compatible
+def test_persistent_group_profile_add(dev):
+ """Create a P2P persistent group with ADD_NETWORK"""
+ passphrase = "passphrase here"
+ id = dev[0].p2pdev_add_network()
+ dev[0].p2pdev_set_network_quoted(id, "ssid", "DIRECT-ab")
+ dev[0].p2pdev_set_network_quoted(id, "psk", passphrase)
+ dev[0].p2pdev_set_network(id, "mode", "3")
+ dev[0].p2pdev_set_network(id, "disabled", "2")
+ dev[0].p2p_start_go(persistent=id, freq=2412)
+
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ res = dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60,
+ social=True, freq=2412)
+ if res['result'] != 'success':
+ raise Exception("Joining the group did not succeed")
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+@remote_compatible
+def test_persistent_group_cancel_on_cli(dev):
+ """P2P persistent group formation, re-invocation, and cancel"""
+ dev[0].global_request("SET p2p_no_group_iface 0")
+ dev[1].global_request("SET p2p_no_group_iface 0")
+ form(dev[0], dev[1])
+
+ invite_from_go(dev[0], dev[1], terminate=False)
+ if "FAIL" not in dev[1].global_request("P2P_CANCEL"):
+ raise Exception("P2P_CANCEL succeeded unexpectedly on CLI")
+ if "FAIL" not in dev[0].global_request("P2P_CANCEL"):
+ raise Exception("P2P_CANCEL succeeded unexpectedly on GO")
+ terminate_group(dev[0], dev[1])
+
+ invite_from_cli(dev[0], dev[1], terminate=False)
+ if "FAIL" not in dev[1].global_request("P2P_CANCEL"):
+ raise Exception("P2P_CANCEL succeeded unexpectedly on CLI")
+ if "FAIL" not in dev[0].global_request("P2P_CANCEL"):
+ raise Exception("P2P_CANCEL succeeded unexpectedly on GO")
+ terminate_group(dev[0], dev[1])
+
+@remote_compatible
+def test_persistent_group_cancel_on_cli2(dev):
+ """P2P persistent group formation, re-invocation, and cancel (2)"""
+ form(dev[0], dev[1])
+ invite_from_go(dev[0], dev[1], terminate=False)
+ if "FAIL" not in dev[1].global_request("P2P_CANCEL"):
+ raise Exception("P2P_CANCEL succeeded unexpectedly on CLI")
+ if "FAIL" not in dev[0].global_request("P2P_CANCEL"):
+ raise Exception("P2P_CANCEL succeeded unexpectedly on GO")
+ terminate_group(dev[0], dev[1])
+
+ invite_from_cli(dev[0], dev[1], terminate=False)
+ if "FAIL" not in dev[1].global_request("P2P_CANCEL"):
+ raise Exception("P2P_CANCEL succeeded unexpectedly on CLI")
+ if "FAIL" not in dev[0].global_request("P2P_CANCEL"):
+ raise Exception("P2P_CANCEL succeeded unexpectedly on GO")
+ terminate_group(dev[0], dev[1])
+
+@remote_compatible
+def test_persistent_group_peer_dropped(dev):
+ """P2P persistent group formation and re-invocation with peer having dropped group"""
+ form(dev[0], dev[1], reverse_init=True)
+ invite_from_cli(dev[0], dev[1])
+
+ logger.info("Remove group on the GO and try to invite from the client")
+ dev[0].global_request("REMOVE_NETWORK all")
+ invite(dev[1], dev[0])
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+ if ev is None:
+ raise Exception("No invitation result seen")
+ if "status=8" not in ev:
+ raise Exception("Unexpected invitation result: " + ev)
+ networks = dev[1].list_networks(p2p=True)
+ if len(networks) > 0:
+ raise Exception("Unexpected network block on client")
+
+ logger.info("Verify that a new group can be formed")
+ form(dev[0], dev[1], reverse_init=True)
+
+@remote_compatible
+def test_persistent_group_peer_dropped2(dev):
+ """P2P persistent group formation and re-invocation with peer having dropped group (2)"""
+ form(dev[0], dev[1])
+ invite_from_go(dev[0], dev[1])
+
+ logger.info("Remove group on the client and try to invite from the GO")
+ dev[1].global_request("REMOVE_NETWORK all")
+ invite(dev[0], dev[1])
+ ev = dev[0].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+ if ev is None:
+ raise Exception("No invitation result seen")
+ if "status=8" not in ev:
+ raise Exception("Unexpected invitation result: " + ev)
+ networks = dev[1].list_networks(p2p=True)
+ if len(networks) > 0:
+ raise Exception("Unexpected network block on client")
+
+ logger.info("Verify that a new group can be formed")
+ form(dev[0], dev[1])
+
+def test_persistent_group_peer_dropped3(dev):
+ """P2P persistent group formation and re-invocation with peer having dropped group (3)"""
+ form(dev[0], dev[1], reverse_init=True)
+ invite_from_cli(dev[0], dev[1])
+
+ logger.info("Remove group on the GO and try to invite from the client")
+ dev[0].global_request("REMOVE_NETWORK all")
+ invite(dev[1], dev[0], use_listen=False)
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RESULT"], timeout=10)
+ if ev is None:
+ raise Exception("No invitation result seen")
+ if "status=8" not in ev:
+ raise Exception("Unexpected invitation result: " + ev)
+ networks = dev[1].list_networks(p2p=True)
+ if len(networks) > 0:
+ raise Exception("Unexpected network block on client")
+
+ time.sleep(0.2)
+ logger.info("Verify that a new group can be formed")
+ form(dev[0], dev[1], reverse_init=True, r_listen=False)
diff --git a/contrib/wpa/tests/hwsim/test_p2p_service.py b/contrib/wpa/tests/hwsim/test_p2p_service.py
new file mode 100644
index 000000000000..a3891c323784
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_service.py
@@ -0,0 +1,586 @@
+# P2P service discovery test cases
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import os
+import time
+import uuid
+
+import hwsim_utils
+
+def add_bonjour_services(dev):
+ dev.global_request("P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027")
+ dev.global_request("P2P_SERVICE_ADD bonjour 076578616d706c650b5f6166706f766572746370c00c001001 00")
+ dev.global_request("P2P_SERVICE_ADD bonjour 045f697070c00c000c01 094d795072696e746572c027")
+ dev.global_request("P2P_SERVICE_ADD bonjour 096d797072696e746572045f697070c00c001001 09747874766572733d311a70646c3d6170706c69636174696f6e2f706f7374736372797074")
+
+def add_upnp_services(dev):
+ dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice")
+ dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::upnp:rootdevice")
+ dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:1122de4e-8574-59ab-9322-333456789044::urn:schemas-upnp-org:service:ContentDirectory:2")
+ dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:5566d33e-9774-09ab-4822-333456785632::urn:schemas-upnp-org:service:ContentDirectory:2")
+ dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::urn:schemas-upnp-org:device:InternetGatewayDevice:1")
+
+def add_extra_services(dev):
+ for i in range(0, 100):
+ dev.global_request("P2P_SERVICE_ADD upnp 10 uuid:" + str(uuid.uuid4()) + "::upnp:rootdevice")
+
+def run_sd(dev, dst, query, exp_query=None, fragment=False, query2=None):
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ add_bonjour_services(dev[0])
+ add_upnp_services(dev[0])
+ if fragment:
+ add_extra_services(dev[0])
+ dev[0].p2p_listen()
+
+ dev[1].global_request("P2P_FLUSH")
+ dev[1].global_request("P2P_SERV_DISC_REQ " + dst + " " + query)
+ if query2:
+ dev[1].global_request("P2P_SERV_DISC_REQ " + dst + " " + query2)
+ if not dev[1].discover_peer(addr0, social=True, force_find=True):
+ raise Exception("Peer " + addr0 + " not found")
+
+ ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr1 not in ev:
+ raise Exception("Unexpected service discovery request source")
+ if exp_query is None:
+ exp_query = query
+ if exp_query not in ev and (query2 is None or query2 not in ev):
+ raise Exception("Unexpected service discovery request contents")
+
+ if query2:
+ ev_list = []
+ for i in range(0, 4):
+ ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr0 in ev:
+ ev_list.append(ev)
+ if len(ev_list) == 2:
+ break
+ return ev_list
+
+ for i in range(0, 2):
+ ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr0 in ev:
+ break
+
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+
+ if "OK" not in dev[0].global_request("P2P_SERVICE_DEL upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice"):
+ raise Exception("Failed to delete a UPnP service")
+ if "FAIL" not in dev[0].global_request("P2P_SERVICE_DEL upnp 10 uuid:6859dede-8574-59ab-9332-123456789012::upnp:rootdevice"):
+ raise Exception("Unexpected deletion success for UPnP service")
+ if "OK" not in dev[0].global_request("P2P_SERVICE_DEL bonjour 0b5f6166706f766572746370c00c000c01"):
+ raise Exception("Failed to delete a Bonjour service")
+ if "FAIL" not in dev[0].global_request("P2P_SERVICE_DEL bonjour 0b5f6166706f766572746370c00c000c01"):
+ raise Exception("Unexpected deletion success for Bonjour service")
+
+ return ev
+
+@remote_compatible
+def test_p2p_service_discovery(dev):
+ """P2P service discovery"""
+ addr0 = dev[0].p2p_dev_addr()
+ for dst in ["00:00:00:00:00:00", addr0]:
+ ev = run_sd(dev, dst, "02000001")
+ if "0b5f6166706f766572746370c00c000c01" not in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ if "496e7465726e6574" not in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP)")
+
+ for req in ["foo 02000001",
+ addr0,
+ addr0 + " upnp qq urn:schemas-upnp-org:device:InternetGatewayDevice:1",
+ addr0 + " upnp 10",
+ addr0 + " 123",
+ addr0 + " qq"]:
+ if "FAIL" not in dev[1].global_request("P2P_SERV_DISC_REQ " + req):
+ raise Exception("Invalid P2P_SERV_DISC_REQ accepted: " + req)
+
+def test_p2p_service_discovery2(dev):
+ """P2P service discovery with one peer having no services"""
+ dev[2].p2p_listen()
+ for dst in ["00:00:00:00:00:00", dev[0].p2p_dev_addr()]:
+ ev = run_sd(dev, dst, "02000001")
+ if "0b5f6166706f766572746370c00c000c01" not in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ if "496e7465726e6574" not in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery3(dev):
+ """P2P service discovery for Bonjour with one peer having no services"""
+ dev[2].p2p_listen()
+ for dst in ["00:00:00:00:00:00", dev[0].p2p_dev_addr()]:
+ ev = run_sd(dev, dst, "02000101")
+ if "0b5f6166706f766572746370c00c000c01" not in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+
+def test_p2p_service_discovery4(dev):
+ """P2P service discovery for UPnP with one peer having no services"""
+ dev[2].p2p_listen()
+ for dst in ["00:00:00:00:00:00", dev[0].p2p_dev_addr()]:
+ ev = run_sd(dev, dst, "02000201")
+ if "496e7465726e6574" not in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP)")
+
+@remote_compatible
+def test_p2p_service_discovery_multiple_queries(dev):
+ """P2P service discovery with multiple queries"""
+ for dst in ["00:00:00:00:00:00", dev[0].p2p_dev_addr()]:
+ ev = run_sd(dev, dst, "02000201", query2="02000101")
+ if "0b5f6166706f766572746370c00c000c01" not in ev[0] + ev[1]:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ if "496e7465726e6574" not in ev[0] + ev[1]:
+ raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery_multiple_queries2(dev):
+ """P2P service discovery with multiple queries with one peer having no services"""
+ dev[2].p2p_listen()
+ for dst in ["00:00:00:00:00:00", dev[0].p2p_dev_addr()]:
+ ev = run_sd(dev, dst, "02000201", query2="02000101")
+ if "0b5f6166706f766572746370c00c000c01" not in ev[0] + ev[1]:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ if "496e7465726e6574" not in ev[0] + ev[1]:
+ raise Exception("Unexpected service discovery response contents (UPnP)")
+
+def test_p2p_service_discovery_fragmentation(dev):
+ """P2P service discovery with fragmentation"""
+ for dst in ["00:00:00:00:00:00", dev[0].p2p_dev_addr()]:
+ ev = run_sd(dev, dst, "02000001", fragment=True)
+ if "long response" not in ev:
+ if "0b5f6166706f766572746370c00c000c01" not in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ if "496e7465726e6574" not in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP)")
+
+@remote_compatible
+def test_p2p_service_discovery_bonjour(dev):
+ """P2P service discovery (Bonjour)"""
+ ev = run_sd(dev, "00:00:00:00:00:00", "02000101")
+ if "0b5f6166706f766572746370c00c000c01" not in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ if "045f697070c00c000c01" not in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ if "496e7465726e6574" in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP not expected)")
+
+@remote_compatible
+def test_p2p_service_discovery_bonjour2(dev):
+ """P2P service discovery (Bonjour AFS)"""
+ ev = run_sd(dev, "00:00:00:00:00:00", "130001010b5f6166706f766572746370c00c000c01")
+ if "0b5f6166706f766572746370c00c000c01" not in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ if "045f697070c00c000c01" in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour mismatching)")
+ if "496e7465726e6574" in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP not expected)")
+
+@remote_compatible
+def test_p2p_service_discovery_bonjour3(dev):
+ """P2P service discovery (Bonjour AFS - no match)"""
+ ev = run_sd(dev, "00:00:00:00:00:00", "130001010b5f6166706f766572746370c00c000c02")
+ if "0300010102" not in ev:
+ raise Exception("Requested-info-not-available was not indicated")
+ if "0b5f6166706f766572746370c00c000c01" in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ if "045f697070c00c000c01" in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour mismatching)")
+ if "496e7465726e6574" in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP not expected)")
+
+@remote_compatible
+def test_p2p_service_discovery_upnp(dev):
+ """P2P service discovery (UPnP)"""
+ ev = run_sd(dev, "00:00:00:00:00:00", "02000201")
+ if "0b5f6166706f766572746370c00c000c01" in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour not expected)")
+ if "496e7465726e6574" not in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP)")
+
+@remote_compatible
+def test_p2p_service_discovery_upnp2(dev):
+ """P2P service discovery (UPnP using request helper)"""
+ ev = run_sd(dev, "00:00:00:00:00:00", "upnp 10 ssdp:all", "0b00020110737364703a616c6c")
+ if "0b5f6166706f766572746370c00c000c01" in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour not expected)")
+ if "496e7465726e6574" not in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP)")
+
+@remote_compatible
+def test_p2p_service_discovery_upnp3(dev):
+ """P2P service discovery (UPnP using request helper - no match)"""
+ ev = run_sd(dev, "00:00:00:00:00:00", "upnp 10 ssdp:foo", "0b00020110737364703a666f6f")
+ if "0300020102" not in ev:
+ raise Exception("Requested-info-not-available was not indicated")
+ if "0b5f6166706f766572746370c00c000c01" in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour not expected)")
+ if "496e7465726e6574" in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP)")
+
+@remote_compatible
+def test_p2p_service_discovery_ws(dev):
+ """P2P service discovery (WS-Discovery)"""
+ ev = run_sd(dev, "00:00:00:00:00:00", "02000301")
+ if "0b5f6166706f766572746370c00c000c01" in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour not expected)")
+ if "496e7465726e6574" in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP not expected)")
+ if "0300030101" not in ev:
+ raise Exception("Unexpected service discovery response contents (WS)")
+
+@remote_compatible
+def test_p2p_service_discovery_wfd(dev):
+ """P2P service discovery (Wi-Fi Display)"""
+ dev[0].global_request("SET wifi_display 1")
+ ev = run_sd(dev, "00:00:00:00:00:00", "02000401")
+ if " 030004" in ev:
+ raise Exception("Unexpected response to invalid WFD SD query")
+ dev[0].global_request("SET wifi_display 0")
+ ev = run_sd(dev, "00:00:00:00:00:00", "0300040100")
+ if "0300040101" not in ev:
+ raise Exception("Unexpected response to WFD SD query (protocol was disabled)")
+
+@remote_compatible
+def test_p2p_service_discovery_req_cancel(dev):
+ """Cancel a P2P service discovery request"""
+ if "FAIL" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ ab"):
+ raise Exception("Unexpected SD cancel success")
+ if "FAIL" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ qq"):
+ raise Exception("Unexpected SD cancel success")
+ query = dev[0].global_request("P2P_SERV_DISC_REQ " + dev[1].p2p_dev_addr() + " 02000001")
+ if "OK" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ " + query):
+ raise Exception("Unexpected SD cancel failure")
+ query1 = dev[0].global_request("P2P_SERV_DISC_REQ " + dev[1].p2p_dev_addr() + " 02000001")
+ query2 = dev[0].global_request("P2P_SERV_DISC_REQ " + dev[1].p2p_dev_addr() + " 02000002")
+ query3 = dev[0].global_request("P2P_SERV_DISC_REQ " + dev[1].p2p_dev_addr() + " 02000003")
+ if "OK" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ " + query2):
+ raise Exception("Unexpected SD cancel failure")
+ if "OK" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ " + query1):
+ raise Exception("Unexpected SD cancel failure")
+ if "OK" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ " + query3):
+ raise Exception("Unexpected SD cancel failure")
+
+ query = dev[0].global_request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 02000001")
+ if "OK" not in dev[0].global_request("P2P_SERV_DISC_CANCEL_REQ " + query):
+ raise Exception("Unexpected SD(broadcast) cancel failure")
+
+@remote_compatible
+def test_p2p_service_discovery_go(dev):
+ """P2P service discovery from GO"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ add_bonjour_services(dev[0])
+ add_upnp_services(dev[0])
+
+ dev[0].p2p_start_go(freq=2412)
+
+ dev[1].global_request("P2P_FLUSH")
+ dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+ if not dev[1].discover_peer(addr0, social=True, force_find=True):
+ raise Exception("Peer " + addr0 + " not found")
+
+ ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr1 not in ev:
+ raise Exception("Unexpected service discovery request source")
+
+ ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr0 not in ev:
+ raise Exception("Unexpected service discovery response source")
+ if "0b5f6166706f766572746370c00c000c01" not in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ if "496e7465726e6574" not in ev:
+ raise Exception("Unexpected service discovery response contents (UPnP)")
+ dev[1].p2p_stop_find()
+
+ dev[0].global_request("P2P_SERVICE_FLUSH")
+
+ dev[1].global_request("P2P_FLUSH")
+ dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+ if not dev[1].discover_peer(addr0, social=True, force_find=True):
+ raise Exception("Peer " + addr0 + " not found")
+ ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr1 not in ev:
+ raise Exception("Unexpected service discovery request source")
+
+ ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr0 not in ev:
+ raise Exception("Unexpected service discovery response source")
+ if "0300000101" not in ev:
+ raise Exception("Unexpected service discovery response contents (Bonjour)")
+ dev[1].p2p_stop_find()
+
+def _test_p2p_service_discovery_external(dev):
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ if "FAIL" not in dev[0].global_request("P2P_SERV_DISC_EXTERNAL 2"):
+ raise Exception("Invalid P2P_SERV_DISC_EXTERNAL accepted")
+ if "OK" not in dev[0].global_request("P2P_SERV_DISC_EXTERNAL 1"):
+ raise Exception("P2P_SERV_DISC_EXTERNAL failed")
+ dev[0].p2p_listen()
+ dev[1].global_request("P2P_FLUSH")
+ dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+ if not dev[1].discover_peer(addr0, social=True, force_find=True):
+ raise Exception("Peer " + addr0 + " not found")
+
+ ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr1 not in ev:
+ raise Exception("Unexpected service discovery request source")
+ arg = ev.split(' ')
+ resp = "0300000101"
+ if "OK" not in dev[0].global_request("P2P_SERV_DISC_RESP %s %s %s %s" % (arg[2], arg[3], arg[4], resp)):
+ raise Exception("P2P_SERV_DISC_RESP failed")
+
+ ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=15)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr0 not in ev:
+ raise Exception("Unexpected address in SD Response: " + ev)
+ if ev.split(' ')[4] != resp:
+ raise Exception("Unexpected response data SD Response: " + ev)
+ ver = ev.split(' ')[3]
+
+ dev[0].global_request("P2P_SERVICE_UPDATE")
+
+ dev[1].global_request("P2P_FLUSH")
+ dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+ if not dev[1].discover_peer(addr0, social=True, force_find=True):
+ raise Exception("Peer " + addr0 + " not found")
+
+ ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr1 not in ev:
+ raise Exception("Unexpected service discovery request source")
+ arg = ev.split(' ')
+ resp = "0300000101"
+ if "OK" not in dev[0].global_request("P2P_SERV_DISC_RESP %s %s %s %s" % (arg[2], arg[3], arg[4], resp)):
+ raise Exception("P2P_SERV_DISC_RESP failed")
+
+ ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=15)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if addr0 not in ev:
+ raise Exception("Unexpected address in SD Response: " + ev)
+ if ev.split(' ')[4] != resp:
+ raise Exception("Unexpected response data SD Response: " + ev)
+ ver2 = ev.split(' ')[3]
+ if ver == ver2:
+ raise Exception("Service list version did not change")
+
+ for cmd in ["%s%s%s%s" % (arg[2], arg[3], arg[4], resp),
+ "%s %s %s %s" % ("0", arg[3], arg[4], resp),
+ "%s %s %s %s" % (arg[2], "foo", arg[4], resp),
+ "%s %s%s%s" % (arg[2], arg[3], arg[4], resp),
+ "%s %s %s%s" % (arg[2], arg[3], arg[4], resp),
+ "%s %s %s %s" % (arg[2], arg[3], arg[4], "12345"),
+ "%s %s %s %s" % (arg[2], arg[3], arg[4], "qq")]:
+ if "FAIL" not in dev[0].global_request("P2P_SERV_DISC_RESP " + cmd):
+ raise Exception("Invalid P2P_SERV_DISC_RESP accepted: " + cmd)
+
+@remote_compatible
+def test_p2p_service_discovery_external(dev):
+ """P2P service discovery using external response"""
+ try:
+ _test_p2p_service_discovery_external(dev)
+ finally:
+ dev[0].global_request("P2P_SERV_DISC_EXTERNAL 0")
+
+@remote_compatible
+def test_p2p_service_discovery_invalid_commands(dev):
+ """P2P service discovery invalid commands"""
+ for cmd in ["bonjour",
+ "bonjour 12",
+ "bonjour 123 12",
+ "bonjour qq 12",
+ "bonjour 12 123",
+ "bonjour 12 qq",
+ "upnp 10",
+ "upnp qq uuid:",
+ "foo bar"]:
+ if "FAIL" not in dev[0].global_request("P2P_SERVICE_ADD " + cmd):
+ raise Exception("Invalid P2P_SERVICE_ADD accepted: " + cmd)
+
+ for cmd in ["bonjour",
+ "bonjour 123",
+ "bonjour qq",
+ "upnp 10",
+ "upnp ",
+ "upnp qq uuid:",
+ "foo bar"]:
+ if "FAIL" not in dev[0].global_request("P2P_SERVICE_DEL " + cmd):
+ raise Exception("Invalid P2P_SERVICE_DEL accepted: " + cmd)
+
+def test_p2p_service_discovery_cancel_during_query(dev):
+ """P2P service discovery and cancel during query"""
+ for i in range(2):
+ add_bonjour_services(dev[i])
+ add_upnp_services(dev[i])
+ add_extra_services(dev[i])
+ dev[i].p2p_listen()
+
+ dev[2].request("P2P_FLUSH")
+ id1 = dev[2].request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 02000201")
+ id2 = dev[2].request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 02000101")
+ dev[2].p2p_find(social=True)
+ ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+ if ev is None:
+ raise Exception("Could not discover peer")
+ if "OK" not in dev[2].request("P2P_SERV_DISC_CANCEL_REQ " + id1):
+ raise Exception("Failed to cancel req1")
+ if "OK" not in dev[2].request("P2P_SERV_DISC_CANCEL_REQ " + id2):
+ raise Exception("Failed to cancel req2")
+ ev = dev[2].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=3)
+ # we may or may not get a response depending on timing, so ignore the result
+ dev[2].p2p_stop_find()
+ dev[1].p2p_stop_find()
+ dev[0].p2p_stop_find()
+
+def get_p2p_state(dev):
+ res = dev.global_request("STATUS")
+ p2p_state = None
+ for line in res.splitlines():
+ if line.startswith("p2p_state="):
+ p2p_state = line.split('=')[1]
+ break
+ if p2p_state is None:
+ raise Exception("Could not get p2p_state")
+ return p2p_state
+
+@remote_compatible
+def test_p2p_service_discovery_peer_not_listening(dev):
+ """P2P service discovery and peer not listening"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ add_bonjour_services(dev[0])
+ add_upnp_services(dev[0])
+ dev[0].p2p_listen()
+ dev[1].global_request("P2P_FIND 4 type=social")
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=4)
+ if ev is None:
+ raise Exception("Peer not found")
+ dev[0].p2p_stop_find()
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=1)
+ ev = dev[1].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=1)
+ time.sleep(0.03)
+ dev[1].request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+ ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=1)
+ if ev is not None:
+ raise Exception("Service discovery request unexpectedly received")
+ ev = dev[1].wait_global_event(["P2P-FIND-STOPPED", "P2P-SERV-DISC-RESP"],
+ timeout=10)
+ if ev is None:
+ raise Exception("P2P-FIND-STOPPED event timed out")
+ if "P2P-SERV-DISC-RESP" in ev:
+ raise Exception("Unexpected SD response")
+ p2p_state = get_p2p_state(dev[1])
+ if p2p_state != "IDLE":
+ raise Exception("Unexpected p2p_state after P2P_FIND timeout: " + p2p_state)
+
+@remote_compatible
+def test_p2p_service_discovery_peer_not_listening2(dev):
+ """P2P service discovery and peer not listening"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ add_bonjour_services(dev[0])
+ add_upnp_services(dev[0])
+ dev[0].p2p_listen()
+ dev[1].global_request("P2P_FIND type=social")
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("Peer not found")
+ dev[0].p2p_stop_find()
+ time.sleep(0.53)
+ dev[1].request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+ ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Service discovery request unexpectedly received")
+ dev[1].p2p_stop_find()
+ ev = dev[1].wait_global_event(["P2P-FIND-STOPPED", "P2P-SERV-DISC-RESP"],
+ timeout=10)
+ if ev is None:
+ raise Exception("P2P-FIND-STOPPED event timed out")
+ if "P2P-SERV-DISC-RESP" in ev:
+ raise Exception("Unexpected SD response")
+ p2p_state = get_p2p_state(dev[1])
+ if p2p_state != "IDLE":
+ raise Exception("Unexpected p2p_state after P2P_FIND timeout: " + p2p_state)
+
+def test_p2p_service_discovery_restart(dev):
+ """P2P service discovery restarted immediately"""
+ try:
+ _test_p2p_service_discovery_restart(dev)
+ finally:
+ dev[1].global_request("P2P_SET disc_int 1 3 -1")
+
+def _test_p2p_service_discovery_restart(dev):
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ # Use shorter listen interval to keep P2P_FIND loop shorter.
+ dev[1].global_request("P2P_SET disc_int 1 1 10")
+
+ add_bonjour_services(dev[0])
+ #add_upnp_services(dev[0])
+ dev[0].p2p_listen()
+
+ dev[1].global_request("P2P_FLUSH")
+ dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+ if not dev[1].discover_peer(addr0, social=True, force_find=True):
+ raise Exception("Peer " + addr0 + " not found")
+
+ ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+
+ # The following P2P_LISTEN operation used to get delayed due to the last
+ # Action frame TX operation in SD Response using wait_time of 200 ms. It is
+ # somewhat difficult to test for this automatically, but the debug log can
+ # be verified to see that the remain-on-channel event for operation arrives
+ # immediately instead of getting delayed 200 ms. We can use a maximum
+ # acceptable time for the SD Response, but need to keep the limit somewhat
+ # high to avoid making this fail under heavy load. Still, it is apparently
+ # possible for this to take about the same amount of time with fixed
+ # implementation every now and then, so run this multiple time and pass the
+ # test if any attempt is fast enough.
+
+ for i in range(10):
+ dev[0].p2p_stop_find()
+ time.sleep(0.01)
+ dev[0].p2p_listen()
+
+ dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " 02000001")
+ start = os.times()[4]
+ ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=10)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ end = os.times()[4]
+ logger.info("Second SD Response in " + str(end - start) + " seconds")
+ if end - start < 0.8:
+ break
+
+ if end - start > 0.8:
+ raise Exception("Unexpectedly slow second SD Response: " + str(end - start) + " seconds")
diff --git a/contrib/wpa/tests/hwsim/test_p2p_set.py b/contrib/wpa/tests/hwsim/test_p2p_set.py
new file mode 100644
index 000000000000..58577994ea5b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_set.py
@@ -0,0 +1,128 @@
+# P2P_SET test cases
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+
+def test_p2p_set(dev):
+ """P2P_SET commands"""
+ for cmd in ["",
+ "foo bar",
+ "noa 1",
+ "noa 1,2",
+ "noa 1,2,3",
+ "noa -1,0,0",
+ "noa 256,0,0",
+ "noa 0,-1,0",
+ "noa 0,0,-1",
+ "noa 0,0,1",
+ "noa 255,10,20",
+ "ps 2",
+ "oppps 1",
+ "ctwindow 1",
+ "conc_pref foo",
+ "peer_filter foo",
+ "client_apsd 0",
+ "client_apsd 0,0",
+ "client_apsd 0,0,0",
+ "disc_int 1",
+ "disc_int 1 2",
+ "disc_int 2 1 10",
+ "disc_int -1 0 10",
+ "disc_int 0 -1 10",
+ "ssid_postfix 123456789012345678901234"]:
+ if "FAIL" not in dev[0].request("P2P_SET " + cmd):
+ raise Exception("Invalid P2P_SET accepted: " + cmd)
+ dev[0].request("P2P_SET ps 1")
+ if "OK" not in dev[0].request("P2P_SET ps 0"):
+ raise Exception("P2P_SET ps 0 failed unexpectedly")
+
+def test_p2p_set_discoverability(dev):
+ """P2P_SET discoverability"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ dev[0].p2p_start_go(freq="2412")
+ if "OK" not in dev[1].request("P2P_SET discoverability 0"):
+ raise Exception("P2P_SET discoverability 0 failed")
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(addr0, pin, timeout=20, social=True, freq="2412")
+
+ if not dev[2].discover_peer(addr1, timeout=10):
+ if not dev[2].discover_peer(addr1, timeout=10):
+ if not dev[2].discover_peer(addr1, timeout=10):
+ raise Exception("Could not discover group client")
+
+ peer = dev[2].get_peer(addr1)
+ if int(peer['dev_capab'], 16) & 0x02 != 0:
+ raise Exception("Discoverability dev_capab reported: " + peer['dev_capab'])
+ dev[2].p2p_stop_find()
+
+ if "OK" not in dev[1].request("P2P_SET discoverability 1"):
+ raise Exception("P2P_SET discoverability 1 failed")
+ dev[1].dump_monitor()
+ dev[1].group_request("REASSOCIATE")
+ ev = dev[1].wait_group_event(["CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Connection timed out")
+
+ dev[2].request("P2P_FLUSH")
+ if not dev[2].discover_peer(addr1, timeout=10):
+ if not dev[2].discover_peer(addr1, timeout=10):
+ if not dev[2].discover_peer(addr1, timeout=10):
+ raise Exception("Could not discover group client")
+
+ peer = dev[2].get_peer(addr1)
+ if int(peer['dev_capab'], 16) & 0x02 != 0x02:
+ raise Exception("Discoverability dev_capab reported: " + peer['dev_capab'])
+ dev[2].p2p_stop_find()
+
+def test_p2p_set_managed(dev):
+ """P2P_SET managed"""
+ addr0 = dev[0].p2p_dev_addr()
+
+ if "OK" not in dev[0].request("P2P_SET managed 1"):
+ raise Exception("P2P_SET managed 1 failed")
+
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0):
+ raise Exception("Could not discover peer")
+ peer = dev[1].get_peer(addr0)
+ if int(peer['dev_capab'], 16) & 0x08 != 0x08:
+ raise Exception("Managed dev_capab not reported: " + peer['dev_capab'])
+ dev[1].p2p_stop_find()
+
+ if "OK" not in dev[0].request("P2P_SET managed 0"):
+ raise Exception("P2P_SET managed 0 failed")
+
+ if not dev[2].discover_peer(addr0):
+ raise Exception("Could not discover peer")
+ peer = dev[2].get_peer(addr0)
+ if int(peer['dev_capab'], 16) & 0x08 != 0:
+ raise Exception("Managed dev_capab reported: " + peer['dev_capab'])
+ dev[2].p2p_stop_find()
+ dev[0].p2p_stop_find()
+
+@remote_compatible
+def test_p2p_set_ssid_postfix(dev):
+ """P2P_SET ssid_postfix"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ postfix = "12345678901234567890123"
+
+ try:
+ if "OK" not in dev[0].request("P2P_SET ssid_postfix " + postfix):
+ raise Exception("P2P_SET ssid_postfix failed")
+ dev[0].p2p_start_go(freq="2412")
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(addr0, pin, timeout=20, social=True, freq="2412")
+ if postfix not in dev[1].get_group_status_field("ssid"):
+ raise Exception("SSID postfix missing from status")
+ if postfix not in dev[1].group_request("SCAN_RESULTS"):
+ raise Exception("SSID postfix missing from scan results")
+ finally:
+ dev[0].request("P2P_SET ssid_postfix ")
diff --git a/contrib/wpa/tests/hwsim/test_p2p_wifi_display.py b/contrib/wpa/tests/hwsim/test_p2p_wifi_display.py
new file mode 100644
index 000000000000..29110bca7c69
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2p_wifi_display.py
@@ -0,0 +1,475 @@
+# Wi-Fi Display test cases
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+from p2p_utils import *
+
+def test_wifi_display(dev):
+ """Wi-Fi Display extensions to P2P"""
+ wfd_devinfo = "00411c440028"
+ dev[0].request("SET wifi_display 1")
+ dev[0].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo)
+ if wfd_devinfo not in dev[0].request("WFD_SUBELEM_GET 0"):
+ raise Exception("Could not fetch back configured subelement")
+
+ # Associated BSSID
+ dev[0].request("WFD_SUBELEM_SET 1 0006020304050607")
+ # Coupled Sink
+ dev[0].request("WFD_SUBELEM_SET 6 000700000000000000")
+ # Session Info
+ dev[0].request("WFD_SUBELEM_SET 9 0000")
+ # WFD Extended Capability
+ dev[0].request("WFD_SUBELEM_SET 7 00020000")
+ # WFD Content Protection
+ prot = "0001" + "00"
+ dev[0].request("WFD_SUBELEM_SET 5 " + prot)
+ # WFD Video Formats
+ video = "0015" + "010203040506070809101112131415161718192021"
+ dev[0].request("WFD_SUBELEM_SET 3 " + video)
+ # WFD 3D Video Formats
+ video_3d = "0011" + "0102030405060708091011121314151617"
+ dev[0].request("WFD_SUBELEM_SET 4 " + video_3d)
+ # WFD Audio Formats
+ audio = "000f" + "010203040506070809101112131415"
+ dev[0].request("WFD_SUBELEM_SET 2 " + audio)
+
+ elems = dev[0].request("WFD_SUBELEM_GET all")
+ if wfd_devinfo not in elems:
+ raise Exception("Could not fetch back configured subelements")
+
+ wfd_devinfo2 = "00001c440028"
+ dev[1].request("SET wifi_display 1")
+ dev[1].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo2)
+ if wfd_devinfo2 not in dev[1].request("WFD_SUBELEM_GET 0"):
+ raise Exception("Could not fetch back configured subelement")
+
+ dev[0].p2p_listen()
+ if "FAIL" in dev[1].global_request("P2P_SERV_DISC_REQ " + dev[0].p2p_dev_addr() + " wifi-display [source][pri-sink] 2,3,4,5"):
+ raise Exception("Setting SD request failed")
+ dev[1].p2p_find(social=True)
+ ev = dev[0].wait_global_event(["P2P-SERV-DISC-REQ"], timeout=10)
+ if ev is None:
+ raise Exception("Device discovery request not reported")
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Device discovery timed out")
+ if "wfd_dev_info=0x" + wfd_devinfo not in ev:
+ raise Exception("Wi-Fi Display Info not in P2P-DEVICE-FOUND event")
+ if "new=1" not in ev:
+ raise Exception("new=1 flag missing from P2P-DEVICE-FOUND event")
+ ev = dev[1].wait_global_event(["P2P-SERV-DISC-RESP"], timeout=5)
+ if ev is None:
+ raise Exception("Service discovery timed out")
+ if prot not in ev:
+ raise Exception("WFD Content Protection missing from WSD response")
+ if video not in ev:
+ raise Exception("WFD Video Formats missing from WSD response")
+ if video_3d not in ev:
+ raise Exception("WFD 3D Video Formats missing from WSD response")
+ if audio not in ev:
+ raise Exception("WFD Audio Formats missing from WSD response")
+
+ dev[1].dump_monitor()
+ dev[0].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo2)
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+ if ev is None:
+ raise Exception("Peer info update timed out")
+ if "new=0" not in ev:
+ raise Exception("new=0 flag missing from P2P-DEVICE-FOUND event")
+ if "wfd_dev_info=0x" + wfd_devinfo2 not in ev:
+ raise Exception("Wi-Fi Display Info not in P2P-DEVICE-FOUND event")
+ dev[1].dump_monitor()
+ dev[0].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo)
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+ if ev is None:
+ raise Exception("Peer info update timed out")
+ if "new=0" not in ev:
+ raise Exception("new=0 flag missing from P2P-DEVICE-FOUND event")
+ if "wfd_dev_info=0x" + wfd_devinfo not in ev:
+ raise Exception("Wi-Fi Display Info not in P2P-DEVICE-FOUND event")
+
+ pin = dev[0].wps_read_pin()
+ dev[0].p2p_go_neg_auth(dev[1].p2p_dev_addr(), pin, 'display')
+ res1 = dev[1].p2p_go_neg_init(dev[0].p2p_dev_addr(), pin, 'enter',
+ timeout=20, go_intent=15, freq=2437)
+ res2 = dev[0].p2p_go_neg_auth_result()
+
+ bss = dev[0].get_bss("p2p_dev_addr=" + dev[1].p2p_dev_addr())
+ if bss['bssid'] != dev[1].p2p_interface_addr():
+ raise Exception("Unexpected BSSID in the BSS entry for the GO")
+ if wfd_devinfo2 not in bss['wfd_subelems']:
+ raise Exception("Could not see wfd_subelems in GO's BSS entry")
+ peer = dev[0].get_peer(dev[1].p2p_dev_addr())
+ if wfd_devinfo2 not in peer['wfd_subelems']:
+ raise Exception("Could not see wfd_subelems in GO's peer entry")
+ peer = dev[1].get_peer(dev[0].p2p_dev_addr())
+ if wfd_devinfo not in peer['wfd_subelems']:
+ raise Exception("Could not see wfd_subelems in client's peer entry")
+
+ wfd_devinfo3 = "00001c440028"
+ dev[2].request("SET wifi_display 1")
+ dev[2].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo3)
+ dev[2].p2p_find(social=True)
+ ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Device discovery timed out")
+ if dev[1].p2p_dev_addr() not in ev:
+ ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Device discovery timed out")
+ if dev[1].p2p_dev_addr() not in ev:
+ raise Exception("Could not discover GO")
+ if "wfd_dev_info=0x" + wfd_devinfo2 not in ev:
+ raise Exception("Wi-Fi Display Info not in P2P-DEVICE-FOUND event")
+ bss = dev[2].get_bss("p2p_dev_addr=" + dev[1].p2p_dev_addr())
+ if bss['bssid'] != dev[1].p2p_interface_addr():
+ raise Exception("Unexpected BSSID in the BSS entry for the GO")
+ if wfd_devinfo2 not in bss['wfd_subelems']:
+ raise Exception("Could not see wfd_subelems in GO's BSS entry")
+ peer = dev[2].get_peer(dev[1].p2p_dev_addr())
+ if wfd_devinfo2 not in peer['wfd_subelems']:
+ raise Exception("Could not see wfd_subelems in GO's peer entry")
+ dev[2].p2p_stop_find()
+
+ if dev[0].request("WFD_SUBELEM_GET 2") != audio:
+ raise Exception("Unexpected WFD_SUBELEM_GET 2 value")
+ if dev[0].request("WFD_SUBELEM_GET 3") != video:
+ raise Exception("Unexpected WFD_SUBELEM_GET 3 value")
+ if dev[0].request("WFD_SUBELEM_GET 4") != video_3d:
+ raise Exception("Unexpected WFD_SUBELEM_GET 42 value")
+ if dev[0].request("WFD_SUBELEM_GET 5") != prot:
+ raise Exception("Unexpected WFD_SUBELEM_GET 5 value")
+ if "FAIL" not in dev[0].request("WFD_SUBELEM_SET "):
+ raise Exception("Unexpected WFD_SUBELEM_SET success")
+ if "FAIL" not in dev[0].request("WFD_SUBELEM_SET 6"):
+ raise Exception("Unexpected WFD_SUBELEM_SET success")
+ if "OK" not in dev[0].request("WFD_SUBELEM_SET 6 "):
+ raise Exception("Unexpected WFD_SUBELEM_SET failure")
+ if "FAIL" not in dev[0].request("WFD_SUBELEM_SET 6 0"):
+ raise Exception("Unexpected WFD_SUBELEM_SET success")
+ if "FAIL" not in dev[0].request("WFD_SUBELEM_SET 6 0q"):
+ raise Exception("Unexpected WFD_SUBELEM_SET success")
+ if dev[0].request("WFD_SUBELEM_GET 6") != "":
+ raise Exception("Unexpected WFD_SUBELEM_GET 6 response")
+ if dev[0].request("WFD_SUBELEM_GET 8") != "":
+ raise Exception("Unexpected WFD_SUBELEM_GET 8 response")
+
+ if dev[0].global_request("WFD_SUBELEM_GET 2") != audio:
+ raise Exception("Unexpected WFD_SUBELEM_GET 2 value from global interface")
+ if "OK" not in dev[0].global_request("WFD_SUBELEM_SET 1 0006020304050608"):
+ raise Exception("WFD_SUBELEM_SET failed on global interface")
+ if dev[0].request("WFD_SUBELEM_GET 1") != "0006020304050608":
+ raise Exception("Unexpected WFD_SUBELEM_GET 1 value (per-interface)")
+
+ elems = dev[0].request("WFD_SUBELEM_GET all")
+ if "OK" not in dev[0].request("WFD_SUBELEM_SET all " + elems):
+ raise Exception("WFD_SUBELEM_SET all failed")
+ if dev[0].request("WFD_SUBELEM_GET all") != elems:
+ raise Exception("Mismatch in WFS_SUBELEM_SET/GET all")
+ test = "00000600411c440028"
+ if "OK" not in dev[0].request("WFD_SUBELEM_SET all " + test):
+ raise Exception("WFD_SUBELEM_SET all failed")
+ if dev[0].request("WFD_SUBELEM_GET all") != test:
+ raise Exception("Mismatch in WFS_SUBELEM_SET/GET all")
+
+ if "FAIL" not in dev[0].request("WFD_SUBELEM_SET all qwerty"):
+ raise Exception("Invalid WFD_SUBELEM_SET all succeeded")
+ if "FAIL" not in dev[0].request("WFD_SUBELEM_SET all 11"):
+ raise Exception("Invalid WFD_SUBELEM_SET all succeeded")
+ dev[0].request("WFD_SUBELEM_SET all 112233445566")
+ dev[0].request("WFD_SUBELEM_SET all ff0000fe0000fd00")
+
+ if "FAIL" not in dev[0].request("WFD_SUBELEM_SET 300 112233"):
+ raise Exception("Invalid WFD_SUBELEM_SET 300 succeeded")
+ if "FAIL" not in dev[0].request("WFD_SUBELEM_SET -1 112233"):
+ raise Exception("Invalid WFD_SUBELEM_SET -1 succeeded")
+ if "FAIL" not in dev[0].request("WFD_SUBELEM_GET 300"):
+ raise Exception("Invalid WFD_SUBELEM_GET 300 succeeded")
+ if "FAIL" not in dev[0].request("WFD_SUBELEM_GET -1"):
+ raise Exception("Invalid WFD_SUBELEM_GET -1 succeeded")
+
+ dev[0].request("SET wifi_display 0")
+ dev[1].request("SET wifi_display 0")
+ dev[2].request("SET wifi_display 0")
+
+def test_wifi_display_r2(dev):
+ """Wi-Fi Display extensions to P2P with R2 subelems"""
+ wfd_devinfo = "00411c440028"
+ dev[0].request("SET wifi_display 1")
+ dev[0].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo)
+
+ # Associated BSSID
+ dev[0].request("WFD_SUBELEM_SET 1 0006020304050607")
+ # Coupled Sink
+ dev[0].request("WFD_SUBELEM_SET 6 000700000000000000")
+ # Session Info
+ dev[0].request("WFD_SUBELEM_SET 9 0000")
+ # WFD Extended Capability
+ dev[0].request("WFD_SUBELEM_SET 7 00020000")
+ # WFD Content Protection
+ prot = "0001" + "00"
+ dev[0].request("WFD_SUBELEM_SET 5 " + prot)
+ # WFD Video Formats
+ video = "0015" + "010203040506070809101112131415161718192021"
+ dev[0].request("WFD_SUBELEM_SET 3 " + video)
+ # WFD 3D Video Formats
+ video_3d = "0011" + "0102030405060708091011121314151617"
+ dev[0].request("WFD_SUBELEM_SET 4 " + video_3d)
+ # WFD Audio Formats
+ audio = "000f" + "010203040506070809101112131415"
+ dev[0].request("WFD_SUBELEM_SET 2 " + audio)
+ # MAC Info
+ mac_info = "0006" + "112233445566"
+ dev[0].request("WFD_SUBELEM_SET 10 " + mac_info)
+ # R2 Device Info
+ r2_dev_info = "0006" + "aabbccddeeff"
+ dev[0].request("WFD_SUBELEM_SET 11 " + r2_dev_info)
+
+ elems = dev[0].request("WFD_SUBELEM_GET all")
+ if wfd_devinfo not in elems:
+ raise Exception("Could not fetch back configured subelements")
+
+ wfd_devinfo2 = "00001c440028"
+ dev[1].request("SET wifi_display 1")
+ dev[1].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo2)
+ if wfd_devinfo2 not in dev[1].request("WFD_SUBELEM_GET 0"):
+ raise Exception("Could not fetch back configured subelement")
+
+ dev[0].p2p_listen()
+ dev[1].p2p_find(social=True)
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=5)
+ if ev is None:
+ raise Exception("Device discovery timed out")
+ if "wfd_dev_info=0x" + wfd_devinfo not in ev:
+ raise Exception("Wi-Fi Display Info not in P2P-DEVICE-FOUND event")
+ if "new=1" not in ev:
+ raise Exception("new=1 flag missing from P2P-DEVICE-FOUND event")
+
+ pin = dev[0].wps_read_pin()
+ dev[0].p2p_go_neg_auth(dev[1].p2p_dev_addr(), pin, 'display')
+ res1 = dev[1].p2p_go_neg_init(dev[0].p2p_dev_addr(), pin, 'enter',
+ timeout=20, go_intent=15, freq=2437)
+ res2 = dev[0].p2p_go_neg_auth_result()
+
+ bss = dev[0].get_bss("p2p_dev_addr=" + dev[1].p2p_dev_addr())
+ if bss['bssid'] != dev[1].p2p_interface_addr():
+ raise Exception("Unexpected BSSID in the BSS entry for the GO")
+ if wfd_devinfo2 not in bss['wfd_subelems']:
+ raise Exception("Could not see wfd_subelems in GO's BSS entry")
+ peer = dev[0].get_peer(dev[1].p2p_dev_addr())
+ if wfd_devinfo2 not in peer['wfd_subelems']:
+ raise Exception("Could not see wfd_subelems in GO's peer entry")
+ peer = dev[1].get_peer(dev[0].p2p_dev_addr())
+ if wfd_devinfo not in peer['wfd_subelems']:
+ raise Exception("Could not see wfd_subelems in client's peer entry")
+ if r2_dev_info not in peer['wfd_subelems']:
+ raise Exception("Could not see r2_dev_info in client's peer entry")
+
+ elems = dev[0].request("WFD_SUBELEM_GET all")
+ if "OK" not in dev[0].request("WFD_SUBELEM_SET all " + elems):
+ raise Exception("WFD_SUBELEM_SET all failed")
+ if dev[0].request("WFD_SUBELEM_GET all") != elems:
+ raise Exception("Mismatch in WFS_SUBELEM_SET/GET all")
+ test = "00000600411c440028"
+ if "OK" not in dev[0].request("WFD_SUBELEM_SET all " + test):
+ raise Exception("WFD_SUBELEM_SET all failed")
+ if dev[0].request("WFD_SUBELEM_GET all") != test:
+ raise Exception("Mismatch in WFS_SUBELEM_SET/GET all")
+
+ dev[0].request("SET wifi_display 0")
+ dev[1].request("SET wifi_display 0")
+ dev[2].request("SET wifi_display 0")
+
+def enable_wifi_display(dev):
+ dev.request("SET wifi_display 1")
+ dev.request("WFD_SUBELEM_SET 0 000600411c440028")
+
+def test_wifi_display_go_invite(dev):
+ """P2P GO with Wi-Fi Display inviting a client to join"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ try:
+ enable_wifi_display(dev[0])
+ enable_wifi_display(dev[1])
+ enable_wifi_display(dev[2])
+
+ dev[1].p2p_listen()
+ if not dev[0].discover_peer(addr1, social=True):
+ raise Exception("Peer " + addr1 + " not found")
+ dev[0].p2p_listen()
+ if not dev[1].discover_peer(addr0, social=True):
+ raise Exception("Peer " + addr0 + " not found")
+ dev[1].p2p_listen()
+
+ logger.info("Authorize invitation")
+ pin = dev[1].wps_read_pin()
+ dev[1].global_request("P2P_CONNECT " + addr0 + " " + pin + " join auth")
+
+ dev[0].p2p_start_go(freq=2412)
+
+ # Add test client to the group
+ connect_cli(dev[0], dev[2], social=True, freq=2412)
+
+ logger.info("Invite peer to join the group")
+ dev[0].p2p_go_authorize_client(pin)
+ dev[0].global_request("P2P_INVITE group=" + dev[0].group_ifname + " peer=" + addr1)
+ ev = dev[1].wait_global_event(["P2P-INVITATION-RECEIVED",
+ "P2P-GROUP-STARTED"], timeout=20)
+ if ev is None:
+ raise Exception("Timeout on invitation on peer")
+ if "P2P-INVITATION-RECEIVED" in ev:
+ raise Exception("Unexpected request to accept pre-authorized invitation")
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+ dev[2].wait_go_ending_session()
+
+ finally:
+ dev[0].request("SET wifi_display 0")
+ dev[1].request("SET wifi_display 0")
+ dev[2].request("SET wifi_display 0")
+
+def test_wifi_display_persistent_group(dev):
+ """P2P persistent group formation and re-invocation with Wi-Fi Display enabled"""
+ try:
+ enable_wifi_display(dev[0])
+ enable_wifi_display(dev[1])
+ enable_wifi_display(dev[2])
+
+ form(dev[0], dev[1])
+ peer = dev[1].get_peer(dev[0].p2p_dev_addr())
+ listen_freq = peer['listen_freq']
+ invite_from_cli(dev[0], dev[1])
+ invite_from_go(dev[0], dev[1])
+
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ networks = dev[0].list_networks(p2p=True)
+ if len(networks) != 1:
+ raise Exception("Unexpected number of networks")
+ if "[P2P-PERSISTENT]" not in networks[0]['flags']:
+ raise Exception("Not the persistent group data")
+ if "OK" not in dev[0].global_request("P2P_GROUP_ADD persistent=" + networks[0]['id'] + " freq=" + listen_freq):
+ raise Exception("Could not start GO")
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=2)
+ if ev is None:
+ raise Exception("GO start up timed out")
+ dev[0].group_form_result(ev)
+
+ connect_cli(dev[0], dev[2], social=True, freq=listen_freq)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ invite(dev[1], dev[0])
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=30)
+ if ev is None:
+ raise Exception("Timeout on group re-invocation (on client)")
+ dev[1].group_form_result(ev)
+
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected P2P-GROUP-START on GO")
+ hwsim_utils.test_connectivity_p2p(dev[0], dev[1])
+
+ finally:
+ dev[0].request("SET wifi_display 0")
+ dev[1].request("SET wifi_display 0")
+ dev[2].request("SET wifi_display 0")
+
+@remote_compatible
+def test_wifi_display_invalid_subelem(dev):
+ """Wi-Fi Display and invalid subelement parsing"""
+ addr1 = dev[1].p2p_dev_addr()
+
+ try:
+ enable_wifi_display(dev[0])
+ enable_wifi_display(dev[1])
+ dev[1].request("WFD_SUBELEM_SET 0 ffff00411c440028")
+
+ dev[1].p2p_listen()
+ dev[0].p2p_find(social=True)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("Device discovery timed out")
+ if "wfd_dev_info=" in ev:
+ raise Exception("Invalid WFD subelement was shown")
+
+ finally:
+ dev[0].request("SET wifi_display 0")
+ dev[1].request("SET wifi_display 0")
+
+def test_wifi_display_parsing(dev):
+ """Wi-Fi Display extensions to P2P and special parsing cases"""
+ try:
+ _test_wifi_display_parsing(dev)
+ finally:
+ dev[1].request("VENDOR_ELEM_REMOVE 11 *")
+ dev[0].request("SET wifi_display 0")
+
+def _test_wifi_display_parsing(dev):
+ wfd_devinfo = "00411c440028"
+ dev[0].request("SET wifi_display 1")
+ dev[0].request("WFD_SUBELEM_SET 0 0006" + wfd_devinfo)
+ dev[0].p2p_start_go(freq=2412)
+
+ # P2P Client with invalid WFD IE
+ if "OK" not in dev[1].request("VENDOR_ELEM_ADD 11 dd10506f9a0a000000010000060000ffffff"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+
+ pin = dev[1].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[1].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60,
+ social=True, freq=2412)
+ bssid = dev[0].get_group_status_field('bssid')
+ dev[2].scan_for_bss(bssid, freq=2412, force_scan=True)
+ bss = dev[2].get_bss(bssid)
+ if bss['wfd_subelems'] != "000006" + wfd_devinfo:
+ raise Exception("Unexpected WFD elements in scan results: " + bss['wfd_subelems'])
+
+ # P2P Client without WFD IE
+ pin = dev[2].wps_read_pin()
+ dev[0].p2p_go_authorize_client(pin)
+ dev[2].p2p_connect_group(dev[0].p2p_dev_addr(), pin, timeout=60,
+ social=True, freq=2412)
+ dev[2].remove_group()
+
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+def test_wifi_display_disable(dev):
+ """Peer disabling Wi-Fi Display advertisement"""
+ try:
+ enable_wifi_display(dev[1])
+ dev[1].p2p_listen()
+ dev[0].p2p_find(social=True)
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=15)
+ if ev is None:
+ raise Exception("Peer not found")
+ if "wfd_dev_info" not in ev:
+ raise Exception("Missing wfd_dev_info")
+
+ dev[1].request("SET wifi_display 0")
+
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("Peer update not indicated")
+ if "new=0" not in ev:
+ raise Exception("Incorrect update event: " + ev)
+ if "wfd_dev_info" in ev:
+ raise Exception("Unexpected wfd_dev_info")
+
+ ev = dev[0].wait_global_event(["P2P-DEVICE-FOUND"], timeout=0.75)
+ if ev is not None:
+ raise Exception("Unexpected peer found event: " + ev)
+ dev[0].p2p_stop_find()
+ dev[1].p2p_stop_find()
+
+ finally:
+ dev[1].request("SET wifi_display 0")
diff --git a/contrib/wpa/tests/hwsim/test_p2ps.py b/contrib/wpa/tests/hwsim/test_p2ps.py
new file mode 100644
index 000000000000..b85fcd766a46
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_p2ps.py
@@ -0,0 +1,1689 @@
+# P2P services
+# Copyright (c) 2014-2015, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import time
+import random
+import re
+
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+import hostapd
+from p2p_utils import *
+from utils import HwsimSkip
+from hwsim import HWSimRadio
+
+# Dev[0] -> Advertiser
+# Dev[1] -> Seeker
+# ev0 -> Event generated at advertiser side
+# ev1 -> Event generated at Seeker side
+
+def p2ps_advertise(r_dev, r_role, svc_name, srv_info, rsp_info=None, cpt=None):
+ """P2PS Advertise function"""
+ adv_id = random.randrange(1, 0xFFFFFFFF)
+ advid = hex(adv_id)[2:]
+
+ cpt_param = (" cpt=" + cpt) if cpt is not None else ""
+
+ if rsp_info is not None and srv_info is not None:
+ if "OK" not in r_dev.global_request("P2P_SERVICE_ADD asp " + str(r_role) + " " + str(advid) + " 1 1108 " + svc_name + cpt_param + " svc_info='" + srv_info + "'" + " rsp_info=" + rsp_info + "'"):
+ raise Exception("P2P_SERVICE_ADD with response info and service info failed")
+
+ if rsp_info is None and srv_info is not None:
+ if "OK" not in r_dev.global_request("P2P_SERVICE_ADD asp " + str(r_role) + " " + str(advid) + " 1 1108 " + svc_name + cpt_param + " svc_info='" + srv_info + "'"):
+ raise Exception("P2P_SERVICE_ADD with service info failed")
+
+ if rsp_info is None and srv_info is None:
+ if "OK" not in r_dev.global_request("P2P_SERVICE_ADD asp " + str(r_role) + " " + str(advid) + " 1 1108 " + svc_name + cpt_param):
+ raise Exception("P2P_SERVICE_ADD without service info and without response info failed")
+
+ if rsp_info is not None and srv_info is None:
+ if "OK" not in r_dev.global_request("P2P_SERVICE_ADD asp " + str(r_role) + " " + str(adv_id) + " 1 1108 " + svc_name + cpt_param + " svc_info='" + " rsp_info=" + rsp_info + "'"):
+ raise Exception("P2P_SERVICE_ADD with response info failed")
+
+ r_dev.p2p_listen()
+ return advid
+
+def p2ps_exact_seek(i_dev, r_dev, svc_name, srv_info=None,
+ single_peer_expected=True):
+ """P2PS exact service seek request"""
+ if srv_info is not None:
+ ev1 = i_dev.global_request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 asp 1 " + svc_name + " '" + srv_info + "'")
+ if ev1 is None:
+ raise Exception("Failed to add Service Discovery request for exact seek request")
+
+ if "OK" not in i_dev.global_request("P2P_FIND 10 type=social seek=" + svc_name):
+ raise Exception("Failed to initiate seek operation")
+
+ timeout = time.time() + 10
+ ev1 = i_dev.wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ while ev1 is not None and not single_peer_expected:
+ if r_dev.p2p_dev_addr() in ev1 and "adv_id=" in ev1:
+ break
+ ev1 = i_dev.wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+
+ if timeout < time.time():
+ raise Exception("Device not found")
+
+ if ev1 is None:
+ raise Exception("P2P-DEVICE-FOUND timeout on seeker side")
+ if r_dev.p2p_dev_addr() not in ev1:
+ raise Exception("Unexpected peer")
+
+ if srv_info is None:
+ adv_id = ev1.split("adv_id=")[1].split(" ")[0]
+ rcvd_svc_name = ev1.split("asp_svc=")[1].split(" ")[0]
+ if rcvd_svc_name != svc_name:
+ raise Exception("service name not matching")
+ else:
+ ev1 = i_dev.wait_global_event(["P2P-SERV-ASP-RESP"], timeout=10)
+ if ev1 is None:
+ raise Exception("Failed to receive Service Discovery Response")
+ if r_dev.p2p_dev_addr() not in ev1:
+ raise Exception("Service Discovery response from Unknown Peer")
+ if srv_info is not None and srv_info not in ev1:
+ raise Exception("service info not available in Service Discovery response")
+ adv_id = ev1.split(" ")[3]
+ rcvd_svc_name = ev1.split(" ")[6]
+ if rcvd_svc_name != svc_name:
+ raise Exception("service name not matching")
+
+ i_dev.p2p_stop_find()
+ return [adv_id, rcvd_svc_name]
+
+def p2ps_nonexact_seek(i_dev, r_dev, svc_name, srv_info=None, adv_num=None):
+ """P2PS nonexact service seek request"""
+ if adv_num is None:
+ adv_num = 1
+ if srv_info is not None:
+ ev1 = i_dev.global_request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 asp 1 " + svc_name + " '" + srv_info + "'")
+ else:
+ ev1 = i_dev.global_request("P2P_SERV_DISC_REQ 00:00:00:00:00:00 asp 1 " + svc_name + " '")
+ if ev1 is None:
+ raise Exception("Failed to add Service Discovery request for nonexact seek request")
+ if "OK" not in i_dev.global_request("P2P_FIND 10 type=social seek="):
+ raise Exception("Failed to initiate seek")
+ ev1 = i_dev.wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev1 is None:
+ raise Exception("P2P-DEVICE-FOUND timeout on seeker side")
+ if r_dev.p2p_dev_addr() not in ev1:
+ raise Exception("Unexpected peer")
+ ev_list = []
+ for i in range(0, adv_num):
+ ev1 = i_dev.wait_global_event(["P2P-SERV-ASP-RESP"], timeout=10)
+ if ev1 is None:
+ raise Exception("Failed to receive Service Discovery Response")
+ if r_dev.p2p_dev_addr() not in ev1:
+ raise Exception("Service Discovery response from Unknown Peer")
+ if srv_info is not None and srv_info not in ev1:
+ raise Exception("service info not available in Service Discovery response")
+ adv_id = ev1.split(" ")[3]
+ rcvd_svc_name = ev1.split(" ")[6]
+ ev_list.append(''.join([adv_id, ' ', rcvd_svc_name]))
+
+ i_dev.p2p_stop_find()
+ return ev_list
+
+def p2ps_parse_event(ev, *args):
+ ret = ()
+ for arg in args:
+ m = re.search("\s+" + arg + r"=(\S+)", ev)
+ ret += (m.group(1) if m is not None else None,)
+ return ret
+
+def p2ps_provision(seeker, advertiser, adv_id, auto_accept=True, method="1000",
+ adv_cpt=None, seeker_cpt=None, handler=None, adv_role=None,
+ seeker_role=None):
+ addr0 = seeker.p2p_dev_addr()
+ addr1 = advertiser.p2p_dev_addr()
+
+ seeker.asp_provision(addr1, adv_id=str(adv_id), adv_mac=addr1, session_id=1,
+ session_mac=addr0, method=method, cpt=seeker_cpt,
+ role=seeker_role)
+
+ if not auto_accept or method == "100":
+ pin = None
+ ev_pd_start = advertiser.wait_global_event(["P2PS-PROV-START"],
+ timeout=10)
+ if ev_pd_start is None:
+ raise Exception("P2PS-PROV-START timeout on Advertiser side")
+ peer = ev_pd_start.split()[1]
+ advert_id, advert_mac, session, session_mac =\
+ p2ps_parse_event(ev_pd_start, "adv_id", "adv_mac", "session", "mac")
+
+ ev = seeker.wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DISC-FAILURE timeout on seeker side")
+
+ if handler:
+ handler(seeker, advertiser)
+
+ # Put seeker into a listen state, since we expect the deferred flow to
+ # continue.
+ seeker.p2p_ext_listen(500, 500)
+
+ if method == "100":
+ ev = advertiser.wait_global_event(["P2P-PROV-DISC-ENTER-PIN"],
+ timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DISC-ENTER-PIN timeout on advertiser side")
+ if addr0 not in ev:
+ raise Exception("Unknown peer " + addr0)
+ ev = seeker.wait_global_event(["P2P-PROV-DISC-SHOW-PIN"],
+ timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DISC-SHOW-PIN timeout on seeker side")
+ if addr1 not in ev:
+ raise Exception("Unknown peer " + addr1)
+ pin = ev.split()[2]
+ elif method == "8":
+ ev = advertiser.wait_global_event(["P2P-PROV-DISC-SHOW-PIN"],
+ timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DISC-SHOW-PIN timeout on advertiser side")
+ if addr0 not in ev:
+ raise Exception("Unknown peer " + addr0)
+ pin = ev.split()[2]
+
+ # Stop P2P_LISTEN before issuing P2P_ASP_PROVISION_RESP to avoid
+ # excessive delay and test case timeouts if it takes large number of
+ # retries to find the peer awake on its Listen channel.
+ advertiser.p2p_stop_find()
+
+ advertiser.asp_provision(peer, adv_id=advert_id, adv_mac=advert_mac,
+ session_id=int(session, 0),
+ session_mac=session_mac, status=12,
+ cpt=adv_cpt, role=adv_role)
+
+ ev1 = seeker.wait_global_event(["P2PS-PROV-DONE"], timeout=10)
+ if ev1 is None:
+ raise Exception("P2PS-PROV-DONE timeout on seeker side")
+
+ ev2 = advertiser.wait_global_event(["P2PS-PROV-DONE"], timeout=10)
+ if ev2 is None:
+ raise Exception("P2PS-PROV-DONE timeout on advertiser side")
+
+ if method == "8":
+ ev = seeker.wait_global_event(["P2P-PROV-DISC-ENTER-PIN"],
+ timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DISC-ENTER-PIN failed on seeker side")
+ if addr1 not in ev:
+ raise Exception("Unknown peer " + addr1)
+
+ seeker.p2p_cancel_ext_listen()
+ if pin is not None:
+ return ev1, ev2, pin
+ return ev1, ev2
+
+ # Auto-accept is true and the method is either P2PS or advertiser is DISPLAY
+ ev1 = seeker.wait_global_event(["P2PS-PROV-DONE"], timeout=10)
+ if ev1 is None:
+ raise Exception("P2PS-PROV-DONE timeout on seeker side")
+
+ ev2 = advertiser.wait_global_event(["P2PS-PROV-DONE"], timeout=10)
+ if ev2 is None:
+ raise Exception("P2PS-PROV-DONE timeout on advertiser side")
+
+ if method == "8":
+ ev = seeker.wait_global_event(["P2P-PROV-DISC-ENTER-PIN"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DISC-ENTER-PIN timeout on seeker side")
+ if addr1 not in ev:
+ raise Exception("Unknown peer " + addr1)
+ ev = advertiser.wait_global_event(["P2P-PROV-DISC-SHOW-PIN"],
+ timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DISC-SHOW-PIN timeout on advertiser side")
+ if addr0 not in ev:
+ raise Exception("Unknown peer " + addr0)
+ pin = ev.split()[2]
+ return ev1, ev2, pin
+
+ return ev1, ev2
+
+def p2ps_connect_pd(dev0, dev1, ev0, ev1, pin=None, join_extra="", go_ev=None):
+ conf_methods_map = {"8": "p2ps", "1": "display", "5": "keypad"}
+ peer0 = ev0.split()[1]
+ peer1 = ev1.split()[1]
+ status0, conncap0, adv_id0, adv_mac0, mac0, session0, dev_passwd_id0, go0, join0, feature_cap0, persist0, group_ssid0 =\
+ p2ps_parse_event(ev0, "status", "conncap", "adv_id", "adv_mac", "mac", "session", "dev_passwd_id", "go", "join", "feature_cap", "persist", "group_ssid")
+ status1, conncap1, adv_id1, adv_mac1, mac1, session1, dev_passwd_id1, go1, join1, feature_cap1, persist1, group_ssid1 =\
+ p2ps_parse_event(ev1, "status", "conncap", "adv_id", "adv_mac", "mac", "session", "dev_passwd_id", "go", "join", "feature_cap", "persist", "group_ssid")
+
+ if status0 != "0" and status0 != "12":
+ raise Exception("PD failed on " + dev0.p2p_dev_addr())
+
+ if status1 != "0" and status1 != "12":
+ raise Exception("PD failed on " + dev1.p2p_dev_addr())
+
+ if status0 == "12" and status1 == "12":
+ raise Exception("Both sides have status 12 which doesn't make sense")
+
+ if adv_id0 != adv_id1 or adv_id0 is None:
+ raise Exception("Adv. IDs don't match")
+
+ if adv_mac0 != adv_mac1 or adv_mac0 is None:
+ raise Exception("Adv. MACs don't match")
+
+ if session0 != session1 or session0 is None:
+ raise Exception("Session IDs don't match")
+
+ if mac0 != mac1 or mac0 is None:
+ raise Exception("Session MACs don't match")
+
+ #TODO: Validate feature capability
+
+ if bool(persist0) != bool(persist1):
+ raise Exception("Only one peer has persistent group")
+
+ if persist0 is None and not all([conncap0, conncap1, dev_passwd_id0,
+ dev_passwd_id1]):
+ raise Exception("Persistent group not used but conncap/dev_passwd_id are missing")
+
+ if persist0 is not None and any([conncap0, conncap1, dev_passwd_id0,
+ dev_passwd_id1]):
+ raise Exception("Persistent group is used but conncap/dev_passwd_id are present")
+
+ # Persistent Connection (todo: handle frequency)
+ if persist0 is not None:
+ dev0.p2p_stop_find()
+ if "OK" not in dev0.global_request("P2P_GROUP_ADD persistent=" + persist0 + " freq=2412"):
+ raise Exception("Could not re-start persistent group")
+ ev0 = dev0.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev0 is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev0.p2p_dev_addr())
+ dev0.group_form_result(ev0)
+
+ if "OK" not in dev1.global_request("P2P_GROUP_ADD persistent=" + persist1 + " freq=2412"):
+ raise Exception("Could not re-start persistent group")
+ ev1 = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev1 is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev1.p2p_dev_addr())
+ dev1.group_form_result(ev1)
+ if "GO" in ev0:
+ ev = dev0.wait_global_event(["AP-STA-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("AP-STA-CONNECTED timeout on " + dev0.p2p_dev_addr())
+ else:
+ ev = dev1.wait_global_event(["AP-STA-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("AP-STA-CONNECTED timeout on " + dev1.p2p_dev_addr())
+ else:
+ try:
+ method0 = conf_methods_map[dev_passwd_id0]
+ method1 = conf_methods_map[dev_passwd_id1]
+ except KeyError:
+ raise Exception("Unsupported method")
+
+ if method0 == "p2ps":
+ pin = "12345670"
+ if pin is None:
+ raise Exception("Pin is not provided")
+
+ if conncap0 == "1" and conncap1 == "1": # NEW/NEW - GON
+ if any([join0, join1, go0, go1]):
+ raise Exception("Unexpected join/go PD attributes")
+ dev0.p2p_listen()
+ if "OK" not in dev0.global_request("P2P_CONNECT " + peer0 + " " + pin + " " + method0 + " persistent auth"):
+ raise Exception("P2P_CONNECT fails on " + dev0.p2p_dev_addr())
+ if "OK" not in dev1.global_request("P2P_CONNECT " + peer1 + " " + pin + " " + method1 + " persistent"):
+ raise Exception("P2P_CONNECT fails on " + dev1.p2p_dev_addr())
+ ev = dev0.wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("GO Neg did not succeed on " + dev0.p2p_dev_addr())
+ ev = dev1.wait_global_event(["P2P-GO-NEG-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("GO Neg did not succeed on " + dev1.p2p_dev_addr())
+ ev = dev0.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev0.p2p_dev_addr())
+ dev0.group_form_result(ev)
+ ev = dev1.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev1.p2p_dev_addr())
+ dev1.group_form_result(ev)
+ else:
+ if conncap0 == "2" and conncap1 == "4": # dev0 CLI, dev1 GO
+ dev_cli, dev_go, go_if, join_address, go_method, cli_method, join_ssid = dev0, dev1, go1, join0, method1, method0, group_ssid0
+ elif conncap0 == "4" and conncap1 == "2": # dev0 GO, dev1 CLI
+ dev_cli, dev_go, go_if, join_address, go_method, cli_method, join_ssid = dev1, dev0, go0, join1, method0, method1, group_ssid1
+ else:
+ raise Exception("Bad connection capabilities")
+
+ if go_if is None:
+ raise Exception("Device " + dev_go.p2p_dev_addr() + " failed to become GO")
+ if join_address is None:
+ raise Exception("Device " + dev_cli.p2p_dev_addr() + " failed to become CLI")
+
+ if not dev_go.get_group_ifname().startswith('p2p-'):
+ if go_ev:
+ ev = go_ev
+ else:
+ ev = dev_go.wait_global_event(["P2P-GROUP-STARTED"],
+ timeout=10)
+ if ev is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev_go.p2p_dev_addr())
+ dev_go.group_form_result(ev)
+
+ if go_method != "p2ps":
+ ev = dev_go.group_request("WPS_PIN any " + pin)
+ if ev is None:
+ raise Exception("Failed to initiate pin authorization on registrar side")
+ if join_ssid:
+ group_ssid_txt = " ssid=" + join_ssid
+ else:
+ group_ssid_txt = ""
+ if "OK" not in dev_cli.global_request("P2P_CONNECT " + join_address + " " + pin + " " + cli_method + join_extra + " persistent join" + group_ssid_txt):
+ raise Exception("P2P_CONNECT failed on " + dev_cli.p2p_dev_addr())
+ ev = dev_cli.wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev_cli.p2p_dev_addr())
+ dev_cli.group_form_result(ev)
+ ev = dev_go.wait_global_event(["AP-STA-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("AP-STA-CONNECTED timeout on " + dev_go.p2p_dev_addr())
+
+ hwsim_utils.test_connectivity_p2p(dev0, dev1)
+
+def set_no_group_iface(dev, enable):
+ if enable:
+ res = dev.get_driver_status()
+ if (int(res['capa.flags'], 0) & 0x20000000):
+ raise HwsimSkip("P2P Device used. Cannot set enable no_group_iface")
+ dev.global_request("SET p2p_no_group_iface 1")
+ else:
+ dev.global_request("SET p2p_no_group_iface 0")
+
+@remote_compatible
+def test_p2ps_exact_search(dev):
+ """P2PS exact service request"""
+ p2ps_advertise(r_dev=dev[0], r_role='1', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx')
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+
+@remote_compatible
+def test_p2ps_exact_search_srvinfo(dev):
+ """P2PS exact service request with service info"""
+ p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+
+@remote_compatible
+def test_p2ps_nonexact_search(dev):
+ """P2PS nonexact seek request"""
+ p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.play.rx',
+ srv_info='I support Miracast Mode ')
+ ev_list = p2ps_nonexact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.play*')
+ adv_id = ev_list[0].split()[0]
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+
+@remote_compatible
+def test_p2ps_nonexact_search_srvinfo(dev):
+ """P2PS nonexact seek request with service info"""
+ p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ ev_list = p2ps_nonexact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send*',
+ srv_info='2 GB')
+ adv_id = ev_list[0].split()[0]
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+
+@remote_compatible
+def test_p2ps_connect_p2ps_method_nonautoaccept(dev):
+ """P2PS connect for non-auto-accept and P2PS config method"""
+ p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ ev_list = p2ps_nonexact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send*',
+ srv_info='2 GB')
+ adv_id = ev_list[0].split()[0]
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id, auto_accept=False)
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_connect_p2ps_method_autoaccept(dev):
+ """P2PS connection with P2PS default config method and auto-accept"""
+ p2ps_advertise(r_dev=dev[0], r_role='1', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_connect_keypad_method_nonautoaccept(dev):
+ """P2PS Connection with non-auto-accept and seeker having keypad method"""
+ p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ ev_list = p2ps_nonexact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send*',
+ srv_info='2 GB')
+ adv_id = ev_list[0].split()[0]
+
+ ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, auto_accept=False, method="8")
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_connect_display_method_nonautoaccept(dev):
+ """P2PS connection with non-auto-accept and seeker having display method"""
+ p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ ev_list = p2ps_nonexact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds*', srv_info='2 GB')
+ adv_id = ev_list[0].split()[0]
+
+ ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, auto_accept=False, method="100")
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_connect_keypad_method_autoaccept(dev):
+ """P2PS connection with auto-accept and keypad method on seeker side"""
+ p2ps_advertise(r_dev=dev[0], r_role='1', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, method="8")
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_connect_display_method_autoaccept(dev):
+ """P2PS connection with auto-accept and display method on seeker side"""
+ p2ps_advertise(r_dev=dev[0], r_role='1', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, method="100")
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_connect_adv_go_p2ps_method(dev):
+ """P2PS auto-accept connection with advertisement as GO and P2PS method"""
+ p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_connect_adv_go_p2ps_method_group_iface(dev):
+ """P2PS auto-accept connection with advertisement as GO and P2PS method using separate group interface"""
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(dev[1], 0)
+ p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_connect_adv_client_p2ps_method(dev):
+ """P2PS auto-accept connection with advertisement as Client and P2PS method"""
+ p2ps_advertise(r_dev=dev[0], r_role='2', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+def p2ps_connect_adv_go_pin_method(dev, keep_group=False):
+ p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+ ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, method="8")
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+ if not keep_group:
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_connect_adv_go_pin_method(dev):
+ """P2PS advertiser as GO with keypad config method on seeker side and auto-accept"""
+ p2ps_connect_adv_go_pin_method(dev)
+
+@remote_compatible
+def test_p2ps_connect_adv_client_pin_method(dev):
+ """P2PS advertiser as client with keypad config method on seeker side and auto-accept"""
+ dev[0].flush_scan_cache()
+ p2ps_advertise(r_dev=dev[0], r_role='2', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id, method="8")
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1, pin)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], dev[1])
+
+def test_p2ps_service_discovery_multiple_queries(dev):
+ """P2P service discovery with multiple queries"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+ adv_id1 = p2ps_advertise(r_dev=dev[0], r_role='0',
+ svc_name='org.wi-fi.wfds.send.tx',
+ srv_info='I can transfer files upto size of 2 GB')
+ adv_id2 = p2ps_advertise(r_dev=dev[0], r_role='0',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size of 2 GB')
+ adv_id3 = p2ps_advertise(r_dev=dev[0], r_role='1',
+ svc_name='org.wi-fi.wfds.display.tx',
+ srv_info='Miracast Mode')
+ adv_id4 = p2ps_advertise(r_dev=dev[0], r_role='1',
+ svc_name='org.wi-fi.wfds.display.rx',
+ srv_info='Miracast Mode')
+
+ dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " asp 1 org.wi-fi.wfds.display.tx 'Miracast Mode'")
+ dev[1].global_request("P2P_FIND 10 type=social seek=org.wi-fi.wfds.display.tx")
+ dev[1].global_request("P2P_SERV_DISC_REQ " + addr0 + " asp 2 org.wi-fi.wfds.send* 'size of 2 GB'")
+ dev[1].p2p_stop_find()
+ dev[1].global_request("P2P_FIND 10 type=social seek=")
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("P2P Device Found timed out")
+ if addr0 not in ev:
+ raise Exception("Unexpected service discovery request source")
+ ev_list = []
+ for i in range(0, 3):
+ ev = dev[1].wait_global_event(["P2P-SERV-ASP-RESP"], timeout=10)
+ if ev is None:
+ raise Exception("P2P Service discovery timed out")
+ if addr0 in ev:
+ ev_list.append(ev)
+ if len(ev_list) == 3:
+ break
+ dev[1].p2p_stop_find()
+
+ for test in [("seek=org.wi-fi.wfds.display.TX",
+ "asp_svc=org.wi-fi.wfds.display.tx"),
+ ("seek=foo seek=org.wi-fi.wfds.display.tx seek=bar",
+ "asp_svc=org.wi-fi.wfds.display.tx"),
+ ("seek=1 seek=2 seek=3 seek=org.wi-fi.wfds.display.tx seek=4 seek=5 seek=6",
+ "asp_svc=org.wi-fi.wfds.display.tx"),
+ ("seek=not-found", None),
+ ("seek=org.wi-fi.wfds", "asp_svc=org.wi-fi.wfds")]:
+ dev[2].global_request("P2P_FIND 10 type=social " + test[0])
+ if test[1] is None:
+ ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected device found: " + ev)
+ continue
+ ev = dev[2].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("P2P device discovery timed out (dev2)")
+ if test[1] not in ev:
+ raise Exception("Expected asp_svc not reported: " + ev)
+ dev[2].p2p_stop_find()
+ dev[2].request("P2P_FLUSH")
+
+ dev[0].p2p_stop_find()
+
+ ev1 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id1))
+ if ev1 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ ev2 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id2))
+ if ev2 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ ev3 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id3))
+ if ev3 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ ev4 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id4))
+ if ev4 is None:
+ raise Exception("Unable to remove the advertisement instance")
+
+ if "OK" not in dev[0].global_request("P2P_SERVICE_ADD asp 1 12345678 1 1108 org.wi-fi.wfds.foobar svc_info='Test'"):
+ raise Exception("P2P_SERVICE_ADD failed")
+ if "OK" not in dev[0].global_request("P2P_SERVICE_DEL asp all"):
+ raise Exception("P2P_SERVICE_DEL asp all failed")
+ if "OK" not in dev[0].global_request("P2P_SERVICE_ADD asp 1 12345678 1 1108 org.wi-fi.wfds.foobar svc_info='Test'"):
+ raise Exception("P2P_SERVICE_ADD failed")
+ if "OK" not in dev[0].global_request("P2P_SERVICE_REP asp 1 12345678 1 1108 org.wi-fi.wfds.foobar svc_info='Test'"):
+ raise Exception("P2P_SERVICE_REP failed")
+ if "FAIL" not in dev[0].global_request("P2P_SERVICE_REP asp 1 12345678 1 1108 org.wi-fi.wfds.Foo svc_info='Test'"):
+ raise Exception("Invalid P2P_SERVICE_REP accepted")
+ if "OK" not in dev[0].global_request("P2P_SERVICE_ADD asp 1 a2345678 1 1108 org.wi-fi.wfds.something svc_info='Test'"):
+ raise Exception("P2P_SERVICE_ADD failed")
+ if "OK" not in dev[0].global_request("P2P_SERVICE_ADD asp 1 a2345679 1 1108 org.wi-fi.wfds.Foo svc_info='Test'"):
+ raise Exception("P2P_SERVICE_ADD failed")
+
+def get_ifnames():
+ with open('/proc/net/dev', 'r') as f:
+ data = f.read()
+ ifnames = []
+ for line in data.splitlines():
+ ifname = line.strip().split(' ')[0]
+ if ':' not in ifname:
+ continue
+ ifname = ifname.split(':')[0]
+ ifnames.append(ifname)
+ return ifnames
+
+def p2ps_connect_p2ps_method(dev, keep_group=False, join_extra="", flush=True):
+ if flush:
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ p2ps_advertise(r_dev=dev[0], r_role='2', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+ go_ev = None
+ if "join=" in ev0 and "go=" in ev1:
+ # dev[1] started GO and dev[0] is about to join it.
+ # Parse P2P-GROUP-STARTED from the GO to learn the operating frequency.
+ go_ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if go_ev is None:
+ raise Exception("P2P-GROUP-STARTED timeout on dev1")
+ res = dev[1].group_form_result(go_ev)
+ if join_extra == "":
+ join_extra = " freq=" + res['freq']
+
+ ifnames = get_ifnames()
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1, join_extra=join_extra,
+ go_ev=go_ev)
+
+ grp_ifname0 = dev[0].get_group_ifname()
+ grp_ifname1 = dev[1].get_group_ifname()
+ if not keep_group:
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ ifnames = ifnames + get_ifnames()
+ remove_group(dev[0], dev[1])
+ ifnames = ifnames + get_ifnames()
+
+ return grp_ifname0, grp_ifname1, ifnames
+
+def has_string_prefix(vals, prefix):
+ for val in vals:
+ if val.startswith(prefix):
+ return True
+ return False
+
+def test_p2ps_connect_p2ps_method_1(dev):
+ """P2PS connection with P2PS method - no group interface"""
+ set_no_group_iface(dev[0], 1)
+ set_no_group_iface(dev[1], 1)
+
+ (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(dev)
+ if grp_ifname0 != dev[0].ifname:
+ raise Exception("unexpected dev0 group ifname: " + grp_ifname0)
+ if grp_ifname1 != dev[1].ifname:
+ raise Exception("unexpected dev1 group ifname: " + grp_ifname1)
+ if has_string_prefix(ifnames, 'p2p-' + grp_ifname0):
+ raise Exception("dev0 group interface unexpectedly present")
+ if has_string_prefix(ifnames, 'p2p-' + grp_ifname1):
+ raise Exception("dev1 group interface unexpectedly present")
+
+def test_p2ps_connect_p2ps_method_2(dev):
+ """P2PS connection with P2PS method - group interface on dev0"""
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(dev[1], 1)
+
+ (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(dev)
+ if not grp_ifname0.startswith('p2p-' + dev[0].ifname + '-'):
+ raise Exception("unexpected dev0 group ifname: " + grp_ifname0)
+ if grp_ifname1 != dev[1].ifname:
+ raise Exception("unexpected dev1 group ifname: " + grp_ifname1)
+ if has_string_prefix(ifnames, 'p2p-' + grp_ifname0):
+ raise Exception("dev0 group interface unexpectedly present")
+
+def test_p2ps_connect_p2ps_method_3(dev):
+ """P2PS connection with P2PS method - group interface on dev1"""
+ set_no_group_iface(dev[0], 1)
+ set_no_group_iface(dev[1], 0)
+
+ (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(dev)
+ if grp_ifname0 != dev[0].ifname:
+ raise Exception("unexpected dev0 group ifname: " + grp_ifname0)
+ if not grp_ifname1.startswith('p2p-' + dev[1].ifname + '-'):
+ raise Exception("unexpected dev1 group ifname: " + grp_ifname1)
+ if has_string_prefix(ifnames, 'p2p-' + grp_ifname0):
+ raise Exception("dev0 group interface unexpectedly present")
+
+def test_p2ps_connect_p2ps_method_4(dev):
+ """P2PS connection with P2PS method - group interface on both"""
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(dev[1], 0)
+
+ (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(dev)
+ if not grp_ifname0.startswith('p2p-' + dev[0].ifname + '-'):
+ raise Exception("unexpected dev0 group ifname: " + grp_ifname0)
+ if not grp_ifname1.startswith('p2p-' + dev[1].ifname + '-'):
+ raise Exception("unexpected dev1 group ifname: " + grp_ifname1)
+
+def test_p2ps_connect_adv_go_persistent(dev):
+ """P2PS auto-accept connection with advertisement as GO and having persistent group"""
+ go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+ p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+ if "persist=" not in ev0 or "persist=" not in ev1:
+ raise Exception("Persistent group isn't used by peers")
+
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+ remove_group(dev[0], dev[1])
+
+def test_p2ps_stale_group_removal(dev):
+ """P2PS stale group removal"""
+ go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+ # Drop the first persistent group on dev[1] and form new persistent groups
+ # on both devices.
+ dev[1].p2pdev_request("FLUSH")
+ go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+ # The GO now has a stale persistent group as the first entry. Try to go
+ # through P2PS sequence to hit stale group removal.
+ if len(dev[0].list_networks(p2p=True)) != 2:
+ raise Exception("Unexpected number of networks on dev[0]")
+ if len(dev[1].list_networks(p2p=True)) != 1:
+ raise Exception("Unexpected number of networks on dev[1]")
+
+ p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+ if "persist=" not in ev0 or "persist=" not in ev1:
+ raise Exception("Persistent group isn't used by peers")
+
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+ remove_group(dev[0], dev[1])
+
+ if len(dev[0].list_networks(p2p=True)) != 1:
+ raise Exception("Unexpected number of networks on dev[0] (2)")
+ if len(dev[1].list_networks(p2p=True)) != 1:
+ raise Exception("Unexpected number of networks on dev[1] (2)")
+
+def test_p2ps_stale_group_removal2(dev):
+ """P2PS stale group removal (2)"""
+ go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=0,
+ r_dev=dev[1], r_intent=15)
+ dev[1].remove_group()
+ dev[0].wait_go_ending_session()
+
+ # Drop the first persistent group on dev[1] and form new persistent groups
+ # on both devices.
+ dev[1].p2pdev_request("FLUSH")
+ go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=0,
+ r_dev=dev[1], r_intent=15)
+ dev[1].remove_group()
+ dev[0].wait_go_ending_session()
+
+ # The P2P Client now has a stale persistent group as the first entry. Try
+ # to go through P2PS sequence to hit stale group removal.
+ if len(dev[0].list_networks(p2p=True)) != 2:
+ raise Exception("Unexpected number of networks on dev[0]")
+ if len(dev[1].list_networks(p2p=True)) != 1:
+ raise Exception("Unexpected number of networks on dev[1]")
+
+ p2ps_advertise(r_dev=dev[1], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[0], r_dev=dev[1],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+ ev0, ev1 = p2ps_provision(dev[0], dev[1], adv_id)
+ # This hits persistent group removal on dev[0] (P2P Client)
+
+def test_p2ps_stale_group_removal3(dev):
+ """P2PS stale group removal (3)"""
+ dev[0].p2p_start_go(persistent=True)
+ dev[0].remove_group()
+ if len(dev[0].list_networks(p2p=True)) != 1:
+ raise Exception("Unexpected number of networks on dev[0]")
+
+ go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+ # The GO now has a stale persistent group as the first entry. Try to go
+ # through P2PS sequence to hit stale group removal.
+ if len(dev[0].list_networks(p2p=True)) != 2:
+ raise Exception("Unexpected number of networks on dev[0] (2)")
+ if len(dev[1].list_networks(p2p=True)) != 1:
+ raise Exception("Unexpected number of networks on dev[1] (2)")
+
+ p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+ if "persist=" not in ev0 or "persist=" not in ev1:
+ raise Exception("Persistent group isn't used by peers")
+
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+ remove_group(dev[0], dev[1])
+
+ if len(dev[0].list_networks(p2p=True)) != 1:
+ raise Exception("Unexpected number of networks on dev[0] (3)")
+ if len(dev[1].list_networks(p2p=True)) != 1:
+ raise Exception("Unexpected number of networks on dev[1] (3)")
+
+@remote_compatible
+def test_p2ps_adv_go_persistent_no_peer_entry(dev):
+ """P2PS advertisement as GO having persistent group (no peer entry)"""
+ go_neg_pin_authorized_persistent(i_dev=dev[0], i_intent=15,
+ r_dev=dev[1], r_intent=0)
+ dev[0].remove_group()
+ dev[1].wait_go_ending_session()
+
+ p2ps_advertise(r_dev=dev[0], r_role='4', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+ dev[0].global_request("P2P_FLUSH")
+ dev[0].p2p_listen()
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+ if "persist=" not in ev0 or "persist=" not in ev1:
+ raise Exception("Persistent group isn't used by peers")
+
+@remote_compatible
+def test_p2ps_pd_follow_on_status_failure(dev):
+ """P2PS PD follow on request with status 11"""
+ addr0 = dev[0].p2p_dev_addr()
+ addr1 = dev[1].p2p_dev_addr()
+
+ p2ps_advertise(r_dev=dev[0], r_role='0', svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+ dev[1].asp_provision(addr0, adv_id=str(adv_id), adv_mac=addr0,
+ session_id=1, session_mac=addr1)
+ ev_pd_start = dev[0].wait_global_event(["P2PS-PROV-START"], timeout=10)
+ if ev_pd_start is None:
+ raise Exception("P2PS-PROV-START timeout on Advertiser side")
+ ev = dev[1].wait_global_event(["P2P-PROV-DISC-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DISC-FAILURE timeout on seeker side")
+ dev[1].p2p_ext_listen(500, 500)
+ dev[0].p2p_stop_find()
+ dev[0].asp_provision(addr1, adv_id=str(adv_id), adv_mac=addr0, session_id=1,
+ session_mac=addr1, status=11, method=0)
+
+ ev = dev[1].wait_global_event(["P2PS-PROV-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DONE timeout on seeker side")
+ if adv_id not in ev:
+ raise Exception("P2P-PROV-DONE without adv_id on seeker side")
+ if "status=11" not in ev:
+ raise Exception("P2P-PROV-DONE without status on seeker side")
+
+ ev = dev[0].wait_global_event(["P2PS-PROV-DONE"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-PROV-DONE timeout on advertiser side")
+ if adv_id not in ev:
+ raise Exception("P2P-PROV-DONE without adv_id on advertiser side")
+ if "status=11" not in ev:
+ raise Exception("P2P-PROV-DONE without status on advertiser side")
+
+def test_p2ps_client_probe(dev):
+ """P2PS CLI discoverability on operating channel"""
+ cli_probe = dev[0].global_request("SET p2p_cli_probe 1")
+ p2ps_connect_p2ps_method(dev, keep_group=True)
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[2], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ single_peer_expected=False)
+ dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ remove_group(dev[0], dev[1])
+
+def test_p2ps_go_probe(dev):
+ """P2PS GO discoverability on operating channel"""
+ p2ps_connect_adv_go_pin_method(dev, keep_group=True)
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[2], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ single_peer_expected=False)
+ dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_wildcard_p2ps(dev):
+ """P2PS wildcard SD Probe Request/Response"""
+ p2ps_wildcard = "org.wi-fi.wfds"
+
+ adv_id = p2ps_advertise(r_dev=dev[0], r_role='1',
+ svc_name='org.foo.service',
+ srv_info='I can do stuff')
+ adv_id2 = p2ps_advertise(r_dev=dev[0], r_role='1',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+
+ if "OK" not in dev[1].global_request("P2P_FIND 10 type=social seek=org.foo.service seek=" + p2ps_wildcard):
+ raise Exception("Failed on P2P_FIND command")
+
+ ev1 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev1 is None:
+ raise Exception("P2P-DEVICE-FOUND timeout on seeker side")
+ if dev[0].p2p_dev_addr() not in ev1:
+ raise Exception("Unexpected peer")
+
+ ev2 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev2 is None:
+ raise Exception("P2P-DEVICE-FOUND timeout on seeker side (2)")
+ if dev[0].p2p_dev_addr() not in ev2:
+ raise Exception("Unexpected peer (2)")
+
+ if p2ps_wildcard not in ev1 + ev2:
+ raise Exception("P2PS Wildcard name not found in P2P-DEVICE-FOUND event")
+ if "org.foo.service" not in ev1 + ev2:
+ raise Exception("Vendor specific service name not found in P2P-DEVICE-FOUND event")
+
+ if "OK" not in dev[1].global_request("P2P_STOP_FIND"):
+ raise Exception("P2P_STOP_FIND failed")
+ dev[1].dump_monitor()
+
+ res = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if res is None:
+ raise Exception("Unable to remove the advertisement instance")
+
+ if "OK" not in dev[1].global_request("P2P_FIND 10 type=social seek=" + p2ps_wildcard):
+ raise Exception("Failed on P2P_FIND command")
+
+ ev1 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev1 is None:
+ raise Exception("P2P-DEVICE-FOUND timeout on seeker side")
+ if dev[0].p2p_dev_addr() not in ev1:
+ raise Exception("Unexpected peer")
+ if p2ps_wildcard not in ev1:
+ raise Exception("P2PS Wildcard name not found in P2P-DEVICE-FOUND event (2)")
+ dev[1].dump_monitor()
+
+ res = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id2))
+ if res is None:
+ raise Exception("Unable to remove the advertisement instance 2")
+
+ dev[1].p2p_stop_find()
+ time.sleep(0.1)
+ if "OK" not in dev[1].global_request("P2P_FIND 10 type=social seek=" + p2ps_wildcard):
+ raise Exception("Failed on P2P_FIND command")
+
+ ev1 = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=2)
+ if ev1 is not None:
+ raise Exception("Unexpected P2P-DEVICE-FOUND event on seeker side")
+ dev[1].p2p_stop_find()
+ dev[1].dump_monitor()
+
+def test_p2ps_many_services_in_probe(dev):
+ """P2PS with large number of services in Probe Request/Response"""
+ long1 = 'org.example.0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.a'
+ long2 = 'org.example.0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.b'
+ long3 = 'org.example.0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.c'
+ long4 = 'org.example.0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.d'
+ long5 = 'org.example.0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789.e'
+ for name in [long1, long2, long3, long4, long5]:
+ p2ps_advertise(r_dev=dev[0], r_role='1',
+ svc_name=name,
+ srv_info='I can do stuff')
+
+ if "OK" not in dev[1].global_request("P2P_FIND 10 type=social seek=%s seek=%s seek=%s seek=%s seek=%s" % (long1, long2, long3, long4, long5)):
+ raise Exception("Failed on P2P_FIND command")
+
+ events = ""
+ # Note: Require only four events since all the services do not fit within
+ # the length limit.
+ for i in range(4):
+ ev = dev[1].wait_global_event(["P2P-DEVICE-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("Missing P2P-DEVICE-FOUND")
+ events = events + ev
+ dev[1].p2p_stop_find()
+ dev[1].dump_monitor()
+ for name in [long2, long3, long4, long5]:
+ if name not in events:
+ raise Exception("Service missing from peer events")
+
+def p2ps_test_feature_capability_cpt(dev, adv_cpt, seeker_cpt, adv_role,
+ result):
+ p2ps_advertise(r_dev=dev[0], r_role=adv_role,
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB', cpt=adv_cpt)
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+ auto_accept = adv_role != "0"
+ ev1, ev0, pin = p2ps_provision(dev[1], dev[0], adv_id,
+ auto_accept=auto_accept, adv_cpt=adv_cpt,
+ seeker_cpt=seeker_cpt, method="8")
+
+ status0, fcap0 = p2ps_parse_event(ev0, "status", "feature_cap")
+ status1, fcap1 = p2ps_parse_event(ev0, "status", "feature_cap")
+
+ if fcap0 is None:
+ raise Exception("Bad feature capability on Seeker side")
+ if fcap1 is None:
+ raise Exception("Bad feature capability on Advertiser side")
+ if fcap0 != fcap1:
+ raise Exception("Incompatible feature capability values")
+
+ if status0 not in ("0", "12") or status1 not in ("0", "12"):
+ raise Exception("Unexpected PD result status")
+
+ if result == "UDP" and fcap0[1] != "1":
+ raise Exception("Unexpected CPT feature capability value (expected: UDP)")
+ elif result == "MAC" and fcap0[1] != "2":
+ raise Exception("Unexpected CPT feature capability value (expected: MAC)")
+
+ ev = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev is None:
+ raise Exception("Unable to remove the advertisement instance")
+
+@remote_compatible
+def test_p2ps_feature_capability_mac_autoaccept(dev):
+ """P2PS PD Feature Capability CPT: advertiser MAC, seeker UDP:MAC, autoaccept"""
+ p2ps_test_feature_capability_cpt(dev, adv_cpt="MAC", seeker_cpt="UDP:MAC",
+ adv_role="4", result="MAC")
+
+@remote_compatible
+def test_p2ps_feature_capability_mac_nonautoaccept(dev):
+ """P2PS PD Feature Capability CPT: advertiser:MAC, seeker UDP:MAC, nonautoaccept"""
+ p2ps_test_feature_capability_cpt(dev, adv_cpt="MAC", seeker_cpt="UDP:MAC",
+ adv_role="0", result="MAC")
+
+@remote_compatible
+def test_p2ps_feature_capability_mac_udp_autoaccept(dev):
+ """P2PS PD Feature Capability CPT: advertiser MAC:UDP, seeker UDP:MAC, autoaccept"""
+ p2ps_test_feature_capability_cpt(dev, adv_cpt="MAC:UDP",
+ seeker_cpt="UDP:MAC", adv_role="2",
+ result="MAC")
+
+@remote_compatible
+def test_p2ps_feature_capability_mac_udp_nonautoaccept(dev):
+ """P2PS PD Feature Capability CPT: advertiser MAC:UDP, seeker UDP:MAC, nonautoaccept"""
+ p2ps_test_feature_capability_cpt(dev, adv_cpt="MAC:UDP",
+ seeker_cpt="UDP:MAC", adv_role="0",
+ result="UDP")
+
+@remote_compatible
+def test_p2ps_feature_capability_udp_mac_autoaccept(dev):
+ """P2PS PD Feature Capability CPT: advertiser UDP:MAC, seeker MAC:UDP, autoaccept"""
+ p2ps_test_feature_capability_cpt(dev, adv_cpt="UDP:MAC",
+ seeker_cpt="MAC:UDP", adv_role="2",
+ result="UDP")
+
+@remote_compatible
+def test_p2ps_feature_capability_udp_mac_nonautoaccept(dev):
+ """P2PS PD Feature Capability CPT: advertiser UDP:MAC, seeker MAC:UDP, nonautoaccept"""
+ p2ps_test_feature_capability_cpt(dev, adv_cpt="UDP:MAC",
+ seeker_cpt="MAC:UDP", adv_role="0",
+ result="MAC")
+
+def test_p2ps_channel_one_connected(dev, apdev):
+ """P2PS connection with P2PS method - one of the stations connected"""
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(dev[1], 0)
+
+ try:
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-2.4ghz', "channel": '7'})
+ dev[1].connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2442")
+
+ (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(dev, keep_group=True, join_extra=" freq=2442")
+ freq = dev[0].get_group_status_field('freq')
+
+ if freq != '2442':
+ raise Exception('Unexpected frequency for group 2442 != ' + freq)
+ finally:
+ dev[0].global_request("P2P_SERVICE_DEL asp all")
+ remove_group(dev[0], dev[1])
+
+def set_random_listen_chan(dev):
+ chan = random.randrange(0, 3) * 5 + 1
+ dev.global_request("P2P_SET listen_channel %d" % chan)
+
+def test_p2ps_channel_both_connected_same(dev, apdev):
+ """P2PS connection with P2PS method - stations connected on same channel"""
+ set_no_group_iface(dev[2], 0)
+ set_no_group_iface(dev[1], 0)
+
+ dev[2].global_request("P2P_SET listen_channel 6")
+ dev[1].global_request("P2P_SET listen_channel 6")
+
+ dev[1].flush_scan_cache()
+ dev[2].flush_scan_cache()
+
+ try:
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-2.4ghz', "channel": '6'})
+
+ dev[2].connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2437")
+ dev[1].connect("bss-2.4ghz", key_mgmt="NONE", scan_freq="2437")
+
+ tmpdev = [dev[2], dev[1]]
+ (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(tmpdev, keep_group=True, join_extra=" freq=2437", flush=False)
+ freq = dev[2].get_group_status_field('freq')
+
+ if freq != '2437':
+ raise Exception('Unexpected frequency for group 2437 != ' + freq)
+ finally:
+ dev[2].global_request("P2P_SERVICE_DEL asp all")
+ for i in range(1, 3):
+ set_random_listen_chan(dev[i])
+ remove_group(dev[2], dev[1])
+
+def disconnect_handler(seeker, advertiser):
+ advertiser.request("DISCONNECT")
+ advertiser.wait_disconnected(timeout=1)
+
+def test_p2ps_channel_both_connected_different(dev, apdev):
+ """P2PS connection with P2PS method - stations connected on different channel"""
+ if dev[0].get_mcc() > 1:
+ raise HwsimSkip('Skip due to MCC being enabled')
+
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(dev[1], 0)
+
+ try:
+ hapd1 = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-channel-3', "channel": '3'})
+
+ hapd2 = hostapd.add_ap(apdev[1],
+ {"ssid": 'bss-channel-10', "channel": '10'})
+
+ dev[0].connect("bss-channel-3", key_mgmt="NONE", scan_freq="2422")
+ dev[1].connect("bss-channel-10", key_mgmt="NONE", scan_freq="2457")
+
+ p2ps_advertise(r_dev=dev[0], r_role='2',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id, auto_accept=False,
+ handler=disconnect_handler)
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+ freq = dev[0].get_group_status_field('freq')
+ if freq != '2457':
+ raise Exception('Unexpected frequency for group 2457 != ' + freq)
+ finally:
+ dev[0].global_request("P2P_SERVICE_DEL asp all")
+ remove_group(dev[0], dev[1])
+
+def test_p2ps_channel_both_connected_different_mcc(dev, apdev):
+ """P2PS connection with P2PS method - stations connected on different channels with mcc"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ set_no_group_iface(wpas, 0)
+ set_no_group_iface(dev[1], 0)
+
+ try:
+ hapd1 = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-channel-3', "channel": '3'})
+
+ hapd2 = hostapd.add_ap(apdev[1],
+ {"ssid": 'bss-channel-10', "channel": '10'})
+
+ wpas.connect("bss-channel-3", key_mgmt="NONE", scan_freq="2422")
+ dev[1].connect("bss-channel-10", key_mgmt="NONE", scan_freq="2457")
+
+ (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method([wpas, dev[1]], keep_group=True)
+ freq = wpas.get_group_status_field('freq')
+
+ if freq != '2422' and freq != '2457':
+ raise Exception('Unexpected frequency for group =' + freq)
+ finally:
+ wpas.global_request("P2P_SERVICE_DEL asp all")
+ remove_group(wpas, dev[1])
+
+def clear_disallow_handler(seeker, advertiser):
+ advertiser.global_request("P2P_SET disallow_freq ")
+
+@remote_compatible
+def test_p2ps_channel_disallow_freq(dev, apdev):
+ """P2PS connection with P2PS method - disallow freqs"""
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(dev[1], 0)
+
+ try:
+ dev[0].global_request("P2P_SET disallow_freq 2412-2457")
+ dev[1].global_request("P2P_SET disallow_freq 2417-2462")
+
+ p2ps_advertise(r_dev=dev[0], r_role='2',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id, auto_accept=False,
+ handler=clear_disallow_handler)
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+ freq = dev[0].get_group_status_field('freq')
+ if freq != '2412':
+ raise Exception('Unexpected frequency for group 2412 != ' + freq)
+ finally:
+ dev[0].global_request("P2P_SERVICE_DEL asp all")
+ dev[0].global_request("P2P_SET disallow_freq ")
+ dev[1].global_request("P2P_SET disallow_freq ")
+ remove_group(dev[0], dev[1])
+
+def test_p2ps_channel_sta_connected_disallow_freq(dev, apdev):
+ """P2PS connection with P2PS method - one station and disallow freqs"""
+ if dev[0].get_mcc() > 1:
+ raise HwsimSkip('Skip due to MCC being enabled')
+
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(dev[1], 0)
+
+ try:
+ dev[0].global_request("P2P_SET disallow_freq 2437")
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-channel-6', "channel": '6'})
+
+ dev[1].connect("bss-channel-6", key_mgmt="NONE", scan_freq="2437")
+
+ p2ps_advertise(r_dev=dev[0], r_role='2',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id, auto_accept=False,
+ handler=clear_disallow_handler)
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+
+ freq = dev[0].get_group_status_field('freq')
+ if freq != '2437':
+ raise Exception('Unexpected frequency for group 2437 != ' + freq)
+ finally:
+ dev[0].global_request("P2P_SET disallow_freq ")
+ dev[0].global_request("P2P_SERVICE_DEL asp all")
+ remove_group(dev[0], dev[1])
+
+def test_p2ps_channel_sta_connected_disallow_freq_mcc(dev, apdev):
+ """P2PS connection with P2PS method - one station and disallow freqs with mcc"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(wpas, 0)
+
+ try:
+ dev[0].global_request("P2P_SET disallow_freq 2437")
+ hapd1 = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-channel-6', "channel": '6'})
+
+ wpas.connect("bss-channel-6", key_mgmt="NONE", scan_freq="2437")
+
+ tmpdev = [dev[0], wpas]
+ (grp_ifname0, grp_ifname1, ifnames) = p2ps_connect_p2ps_method(tmpdev, keep_group=True)
+
+ freq = dev[0].get_group_status_field('freq')
+ if freq == '2437':
+ raise Exception('Unexpected frequency=2437')
+ finally:
+ dev[0].global_request("P2P_SET disallow_freq ")
+ dev[0].global_request("P2P_SERVICE_DEL asp all")
+ remove_group(dev[0], wpas)
+
+@remote_compatible
+def test_p2ps_active_go_adv(dev, apdev):
+ """P2PS connection with P2PS method - active GO on advertiser"""
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(dev[1], 0)
+
+ try:
+ # Add a P2P GO
+ dev[0].global_request("P2P_GROUP_ADD persistent")
+ ev = dev[0].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev[0].p2p_dev_addr())
+
+ dev[0].group_form_result(ev)
+
+ p2ps_advertise(r_dev=dev[0], r_role='4',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ single_peer_expected=False)
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+
+ # explicitly stop find/listen as otherwise the long listen started by
+ # the advertiser would prevent the seeker to connect with the P2P GO
+ dev[0].p2p_stop_find()
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+ finally:
+ dev[0].global_request("P2P_SERVICE_DEL asp all")
+ remove_group(dev[0], dev[1])
+
+@remote_compatible
+def test_p2ps_active_go_seeker(dev, apdev):
+ """P2PS connection with P2PS method - active GO on seeker"""
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(dev[1], 0)
+
+ try:
+ # Add a P2P GO on the seeker
+ dev[1].global_request("P2P_GROUP_ADD persistent")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev[1].p2p_dev_addr())
+
+ res = dev[1].group_form_result(ev)
+
+ p2ps_advertise(r_dev=dev[0], r_role='2',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id)
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1,
+ join_extra=" freq=" + res['freq'])
+ finally:
+ dev[0].global_request("P2P_SERVICE_DEL asp all")
+ remove_group(dev[0], dev[1])
+
+def test_p2ps_channel_active_go_and_station_same(dev, apdev):
+ """P2PS connection, active P2P GO and station on channel"""
+ set_no_group_iface(dev[2], 0)
+ set_no_group_iface(dev[1], 0)
+
+ dev[2].global_request("P2P_SET listen_channel 11")
+ dev[1].global_request("P2P_SET listen_channel 11")
+ try:
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-channel-11', "channel": '11'})
+
+ dev[2].connect("bss-channel-11", key_mgmt="NONE", scan_freq="2462")
+
+ # Add a P2P GO on the seeker
+ dev[1].global_request("P2P_GROUP_ADD freq=2462 persistent")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev[1].p2p_dev_addr())
+
+ dev[1].group_form_result(ev)
+
+ p2ps_advertise(r_dev=dev[2], r_role='2',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[2],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[2], adv_id)
+ p2ps_connect_pd(dev[2], dev[1], ev0, ev1, join_extra=" freq=2462")
+ finally:
+ dev[2].global_request("P2P_SERVICE_DEL asp all")
+ for i in range(1, 3):
+ set_random_listen_chan(dev[i])
+ remove_group(dev[2], dev[1])
+
+def test_p2ps_channel_active_go_and_station_different(dev, apdev):
+ """P2PS connection, active P2P GO and station on channel"""
+ if dev[0].get_mcc() > 1:
+ raise HwsimSkip('Skip due to MCC being enabled')
+
+ set_no_group_iface(dev[0], 0)
+ set_no_group_iface(dev[1], 0)
+
+ try:
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-channel-2', "channel": '2'})
+
+ dev[0].connect("bss-channel-2", key_mgmt="NONE", scan_freq="2417")
+
+ # Add a P2P GO on the seeker. Force the listen channel to be the same,
+ # as extended listen will not kick as long as P2P GO is waiting for
+ # initial connection.
+ dev[1].global_request("P2P_SET listen_channel 11")
+ dev[1].global_request("P2P_GROUP_ADD freq=2462 persistent")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev[1].p2p_dev_addr())
+
+ dev[1].group_form_result(ev)
+
+ p2ps_advertise(r_dev=dev[0], r_role='2',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], dev[0], adv_id, auto_accept=False,
+ handler=disconnect_handler, adv_role='2',
+ seeker_role='4')
+ p2ps_connect_pd(dev[0], dev[1], ev0, ev1)
+ freq = dev[0].get_group_status_field('freq')
+ if freq != '2462':
+ raise Exception('Unexpected frequency for group 2462!=' + freq)
+ finally:
+ dev[0].global_request("P2P_SERVICE_DEL asp all")
+ set_random_listen_chan(dev[1])
+
+@remote_compatible
+def test_p2ps_channel_active_go_and_station_different_mcc(dev, apdev):
+ """P2PS connection, active P2P GO and station on channel"""
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ set_no_group_iface(wpas, 0)
+ set_no_group_iface(dev[1], 0)
+
+ try:
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": 'bss-channel-6', "channel": '6'})
+
+ wpas.global_request("P2P_SET listen_channel 1")
+ wpas.connect("bss-channel-6", key_mgmt="NONE", scan_freq="2437")
+
+ # Add a P2P GO on the seeker
+ dev[1].global_request("P2P_SET listen_channel 1")
+ dev[1].global_request("P2P_GROUP_ADD freq=2462 persistent")
+ ev = dev[1].wait_global_event(["P2P-GROUP-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("P2P-GROUP-STARTED timeout on " + dev[1].p2p_dev_addr())
+
+ dev[1].group_form_result(ev)
+
+ p2ps_advertise(r_dev=wpas, r_role='2',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[1], r_dev=wpas,
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[1], wpas, adv_id)
+ p2ps_connect_pd(wpas, dev[1], ev0, ev1)
+ finally:
+ set_random_listen_chan(dev[1])
+ set_random_listen_chan(wpas)
+ wpas.request("DISCONNECT")
+ hapd.disable()
+ wpas.global_request("P2P_SERVICE_DEL asp all")
+ remove_group(wpas, dev[1], allow_failure=True)
+
+def test_p2ps_connect_p2p_device(dev):
+ """P2PS connection using cfg80211 P2P Device"""
+ run_p2ps_connect_p2p_device(dev, 0)
+
+def test_p2ps_connect_p2p_device_no_group_iface(dev):
+ """P2PS connection using cfg80211 P2P Device (no separate group interface)"""
+ run_p2ps_connect_p2p_device(dev, 1)
+
+def run_p2ps_connect_p2p_device(dev, no_group_iface):
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface %d" % no_group_iface)
+
+ p2ps_advertise(r_dev=dev[0], r_role='1',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=wpas, r_dev=dev[0],
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(wpas, dev[0], adv_id)
+ p2ps_connect_pd(dev[0], wpas, ev0, ev1)
+
+ ev0 = dev[0].global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(dev[0], wpas)
+
+def test_p2ps_connect_p2p_device2(dev):
+ """P2PS connection using cfg80211 P2P Device (reverse)"""
+ run_p2ps_connect_p2p_device2(dev, 0)
+
+def test_p2ps_connect_p2p_device2_no_group_iface(dev):
+ """P2PS connection using cfg80211 P2P Device (reverse) (no separate group interface)"""
+ run_p2ps_connect_p2p_device2(dev, 1)
+
+def run_p2ps_connect_p2p_device2(dev, no_group_iface):
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ wpas.global_request("SET p2p_no_group_iface %d" % no_group_iface)
+
+ p2ps_advertise(r_dev=wpas, r_role='1',
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='I can receive files upto size 2 GB')
+ [adv_id, rcvd_svc_name] = p2ps_exact_seek(i_dev=dev[0], r_dev=wpas,
+ svc_name='org.wi-fi.wfds.send.rx',
+ srv_info='2 GB')
+
+ ev1, ev0 = p2ps_provision(dev[0], wpas, adv_id)
+ p2ps_connect_pd(wpas, dev[0], ev0, ev1)
+
+ ev0 = wpas.global_request("P2P_SERVICE_DEL asp " + str(adv_id))
+ if ev0 is None:
+ raise Exception("Unable to remove the advertisement instance")
+ remove_group(wpas, dev[0])
+
+@remote_compatible
+def test_p2ps_connect_p2ps_method_no_pin(dev):
+ """P2P group formation using P2PS method without specifying PIN"""
+ dev[0].p2p_listen()
+ dev[1].p2p_go_neg_auth(dev[0].p2p_dev_addr(), None, "p2ps", go_intent=15)
+ dev[1].p2p_listen()
+ i_res = dev[0].p2p_go_neg_init(dev[1].p2p_dev_addr(), None, "p2ps",
+ timeout=20, go_intent=0)
+ r_res = dev[1].p2p_go_neg_auth_result()
+ logger.debug("i_res: " + str(i_res))
+ logger.debug("r_res: " + str(r_res))
+ check_grpform_results(i_res, r_res)
+ remove_group(dev[0], dev[1])
diff --git a/contrib/wpa/tests/hwsim/test_pasn.py b/contrib/wpa/tests/hwsim/test_pasn.py
new file mode 100644
index 000000000000..c8bcd63f6ac7
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_pasn.py
@@ -0,0 +1,850 @@
+# Test cases for PASN
+# Copyright (C) 2019 Intel Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import os
+import time
+import logging
+logger = logging.getLogger()
+import socket
+import struct
+import subprocess
+import re
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+from hwsim import HWSimRadio
+from test_erp import start_erp_as
+from test_ap_ft import run_roams, ft_params1, ft_params2
+
+def check_pasn_capab(dev):
+ if "PASN" not in dev.get_capability("auth_alg"):
+ raise HwsimSkip("PASN not supported")
+
+def pasn_ap_params(akmp="PASN", cipher="CCMP", group="19"):
+ params = {"ssid": "test-wpa2-pasn",
+ "wpa_passphrase": "12345678",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "WPA-PSK " + akmp,
+ "rsn_pairwise": cipher,
+ "pasn_groups" : group}
+
+ return params
+
+def start_pasn_ap(apdev, params):
+ try:
+ return hostapd.add_ap(apdev, params)
+ except Exception as e:
+ if "Failed to set hostapd parameter wpa_key_mgmt" in str(e) or \
+ "Failed to set hostapd parameter force_kdk_derivation" in str(e):
+ raise HwsimSkip("PASN not supported")
+ raise
+
+def check_pasn_ptk(dev, hapd, cipher, fail_ptk=False, clear_keys=True):
+ sta_ptksa = dev.get_ptksa(hapd.own_addr(), cipher)
+ ap_ptksa = hapd.get_ptksa(dev.own_addr(), cipher)
+
+ if not (sta_ptksa and ap_ptksa):
+ if fail_ptk:
+ return
+ raise Exception("Could not get PTKSA entry")
+
+ logger.info("sta: TK: %s KDK: %s" % (sta_ptksa['tk'], sta_ptksa['kdk']))
+ logger.info("ap : TK: %s KDK: %s" % (ap_ptksa['tk'], ap_ptksa['kdk']))
+
+ if sta_ptksa['tk'] != ap_ptksa['tk'] or sta_ptksa['kdk'] != ap_ptksa['kdk']:
+ raise Exception("TK/KDK mismatch")
+ elif fail_ptk:
+ raise Exception("TK/KDK match although key derivation should have failed")
+ elif clear_keys:
+ cmd = "PASN_DEAUTH bssid=%s" % hapd.own_addr()
+ dev.request(cmd)
+
+ # Wait a little to let the AP process the deauth
+ time.sleep(0.2)
+
+ sta_ptksa = dev.get_ptksa(hapd.own_addr(), cipher)
+ ap_ptksa = hapd.get_ptksa(dev.own_addr(), cipher)
+ if sta_ptksa or ap_ptksa:
+ raise Exception("TK/KDK not deleted as expected")
+
+def check_pasn_akmp_cipher(dev, hapd, akmp="PASN", cipher="CCMP",
+ group="19", status=0, fail=0, nid="",
+ fail_ptk=False):
+ dev.flush_scan_cache()
+ dev.scan(type="ONLY", freq=2412)
+
+ cmd = "PASN_START bssid=%s akmp=%s cipher=%s group=%s" % (hapd.own_addr(), akmp, cipher, group)
+
+ if nid != "":
+ cmd += " nid=%s" % nid
+
+ resp = dev.request(cmd)
+
+ if fail:
+ if "OK" in resp:
+ raise Exception("Unexpected success to start PASN authentication")
+ return
+
+ if "OK" not in resp:
+ raise Exception("Failed to start PASN authentication")
+
+ ev = dev.wait_event(["PASN-AUTH-STATUS"], 3)
+ if not ev:
+ raise Exception("PASN: PASN-AUTH-STATUS not seen")
+
+ if hapd.own_addr() + " akmp=" + akmp + ", status=" + str(status) not in ev:
+ raise Exception("PASN: unexpected status")
+
+ if status:
+ return
+
+ check_pasn_ptk(dev, hapd, cipher, fail_ptk)
+
+@remote_compatible
+def test_pasn_ccmp(dev, apdev):
+ """PASN authentication with WPA2/CCMP AP"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "CCMP", "19")
+ hapd = start_pasn_ap(apdev[0], params)
+
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP")
+
+@remote_compatible
+def test_pasn_gcmp(dev, apdev):
+ """PASN authentication with WPA2/GCMP AP"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "GCMP", "19")
+ hapd = start_pasn_ap(apdev[0], params)
+
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "GCMP")
+
+@remote_compatible
+def test_pasn_ccmp_256(dev, apdev):
+ """PASN authentication with WPA2/CCMP256 AP"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "CCMP-256", "19")
+ hapd = start_pasn_ap(apdev[0], params)
+
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP-256")
+
+@remote_compatible
+def test_pasn_gcmp_256(dev, apdev):
+ """PASN authentication with WPA2/GCMP-256 AP"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "GCMP-256", "19")
+ hapd = start_pasn_ap(apdev[0], params)
+
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "GCMP-256")
+
+@remote_compatible
+def test_pasn_group_mismatch(dev, apdev):
+ """PASN authentication with WPA2/CCMP AP with group mismatch"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "CCMP", "20")
+ hapd = start_pasn_ap(apdev[0], params)
+
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", status=77)
+
+@remote_compatible
+def test_pasn_channel_mismatch(dev, apdev):
+ """PASN authentication with WPA2/CCMP AP with channel mismatch"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "CCMP")
+ params['channel'] = "6"
+ hapd = start_pasn_ap(apdev[0], params)
+
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail=1)
+
+@remote_compatible
+def test_pasn_while_connected_same_channel(dev, apdev):
+ """PASN authentication with WPA2/CCMP AP while connected same channel"""
+ check_pasn_capab(dev[0])
+
+ ssid = "test-wpa2-psk"
+ psk = '602e323e077bc63bd80307ef4745b754b0ae0a925c2638ecd13a794b9527b9e6'
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_psk'] = psk
+ hapd = start_pasn_ap(apdev[0], params)
+
+ dev[0].connect(ssid, raw_psk=psk, scan_freq="2412")
+
+ params = pasn_ap_params("PASN", "CCMP")
+ hapd = start_pasn_ap(apdev[1], params)
+
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP")
+
+@remote_compatible
+def test_pasn_while_connected_same_ap(dev, apdev):
+ """PASN authentication with WPA2/CCMP AP while connected to it"""
+ check_pasn_capab(dev[0])
+
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk",
+ passphrase="12345678")
+ hapd = start_pasn_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail=1)
+
+@remote_compatible
+def test_pasn_while_connected_diff_channel(dev, apdev):
+ """PASN authentication with WPA2/CCMP AP while connected diff channel"""
+ check_pasn_capab(dev[0])
+
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise HwsimSkip("PASN: New radio does not support MCC")
+
+ params = hostapd.wpa2_params(ssid="test-wpa2-psk",
+ passphrase="12345678")
+ params['channel'] = "6"
+ hapd = start_pasn_ap(apdev[0], params)
+ wpas.connect("test-wpa2-psk", psk="12345678", scan_freq="2437")
+
+ params = pasn_ap_params("PASN", "CCMP")
+ hapd2 = start_pasn_ap(apdev[1], params)
+
+ check_pasn_akmp_cipher(wpas, hapd2, "PASN", "CCMP")
+
+@remote_compatible
+def test_pasn_sae_pmksa_cache(dev, apdev):
+ """PASN authentication with SAE AP with PMKSA caching"""
+ check_pasn_capab(dev[0])
+ check_sae_capab(dev[0])
+
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['sae_pwe'] = "2"
+ hapd = start_pasn_ap(apdev[0], params)
+
+ try:
+ dev[0].set("sae_groups", "19")
+ dev[0].set("sae_pwe", "2")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412")
+
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP")
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def check_pasn_fils_pmksa_cache(dev, apdev, params, key_mgmt):
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+ check_pasn_capab(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = key_mgmt + " PASN"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ hapd = start_pasn_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+
+ id = dev[0].connect("fils", key_mgmt=key_mgmt,
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP")
+
+@remote_compatible
+def test_pasn_fils_sha256_pmksa_cache(dev, apdev, params):
+ """PASN authentication with FILS-SHA256 with PMKSA caching"""
+ check_pasn_fils_pmksa_cache(dev, apdev, params, "FILS-SHA256")
+
+@remote_compatible
+def test_pasn_fils_sha384_pmksa_cache(dev, apdev, params):
+ """PASN authentication with FILS-SHA384 with PMKSA caching"""
+ check_pasn_fils_pmksa_cache(dev, apdev, params, "FILS-SHA384")
+
+@remote_compatible
+def test_pasn_sae_kdk(dev, apdev):
+ """Station authentication with SAE AP with KDK derivation during connection"""
+ check_pasn_capab(dev[0])
+ check_sae_capab(dev[0])
+
+ try:
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['sae_pwe'] = "2"
+ params['force_kdk_derivation'] = "1"
+ hapd = start_pasn_ap(apdev[0], params)
+
+ dev[0].set("force_kdk_derivation", "1")
+ dev[0].set("sae_pwe", "2")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+
+ check_pasn_ptk(dev[0], hapd, "CCMP", clear_keys=False)
+ finally:
+ dev[0].set("force_kdk_derivation", "0")
+ dev[0].set("sae_pwe", "0")
+
+
+def check_pasn_fils_kdk(dev, apdev, params, key_mgmt):
+ check_fils_capa(dev[0])
+ check_erp_capa(dev[0])
+ check_pasn_capab(dev[0])
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ try:
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = key_mgmt
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ params['force_kdk_derivation'] = "1"
+ hapd = start_pasn_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].request("ERP_FLUSH")
+ dev[0].set("force_kdk_derivation", "1")
+
+ id = dev[0].connect("fils", key_mgmt=key_mgmt,
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ check_pasn_ptk(dev[0], hapd, "CCMP", clear_keys=False)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].dump_monitor()
+ dev[0].select_network(id, freq=2412)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "EVENT-ASSOC-REJECT",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection using FILS/ERP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if "EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Association failed")
+
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ check_pasn_ptk(dev[0], hapd, "CCMP", clear_keys=False)
+ finally:
+ dev[0].set("force_kdk_derivation", "0")
+
+@remote_compatible
+def test_pasn_fils_sha256_kdk(dev, apdev, params):
+ """Station authentication with FILS-SHA256 with KDK derivation during connection"""
+ check_pasn_fils_kdk(dev, apdev, params, "FILS-SHA256")
+
+@remote_compatible
+def test_pasn_fils_sha384_kdk(dev, apdev, params):
+ """Station authentication with FILS-SHA384 with KDK derivation during connection"""
+ check_pasn_fils_kdk(dev, apdev, params, "FILS-SHA384")
+
+@remote_compatible
+def test_pasn_sae(dev, apdev):
+ """PASN authentication with SAE AP with PMK derivation + PMKSA caching"""
+ check_pasn_capab(dev[0])
+ check_sae_capab(dev[0])
+
+ params = hostapd.wpa2_params(ssid="test-pasn-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['sae_pwe'] = "2"
+ hapd = start_pasn_ap(apdev[0], params)
+
+ try:
+ dev[0].set("sae_pwe", "2")
+ dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
+
+ # first test with a valid PSK
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="0")
+
+ # And now with PMKSA caching
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP")
+
+ # And now with a wrong passphrase
+ if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+
+ dev[0].set_network_quoted(0, "psk", "12345678787")
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", status=1, nid="0")
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+@remote_compatible
+def test_pasn_sae_while_connected_same_channel(dev, apdev):
+ """PASN SAE authentication while connected same channel"""
+ check_pasn_capab(dev[0])
+ check_sae_capab(dev[0])
+
+ params = hostapd.wpa2_params(ssid="test-pasn-wpa2-psk",
+ passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ dev[0].set("sae_pwe", "2")
+ dev[0].connect("test-pasn-wpa2-psk", psk="12345678", scan_freq="2412")
+
+ params = hostapd.wpa2_params(ssid="test-pasn-sae",
+ passphrase="12345678")
+
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['sae_pwe'] = "2"
+ hapd = start_pasn_ap(apdev[1], params)
+
+ dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
+
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="1")
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+@remote_compatible
+def test_pasn_sae_while_connected_diff_channel(dev, apdev):
+ """PASN SAE authentication while connected diff channel"""
+ check_pasn_capab(dev[0])
+ check_sae_capab(dev[0])
+
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+
+ if wpas.get_mcc() < 2:
+ raise HwsimSkip("PASN: New radio does not support MCC")
+
+ params = hostapd.wpa2_params(ssid="test-pasn-wpa2-psk",
+ passphrase="12345678")
+ params['channel'] = "6"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ wpas.set("sae_pwe", "2")
+ wpas.connect("test-pasn-wpa2-psk", psk="12345678", scan_freq="2437")
+
+ params = hostapd.wpa2_params(ssid="test-pasn-sae",
+ passphrase="12345678")
+
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['sae_pwe'] = "2"
+ hapd = start_pasn_ap(apdev[1], params)
+
+ wpas.connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
+
+ check_pasn_akmp_cipher(wpas, hapd, "SAE", "CCMP", nid="1")
+ finally:
+ wpas.set("sae_pwe", "0")
+
+def pasn_fils_setup(wpas, apdev, params, key_mgmt):
+ check_fils_capa(wpas)
+ check_erp_capa(wpas)
+
+ wpas.flush_scan_cache()
+
+ start_erp_as(msk_dump=os.path.join(params['logdir'], "msk.lst"))
+
+ bssid = apdev[0]['bssid']
+ params = hostapd.wpa2_eap_params(ssid="fils")
+ params['wpa_key_mgmt'] = key_mgmt + " PASN"
+ params['auth_server_port'] = "18128"
+ params['erp_domain'] = 'example.com'
+ params['fils_realm'] = 'example.com'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ id = wpas.connect("fils", key_mgmt=key_mgmt,
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ erp="1", scan_freq="2412")
+
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+
+ if "FAIL" in wpas.request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+
+ return hapd
+
+def check_pasn_fils(dev, apdev, params, key_mgmt):
+ check_pasn_capab(dev[0])
+
+ hapd = pasn_fils_setup(dev[0], apdev, params, key_mgmt);
+ check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP", nid="0")
+
+@remote_compatible
+def test_pasn_fils_sha256(dev, apdev, params):
+ """PASN FILS authentication using SHA-256"""
+ check_pasn_fils(dev, apdev, params, "FILS-SHA256")
+
+@remote_compatible
+def test_pasn_fils_sha384(dev, apdev, params):
+ """PASN FILS authentication using SHA-384"""
+ check_pasn_fils(dev, apdev, params, "FILS-SHA384")
+
+def check_pasn_fils_connected_same_channel(dev, apdev, params, key_mgmt):
+ check_pasn_capab(dev[0])
+
+ hapd = pasn_fils_setup(dev[0], apdev, params, key_mgmt);
+
+ # Connect to another AP on the same channel
+ hapd1 = hostapd.add_ap(apdev[1], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bg_scan_period="0")
+
+ hwsim_utils.test_connectivity(dev[0], hapd1)
+
+ # And perform the PASN authentication with FILS
+ check_pasn_akmp_cipher(dev[0], hapd, key_mgmt, "CCMP", nid="0")
+
+@remote_compatible
+def test_pasn_fils_sha256_connected_same_channel(dev, apdev, params):
+ """PASN FILS authentication using SHA-256 while connected same channel"""
+ check_pasn_fils_connected_same_channel(dev, apdev, params, "FILS-SHA256")
+
+@remote_compatible
+def test_pasn_fils_sha384_connected_same_channel(dev, apdev, params):
+ """PASN FILS authentication using SHA-384 while connected same channel"""
+ check_pasn_fils_connected_same_channel(dev, apdev, params, "FILS-SHA384")
+
+def check_pasn_fils_connected_diff_channel(dev, apdev, params, key_mgmt):
+ check_pasn_capab(dev[0])
+
+ with HWSimRadio(n_channels=2) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ if wpas.get_mcc() < 2:
+ raise Exception("New radio does not support MCC")
+
+ hapd = pasn_fils_setup(wpas, apdev, params, key_mgmt);
+
+ # Connect to another AP on a different channel
+ hapd1 = hostapd.add_ap(apdev[1], {"ssid": "open", "channel" : "6"})
+ wpas.connect("open", key_mgmt="NONE", scan_freq="2437",
+ bg_scan_period="0")
+
+ hwsim_utils.test_connectivity(wpas, hapd1)
+
+ # And perform the PASN authentication with FILS
+ check_pasn_akmp_cipher(wpas, hapd, key_mgmt, "CCMP", nid="0")
+
+@remote_compatible
+def test_pasn_fils_sha256_connected_diff_channel(dev, apdev, params):
+ """PASN FILS authentication using SHA-256 while connected diff channel"""
+ check_pasn_fils_connected_diff_channel(dev, apdev, params, "FILS-SHA256")
+
+@remote_compatible
+def test_pasn_fils_sha384_connected_diff_channel(dev, apdev, params):
+ """PASN FILS authentication using SHA-384 while connected diff channel"""
+ check_pasn_fils_connected_diff_channel(dev, apdev, params, "FILS-SHA384")
+
+def test_pasn_ft_psk(dev, apdev):
+ """PASN authentication with FT-PSK"""
+ check_pasn_capab(dev[0])
+
+ ssid = "test-pasn-ft-psk"
+ passphrase = "12345678"
+
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] += " PASN"
+ hapd0 = hostapd.add_ap(apdev[0], params)
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] += " PASN"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
+
+ if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
+ pasn_hapd = hapd1
+ else:
+ pasn_hapd = hapd0
+
+ check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-PSK", "CCMP")
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, only_one_way=1)
+
+ if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
+ pasn_hapd = hapd1
+ else:
+ pasn_hapd = hapd0
+
+ check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-PSK", "CCMP")
+
+def test_pasn_ft_eap(dev, apdev):
+ """PASN authentication with FT-EAP"""
+ check_pasn_capab(dev[0])
+
+ ssid = "test-pasn-ft-psk"
+ passphrase = "12345678"
+ identity = "gpsk user"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-EAP PASN"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd0 = hostapd.add_ap(apdev[0], params)
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params['wpa_key_mgmt'] = "FT-EAP PASN"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True,
+ eap_identity=identity)
+
+ if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
+ pasn_hapd = hapd1
+ else:
+ pasn_hapd = hapd0
+
+ check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-EAP", "CCMP")
+
+def test_pasn_ft_eap_sha384(dev, apdev):
+ """PASN authentication with FT-EAP-SHA-384"""
+ check_pasn_capab(dev[0])
+
+ ssid = "test-pasn-ft-psk"
+ passphrase = "12345678"
+ identity = "gpsk user"
+
+ radius = hostapd.radius_params()
+ params = ft_params1(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-EAP-SHA384 PASN"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd0 = hostapd.add_ap(apdev[0], params)
+
+ params = ft_params2(ssid=ssid, passphrase=passphrase)
+ params["ieee80211w"] = "2"
+ params['wpa_key_mgmt'] = "FT-EAP-SHA384 PASN"
+ params["ieee8021x"] = "1"
+ params = dict(list(radius.items()) + list(params.items()))
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, eap=True,
+ sha384=True)
+
+ if dev[0].get_status_field('bssid') == apdev[0]['bssid']:
+ pasn_hapd = hapd1
+ else:
+ pasn_hapd = hapd0
+
+ check_pasn_akmp_cipher(dev[0], pasn_hapd, "FT-EAP-SHA384", "CCMP")
+
+def test_pasn_sta_mic_error(dev, apdev):
+ """PASN authentication with WPA2/CCMP AP with corrupted MIC on station"""
+ params = pasn_ap_params("PASN", "CCMP", "19")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ # When forcing MIC corruption, the exchange would be still successful
+ # on the station side, but the AP would fail the exchange and would not
+ # store the keys.
+ dev[0].set("pasn_corrupt_mic", "1")
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP", fail_ptk=True)
+ finally:
+ dev[0].set("pasn_corrupt_mic", "0")
+
+ # Now verify the successful case
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP")
+
+def test_pasn_ap_mic_error(dev, apdev):
+ """PASN authentication with WPA2/CCMP AP with corrupted MIC on AP"""
+ params = pasn_ap_params("PASN", "CCMP", "19")
+ hapd0 = hostapd.add_ap(apdev[0], params)
+
+ params['pasn_corrupt_mic'] = "1"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ check_pasn_akmp_cipher(dev[0], hapd1, "PASN", "CCMP", status=1)
+ check_pasn_akmp_cipher(dev[0], hapd0, "PASN", "CCMP")
+
+@remote_compatible
+def test_pasn_comeback(dev, apdev, params):
+ """PASN authentication with comeback flow"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "CCMP", "19")
+ params['sae_anti_clogging_threshold'] = '0'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan(type="ONLY", freq=2412)
+ cmd = "PASN_START bssid=%s akmp=PASN cipher=CCMP group=19" % bssid
+
+ resp = dev[0].request(cmd)
+ if "OK" not in resp:
+ raise Exception("Failed to start PASN authentication")
+
+ ev = dev[0].wait_event(["PASN-AUTH-STATUS"], 3)
+ if not ev:
+ raise Exception("PASN: PASN-AUTH-STATUS not seen")
+
+ if bssid + " akmp=PASN, status=30 comeback_after=" not in ev:
+ raise Exception("PASN: unexpected status")
+
+ comeback = re.split("comeback=", ev)[1]
+
+ cmd = "PASN_START bssid=%s akmp=PASN cipher=CCMP group=19 comeback=%s" % \
+ (bssid, comeback)
+
+ resp = dev[0].request(cmd)
+ if "OK" not in resp:
+ raise Exception("Failed to start PASN authentication")
+
+ ev = dev[0].wait_event(["PASN-AUTH-STATUS"], 3)
+ if not ev:
+ raise Exception("PASN: PASN-AUTH-STATUS not seen")
+
+ if bssid + " akmp=PASN, status=0" not in ev:
+ raise Exception("PASN: unexpected status with comeback token")
+
+ check_pasn_ptk(dev[0], hapd, "CCMP")
+
+@remote_compatible
+def test_pasn_comeback_after_0(dev, apdev, params):
+ """PASN authentication with comeback flow with comeback after set to 0"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "CCMP", "19")
+ params['anti_clogging_threshold'] = '0'
+ params['pasn_comeback_after'] = '0'
+ hapd = start_pasn_ap(apdev[0], params)
+
+ check_pasn_akmp_cipher(dev[0], hapd, "PASN", "CCMP")
+
+@remote_compatible
+def test_pasn_comeback_after_0_sae(dev, apdev):
+ """PASN authentication with SAE, with comeback flow where comeback after is set to 0"""
+ check_pasn_capab(dev[0])
+ check_sae_capab(dev[0])
+
+ params = hostapd.wpa2_params(ssid="test-pasn-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['anti_clogging_threshold'] = '0'
+ params['pasn_comeback_after'] = '0'
+ params['sae_pwe'] = "2"
+ hapd = start_pasn_ap(apdev[0], params)
+
+ try:
+ dev[0].set("sae_pwe", "2")
+ dev[0].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
+
+ # first test with a valid PSK
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", nid="0")
+
+ # And now with PMKSA caching
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP")
+
+ # And now with a wrong passphrase
+ if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+
+ dev[0].set_network_quoted(0, "psk", "12345678787")
+ check_pasn_akmp_cipher(dev[0], hapd, "SAE", "CCMP", status=1, nid="0")
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+@remote_compatible
+def test_pasn_comeback_multi(dev, apdev):
+ """PASN authentication with SAE, with multiple stations with comeback"""
+ check_pasn_capab(dev[0])
+ check_sae_capab(dev[0])
+
+ params = hostapd.wpa2_params(ssid="test-pasn-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE PASN'
+ params['anti_clogging_threshold'] = '1'
+ params['pasn_comeback_after'] = '0'
+ hapd = start_pasn_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ id = {}
+ for i in range(0, 2):
+ dev[i].flush_scan_cache()
+ dev[i].scan(type="ONLY", freq=2412)
+ id[i] = dev[i].connect("test-pasn-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
+
+ for i in range(0, 2):
+ cmd = "PASN_START bssid=%s akmp=PASN cipher=CCMP group=19, nid=%s" % (bssid, id[i])
+ resp = dev[i].request(cmd)
+
+ if "OK" not in resp:
+ raise Exception("Failed to start pasn authentication")
+
+ for i in range(0, 2):
+ ev = dev[i].wait_event(["PASN-AUTH-STATUS"], 3)
+ if not ev:
+ raise Exception("PASN: PASN-AUTH-STATUS not seen")
+
+ if bssid + " akmp=PASN, status=0" not in ev:
+ raise Exception("PASN: unexpected status")
+
+ check_pasn_ptk(dev[i], hapd, "CCMP")
+
+def test_pasn_kdk_derivation(dev, apdev):
+ """PASN authentication with forced KDK derivation"""
+ check_pasn_capab(dev[0])
+
+ params = pasn_ap_params("PASN", "CCMP", "19")
+ hapd0 = start_pasn_ap(apdev[0], params)
+
+ params['force_kdk_derivation'] = "1"
+ hapd1 = start_pasn_ap(apdev[1], params)
+
+ try:
+ check_pasn_akmp_cipher(dev[0], hapd0, "PASN", "CCMP")
+ dev[0].set("force_kdk_derivation", "1")
+ check_pasn_akmp_cipher(dev[0], hapd1, "PASN", "CCMP")
+ finally:
+ dev[0].set("force_kdk_derivation", "0")
diff --git a/contrib/wpa/tests/hwsim/test_pmksa_cache.py b/contrib/wpa/tests/hwsim/test_pmksa_cache.py
new file mode 100644
index 000000000000..10d76a394f8d
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_pmksa_cache.py
@@ -0,0 +1,1267 @@
+# WPA2-Enterprise PMKSA caching tests
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import logging
+logger = logging.getLogger()
+import socket
+import struct
+import subprocess
+import time
+
+import hostapd
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+from utils import alloc_fail, HwsimSkip, wait_fail_trigger
+from test_ap_eap import eap_connect
+
+def test_pmksa_cache_on_roam_back(dev, apdev):
+ """PMKSA cache to skip EAP on reassociation back to same AP"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+ if pmksa['opportunistic'] != '0':
+ raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].dump_monitor()
+ logger.info("Roam to AP2")
+ # It can take some time for the second AP to become ready to reply to Probe
+ # Request frames especially under heavy CPU load, so allow couple of rounds
+ # of scanning to avoid reporting errors incorrectly just because of scans
+ # not having seen the target AP.
+ for i in range(0, 10):
+ dev[0].scan(freq="2412")
+ if dev[0].get_bss(bssid2) is not None:
+ break
+ logger.info("Scan again to find target AP")
+ dev[0].request("ROAM " + bssid2)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ dev[0].wait_connected(timeout=10, error="Roaming timed out")
+ pmksa2 = dev[0].get_pmksa(bssid2)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa2['opportunistic'] != '0':
+ raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+ dev[0].dump_monitor()
+ logger.info("Roam back to AP1")
+ dev[0].scan(freq="2412")
+ dev[0].request("ROAM " + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ pmksa1b = dev[0].get_pmksa(bssid)
+ if pmksa1b is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa['pmkid'] != pmksa1b['pmkid']:
+ raise Exception("Unexpected PMKID change for AP1")
+
+ dev[0].dump_monitor()
+ if "FAIL" in dev[0].request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+ if dev[0].get_pmksa(bssid) is not None or dev[0].get_pmksa(bssid2) is not None:
+ raise Exception("PMKSA_FLUSH did not remove PMKSA entries")
+ dev[0].wait_disconnected(timeout=5)
+ dev[0].wait_connected(timeout=15, error="Reconnection timed out")
+
+def test_pmksa_cache_and_reauth(dev, apdev):
+ """PMKSA caching and EAPOL reauthentication"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].dump_monitor()
+ logger.info("Roam to AP2")
+ # It can take some time for the second AP to become ready to reply to Probe
+ # Request frames especially under heavy CPU load, so allow couple of rounds
+ # of scanning to avoid reporting errors incorrectly just because of scans
+ # not having seen the target AP.
+ for i in range(0, 10):
+ dev[0].scan(freq="2412")
+ if dev[0].get_bss(bssid2) is not None:
+ break
+ logger.info("Scan again to find target AP")
+ dev[0].request("ROAM " + bssid2)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ dev[0].wait_connected(timeout=10, error="Roaming timed out")
+
+ dev[0].dump_monitor()
+ logger.info("Roam back to AP1")
+ dev[0].scan(freq="2412")
+ dev[0].request("ROAM " + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+
+ # Verify EAPOL reauthentication after PMKSA caching
+ hapd.request("EAPOL_REAUTH " + dev[0].own_addr())
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not start")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=5)
+ if ev is None:
+ raise Exception("EAP authentication did not succeed")
+
+def test_pmksa_cache_and_ptk_rekey_ap(dev, apdev):
+ """PMKSA caching and PTK rekey triggered by AP"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ params['wpa_ptk_rekey'] = '2'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].dump_monitor()
+ logger.info("Roam to AP2")
+ # It can take some time for the second AP to become ready to reply to Probe
+ # Request frames especially under heavy CPU load, so allow couple of rounds
+ # of scanning to avoid reporting errors incorrectly just because of scans
+ # not having seen the target AP.
+ for i in range(0, 10):
+ dev[0].scan(freq="2412")
+ if dev[0].get_bss(bssid2) is not None:
+ break
+ logger.info("Scan again to find target AP")
+ dev[0].request("ROAM " + bssid2)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ dev[0].wait_connected(timeout=10, error="Roaming timed out")
+
+ dev[0].dump_monitor()
+ logger.info("Roam back to AP1")
+ dev[0].scan(freq="2412")
+ dev[0].request("ROAM " + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+
+ # Verify PTK rekeying after PMKSA caching
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=3)
+ if ev is None:
+ raise Exception("PTK rekey timed out")
+
+def test_pmksa_cache_opportunistic_only_on_sta(dev, apdev):
+ """Opportunistic PMKSA caching enabled only on station"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef", okc=True,
+ scan_freq="2412")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+ if pmksa['opportunistic'] != '0':
+ raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].dump_monitor()
+ logger.info("Roam to AP2")
+ dev[0].scan(freq="2412")
+ dev[0].request("ROAM " + bssid2)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ dev[0].wait_connected(timeout=10, error="Roaming timed out")
+ pmksa2 = dev[0].get_pmksa(bssid2)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa2['opportunistic'] != '0':
+ raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+ dev[0].dump_monitor()
+ logger.info("Roam back to AP1")
+ dev[0].scan(freq="2412")
+ dev[0].request("ROAM " + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ pmksa1b = dev[0].get_pmksa(bssid)
+ if pmksa1b is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa['pmkid'] != pmksa1b['pmkid']:
+ raise Exception("Unexpected PMKID change for AP1")
+
+def test_pmksa_cache_opportunistic(dev, apdev):
+ """Opportunistic PMKSA caching"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ params['okc'] = "1"
+ hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef", okc=True,
+ scan_freq="2412")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+ if pmksa['opportunistic'] != '0':
+ raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].dump_monitor()
+ logger.info("Roam to AP2")
+ dev[0].scan(freq="2412")
+ dev[0].request("ROAM " + bssid2)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ pmksa2 = dev[0].get_pmksa(bssid2)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry created")
+
+ dev[0].dump_monitor()
+ logger.info("Roam back to AP1")
+ dev[0].scan(freq="2412")
+ dev[0].request("ROAM " + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+
+ pmksa1b = dev[0].get_pmksa(bssid)
+ if pmksa1b is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa['pmkid'] != pmksa1b['pmkid']:
+ raise Exception("Unexpected PMKID change for AP1")
+
+def test_pmksa_cache_opportunistic_connect(dev, apdev):
+ """Opportunistic PMKSA caching with connect API"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ params['okc'] = "1"
+ hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ wpas.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef", okc=True,
+ scan_freq="2412")
+ pmksa = wpas.get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+ if pmksa['opportunistic'] != '0':
+ raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ wpas.dump_monitor()
+ logger.info("Roam to AP2")
+ wpas.scan_for_bss(bssid2, freq="2412", force_scan=True)
+ wpas.request("ROAM " + bssid2)
+ ev = wpas.wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ pmksa2 = wpas.get_pmksa(bssid2)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry created")
+
+ wpas.dump_monitor()
+ logger.info("Roam back to AP1")
+ wpas.scan(freq="2412")
+ wpas.request("ROAM " + bssid)
+ ev = wpas.wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+
+ pmksa1b = wpas.get_pmksa(bssid)
+ if pmksa1b is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa['pmkid'] != pmksa1b['pmkid']:
+ raise Exception("Unexpected PMKID change for AP1")
+
+def test_pmksa_cache_expiration(dev, apdev):
+ """PMKSA cache entry expiration"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].request("SET dot11RSNAConfigPMKLifetime 10")
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ hapd.wait_sta()
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+ logger.info("Wait for PMKSA cache entry to expire")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed",
+ "CTRL-EVENT-DISCONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("No EAP reauthentication seen")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ raise Exception("Unexpected disconnection")
+ pmksa2 = dev[0].get_pmksa(bssid)
+ if pmksa['pmkid'] == pmksa2['pmkid']:
+ raise Exception("PMKID did not change")
+ hapd.wait_ptkinitdone(dev[0].own_addr())
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_pmksa_cache_expiration_disconnect(dev, apdev):
+ """PMKSA cache entry expiration (disconnect)"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].request("SET dot11RSNAConfigPMKLifetime 2")
+ dev[0].request("SET dot11RSNAConfigPMKReauthThreshold 100")
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+ hapd.request("SET auth_server_shared_secret incorrect")
+ logger.info("Wait for PMKSA cache entry to expire")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed",
+ "CTRL-EVENT-DISCONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("No EAP reauthentication seen")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Missing disconnection")
+ hapd.request("SET auth_server_shared_secret radius")
+ ev = dev[0].wait_event(["WPA: Key negotiation completed"], timeout=15)
+ if ev is None:
+ raise Exception("No EAP reauthentication seen")
+ pmksa2 = dev[0].get_pmksa(bssid)
+ if pmksa['pmkid'] == pmksa2['pmkid']:
+ raise Exception("PMKID did not change")
+
+def test_pmksa_cache_and_cui(dev, apdev):
+ """PMKSA cache and Chargeable-User-Identity"""
+ params = hostapd.wpa2_eap_params(ssid="cui")
+ params['radius_request_cui'] = '1'
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("cui", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk-cui",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+
+ dev[0].dump_monitor()
+ logger.info("Disconnect and reconnect to the same AP")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Reconnect timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ pmksa1b = dev[0].get_pmksa(bssid)
+ if pmksa1b is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa['pmkid'] != pmksa1b['pmkid']:
+ raise Exception("Unexpected PMKID change for AP1")
+
+ dev[0].request("REAUTHENTICATE")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ for i in range(0, 20):
+ state = dev[0].get_status_field("wpa_state")
+ if state == "COMPLETED":
+ break
+ time.sleep(0.1)
+ if state != "COMPLETED":
+ raise Exception("Reauthentication did not complete")
+
+def test_pmksa_cache_preauth_auto(dev, apdev):
+ """RSN pre-authentication based on pre-connection scan results"""
+ try:
+ run_pmksa_cache_preauth_auto(dev, apdev)
+ finally:
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev',
+ 'ap-br0', 'down', '2>', '/dev/null'],
+ shell=True)
+ hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', 'ap-br0',
+ '2>', '/dev/null'], shell=True)
+
+def run_pmksa_cache_preauth_auto(dev, apdev):
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['bridge'] = 'ap-br0'
+ params['rsn_preauth'] = '1'
+ params['rsn_preauth_interfaces'] = 'ap-br0'
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.cmd_execute(['brctl', 'setfd', 'ap-br0', '0'])
+ hapd.cmd_execute(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ eap_connect(dev[0], None, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+
+ found = False
+ for i in range(20):
+ time.sleep(0.5)
+ res1 = dev[0].get_pmksa(apdev[0]['bssid'])
+ res2 = dev[0].get_pmksa(apdev[1]['bssid'])
+ if res1 and res2:
+ found = True
+ break
+ if not found:
+ raise Exception("The expected PMKSA cache entries not found")
+
+def generic_pmksa_cache_preauth(dev, apdev, extraparams, identity, databridge,
+ force_disconnect=False):
+ if not extraparams:
+ extraparams = [{}, {}]
+ try:
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['bridge'] = 'ap-br0'
+ for key, value in extraparams[0].items():
+ params[key] = value
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.cmd_execute(['brctl', 'setfd', 'ap-br0', '0'])
+ hapd.cmd_execute(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+ eap_connect(dev[0], hapd, "PAX", identity,
+ password_hex="0123456789abcdef0123456789abcdef")
+
+ # Verify connectivity in the correct VLAN
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, databridge)
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['bridge'] = 'ap-br0'
+ params['rsn_preauth'] = '1'
+ params['rsn_preauth_interfaces'] = databridge
+ for key, value in extraparams[1].items():
+ params[key] = value
+ hapd1 = hostapd.add_ap(apdev[1], params)
+ bssid1 = apdev[1]['bssid']
+ dev[0].scan(freq="2412")
+ success = False
+ status_seen = False
+ for i in range(0, 50):
+ if not status_seen:
+ status = dev[0].request("STATUS")
+ if "Pre-authentication EAPOL state machines:" in status:
+ status_seen = True
+ time.sleep(0.1)
+ pmksa = dev[0].get_pmksa(bssid1)
+ if pmksa:
+ success = True
+ break
+ if not success:
+ raise Exception("No PMKSA cache entry created from pre-authentication")
+ if not status_seen:
+ raise Exception("Pre-authentication EAPOL status was not available")
+
+ dev[0].scan(freq="2412")
+ if "[WPA2-EAP-CCMP-preauth]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Scan results missing RSN element info")
+ dev[0].request("ROAM " + bssid1)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ pmksa2 = dev[0].get_pmksa(bssid1)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry")
+ if pmksa['pmkid'] != pmksa2['pmkid']:
+ raise Exception("Unexpected PMKID change")
+
+ hapd1.wait_sta()
+ # Verify connectivity in the correct VLAN
+ hwsim_utils.test_connectivity_iface(dev[0], hapd, databridge)
+
+ if not force_disconnect:
+ return
+
+ # Disconnect the STA from both APs to avoid forceful ifdown by the
+ # test script on a VLAN that this has an associated STA. That used to
+ # trigger a mac80211 warning.
+ dev[0].request("DISCONNECT")
+ hapd.request("DISABLE")
+
+ finally:
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev',
+ 'ap-br0', 'down', '2>', '/dev/null'],
+ shell=True)
+ hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', 'ap-br0',
+ '2>', '/dev/null'], shell=True)
+
+def test_pmksa_cache_preauth(dev, apdev):
+ """RSN pre-authentication to generate PMKSA cache entry"""
+ generic_pmksa_cache_preauth(dev, apdev, None,
+ "pax.user@example.com", "ap-br0")
+
+def test_pmksa_cache_preauth_per_sta_vif(dev, apdev):
+ """RSN pre-authentication to generate PMKSA cache entry with per_sta_vif"""
+ extraparams = [{}, {}]
+ extraparams[0]['per_sta_vif'] = "1"
+ extraparams[1]['per_sta_vif'] = "1"
+ generic_pmksa_cache_preauth(dev, apdev, extraparams,
+ "pax.user@example.com", "ap-br0")
+
+def test_pmksa_cache_preauth_vlan_enabled(dev, apdev):
+ """RSN pre-authentication to generate PMKSA cache entry (dynamic_vlan optional but station without VLAN set)"""
+ extraparams = [{}, {}]
+ extraparams[0]['dynamic_vlan'] = '1'
+ extraparams[1]['dynamic_vlan'] = '1'
+ generic_pmksa_cache_preauth(dev, apdev, extraparams,
+ "pax.user@example.com", "ap-br0")
+
+def test_pmksa_cache_preauth_vlan_enabled_per_sta_vif(dev, apdev):
+ """RSN pre-authentication to generate PMKSA cache entry (dynamic_vlan optional but station without VLAN set, with per_sta_vif enabled)"""
+ extraparams = [{}, {}]
+ extraparams[0]['per_sta_vif'] = "1"
+ extraparams[1]['per_sta_vif'] = "1"
+ extraparams[0]['dynamic_vlan'] = '1'
+ extraparams[1]['dynamic_vlan'] = '1'
+ generic_pmksa_cache_preauth(dev, apdev, extraparams,
+ "pax.user@example.com", "ap-br0")
+
+def test_pmksa_cache_preauth_vlan_used(dev, apdev):
+ """RSN pre-authentication to generate PMKSA cache entry (station with VLAN set)"""
+ run_pmksa_cache_preauth_vlan_used(dev, apdev, None, force_disconnect=True)
+
+def run_pmksa_cache_preauth_vlan_used(dev, apdev, extraparams=None,
+ force_disconnect=False):
+ try:
+ subprocess.call(['brctl', 'addbr', 'brvlan1'])
+ subprocess.call(['brctl', 'setfd', 'brvlan1', '0'])
+ if not extraparams:
+ extraparams = [{}, {}]
+ extraparams[0]['dynamic_vlan'] = '1'
+ extraparams[0]['vlan_file'] = 'hostapd.wlan3.vlan'
+ extraparams[1]['dynamic_vlan'] = '1'
+ extraparams[1]['vlan_file'] = 'hostapd.wlan4.vlan'
+ generic_pmksa_cache_preauth(dev, apdev, extraparams,
+ "vlan1", "brvlan1",
+ force_disconnect=force_disconnect)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', 'brvlan1', 'down'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'wlan3.1', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['ip', 'link', 'set', 'dev', 'wlan4.1', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delif', 'brvlan1', 'wlan3.1'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delif', 'brvlan1', 'wlan4.1'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'brvlan1'])
+
+def test_pmksa_cache_preauth_vlan_used_per_sta_vif(dev, apdev):
+ """RSN pre-authentication to generate PMKSA cache entry (station with VLAN set, per_sta_vif=1)"""
+ extraparams = [{}, {}]
+ extraparams[0]['per_sta_vif'] = "1"
+ extraparams[1]['per_sta_vif'] = "1"
+ run_pmksa_cache_preauth_vlan_used(dev, apdev, extraparams)
+
+def test_pmksa_cache_disabled(dev, apdev):
+ """PMKSA cache disabling on AP"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ params['disable_pmksa_caching'] = '1'
+ hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].dump_monitor()
+ logger.info("Roam to AP2")
+ dev[0].scan_for_bss(bssid2, freq="2412")
+ dev[0].request("ROAM " + bssid2)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ dev[0].wait_connected(timeout=10, error="Roaming timed out")
+
+ dev[0].dump_monitor()
+ logger.info("Roam back to AP1")
+ dev[0].scan(freq="2412")
+ dev[0].request("ROAM " + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("EAP exchange missing")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+
+def test_pmksa_cache_ap_expiration(dev, apdev):
+ """PMKSA cache entry expiring on AP"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].cmd_execute(['iw', 'dev', dev[0].ifname,
+ 'set', 'power_save', 'off'])
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk-user-session-timeout",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ hapd.dump_monitor()
+
+ dev[0].request("DISCONNECT")
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No disconnection event received from hostapd")
+ dev[0].wait_disconnected()
+
+ # Wait for session timeout to remove PMKSA cache entry
+ time.sleep(5)
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Reconnection with the AP timed out")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("EAP exchange missing")
+ dev[0].wait_connected(timeout=20, error="Reconnect timed out")
+ dev[0].dump_monitor()
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd [2]")
+ hapd.dump_monitor()
+
+ # Wait for session timeout
+ ev = hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("No disconnection event received from hostapd [2]")
+ dev[0].wait_disconnected(timeout=20)
+ dev[0].wait_connected(timeout=20, error="Reassociation timed out")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd [3]")
+ hapd.dump_monitor()
+ dev[0].dump_monitor()
+
+def test_pmksa_cache_multiple_sta(dev, apdev):
+ """PMKSA cache with multiple stations"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ for d in dev:
+ d.flush_scan_cache()
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk-user-session-timeout",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ dev[1].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ dev[2].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk-user-session-timeout",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.flush_scan_cache()
+ wpas.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ logger.info("Roam to AP2")
+ for sta in [dev[1], dev[0], dev[2], wpas]:
+ sta.dump_monitor()
+ sta.scan_for_bss(bssid2, freq="2412")
+ if "OK" not in sta.request("ROAM " + bssid2):
+ raise Exception("ROAM command failed (" + sta.ifname + ")")
+ ev = sta.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ sta.wait_connected(timeout=10, error="Roaming timed out")
+ sta.dump_monitor()
+
+ logger.info("Roam back to AP1")
+ for sta in [dev[1], wpas, dev[0], dev[2]]:
+ sta.dump_monitor()
+ sta.scan(freq="2412")
+ sta.dump_monitor()
+ sta.request("ROAM " + bssid)
+ sta.wait_connected(timeout=10, error="Roaming timed out")
+ sta.dump_monitor()
+
+ time.sleep(4)
+
+ logger.info("Roam back to AP2")
+ for sta in [dev[1], wpas, dev[0], dev[2]]:
+ sta.dump_monitor()
+ sta.scan(freq="2412")
+ sta.dump_monitor()
+ sta.request("ROAM " + bssid2)
+ sta.wait_connected(timeout=10, error="Roaming timed out")
+ sta.dump_monitor()
+
+def test_pmksa_cache_opportunistic_multiple_sta(dev, apdev):
+ """Opportunistic PMKSA caching with multiple stations"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ params['okc'] = "1"
+ hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ for d in dev:
+ d.flush_scan_cache()
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.flush_scan_cache()
+ for sta in [dev[0], dev[1], dev[2], wpas]:
+ sta.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef", okc=True,
+ scan_freq="2412")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ logger.info("Roam to AP2")
+ for sta in [dev[2], dev[0], wpas, dev[1]]:
+ sta.dump_monitor()
+ sta.scan_for_bss(bssid2, freq="2412")
+ if "OK" not in sta.request("ROAM " + bssid2):
+ raise Exception("ROAM command failed")
+ ev = sta.wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ pmksa2 = sta.get_pmksa(bssid2)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry created")
+ sta.dump_monitor()
+
+ logger.info("Roam back to AP1")
+ for sta in [dev[0], dev[1], dev[2], wpas]:
+ sta.dump_monitor()
+ sta.scan_for_bss(bssid, freq="2412")
+ sta.request("ROAM " + bssid)
+ ev = sta.wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+
+def test_pmksa_cache_preauth_oom(dev, apdev):
+ """RSN pre-authentication to generate PMKSA cache entry and OOM"""
+ try:
+ _test_pmksa_cache_preauth_oom(dev, apdev)
+ finally:
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', 'ap-br0',
+ 'down'])
+ hostapd.cmd_execute(apdev[0], ['brctl', 'delbr', 'ap-br0'])
+
+def _test_pmksa_cache_preauth_oom(dev, apdev):
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['bridge'] = 'ap-br0'
+ hapd = hostapd.add_ap(apdev[0], params)
+ hostapd.cmd_execute(apdev[0], ['brctl', 'setfd', 'ap-br0', '0'])
+ hostapd.cmd_execute(apdev[0], ['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ bssid=apdev[0]['bssid'])
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['bridge'] = 'ap-br0'
+ params['rsn_preauth'] = '1'
+ params['rsn_preauth_interfaces'] = 'ap-br0'
+ hapd = hostapd.add_ap(apdev[1], params)
+ bssid1 = apdev[1]['bssid']
+
+ tests = [(1, "rsn_preauth_receive"),
+ (2, "rsn_preauth_receive"),
+ (1, "rsn_preauth_send"),
+ (1, "wpa_auth_pmksa_add_preauth;rsn_preauth_finished")]
+ for test in tests:
+ hapd.request("DEAUTHENTICATE ff:ff:ff:ff:ff:ff")
+ with alloc_fail(hapd, test[0], test[1]):
+ dev[0].scan_for_bss(bssid1, freq="2412")
+ if "OK" not in dev[0].request("PREAUTH " + bssid1):
+ raise Exception("PREAUTH failed")
+
+ success = False
+ count = 0
+ for i in range(50):
+ time.sleep(0.1)
+ pmksa = dev[0].get_pmksa(bssid1)
+ if pmksa:
+ success = True
+ break
+ state = hapd.request('GET_ALLOC_FAIL')
+ if state.startswith('0:'):
+ count += 1
+ if count > 2:
+ break
+ logger.info("PMKSA cache success: " + str(success))
+
+ dev[0].request("PMKSA_FLUSH")
+ dev[0].wait_disconnected()
+ dev[0].wait_connected()
+ dev[0].dump_monitor()
+
+def test_pmksa_cache_size_limit(dev, apdev):
+ """PMKSA cache size limit in wpa_supplicant"""
+ try:
+ _test_pmksa_cache_size_limit(dev, apdev)
+ finally:
+ try:
+ hapd = hostapd.HostapdGlobal(apdev[0])
+ hapd.flush()
+ hapd.remove(apdev[0]['ifname'])
+ except:
+ pass
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ bssid = apdev[0]['bssid']
+ params['bssid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+
+def _test_pmksa_cache_size_limit(dev, apdev):
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412", only_add_network=True)
+ for i in range(33):
+ bssid = apdev[0]['bssid'][0:15] + "%02x" % i
+ logger.info("Iteration with BSSID " + bssid)
+ params['bssid'] = bssid
+ hostapd.add_ap(apdev[0], params)
+ dev[0].request("BSS_FLUSH 0")
+ dev[0].scan_for_bss(bssid, freq=2412, only_new=True)
+ dev[0].select_network(id)
+ dev[0].wait_connected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ entries = len(dev[0].request("PMKSA").splitlines()) - 1
+ if i == 32:
+ if entries != 32:
+ raise Exception("Unexpected number of PMKSA entries after expected removal of the oldest entry")
+ elif i + 1 != entries:
+ raise Exception("Unexpected number of PMKSA entries")
+
+ hapd = hostapd.HostapdGlobal(apdev[0])
+ hapd.flush()
+ hapd.remove(apdev[0]['ifname'])
+
+def test_pmksa_cache_preauth_timeout(dev, apdev):
+ """RSN pre-authentication timing out"""
+ try:
+ _test_pmksa_cache_preauth_timeout(dev, apdev)
+ finally:
+ dev[0].request("SET dot11RSNAConfigSATimeout 60")
+
+def _test_pmksa_cache_preauth_timeout(dev, apdev):
+ dev[0].request("SET dot11RSNAConfigSATimeout 1")
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ bssid=apdev[0]['bssid'])
+ if "OK" not in dev[0].request("PREAUTH f2:11:22:33:44:55"):
+ raise Exception("PREAUTH failed")
+ ev = dev[0].wait_event(["RSN: pre-authentication with"], timeout=5)
+ if ev is None:
+ raise Exception("No timeout event seen")
+ if "timed out" not in ev:
+ raise Exception("Unexpected event: " + ev)
+
+def test_pmksa_cache_preauth_wpas_oom(dev, apdev):
+ """RSN pre-authentication OOM in wpa_supplicant"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ hapd = hostapd.add_ap(apdev[0], params)
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ bssid=apdev[0]['bssid'])
+ for i in range(1, 11):
+ with alloc_fail(dev[0], i, "rsn_preauth_init"):
+ res = dev[0].request("PREAUTH f2:11:22:33:44:55").strip()
+ logger.info("Iteration %d - PREAUTH command results: %s" % (i, res))
+ for j in range(10):
+ state = dev[0].request('GET_ALLOC_FAIL')
+ if state.startswith('0:'):
+ break
+ time.sleep(0.05)
+
+def test_pmksa_cache_ctrl(dev, apdev):
+ """PMKSA cache control interface operations"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ addr = dev[0].own_addr()
+
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ pmksa_sta = dev[0].get_pmksa(bssid)
+ if pmksa_sta is None:
+ raise Exception("No PMKSA cache entry created on STA")
+ pmksa_ap = hapd.get_pmksa(addr)
+ if pmksa_ap is None:
+ raise Exception("No PMKSA cache entry created on AP")
+ if pmksa_sta['pmkid'] != pmksa_ap['pmkid']:
+ raise Exception("PMKID mismatch in PMKSA cache entries")
+
+ if "OK" not in hapd.request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+ pmksa_ap = hapd.get_pmksa(addr)
+ if pmksa_ap is not None:
+ raise Exception("PMKSA cache entry was not removed on AP")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+ pmksa_sta2 = dev[0].get_pmksa(bssid)
+ if pmksa_sta2 is None:
+ raise Exception("No PMKSA cache entry created on STA after reconnect")
+ pmksa_ap2 = hapd.get_pmksa(addr)
+ if pmksa_ap2 is None:
+ raise Exception("No PMKSA cache entry created on AP after reconnect")
+ if pmksa_sta2['pmkid'] != pmksa_ap2['pmkid']:
+ raise Exception("PMKID mismatch in PMKSA cache entries after reconnect")
+ if pmksa_sta2['pmkid'] == pmksa_sta['pmkid']:
+ raise Exception("PMKID did not change after reconnect")
+
+def test_pmksa_cache_ctrl_events(dev, apdev):
+ """PMKSA cache control interface events"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412", wait_connect=False)
+
+ ev = dev[0].wait_event(["PMKSA-CACHE-ADDED"], timeout=15)
+ if ev is None:
+ raise Exception("No PMKSA-CACHE-ADDED event")
+ dev[0].wait_connected()
+ items = ev.split(' ')
+ if items[1] != bssid:
+ raise Exception("BSSID mismatch: " + ev)
+ if int(items[2]) != id:
+ raise Exception("network_id mismatch: " + ev)
+
+ dev[0].request("PMKSA_FLUSH")
+ ev = dev[0].wait_event(["PMKSA-CACHE-REMOVED"], timeout=15)
+ if ev is None:
+ raise Exception("No PMKSA-CACHE-REMOVED event")
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+ items = ev.split(' ')
+ if items[1] != bssid:
+ raise Exception("BSSID mismatch: " + ev)
+ if int(items[2]) != id:
+ raise Exception("network_id mismatch: " + ev)
+
+def test_pmksa_cache_ctrl_ext(dev, apdev):
+ """PMKSA cache control interface for external management"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ res1 = dev[0].request("PMKSA_GET %d" % id)
+ logger.info("PMKSA_GET: " + res1)
+ if "UNKNOWN COMMAND" in res1:
+ raise HwsimSkip("PMKSA_GET not supported in the build")
+ if bssid not in res1:
+ raise Exception("PMKSA cache entry missing")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+ dev[0].scan_for_bss(bssid2, freq=2412, force_scan=True)
+ dev[0].request("ROAM " + bssid2)
+ dev[0].wait_connected()
+
+ res2 = dev[0].request("PMKSA_GET %d" % id)
+ logger.info("PMKSA_GET: " + res2)
+ if bssid not in res2:
+ raise Exception("PMKSA cache entry 1 missing")
+ if bssid2 not in res2:
+ raise Exception("PMKSA cache entry 2 missing")
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].request("PMKSA_FLUSH")
+
+ id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412", only_add_network=True)
+ res3 = dev[0].request("PMKSA_GET %d" % id)
+ if res3 != '':
+ raise Exception("Unexpected PMKSA cache entry remains: " + res3)
+ res4 = dev[0].request("PMKSA_GET %d" % (id + 1234))
+ if not res4.startswith('FAIL'):
+ raise Exception("Unexpected PMKSA cache entry for unknown network: " + res4)
+
+ for entry in res2.splitlines():
+ if "OK" not in dev[0].request("PMKSA_ADD %d %s" % (id, entry)):
+ raise Exception("Failed to add PMKSA entry")
+
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange after external PMKSA cache restore")
+
+def test_pmksa_cache_ctrl_ext_ft(dev, apdev):
+ """PMKSA cache control interface for external management (FT)"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ params['wpa_key_mgmt'] = "FT-EAP"
+ params['nas_identifier'] = "nas.w1.fi"
+ params['r1_key_holder'] = "000102030406"
+ params["mobility_domain"] = "a1b2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="FT-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ res1 = dev[0].request("PMKSA_GET %d" % id)
+ logger.info("PMKSA_GET: " + res1)
+ if "UNKNOWN COMMAND" in res1:
+ raise HwsimSkip("PMKSA_GET not supported in the build")
+ if bssid not in res1:
+ raise Exception("PMKSA cache entry missing")
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[0].request("PMKSA_FLUSH")
+
+ id = dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="FT-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ ft_eap_pmksa_caching="1",
+ scan_freq="2412", only_add_network=True)
+ res3 = dev[0].request("PMKSA_GET %d" % id)
+ if res3 != '':
+ raise Exception("Unexpected PMKSA cache entry remains: " + res3)
+
+ for entry in res1.splitlines():
+ if "OK" not in dev[0].request("PMKSA_ADD %d %s" % (id, entry)):
+ raise Exception("Failed to add PMKSA entry")
+
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Connection with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange after external PMKSA cache restore")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[0].request("PMKSA_FLUSH")
+ # Add a PMKSA cache entry for FT-EAP with PMKSA caching disabled to confirm
+ # that the PMKID is not configured to the driver (this part requires manual
+ # check of the debug log currently).
+ dev[0].set_network(id, "ft_eap_pmksa_caching", "0")
+ for entry in res1.splitlines():
+ if "OK" not in dev[0].request("PMKSA_ADD %d %s" % (id, entry)):
+ raise Exception("Failed to add PMKSA entry")
+
+def test_rsn_preauth_processing(dev, apdev):
+ """RSN pre-authentication processing on AP"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['rsn_preauth'] = '1'
+ params['rsn_preauth_interfaces'] = "lo"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ _bssid = binascii.unhexlify(bssid.replace(':', ''))
+ eap_connect(dev[0], hapd, "PAX", "pax.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+ addr = dev[0].own_addr()
+ _addr = binascii.unhexlify(addr.replace(':', ''))
+
+ sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
+ socket.htons(0x88c7))
+ sock.bind(("lo", socket.htons(0x88c7)))
+
+ foreign = b"\x02\x03\x04\x05\x06\x07"
+ proto = b"\x88\xc7"
+ tests = []
+ # RSN: too short pre-auth packet (len=14)
+ tests += [_bssid + foreign + proto]
+ # Not EAPOL-Start
+ tests += [_bssid + foreign + proto + struct.pack('>BBH', 0, 0, 0)]
+ # RSN: pre-auth for foreign address 02:03:04:05:06:07
+ tests += [foreign + foreign + proto + struct.pack('>BBH', 0, 0, 0)]
+ # RSN: pre-auth for already association STA 02:00:00:00:00:00
+ tests += [_bssid + _addr + proto + struct.pack('>BBH', 0, 0, 0)]
+ # New STA
+ tests += [_bssid + foreign + proto + struct.pack('>BBH', 0, 1, 1)]
+ # IEEE 802.1X: received EAPOL-Start from STA
+ tests += [_bssid + foreign + proto + struct.pack('>BBH', 0, 1, 0)]
+ # frame too short for this IEEE 802.1X packet
+ tests += [_bssid + foreign + proto + struct.pack('>BBH', 0, 1, 1)]
+ # EAPOL-Key - Dropped key data from unauthorized Supplicant
+ tests += [_bssid + foreign + proto + struct.pack('>BBH', 2, 3, 0)]
+ # EAPOL-Encapsulated-ASF-Alert
+ tests += [_bssid + foreign + proto + struct.pack('>BBH', 2, 4, 0)]
+ # unknown IEEE 802.1X packet type
+ tests += [_bssid + foreign + proto + struct.pack('>BBH', 2, 255, 0)]
+ for t in tests:
+ sock.send(t)
+
+def test_rsn_preauth_local_errors(dev, apdev):
+ """RSN pre-authentication and local errors on AP"""
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['rsn_preauth'] = '1'
+ params['rsn_preauth_interfaces'] = "lo"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ _bssid = binascii.unhexlify(bssid.replace(':', ''))
+
+ sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
+ socket.htons(0x88c7))
+ sock.bind(("lo", socket.htons(0x88c7)))
+
+ foreign = b"\x02\x03\x04\x05\x06\x07"
+ foreign2 = b"\x02\x03\x04\x05\x06\x08"
+ proto = b"\x88\xc7"
+
+ with alloc_fail(hapd, 1, "ap_sta_add;rsn_preauth_receive"):
+ sock.send(_bssid + foreign + proto + struct.pack('>BBH', 2, 1, 0))
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+
+ with alloc_fail(hapd, 1, "eapol_auth_alloc;rsn_preauth_receive"):
+ sock.send(_bssid + foreign + proto + struct.pack('>BBH', 2, 1, 0))
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+ sock.send(_bssid + foreign + proto + struct.pack('>BBH', 2, 1, 0))
+
+ with alloc_fail(hapd, 1, "eap_server_sm_init;ieee802_1x_new_station;rsn_preauth_receive"):
+ sock.send(_bssid + foreign2 + proto + struct.pack('>BBH', 2, 1, 0))
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+ sock.send(_bssid + foreign2 + proto + struct.pack('>BBH', 2, 1, 0))
+
+ hapd.request("DISABLE")
+ tests = [(1, "=rsn_preauth_iface_add"),
+ (2, "=rsn_preauth_iface_add"),
+ (1, "l2_packet_init;rsn_preauth_iface_add"),
+ (1, "rsn_preauth_iface_init"),
+ (1, "rsn_preauth_iface_init")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE succeeded unexpectedly")
+
+ hapd.set("rsn_preauth_interfaces", "lo lo lo does-not-exist lo ")
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE succeeded unexpectedly")
+ hapd.set("rsn_preauth_interfaces", " lo lo ")
+ if "OK" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE failed")
+ sock.send(_bssid + foreign + proto + struct.pack('>BBH', 2, 1, 0))
+ sock.send(_bssid + foreign2 + proto + struct.pack('>BBH', 2, 1, 0))
+
+def test_pmksa_cache_add_failure(dev, apdev):
+ """PMKSA cache add failure"""
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ with alloc_fail(dev[0], 1, "pmksa_cache_add"):
+ dev[0].connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ pmksa = dev[0].get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
diff --git a/contrib/wpa/tests/hwsim/test_radio_work.py b/contrib/wpa/tests/hwsim/test_radio_work.py
new file mode 100644
index 000000000000..536afba740f7
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_radio_work.py
@@ -0,0 +1,133 @@
+# Radio work tests
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import logging
+logger = logging.getLogger()
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+
+def test_ext_radio_work(dev, apdev):
+ """External radio work item"""
+ id = dev[0].request("RADIO_WORK add test-work-a")
+ if "FAIL" in id:
+ raise Exception("Failed to add radio work")
+ id2 = dev[0].request("RADIO_WORK add test-work-b freq=2417")
+ if "FAIL" in id2:
+ raise Exception("Failed to add radio work")
+ id3 = dev[0].request("RADIO_WORK add test-work-c")
+ if "FAIL" in id3:
+ raise Exception("Failed to add radio work")
+
+ ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ if "EXT-RADIO-WORK-START " + id not in ev:
+ raise Exception("Unexpected radio work start id")
+
+ items = dev[0].request("RADIO_WORK show")
+ if "ext:test-work-a@wlan0:0:1:" not in items:
+ logger.info("Pending radio work items:\n" + items)
+ raise Exception("Radio work item(a) missing from the list")
+ if "ext:test-work-b@wlan0:2417:0:" not in items:
+ logger.info("Pending radio work items:\n" + items)
+ raise Exception("Radio work item(b) missing from the list")
+ if "ext:test-work-c@wlan0:0:0:" not in items:
+ logger.info("Pending radio work items:\n" + items)
+ raise Exception("Radio work item(c) missing from the list")
+
+ dev[0].request("RADIO_WORK done " + id2)
+ dev[0].request("RADIO_WORK done " + id)
+
+ ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ if "EXT-RADIO-WORK-START " + id3 not in ev:
+ raise Exception("Unexpected radio work start id")
+ dev[0].request("RADIO_WORK done " + id3)
+ items = dev[0].request("RADIO_WORK show")
+ if "ext:" in items:
+ logger.info("Pending radio work items:\n" + items)
+ raise Exception("Unexpected remaining radio work item")
+
+ id = dev[0].request("RADIO_WORK add test-work timeout=1")
+ ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ ev = dev[0].wait_event(["EXT-RADIO-WORK-TIMEOUT"], timeout=2)
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to time out")
+ if id not in ev:
+ raise Exception("Radio work id mismatch")
+
+ for i in range(5):
+ dev[0].request(("RADIO_WORK add test-work-%d-" % i) + 100*'a')
+ ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ if "FAIL" not in dev[0].request("RADIO_WORK done 12345678"):
+ raise Exception("Invalid RADIO_WORK done accepted")
+ if "FAIL" not in dev[0].request("RADIO_WORK foo"):
+ raise Exception("Invalid RADIO_WORK accepted")
+ dev[0].request("FLUSH")
+ items = dev[0].request("RADIO_WORK show")
+ if items != "":
+ raise Exception("Unexpected radio work remaining after FLUSH: " + items)
+
+def test_radio_work_cancel(dev, apdev):
+ """Radio work items cancelled on interface removal"""
+ params = hostapd.wpa2_params(ssid="radio", passphrase="12345678")
+ hostapd.add_ap(apdev[0], params)
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.scan(freq="2412")
+
+ id = wpas.request("RADIO_WORK add test-work-a")
+ if "FAIL" in id:
+ raise Exception("Failed to add radio work")
+ ev = wpas.wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ if "EXT-RADIO-WORK-START " + id not in ev:
+ raise Exception("Unexpected radio work start id")
+
+ wpas.connect("radio", psk="12345678", scan_freq="2412",
+ wait_connect=False)
+ time.sleep(1)
+ wpas.interface_remove("wlan5")
+ # add to allow log file renaming
+ wpas.interface_add("wlan5")
+
+def test_ext_radio_work_disconnect_connect(dev, apdev):
+ """External radio work and DISCONNECT clearing connection attempt"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+
+ # Start a radio work to block connection attempt
+ id1 = dev[0].request("RADIO_WORK add test-work-a")
+ if "FAIL" in id1:
+ raise Exception("Failed to add radio work")
+ ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ items = dev[0].request("RADIO_WORK show")
+ if "connect" not in items:
+ raise Exception("Connection radio work not scheduled")
+ dev[0].request("DISCONNECT")
+ items = dev[0].request("RADIO_WORK show")
+ if "connect" in items:
+ raise Exception("Connection radio work not removed on DISCONNECT")
+
+ # Clear radio work to allow any pending work to be started
+ dev[0].request("RADIO_WORK done " + id1)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected connection seen")
diff --git a/contrib/wpa/tests/hwsim/test_radius.py b/contrib/wpa/tests/hwsim/test_radius.py
new file mode 100644
index 000000000000..ca96c979e125
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_radius.py
@@ -0,0 +1,1710 @@
+# RADIUS tests
+# Copyright (c) 2013-2016, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import hashlib
+import hmac
+import logging
+logger = logging.getLogger()
+import os
+import select
+import struct
+import subprocess
+import threading
+import time
+
+import hostapd
+from utils import *
+from test_ap_hs20 import build_dhcp_ack
+from test_ap_ft import ft_params1
+
+def connect(dev, ssid, wait_connect=True):
+ dev.connect(ssid, key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ wait_connect=wait_connect)
+
+@remote_compatible
+def test_radius_auth_unreachable(dev, apdev):
+ """RADIUS Authentication server unreachable"""
+ params = hostapd.wpa2_eap_params(ssid="radius-auth")
+ params['auth_server_port'] = "18139"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-auth", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ logger.info("Checking for RADIUS retries")
+ time.sleep(4)
+ mib = hapd.get_mib()
+ if "radiusAuthClientAccessRequests" not in mib:
+ raise Exception("Missing MIB fields")
+ if int(mib["radiusAuthClientAccessRetransmissions"]) < 1:
+ raise Exception("Missing RADIUS Authentication retransmission")
+ if int(mib["radiusAuthClientPendingRequests"]) < 1:
+ raise Exception("Missing pending RADIUS Authentication request")
+
+def test_radius_auth_unreachable2(dev, apdev):
+ """RADIUS Authentication server unreachable (2)"""
+ subprocess.call(['ip', 'ro', 'replace', '192.168.213.17', 'dev', 'lo'])
+ params = hostapd.wpa2_eap_params(ssid="radius-auth")
+ params['auth_server_addr'] = "192.168.213.17"
+ params['auth_server_port'] = "18139"
+ hapd = hostapd.add_ap(apdev[0], params)
+ subprocess.call(['ip', 'ro', 'del', '192.168.213.17', 'dev', 'lo'])
+ connect(dev[0], "radius-auth", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ logger.info("Checking for RADIUS retries")
+ time.sleep(4)
+ mib = hapd.get_mib()
+ if "radiusAuthClientAccessRequests" not in mib:
+ raise Exception("Missing MIB fields")
+ logger.info("radiusAuthClientAccessRetransmissions: " + mib["radiusAuthClientAccessRetransmissions"])
+
+def test_radius_auth_unreachable3(dev, apdev):
+ """RADIUS Authentication server initially unreachable, but then available"""
+ subprocess.call(['ip', 'ro', 'replace', 'blackhole', '192.168.213.18'])
+ params = hostapd.wpa2_eap_params(ssid="radius-auth")
+ params['auth_server_addr'] = "192.168.213.18"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-auth", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ subprocess.call(['ip', 'ro', 'del', 'blackhole', '192.168.213.18'])
+ time.sleep(0.1)
+ dev[0].request("DISCONNECT")
+ hapd.set('auth_server_addr_replace', '127.0.0.1')
+ dev[0].request("RECONNECT")
+
+ dev[0].wait_connected()
+
+def test_radius_acct_unreachable(dev, apdev):
+ """RADIUS Accounting server unreachable"""
+ params = hostapd.wpa2_eap_params(ssid="radius-acct")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "18139"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-acct")
+ logger.info("Checking for RADIUS retries")
+ time.sleep(4)
+ mib = hapd.get_mib()
+ if "radiusAccClientRetransmissions" not in mib:
+ raise Exception("Missing MIB fields")
+ if int(mib["radiusAccClientRetransmissions"]) < 2:
+ raise Exception("Missing RADIUS Accounting retransmissions")
+ if int(mib["radiusAccClientPendingRequests"]) < 2:
+ raise Exception("Missing pending RADIUS Accounting requests")
+
+def test_radius_acct_unreachable2(dev, apdev):
+ """RADIUS Accounting server unreachable(2)"""
+ subprocess.call(['ip', 'ro', 'replace', '192.168.213.17', 'dev', 'lo'])
+ params = hostapd.wpa2_eap_params(ssid="radius-acct")
+ params['acct_server_addr'] = "192.168.213.17"
+ params['acct_server_port'] = "18139"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+ subprocess.call(['ip', 'ro', 'del', '192.168.213.17', 'dev', 'lo'])
+ connect(dev[0], "radius-acct")
+ logger.info("Checking for RADIUS retries")
+ found = False
+ for i in range(4):
+ time.sleep(1)
+ mib = hapd.get_mib()
+ if "radiusAccClientRetransmissions" not in mib:
+ raise Exception("Missing MIB fields")
+ if int(mib["radiusAccClientRetransmissions"]) > 0 or \
+ int(mib["radiusAccClientPendingRequests"]) > 0:
+ found = True
+ if not found:
+ raise Exception("Missing pending or retransmitted RADIUS Accounting requests")
+
+def test_radius_acct_unreachable3(dev, apdev):
+ """RADIUS Accounting server initially unreachable, but then available"""
+ require_under_vm()
+ subprocess.call(['ip', 'ro', 'replace', 'blackhole', '192.168.213.18'])
+ as_hapd = hostapd.Hostapd("as")
+ as_mib_start = as_hapd.get_mib(param="radius_server")
+ params = hostapd.wpa2_eap_params(ssid="radius-acct")
+ params['acct_server_addr'] = "192.168.213.18"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-acct")
+ subprocess.call(['ip', 'ro', 'del', 'blackhole', '192.168.213.18'])
+ time.sleep(0.1)
+ dev[0].request("DISCONNECT")
+ hapd.set('acct_server_addr_replace', '127.0.0.1')
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+ time.sleep(1)
+ as_mib_end = as_hapd.get_mib(param="radius_server")
+ req_s = int(as_mib_start['radiusAccServTotalResponses'])
+ req_e = int(as_mib_end['radiusAccServTotalResponses'])
+ if req_e <= req_s:
+ raise Exception("Unexpected RADIUS server acct MIB value")
+
+def test_radius_acct_unreachable4(dev, apdev):
+ """RADIUS Accounting server unreachable and multiple STAs"""
+ params = hostapd.wpa2_eap_params(ssid="radius-acct")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "18139"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+ for i in range(20):
+ connect(dev[0], "radius-acct")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_radius_acct(dev, apdev):
+ """RADIUS Accounting"""
+ as_hapd = hostapd.Hostapd("as")
+ as_mib_start = as_hapd.get_mib(param="radius_server")
+ params = hostapd.wpa2_eap_params(ssid="radius-acct")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ params['radius_auth_req_attr'] = ["126:s:Operator", "77:s:testing",
+ "62:d:1"]
+ params['radius_acct_req_attr'] = ["126:s:Operator", "62:d:1",
+ "77:s:testing"]
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-acct")
+ dev[1].connect("radius-acct", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="test-class",
+ password_hex="0123456789abcdef0123456789abcdef")
+ dev[2].connect("radius-acct", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk-cui",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ logger.info("Checking for RADIUS counters")
+ count = 0
+ while True:
+ mib = hapd.get_mib()
+ if int(mib['radiusAccClientResponses']) >= 3:
+ break
+ time.sleep(0.1)
+ count += 1
+ if count > 10:
+ raise Exception("Did not receive Accounting-Response packets")
+
+ if int(mib['radiusAccClientRetransmissions']) > 0:
+ raise Exception("Unexpected Accounting-Request retransmission")
+
+ as_mib_end = as_hapd.get_mib(param="radius_server")
+
+ req_s = int(as_mib_start['radiusAccServTotalRequests'])
+ req_e = int(as_mib_end['radiusAccServTotalRequests'])
+ if req_e < req_s + 2:
+ raise Exception("Unexpected RADIUS server acct MIB value")
+
+ acc_s = int(as_mib_start['radiusAuthServAccessAccepts'])
+ acc_e = int(as_mib_end['radiusAuthServAccessAccepts'])
+ if acc_e < acc_s + 1:
+ raise Exception("Unexpected RADIUS server auth MIB value")
+
+def test_radius_req_attr(dev, apdev, params):
+ """RADIUS request attributes"""
+ try:
+ import sqlite3
+ except ImportError:
+ raise HwsimSkip("No sqlite3 module available")
+ db = os.path.join(params['logdir'], "radius_req_attr.sqlite")
+ as_hapd = hostapd.Hostapd("as")
+ params = hostapd.wpa2_eap_params(ssid="radius-req-attr")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ params['radius_auth_req_attr'] = ["126:s:Operator"]
+ params['radius_acct_req_attr'] = ["126:s:Operator"]
+ params['radius_req_attr_sqlite'] = db
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with sqlite3.connect(db) as conn:
+ sql = "INSERT INTO radius_attributes(sta,reqtype,attr) VALUES (?,?,?)"
+ for e in [(dev[0].own_addr(), "auth", "77:s:conn-info-0"),
+ (dev[1].own_addr(), "auth", "77:s:conn-info-1"),
+ (dev[1].own_addr(), "auth", "77:s:conn-info-1a"),
+ (dev[1].own_addr(), "acct", "77:s:conn-info-1b")]:
+ conn.execute(sql, e)
+ conn.commit()
+
+ connect(dev[0], "radius-req-attr")
+ connect(dev[1], "radius-req-attr")
+ connect(dev[2], "radius-req-attr")
+
+def test_radius_acct_non_ascii_ssid(dev, apdev):
+ """RADIUS Accounting and non-ASCII SSID"""
+ params = hostapd.wpa2_eap_params()
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ ssid2 = "740665007374"
+ params['ssid2'] = ssid2
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid2=ssid2, key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PSK", identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef")
+
+def test_radius_acct_pmksa_caching(dev, apdev):
+ """RADIUS Accounting with PMKSA caching"""
+ as_hapd = hostapd.Hostapd("as")
+ as_mib_start = as_hapd.get_mib(param="radius_server")
+ params = hostapd.wpa2_eap_params(ssid="radius-acct")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-acct")
+ dev[1].connect("radius-acct", key_mgmt="WPA-EAP", scan_freq="2412",
+ eap="PAX", identity="test-class",
+ password_hex="0123456789abcdef0123456789abcdef")
+ for d in [dev[0], dev[1]]:
+ d.request("REASSOCIATE")
+ d.wait_connected(timeout=15, error="Reassociation timed out")
+
+ count = 0
+ while True:
+ mib = hapd.get_mib()
+ if int(mib['radiusAccClientResponses']) >= 4:
+ break
+ time.sleep(0.1)
+ count += 1
+ if count > 10:
+ raise Exception("Did not receive Accounting-Response packets")
+
+ if int(mib['radiusAccClientRetransmissions']) > 0:
+ raise Exception("Unexpected Accounting-Request retransmission")
+
+ as_mib_end = as_hapd.get_mib(param="radius_server")
+
+ req_s = int(as_mib_start['radiusAccServTotalRequests'])
+ req_e = int(as_mib_end['radiusAccServTotalRequests'])
+ if req_e < req_s + 2:
+ raise Exception("Unexpected RADIUS server acct MIB value")
+
+ acc_s = int(as_mib_start['radiusAuthServAccessAccepts'])
+ acc_e = int(as_mib_end['radiusAuthServAccessAccepts'])
+ if acc_e < acc_s + 1:
+ raise Exception("Unexpected RADIUS server auth MIB value")
+
+def test_radius_acct_interim(dev, apdev):
+ """RADIUS Accounting interim update"""
+ as_hapd = hostapd.Hostapd("as")
+ params = hostapd.wpa2_eap_params(ssid="radius-acct")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ params['radius_acct_interim_interval'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-acct")
+ logger.info("Checking for RADIUS counters")
+ as_mib_start = as_hapd.get_mib(param="radius_server")
+ time.sleep(4.1)
+ as_mib_end = as_hapd.get_mib(param="radius_server")
+ req_s = int(as_mib_start['radiusAccServTotalRequests'])
+ req_e = int(as_mib_end['radiusAccServTotalRequests'])
+ if req_e < req_s + 3:
+ raise Exception("Unexpected RADIUS server acct MIB value (req_e=%d req_s=%d)" % (req_e, req_s))
+ # Disable Accounting server and wait for interim update retries to fail and
+ # expire.
+ as_hapd.disable()
+ time.sleep(15)
+ as_hapd.enable()
+ ok = False
+ for i in range(10):
+ time.sleep(1)
+ as_mib = as_hapd.get_mib(param="radius_server")
+ if int(as_mib['radiusAccServTotalRequests']) > 0:
+ ok = True
+ break
+ if not ok:
+ raise Exception("Accounting updates did not seen after server restart")
+
+def test_radius_acct_interim_unreachable(dev, apdev):
+ """RADIUS Accounting interim update with unreachable server"""
+ params = hostapd.wpa2_eap_params(ssid="radius-acct")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "18139"
+ params['acct_server_shared_secret'] = "radius"
+ params['radius_acct_interim_interval'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ start = hapd.get_mib()
+ connect(dev[0], "radius-acct")
+ logger.info("Waiting for interium accounting updates")
+ time.sleep(3.1)
+ end = hapd.get_mib()
+ req_s = int(start['radiusAccClientTimeouts'])
+ req_e = int(end['radiusAccClientTimeouts'])
+ if req_e < req_s + 2:
+ raise Exception("Unexpected RADIUS server acct MIB value")
+
+def test_radius_acct_interim_unreachable2(dev, apdev):
+ """RADIUS Accounting interim update with unreachable server (retry)"""
+ params = hostapd.wpa2_eap_params(ssid="radius-acct")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "18139"
+ params['acct_server_shared_secret'] = "radius"
+ # Use long enough interim update interval to allow RADIUS retransmission
+ # case (3 seconds) to trigger first.
+ params['radius_acct_interim_interval'] = "4"
+ hapd = hostapd.add_ap(apdev[0], params)
+ start = hapd.get_mib()
+ connect(dev[0], "radius-acct")
+ logger.info("Waiting for interium accounting updates")
+ time.sleep(7.5)
+ end = hapd.get_mib()
+ req_s = int(start['radiusAccClientTimeouts'])
+ req_e = int(end['radiusAccClientTimeouts'])
+ if req_e < req_s + 2:
+ raise Exception("Unexpected RADIUS server acct MIB value")
+
+def test_radius_acct_ipaddr(dev, apdev):
+ """RADIUS Accounting and Framed-IP-Address"""
+ try:
+ _test_radius_acct_ipaddr(dev, apdev)
+ finally:
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'ap-br0'],
+ stderr=open('/dev/null', 'w'))
+
+def _test_radius_acct_ipaddr(dev, apdev):
+ params = {"ssid": "radius-acct-open",
+ 'acct_server_addr': "127.0.0.1",
+ 'acct_server_port': "1813",
+ 'acct_server_shared_secret': "radius",
+ 'proxy_arp': '1',
+ 'ap_isolate': '1',
+ 'bridge': 'ap-br0'}
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ try:
+ hapd.enable()
+ except:
+ # For now, do not report failures due to missing kernel support
+ raise HwsimSkip("Could not start hostapd - assume proxyarp not supported in kernel version")
+ bssid = apdev[0]['bssid']
+
+ subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+ dev[0].connect("radius-acct-open", key_mgmt="NONE", scan_freq="2412")
+ addr0 = dev[0].own_addr()
+
+ pkt = build_dhcp_ack(dst_ll="ff:ff:ff:ff:ff:ff", src_ll=bssid,
+ ip_src="192.168.1.1", ip_dst="255.255.255.255",
+ yiaddr="192.168.1.123", chaddr=addr0)
+ if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt).decode()):
+ raise Exception("DATA_TEST_FRAME failed")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.disable()
+
+def send_and_check_reply(srv, req, code, error_cause=0):
+ reply = srv.SendPacket(req)
+ logger.debug("RADIUS response from hostapd")
+ for i in list(reply.keys()):
+ logger.debug("%s: %s" % (i, reply[i]))
+ if reply.code != code:
+ raise Exception("Unexpected response code")
+ if error_cause:
+ if 'Error-Cause' not in reply:
+ raise Exception("Missing Error-Cause")
+ if reply['Error-Cause'][0] != error_cause:
+ raise Exception("Unexpected Error-Cause: {}".format(reply['Error-Cause']))
+
+def test_radius_acct_psk(dev, apdev):
+ """RADIUS Accounting - PSK"""
+ as_hapd = hostapd.Hostapd("as")
+ params = hostapd.wpa2_params(ssid="radius-acct", passphrase="12345678")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("radius-acct", psk="12345678", scan_freq="2412")
+
+def test_radius_acct_psk_sha256(dev, apdev):
+ """RADIUS Accounting - PSK SHA256"""
+ as_hapd = hostapd.Hostapd("as")
+ params = hostapd.wpa2_params(ssid="radius-acct", passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("radius-acct", key_mgmt="WPA-PSK-SHA256",
+ psk="12345678", scan_freq="2412")
+
+def test_radius_acct_ft_psk(dev, apdev):
+ """RADIUS Accounting - FT-PSK"""
+ as_hapd = hostapd.Hostapd("as")
+ params = ft_params1(ssid="radius-acct", passphrase="12345678")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("radius-acct", key_mgmt="FT-PSK",
+ psk="12345678", scan_freq="2412")
+
+def test_radius_acct_ieee8021x(dev, apdev):
+ """RADIUS Accounting - IEEE 802.1X"""
+ check_wep_capa(dev[0])
+ skip_with_fips(dev[0])
+ as_hapd = hostapd.Hostapd("as")
+ params = hostapd.radius_params()
+ params["ssid"] = "radius-acct-1x"
+ params["ieee8021x"] = "1"
+ params["wep_key_len_broadcast"] = "13"
+ params["wep_key_len_unicast"] = "13"
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("radius-acct-1x", key_mgmt="IEEE8021X", eap="PSK",
+ identity="psk.user@example.com",
+ password_hex="0123456789abcdef0123456789abcdef",
+ scan_freq="2412")
+
+def test_radius_das_disconnect(dev, apdev):
+ """RADIUS Dynamic Authorization Extensions - Disconnect"""
+ try:
+ import pyrad.client
+ import pyrad.packet
+ import pyrad.dictionary
+ import radius_das
+ except ImportError:
+ raise HwsimSkip("No pyrad modules available")
+
+ params = hostapd.wpa2_eap_params(ssid="radius-das")
+ params['radius_das_port'] = "3799"
+ params['radius_das_client'] = "127.0.0.1 secret"
+ params['radius_das_require_event_timestamp'] = "1"
+ params['own_ip_addr'] = "127.0.0.1"
+ params['nas_identifier'] = "nas.example.com"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-das")
+ addr = dev[0].p2p_interface_addr()
+ sta = hapd.get_sta(addr)
+ id = sta['dot1xAuthSessionId']
+
+ dict = pyrad.dictionary.Dictionary("dictionary.radius")
+
+ srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
+ secret=b"secret", dict=dict)
+ srv.retries = 1
+ srv.timeout = 1
+
+ logger.info("Disconnect-Request with incorrect secret")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"incorrect",
+ User_Name="foo",
+ NAS_Identifier="localhost",
+ Event_Timestamp=int(time.time()))
+ logger.debug(req)
+ try:
+ reply = srv.SendPacket(req)
+ raise Exception("Unexpected response to Disconnect-Request")
+ except pyrad.client.Timeout:
+ logger.info("Disconnect-Request with incorrect secret properly ignored")
+
+ logger.info("Disconnect-Request without Event-Timestamp")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ User_Name="psk.user@example.com")
+ logger.debug(req)
+ try:
+ reply = srv.SendPacket(req)
+ raise Exception("Unexpected response to Disconnect-Request")
+ except pyrad.client.Timeout:
+ logger.info("Disconnect-Request without Event-Timestamp properly ignored")
+
+ logger.info("Disconnect-Request with non-matching Event-Timestamp")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ User_Name="psk.user@example.com",
+ Event_Timestamp=123456789)
+ logger.debug(req)
+ try:
+ reply = srv.SendPacket(req)
+ raise Exception("Unexpected response to Disconnect-Request")
+ except pyrad.client.Timeout:
+ logger.info("Disconnect-Request with non-matching Event-Timestamp properly ignored")
+
+ logger.info("Disconnect-Request with unsupported attribute")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ User_Name="foo",
+ User_Password="foo",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 401)
+
+ logger.info("Disconnect-Request with invalid Calling-Station-Id")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ User_Name="foo",
+ Calling_Station_Id="foo",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 407)
+
+ logger.info("Disconnect-Request with mismatching User-Name")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ User_Name="foo",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+ logger.info("Disconnect-Request with mismatching Calling-Station-Id")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ Calling_Station_Id="12:34:56:78:90:aa",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+ logger.info("Disconnect-Request with mismatching Acct-Session-Id")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ Acct_Session_Id="12345678-87654321",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+ logger.info("Disconnect-Request with mismatching Acct-Session-Id (len)")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ Acct_Session_Id="12345678",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+ logger.info("Disconnect-Request with mismatching Acct-Multi-Session-Id")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ Acct_Multi_Session_Id="12345678+87654321",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+ logger.info("Disconnect-Request with mismatching Acct-Multi-Session-Id (len)")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ Acct_Multi_Session_Id="12345678",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+ logger.info("Disconnect-Request with no session identification attributes")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 503)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+ logger.info("Disconnect-Request with mismatching NAS-IP-Address")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="192.168.3.4",
+ Acct_Session_Id=id,
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 403)
+
+ logger.info("Disconnect-Request with mismatching NAS-Identifier")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_Identifier="unknown.example.com",
+ Acct_Session_Id=id,
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, 403)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+ logger.info("Disconnect-Request with matching Acct-Session-Id")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ NAS_Identifier="nas.example.com",
+ Acct_Session_Id=id,
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+ dev[0].wait_disconnected(timeout=10)
+ dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+ logger.info("Disconnect-Request with matching Acct-Multi-Session-Id")
+ sta = hapd.get_sta(addr)
+ multi_sess_id = sta['authMultiSessionId']
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ NAS_Identifier="nas.example.com",
+ Acct_Multi_Session_Id=multi_sess_id,
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+ dev[0].wait_disconnected(timeout=10)
+ dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+ logger.info("Disconnect-Request with matching User-Name")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_Identifier="nas.example.com",
+ User_Name="psk.user@example.com",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+ dev[0].wait_disconnected(timeout=10)
+ dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+ logger.info("Disconnect-Request with matching Calling-Station-Id")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ Calling_Station_Id=addr,
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+ dev[0].wait_disconnected(timeout=10)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED", "CTRL-EVENT-CONNECTED"])
+ if ev is None:
+ raise Exception("Timeout while waiting for re-connection")
+ if "CTRL-EVENT-EAP-STARTED" not in ev:
+ raise Exception("Unexpected skipping of EAP authentication in reconnection")
+ dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+ logger.info("Disconnect-Request with matching Calling-Station-Id and non-matching CUI")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ Calling_Station_Id=addr,
+ Chargeable_User_Identity="foo@example.com",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, error_cause=503)
+
+ logger.info("Disconnect-Request with matching CUI")
+ dev[1].connect("radius-das", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk-cui",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ Chargeable_User_Identity="gpsk-chargeable-user-identity",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+ dev[1].wait_disconnected(timeout=10)
+ dev[1].wait_connected(timeout=10, error="Re-connection timed out")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+ connect(dev[2], "radius-das")
+
+ logger.info("Disconnect-Request with matching User-Name - multiple sessions matching")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_Identifier="nas.example.com",
+ User_Name="psk.user@example.com",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, error_cause=508)
+
+ logger.info("Disconnect-Request with User-Name matching multiple sessions, Calling-Station-Id only one")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_Identifier="nas.example.com",
+ Calling_Station_Id=addr,
+ User_Name="psk.user@example.com",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+ dev[0].wait_disconnected(timeout=10)
+ dev[0].wait_connected(timeout=10, error="Re-connection timed out")
+
+ ev = dev[2].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+
+ logger.info("Disconnect-Request with matching Acct-Multi-Session-Id after disassociation")
+ sta = hapd.get_sta(addr)
+ multi_sess_id = sta['authMultiSessionId']
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=10)
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ NAS_Identifier="nas.example.com",
+ Acct_Multi_Session_Id=multi_sess_id,
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].wait_connected(timeout=15)
+
+ logger.info("Disconnect-Request with matching User-Name after disassociation")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=10)
+ dev[2].request("DISCONNECT")
+ dev[2].wait_disconnected(timeout=10)
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ NAS_Identifier="nas.example.com",
+ User_Name="psk.user@example.com",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+ logger.info("Disconnect-Request with matching CUI after disassociation")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected(timeout=10)
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ NAS_Identifier="nas.example.com",
+ Chargeable_User_Identity="gpsk-chargeable-user-identity",
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+ logger.info("Disconnect-Request with matching Calling-Station-Id after disassociation")
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ dev[0].wait_connected(timeout=15)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=10)
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ NAS_Identifier="nas.example.com",
+ Calling_Station_Id=addr,
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+ logger.info("Disconnect-Request with mismatching Calling-Station-Id after disassociation")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ NAS_Identifier="nas.example.com",
+ Calling_Station_Id=addr,
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectNAK, error_cause=503)
+
+def add_message_auth_req(req):
+ req.authenticator = req.CreateAuthenticator()
+ hmac_obj = hmac.new(req.secret, digestmod=hashlib.md5)
+ hmac_obj.update(struct.pack("B", req.code))
+ hmac_obj.update(struct.pack("B", req.id))
+
+ # request attributes
+ req.AddAttribute("Message-Authenticator", 16*b"\x00")
+ attrs = b''
+ for code, datalst in sorted(req.items()):
+ for data in datalst:
+ attrs += req._PktEncodeAttribute(code, data)
+
+ # Length
+ flen = 4 + 16 + len(attrs)
+ hmac_obj.update(struct.pack(">H", flen))
+ hmac_obj.update(16*b"\x00") # all zeros Authenticator in calculation
+ hmac_obj.update(attrs)
+ del req[80]
+ req.AddAttribute("Message-Authenticator", hmac_obj.digest())
+
+def test_radius_das_disconnect_time_window(dev, apdev):
+ """RADIUS Dynamic Authorization Extensions - Disconnect - time window"""
+ try:
+ import pyrad.client
+ import pyrad.packet
+ import pyrad.dictionary
+ import radius_das
+ except ImportError:
+ raise HwsimSkip("No pyrad modules available")
+
+ params = hostapd.wpa2_eap_params(ssid="radius-das")
+ params['radius_das_port'] = "3799"
+ params['radius_das_client'] = "127.0.0.1 secret"
+ params['radius_das_require_event_timestamp'] = "1"
+ params['radius_das_require_message_authenticator'] = "1"
+ params['radius_das_time_window'] = "10"
+ params['own_ip_addr'] = "127.0.0.1"
+ params['nas_identifier'] = "nas.example.com"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-das")
+ addr = dev[0].own_addr()
+ sta = hapd.get_sta(addr)
+ id = sta['dot1xAuthSessionId']
+
+ dict = pyrad.dictionary.Dictionary("dictionary.radius")
+
+ srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
+ secret=b"secret", dict=dict)
+ srv.retries = 1
+ srv.timeout = 1
+
+ logger.info("Disconnect-Request with unsupported attribute")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ NAS_Identifier="nas.example.com",
+ Calling_Station_Id=addr,
+ Event_Timestamp=int(time.time()) - 50)
+ add_message_auth_req(req)
+ logger.debug(req)
+ try:
+ reply = srv.SendPacket(req)
+ raise Exception("Unexpected response to Disconnect-Request")
+ except pyrad.client.Timeout:
+ logger.info("Disconnect-Request with non-matching Event-Timestamp properly ignored")
+
+ logger.info("Disconnect-Request with unsupported attribute")
+ req = radius_das.DisconnectPacket(dict=dict, secret=b"secret",
+ NAS_IP_Address="127.0.0.1",
+ NAS_Identifier="nas.example.com",
+ Calling_Station_Id=addr,
+ Event_Timestamp=int(time.time()))
+ add_message_auth_req(req)
+ send_and_check_reply(srv, req, pyrad.packet.DisconnectACK)
+
+def test_radius_das_coa(dev, apdev):
+ """RADIUS Dynamic Authorization Extensions - CoA"""
+ try:
+ import pyrad.client
+ import pyrad.packet
+ import pyrad.dictionary
+ import radius_das
+ except ImportError:
+ raise HwsimSkip("No pyrad modules available")
+
+ params = hostapd.wpa2_eap_params(ssid="radius-das")
+ params['radius_das_port'] = "3799"
+ params['radius_das_client'] = "127.0.0.1 secret"
+ params['radius_das_require_event_timestamp'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-das")
+ addr = dev[0].p2p_interface_addr()
+ sta = hapd.get_sta(addr)
+ id = sta['dot1xAuthSessionId']
+
+ dict = pyrad.dictionary.Dictionary("dictionary.radius")
+
+ srv = pyrad.client.Client(server="127.0.0.1", acctport=3799,
+ secret=b"secret", dict=dict)
+ srv.retries = 1
+ srv.timeout = 1
+
+ # hostapd does not currently support CoA-Request, so NAK is expected
+ logger.info("CoA-Request with matching Acct-Session-Id")
+ req = radius_das.CoAPacket(dict=dict, secret=b"secret",
+ Acct_Session_Id=id,
+ Event_Timestamp=int(time.time()))
+ send_and_check_reply(srv, req, pyrad.packet.CoANAK, error_cause=405)
+
+def test_radius_ipv6(dev, apdev):
+ """RADIUS connection over IPv6"""
+ params = {}
+ params['ssid'] = 'as'
+ params['beacon_int'] = '2000'
+ params['radius_server_clients'] = 'auth_serv/radius_clients_ipv6.conf'
+ params['radius_server_ipv6'] = '1'
+ params['radius_server_auth_port'] = '18129'
+ params['radius_server_acct_port'] = '18139'
+ params['eap_server'] = '1'
+ params['eap_user_file'] = 'auth_serv/eap_user.conf'
+ params['ca_cert'] = 'auth_serv/ca.pem'
+ params['server_cert'] = 'auth_serv/server.pem'
+ params['private_key'] = 'auth_serv/server.key'
+ hostapd.add_ap(apdev[1], params)
+
+ params = hostapd.wpa2_eap_params(ssid="radius-ipv6")
+ params['auth_server_addr'] = "::0"
+ params['auth_server_port'] = "18129"
+ params['acct_server_addr'] = "::0"
+ params['acct_server_port'] = "18139"
+ params['acct_server_shared_secret'] = "radius"
+ params['own_ip_addr'] = "::0"
+ hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-ipv6")
+
+def test_radius_macacl(dev, apdev):
+ """RADIUS MAC ACL"""
+ params = hostapd.radius_params()
+ params["ssid"] = "radius"
+ params["macaddr_acl"] = "2"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("radius", key_mgmt="NONE", scan_freq="2412")
+
+ # Invalid VLAN ID from RADIUS server
+ dev[2].connect("radius", key_mgmt="NONE", scan_freq="2412")
+ dev[2].request("REMOVE_NETWORK all")
+ dev[2].wait_disconnected()
+ dev[2].connect("radius", key_mgmt="NONE", scan_freq="2412")
+
+def test_radius_macacl_acct(dev, apdev):
+ """RADIUS MAC ACL and accounting enabled"""
+ params = hostapd.radius_params()
+ params["ssid"] = "radius"
+ params["macaddr_acl"] = "2"
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ hostapd.add_ap(apdev[0], params)
+ dev[0].connect("radius", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("radius", key_mgmt="NONE", scan_freq="2412")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ dev[1].request("RECONNECT")
+
+def test_radius_macacl_oom(dev, apdev):
+ """RADIUS MAC ACL and OOM"""
+ params = hostapd.radius_params()
+ params["ssid"] = "radius"
+ params["macaddr_acl"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ with alloc_fail(hapd, 1, "hostapd_allowed_address"):
+ dev[0].connect("radius", key_mgmt="NONE", scan_freq="2412")
+
+ dev[1].scan_for_bss(bssid, freq="2412")
+ with alloc_fail(hapd, 2, "hostapd_allowed_address"):
+ dev[1].connect("radius", key_mgmt="NONE", scan_freq="2412")
+
+ dev[2].scan_for_bss(bssid, freq="2412")
+ with alloc_fail(hapd, 2, "=hostapd_allowed_address"):
+ dev[2].connect("radius", key_mgmt="NONE", scan_freq="2412")
+
+def test_radius_macacl_unreachable(dev, apdev):
+ """RADIUS MAC ACL and server unreachable"""
+ params = hostapd.radius_params()
+ params['auth_server_port'] = "18139"
+ params["ssid"] = "radius"
+ params["macaddr_acl"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ dev[0].connect("radius", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=3)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+
+ logger.info("Fix authentication server port")
+ hapd.set("auth_server_port", "1812")
+ hapd.disable()
+ hapd.enable()
+ dev[0].wait_connected(timeout=20)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_radius_failover(dev, apdev):
+ """RADIUS Authentication and Accounting server failover"""
+ subprocess.call(['ip', 'ro', 'replace', '192.168.213.17', 'dev', 'lo'])
+ as_hapd = hostapd.Hostapd("as")
+ as_mib_start = as_hapd.get_mib(param="radius_server")
+ params = hostapd.wpa2_eap_params(ssid="radius-failover")
+ params["auth_server_addr"] = "192.168.213.17"
+ params["auth_server_port"] = "1812"
+ params["auth_server_shared_secret"] = "testing"
+ params['acct_server_addr'] = "192.168.213.17"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "testing"
+ params['radius_retry_primary_interval'] = "20"
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ hapd.set("auth_server_addr", "127.0.0.1")
+ hapd.set("auth_server_port", "1812")
+ hapd.set("auth_server_shared_secret", "radius")
+ hapd.set('acct_server_addr', "127.0.0.1")
+ hapd.set('acct_server_port', "1813")
+ hapd.set('acct_server_shared_secret', "radius")
+ hapd.enable()
+ ev = hapd.wait_event(["AP-ENABLED", "AP-DISABLED"], timeout=30)
+ if ev is None:
+ raise Exception("AP startup timed out")
+ if "AP-ENABLED" not in ev:
+ raise Exception("AP startup failed")
+ start = os.times()[4]
+
+ try:
+ subprocess.call(['ip', 'ro', 'replace', 'prohibit', '192.168.213.17'])
+ dev[0].request("SET EAPOL::authPeriod 5")
+ connect(dev[0], "radius-failover", wait_connect=False)
+ dev[0].wait_connected(timeout=20)
+ finally:
+ dev[0].request("SET EAPOL::authPeriod 30")
+ subprocess.call(['ip', 'ro', 'del', '192.168.213.17'])
+
+ as_mib_end = as_hapd.get_mib(param="radius_server")
+ req_s = int(as_mib_start['radiusAccServTotalRequests'])
+ req_e = int(as_mib_end['radiusAccServTotalRequests'])
+ if req_e <= req_s:
+ raise Exception("Unexpected RADIUS server acct MIB value")
+
+ end = os.times()[4]
+ try:
+ subprocess.call(['ip', 'ro', 'replace', 'prohibit', '192.168.213.17'])
+ dev[1].request("SET EAPOL::authPeriod 5")
+ if end - start < 21:
+ time.sleep(21 - (end - start))
+ connect(dev[1], "radius-failover", wait_connect=False)
+ dev[1].wait_connected(timeout=20)
+ finally:
+ dev[1].request("SET EAPOL::authPeriod 30")
+ subprocess.call(['ip', 'ro', 'del', '192.168.213.17'])
+
+def run_pyrad_server(srv, t_events):
+ srv.RunWithStop(t_events)
+
+def test_radius_protocol(dev, apdev):
+ """RADIUS Authentication protocol tests with a fake server"""
+ try:
+ import pyrad.server
+ import pyrad.packet
+ import pyrad.dictionary
+ except ImportError:
+ raise HwsimSkip("No pyrad modules available")
+
+ class TestServer(pyrad.server.Server):
+ def _HandleAuthPacket(self, pkt):
+ pyrad.server.Server._HandleAuthPacket(self, pkt)
+ logger.info("Received authentication request")
+ reply = self.CreateReplyPacket(pkt)
+ reply.code = pyrad.packet.AccessAccept
+ if self.t_events['msg_auth'].is_set():
+ logger.info("Add Message-Authenticator")
+ if self.t_events['wrong_secret'].is_set():
+ logger.info("Use incorrect RADIUS shared secret")
+ pw = b"incorrect"
+ else:
+ pw = reply.secret
+ hmac_obj = hmac.new(pw, digestmod=hashlib.md5)
+ hmac_obj.update(struct.pack("B", reply.code))
+ hmac_obj.update(struct.pack("B", reply.id))
+
+ # reply attributes
+ reply.AddAttribute("Message-Authenticator", 16*b"\x00")
+ attrs = reply._PktEncodeAttributes()
+
+ # Length
+ flen = 4 + 16 + len(attrs)
+ hmac_obj.update(struct.pack(">H", flen))
+ hmac_obj.update(pkt.authenticator)
+ hmac_obj.update(attrs)
+ if self.t_events['double_msg_auth'].is_set():
+ logger.info("Include two Message-Authenticator attributes")
+ else:
+ del reply[80]
+ reply.AddAttribute("Message-Authenticator", hmac_obj.digest())
+ self.SendReplyPacket(pkt.fd, reply)
+
+ def RunWithStop(self, t_events):
+ self._poll = select.poll()
+ self._fdmap = {}
+ self._PrepareSockets()
+ self.t_events = t_events
+
+ while not t_events['stop'].is_set():
+ for (fd, event) in self._poll.poll(1000):
+ if event == select.POLLIN:
+ try:
+ fdo = self._fdmap[fd]
+ self._ProcessInput(fdo)
+ except pyrad.server.ServerPacketError as err:
+ logger.info("pyrad server dropping packet: " + str(err))
+ except pyrad.packet.PacketError as err:
+ logger.info("pyrad server received invalid packet: " + str(err))
+ else:
+ logger.error("Unexpected event in pyrad server main loop")
+
+ for fd in self.authfds + self.acctfds:
+ fd.close()
+
+ srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
+ authport=18138, acctport=18139)
+ srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
+ b"radius",
+ "localhost")
+ srv.BindToAddress("")
+ t_events = {}
+ t_events['stop'] = threading.Event()
+ t_events['msg_auth'] = threading.Event()
+ t_events['wrong_secret'] = threading.Event()
+ t_events['double_msg_auth'] = threading.Event()
+ t = threading.Thread(target=run_pyrad_server, args=(srv, t_events))
+ t.start()
+
+ try:
+ params = hostapd.wpa2_eap_params(ssid="radius-test")
+ params['auth_server_port'] = "18138"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-test", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"], timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ time.sleep(1)
+ dev[0].request("REMOVE_NETWORK all")
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ t_events['msg_auth'].set()
+ t_events['wrong_secret'].set()
+ connect(dev[0], "radius-test", wait_connect=False)
+ time.sleep(1)
+ dev[0].request("REMOVE_NETWORK all")
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ t_events['wrong_secret'].clear()
+ connect(dev[0], "radius-test", wait_connect=False)
+ time.sleep(1)
+ dev[0].request("REMOVE_NETWORK all")
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ t_events['double_msg_auth'].set()
+ connect(dev[0], "radius-test", wait_connect=False)
+ time.sleep(1)
+ finally:
+ t_events['stop'].set()
+ t.join()
+
+def build_tunnel_password(secret, authenticator, psk):
+ a = b"\xab\xcd"
+ psk = psk.encode()
+ padlen = 16 - (1 + len(psk)) % 16
+ if padlen == 16:
+ padlen = 0
+ p = struct.pack('B', len(psk)) + psk + padlen * b'\x00'
+ cc_all = bytes()
+ b = hashlib.md5(secret + authenticator + a).digest()
+ while len(p) > 0:
+ pp = bytearray(p[0:16])
+ p = p[16:]
+ bb = bytearray(b)
+ cc = bytearray(pp[i] ^ bb[i] for i in range(len(bb)))
+ cc_all += cc
+ b = hashlib.md5(secret + cc).digest()
+ data = b'\x00' + a + bytes(cc_all)
+ return data
+
+def start_radius_psk_server(psk, invalid_code=False, acct_interim_interval=0,
+ session_timeout=0, reject=False):
+ try:
+ import pyrad.server
+ import pyrad.packet
+ import pyrad.dictionary
+ except ImportError:
+ raise HwsimSkip("No pyrad modules available")
+
+ class TestServer(pyrad.server.Server):
+ def _HandleAuthPacket(self, pkt):
+ pyrad.server.Server._HandleAuthPacket(self, pkt)
+ logger.info("Received authentication request")
+ reply = self.CreateReplyPacket(pkt)
+ reply.code = pyrad.packet.AccessAccept
+ if self.t_events['invalid_code']:
+ reply.code = pyrad.packet.AccessRequest
+ if self.t_events['reject']:
+ reply.code = pyrad.packet.AccessReject
+ data = build_tunnel_password(reply.secret, pkt.authenticator,
+ self.t_events['psk'])
+ reply.AddAttribute("Tunnel-Password", data)
+ if self.t_events['acct_interim_interval']:
+ reply.AddAttribute("Acct-Interim-Interval",
+ self.t_events['acct_interim_interval'])
+ if self.t_events['session_timeout']:
+ reply.AddAttribute("Session-Timeout",
+ self.t_events['session_timeout'])
+ self.SendReplyPacket(pkt.fd, reply)
+
+ def RunWithStop(self, t_events):
+ self._poll = select.poll()
+ self._fdmap = {}
+ self._PrepareSockets()
+ self.t_events = t_events
+
+ while not t_events['stop'].is_set():
+ for (fd, event) in self._poll.poll(1000):
+ if event == select.POLLIN:
+ try:
+ fdo = self._fdmap[fd]
+ self._ProcessInput(fdo)
+ except pyrad.server.ServerPacketError as err:
+ logger.info("pyrad server dropping packet: " + str(err))
+ except pyrad.packet.PacketError as err:
+ logger.info("pyrad server received invalid packet: " + str(err))
+ else:
+ logger.error("Unexpected event in pyrad server main loop")
+
+ for fd in self.authfds + self.acctfds:
+ fd.close()
+
+ srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
+ authport=18138, acctport=18139)
+ srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
+ b"radius",
+ "localhost")
+ srv.BindToAddress("")
+ t_events = {}
+ t_events['stop'] = threading.Event()
+ t_events['psk'] = psk
+ t_events['invalid_code'] = invalid_code
+ t_events['acct_interim_interval'] = acct_interim_interval
+ t_events['session_timeout'] = session_timeout
+ t_events['reject'] = reject
+ t = threading.Thread(target=run_pyrad_server, args=(srv, t_events))
+ t.start()
+ return t, t_events
+
+def hostapd_radius_psk_test_params():
+ params = hostapd.radius_params()
+ params['ssid'] = "test-wpa2-psk"
+ params["wpa"] = "2"
+ params["wpa_key_mgmt"] = "WPA-PSK"
+ params["rsn_pairwise"] = "CCMP"
+ params['macaddr_acl'] = '2'
+ params['wpa_psk_radius'] = '2'
+ params['auth_server_port'] = "18138"
+ return params
+
+def test_radius_psk(dev, apdev):
+ """WPA2 with PSK from RADIUS"""
+ t, t_events = start_radius_psk_server("12345678")
+
+ try:
+ params = hostapd_radius_psk_test_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412")
+ t_events['psk'] = "0123456789abcdef"
+ dev[1].connect("test-wpa2-psk", psk="0123456789abcdef",
+ scan_freq="2412")
+ finally:
+ t_events['stop'].set()
+ t.join()
+
+def test_radius_psk_invalid(dev, apdev):
+ """WPA2 with invalid PSK from RADIUS"""
+ t, t_events = start_radius_psk_server("1234567")
+
+ try:
+ params = hostapd_radius_psk_test_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412",
+ wait_connect=False)
+ time.sleep(1)
+ finally:
+ t_events['stop'].set()
+ t.join()
+
+def test_radius_psk_invalid2(dev, apdev):
+ """WPA2 with invalid PSK (hexstring) from RADIUS"""
+ t, t_events = start_radius_psk_server(64*'q')
+
+ try:
+ params = hostapd_radius_psk_test_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412",
+ wait_connect=False)
+ time.sleep(1)
+ finally:
+ t_events['stop'].set()
+ t.join()
+
+def test_radius_psk_hex_psk(dev, apdev):
+ """WPA2 with PSK hexstring from RADIUS"""
+ t, t_events = start_radius_psk_server(64*'2', acct_interim_interval=19,
+ session_timeout=123)
+
+ try:
+ params = hostapd_radius_psk_test_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-psk", raw_psk=64*'2', scan_freq="2412")
+ finally:
+ t_events['stop'].set()
+ t.join()
+
+def test_radius_psk_unknown_code(dev, apdev):
+ """WPA2 with PSK from RADIUS and unknown code"""
+ t, t_events = start_radius_psk_server(64*'2', invalid_code=True)
+
+ try:
+ params = hostapd_radius_psk_test_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412",
+ wait_connect=False)
+ time.sleep(1)
+ finally:
+ t_events['stop'].set()
+ t.join()
+
+def test_radius_psk_reject(dev, apdev):
+ """WPA2 with PSK from RADIUS and reject"""
+ t, t_events = start_radius_psk_server("12345678", reject=True)
+
+ try:
+ params = hostapd_radius_psk_test_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("No CTRL-EVENT-AUTH-REJECT event")
+ dev[0].request("DISCONNECT")
+ finally:
+ t_events['stop'].set()
+ t.join()
+
+def test_radius_psk_oom(dev, apdev):
+ """WPA2 with PSK from RADIUS and OOM"""
+ t, t_events = start_radius_psk_server(64*'2')
+
+ try:
+ params = hostapd_radius_psk_test_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+ dev[0].scan_for_bss(bssid, freq="2412")
+ with alloc_fail(hapd, 1, "=hostapd_acl_recv_radius"):
+ dev[0].connect("test-wpa2-psk", psk="12345678", scan_freq="2412",
+ wait_connect=False)
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+ finally:
+ t_events['stop'].set()
+ t.join()
+
+def test_radius_psk_default(dev, apdev):
+ """WPA2 with default PSK"""
+ ssid = "test-wpa2-psk"
+ params = hostapd.radius_params()
+ params['ssid'] = ssid
+ params["wpa"] = "2"
+ params["wpa_key_mgmt"] = "WPA-PSK"
+ params["rsn_pairwise"] = "CCMP"
+ params['macaddr_acl'] = '2'
+ params['wpa_psk_radius'] = '1'
+ params['wpa_passphrase'] = 'qwertyuiop'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(ssid, psk="qwertyuiop", scan_freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ hapd.disable()
+ hapd.set("wpa_psk_radius", "2")
+ hapd.enable()
+ dev[0].connect(ssid, psk="qwertyuiop", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-AUTH-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("No CTRL-EVENT-AUTH-REJECT event")
+ dev[0].request("DISCONNECT")
+
+def test_radius_auth_force_client_addr(dev, apdev):
+ """RADIUS client address specified"""
+ params = hostapd.wpa2_eap_params(ssid="radius-auth")
+ params['radius_client_addr'] = "127.0.0.1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-auth")
+
+def test_radius_auth_force_client_dev(dev, apdev):
+ """RADIUS client device specified"""
+ params = hostapd.wpa2_eap_params(ssid="radius-auth")
+ params['radius_client_dev'] = "lo"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-auth")
+
+@remote_compatible
+def test_radius_auth_force_invalid_client_addr(dev, apdev):
+ """RADIUS client address specified and invalid address"""
+ params = hostapd.wpa2_eap_params(ssid="radius-auth")
+ #params['radius_client_addr'] = "10.11.12.14"
+ params['radius_client_addr'] = "1::2"
+ hapd = hostapd.add_ap(apdev[0], params)
+ connect(dev[0], "radius-auth", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED"])
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+
+def add_message_auth(req):
+ req.authenticator = req.CreateAuthenticator()
+ hmac_obj = hmac.new(req.secret, digestmod=hashlib.md5)
+ hmac_obj.update(struct.pack("B", req.code))
+ hmac_obj.update(struct.pack("B", req.id))
+
+ # request attributes
+ req.AddAttribute("Message-Authenticator", 16*b"\x00")
+ attrs = req._PktEncodeAttributes()
+
+ # Length
+ flen = 4 + 16 + len(attrs)
+ hmac_obj.update(struct.pack(">H", flen))
+ hmac_obj.update(req.authenticator)
+ hmac_obj.update(attrs)
+ del req[80]
+ req.AddAttribute("Message-Authenticator", hmac_obj.digest())
+
+def test_radius_server_failures(dev, apdev):
+ """RADIUS server failure cases"""
+ try:
+ import pyrad.client
+ import pyrad.packet
+ import pyrad.dictionary
+ except ImportError:
+ raise HwsimSkip("No pyrad modules available")
+
+ dict = pyrad.dictionary.Dictionary("dictionary.radius")
+ client = pyrad.client.Client(server="127.0.0.1", authport=1812,
+ secret=b"radius", dict=dict)
+ client.retries = 1
+ client.timeout = 1
+
+ # unexpected State
+ req = client.CreateAuthPacket(code=pyrad.packet.AccessRequest,
+ User_Name="foo")
+ req['State'] = b'foo-state'
+ add_message_auth(req)
+ reply = client.SendPacket(req)
+ if reply.code != pyrad.packet.AccessReject:
+ raise Exception("Unexpected RADIUS response code " + str(reply.code))
+
+ # no EAP-Message
+ req = client.CreateAuthPacket(code=pyrad.packet.AccessRequest,
+ User_Name="foo")
+ add_message_auth(req)
+ try:
+ reply = client.SendPacket(req)
+ raise Exception("Unexpected response")
+ except pyrad.client.Timeout:
+ pass
+
+def test_ap_vlan_wpa2_psk_radius_required(dev, apdev):
+ """AP VLAN with WPA2-PSK and RADIUS attributes required"""
+ try:
+ import pyrad.server
+ import pyrad.packet
+ import pyrad.dictionary
+ except ImportError:
+ raise HwsimSkip("No pyrad modules available")
+
+ class TestServer(pyrad.server.Server):
+ def _HandleAuthPacket(self, pkt):
+ pyrad.server.Server._HandleAuthPacket(self, pkt)
+ logger.info("Received authentication request")
+ reply = self.CreateReplyPacket(pkt)
+ reply.code = pyrad.packet.AccessAccept
+ secret = reply.secret
+ if self.t_events['extra'].is_set():
+ reply.AddAttribute("Chargeable-User-Identity", "test-cui")
+ reply.AddAttribute("User-Name", "test-user")
+ if self.t_events['long'].is_set():
+ reply.AddAttribute("Tunnel-Type", 13)
+ reply.AddAttribute("Tunnel-Medium-Type", 6)
+ reply.AddAttribute("Tunnel-Private-Group-ID", "1")
+ self.SendReplyPacket(pkt.fd, reply)
+
+ def RunWithStop(self, t_events):
+ self._poll = select.poll()
+ self._fdmap = {}
+ self._PrepareSockets()
+ self.t_events = t_events
+
+ while not t_events['stop'].is_set():
+ for (fd, event) in self._poll.poll(1000):
+ if event == select.POLLIN:
+ try:
+ fdo = self._fdmap[fd]
+ self._ProcessInput(fdo)
+ except pyrad.server.ServerPacketError as err:
+ logger.info("pyrad server dropping packet: " + str(err))
+ except pyrad.packet.PacketError as err:
+ logger.info("pyrad server received invalid packet: " + str(err))
+ else:
+ logger.error("Unexpected event in pyrad server main loop")
+
+ for fd in self.authfds + self.acctfds:
+ fd.close()
+
+ srv = TestServer(dict=pyrad.dictionary.Dictionary("dictionary.radius"),
+ authport=18138, acctport=18139)
+ srv.hosts["127.0.0.1"] = pyrad.server.RemoteHost("127.0.0.1",
+ b"radius",
+ "localhost")
+ srv.BindToAddress("")
+ t_events = {}
+ t_events['stop'] = threading.Event()
+ t_events['long'] = threading.Event()
+ t_events['extra'] = threading.Event()
+ t = threading.Thread(target=run_pyrad_server, args=(srv, t_events))
+ t.start()
+
+ try:
+ ssid = "test-wpa2-psk"
+ params = hostapd.radius_params()
+ params['ssid'] = ssid
+ params["wpa"] = "2"
+ params["wpa_key_mgmt"] = "WPA-PSK"
+ params["rsn_pairwise"] = "CCMP"
+ params['macaddr_acl'] = '2'
+ params['dynamic_vlan'] = "2"
+ params['wpa_passphrase'] = '0123456789abcdefghi'
+ params['auth_server_port'] = "18138"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ logger.info("connecting without VLAN")
+ dev[0].connect(ssid, psk="0123456789abcdefghi", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=20)
+ if ev is None:
+ raise Exception("Timeout on connection attempt")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected success without vlan parameters")
+ logger.info("connecting without VLAN failed as expected")
+
+ logger.info("connecting without VLAN (CUI/User-Name)")
+ t_events['extra'].set()
+ dev[1].connect(ssid, psk="0123456789abcdefghi", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=20)
+ if ev is None:
+ raise Exception("Timeout on connection attempt")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected success without vlan parameters(2)")
+ logger.info("connecting without VLAN failed as expected(2)")
+ t_events['extra'].clear()
+
+ t_events['long'].set()
+ logger.info("connecting with VLAN")
+ dev[2].connect(ssid, psk="0123456789abcdefghi", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=20)
+ if ev is None:
+ raise Exception("Timeout on connection attempt")
+ if "CTRL-EVENT-SSID-TEMP-DISABLED" in ev:
+ raise Exception("Unexpected failure with vlan parameters")
+ logger.info("connecting with VLAN succeeded as expected")
+ finally:
+ t_events['stop'].set()
+ t.join()
+
+def test_radius_mppe_failure(dev, apdev):
+ """RADIUS failure when adding MPPE keys"""
+ params = {"ssid": "as", "beacon_int": "2000",
+ "radius_server_clients": "auth_serv/radius_clients.conf",
+ "radius_server_auth_port": '18127',
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ca.pem",
+ "server_cert": "auth_serv/server.pem",
+ "private_key": "auth_serv/server.key"}
+ authsrv = hostapd.add_ap(apdev[1], params)
+
+ params = hostapd.wpa2_eap_params(ssid="test-wpa2-eap")
+ params['auth_server_port'] = "18127"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with fail_test(authsrv, 1, "os_get_random;radius_msg_add_mppe_keys"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="user", anonymous_identity="ttls",
+ password="password",
+ ca_cert="auth_serv/ca.pem", phase2="autheap=GTC",
+ wait_connect=False, scan_freq="2412")
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_radius_acct_failure(dev, apdev):
+ """RADIUS Accounting and failure to add attributes"""
+ # Connection goes through, but Accounting-Request cannot be sent out due to
+ # NAS-Identifier being too long to fit into a RADIUS attribute.
+ params = {"ssid": "radius-acct-open",
+ 'acct_server_addr': "127.0.0.1",
+ 'acct_server_port': "1813",
+ 'acct_server_shared_secret': "radius",
+ 'nas_identifier': 255*'A'}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("radius-acct-open", key_mgmt="NONE", scan_freq="2412")
+
+def test_radius_acct_failure_oom(dev, apdev):
+ """RADIUS Accounting and failure to add attributes due to OOM"""
+ params = {"ssid": "radius-acct-open",
+ 'acct_server_addr': "127.0.0.1",
+ 'acct_server_port': "1813",
+ 'acct_server_shared_secret': "radius",
+ 'radius_acct_interim_interval': "1",
+ 'nas_identifier': 250*'A',
+ 'radius_acct_req_attr': ["126:s:" + 250*'B',
+ "77:s:" + 250*'C',
+ "127:s:" + 250*'D',
+ "181:s:" + 250*'E']}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ with alloc_fail(hapd, 1, "radius_msg_add_attr;?radius_msg_add_attr_int32;=accounting_msg"):
+ dev[0].connect("radius-acct-open", key_mgmt="NONE", scan_freq="2412")
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[1].scan_for_bss(bssid, freq="2412")
+ with alloc_fail(hapd, 1, "accounting_sta_report"):
+ dev[1].connect("radius-acct-open", key_mgmt="NONE", scan_freq="2412")
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[1].wait_disconnected()
+
+ tests = [(1, "radius_msg_add_attr;?radius_msg_add_attr_int32;=accounting_msg"),
+ (2, "radius_msg_add_attr;accounting_msg"),
+ (3, "radius_msg_add_attr;accounting_msg")]
+ for count, func in tests:
+ with fail_test(hapd, count, func):
+ dev[0].connect("radius-acct-open", key_mgmt="NONE",
+ scan_freq="2412")
+ wait_fail_trigger(hapd, "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].connect("radius-acct-open", key_mgmt="NONE", scan_freq="2412")
+ with fail_test(hapd, 8,
+ "radius_msg_add_attr;?radius_msg_add_attr_int32;=accounting_sta_report"):
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ wait_fail_trigger(hapd, "GET_FAIL")
+
+ with fail_test(hapd, 1, "radius_msg_add_attr;=accounting_report_state"):
+ hapd.disable()
+
+def test_radius_acct_failure_oom_rsn(dev, apdev):
+ """RADIUS Accounting in RSN and failure to add attributes due to OOM"""
+ params = hostapd.wpa2_eap_params(ssid="radius-acct")
+ params['acct_server_addr'] = "127.0.0.1"
+ params['acct_server_port'] = "1813"
+ params['acct_server_shared_secret'] = "radius"
+ params['radius_acct_interim_interval'] = "1"
+ params['nas_identifier'] = 250*'A'
+ params['radius_acct_req_attr'] = ["126:s:" + 250*'B',
+ "77:s:" + 250*'C',
+ "127:s:" + 250*'D',
+ "181:s:" + 250*'E']
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq="2412")
+ with alloc_fail(hapd, 1, "radius_msg_add_attr;?radius_msg_add_attr_int32;=accounting_msg"):
+ connect(dev[0], "radius-acct")
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+
+ dev[1].scan_for_bss(bssid, freq="2412")
+ with alloc_fail(hapd, 1, "accounting_sta_report"):
+ connect(dev[1], "radius-acct")
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+
+ dev[2].scan_for_bss(bssid, freq="2412")
+ connect(dev[2], "radius-acct")
+
+ for i in range(1, 8):
+ with alloc_fail(hapd, i, "radius_msg_add_attr;?radius_msg_add_attr_int32;=accounting_msg"):
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+
+ for i in range(1, 15):
+ with alloc_fail(hapd, i, "radius_msg_add_attr;?radius_msg_add_attr_int32;=accounting_sta_report"):
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+
+def test_radius_acct_failure_sta_data(dev, apdev):
+ """RADIUS Accounting and failure to get STA data"""
+ params = {"ssid": "radius-acct-open",
+ 'acct_server_addr': "127.0.0.1",
+ 'acct_server_port': "1813",
+ 'acct_server_shared_secret': "radius"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with fail_test(hapd, 1, "accounting_sta_update_stats"):
+ dev[0].connect("radius-acct-open", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ hapd.wait_event(["AP-STA-DISCONNECTED"], timeout=1)
diff --git a/contrib/wpa/tests/hwsim/test_rfkill.py b/contrib/wpa/tests/hwsim/test_rfkill.py
new file mode 100644
index 000000000000..5acfb5663d9a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_rfkill.py
@@ -0,0 +1,242 @@
+# rfkill tests
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import time
+
+import hostapd
+from hostapd import HostapdGlobal
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+from rfkill import RFKill
+from utils import HwsimSkip
+from hwsim import HWSimRadio
+
+def get_rfkill(dev):
+ phy = dev.get_driver_status_field("phyname")
+ try:
+ for r, s, h in RFKill.list():
+ if r.name == phy:
+ return r
+ except Exception as e:
+ raise HwsimSkip("No rfkill available: " + str(e))
+ raise HwsimSkip("No rfkill match found for the interface")
+
+def test_rfkill_open(dev, apdev):
+ """rfkill block/unblock during open mode connection"""
+ rfk = get_rfkill(dev[0])
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ try:
+ logger.info("rfkill block")
+ rfk.block()
+ dev[0].wait_disconnected(timeout=10,
+ error="Missing disconnection event on rfkill block")
+
+ if "FAIL" not in dev[0].request("REASSOCIATE"):
+ raise Exception("REASSOCIATE accepted while disabled")
+ if "FAIL" not in dev[0].request("REATTACH"):
+ raise Exception("REATTACH accepted while disabled")
+ if "FAIL" not in dev[0].request("RECONNECT"):
+ raise Exception("RECONNECT accepted while disabled")
+ if "FAIL" not in dev[0].request("FETCH_OSU"):
+ raise Exception("FETCH_OSU accepted while disabled")
+
+ logger.info("rfkill unblock")
+ rfk.unblock()
+ dev[0].wait_connected(timeout=10,
+ error="Missing connection event on rfkill unblock")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ rfk.unblock()
+
+def test_rfkill_wpa2_psk(dev, apdev):
+ """rfkill block/unblock during WPA2-PSK connection"""
+ rfk = get_rfkill(dev[0])
+
+ ssid = "test-wpa2-psk"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect(ssid, psk=passphrase, scan_freq="2412")
+ hapd.wait_sta()
+ try:
+ logger.info("rfkill block")
+ rfk.block()
+ dev[0].wait_disconnected(timeout=10,
+ error="Missing disconnection event on rfkill block")
+
+ logger.info("rfkill unblock")
+ rfk.unblock()
+ dev[0].wait_connected(timeout=10,
+ error="Missing connection event on rfkill unblock")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ finally:
+ rfk.unblock()
+
+def test_rfkill_autogo(dev, apdev):
+ """rfkill block/unblock for autonomous P2P GO"""
+ rfk0 = get_rfkill(dev[0])
+ rfk1 = get_rfkill(dev[1])
+
+ dev[0].p2p_start_go()
+ dev[1].request("SET p2p_no_group_iface 0")
+ dev[1].p2p_start_go()
+
+ try:
+ logger.info("rfkill block 0")
+ rfk0.block()
+ ev = dev[0].wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+ if ev is None:
+ raise Exception("Group removal not reported")
+ if "reason=UNAVAILABLE" not in ev:
+ raise Exception("Unexpected group removal reason: " + ev)
+ if "FAIL" not in dev[0].request("P2P_LISTEN 1"):
+ raise Exception("P2P_LISTEN accepted unexpectedly")
+ if "FAIL" not in dev[0].request("P2P_LISTEN"):
+ raise Exception("P2P_LISTEN accepted unexpectedly")
+
+ logger.info("rfkill block 1")
+ rfk1.block()
+ ev = dev[1].wait_global_event(["P2P-GROUP-REMOVED"], timeout=10)
+ if ev is None:
+ raise Exception("Group removal not reported")
+ if "reason=UNAVAILABLE" not in ev:
+ raise Exception("Unexpected group removal reason: " + ev)
+
+ logger.info("rfkill unblock 0")
+ rfk0.unblock()
+ logger.info("rfkill unblock 1")
+ rfk1.unblock()
+ time.sleep(1)
+ finally:
+ rfk0.unblock()
+ rfk1.unblock()
+
+def _test_rfkill_p2p_discovery(dev0, dev1):
+ """rfkill block/unblock P2P Discovery"""
+ rfk0 = get_rfkill(dev0)
+ rfk1 = get_rfkill(dev1)
+
+ try:
+ addr0 = dev0.p2p_dev_addr()
+
+ logger.info("rfkill block 0")
+ rfk0.block()
+ logger.info("rfkill block 1")
+ rfk1.block()
+
+ for i in range(10):
+ time.sleep(0.1)
+ if dev0.get_status_field("wpa_state") == "INTERFACE_DISABLED" and dev1.get_status_field("wpa_state") == "INTERFACE_DISABLED":
+ break
+
+ if "OK" in dev0.p2p_listen():
+ raise Exception("P2P Listen success although in rfkill")
+
+ if "OK" in dev1.p2p_find():
+ raise Exception("P2P Find success although in rfkill")
+
+ dev0.dump_monitor()
+ dev1.dump_monitor()
+
+ logger.info("rfkill unblock 0")
+ rfk0.unblock()
+ logger.info("rfkill unblock 1")
+ rfk1.unblock()
+
+ for i in range(10):
+ time.sleep(0.1)
+ if dev0.get_status_field("wpa_state") != "INTERFACE_DISABLED" and dev1.get_status_field("wpa_state") != "INTERFACE_DISABLED":
+ break
+
+ if "OK" not in dev0.p2p_listen():
+ raise Exception("P2P Listen failed after unblocking rfkill")
+
+ if not dev1.discover_peer(addr0, social=True):
+ raise Exception("Failed to discover peer after unblocking rfkill")
+
+ finally:
+ rfk0.unblock()
+ rfk1.unblock()
+ dev0.p2p_stop_find()
+ dev1.p2p_stop_find()
+ dev0.dump_monitor()
+ dev1.dump_monitor()
+
+def test_rfkill_p2p_discovery(dev, apdev):
+ """rfkill block/unblock P2P Discovery"""
+ _test_rfkill_p2p_discovery(dev[0], dev[1])
+
+def test_rfkill_p2p_discovery_p2p_dev(dev, apdev):
+ """rfkill block/unblock P2P Discovery with P2P Device"""
+ with HWSimRadio(use_p2p_device=True) as (radio, iface):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add(iface)
+ _test_rfkill_p2p_discovery(dev[0], wpas)
+ _test_rfkill_p2p_discovery(wpas, dev[1])
+
+def test_rfkill_hostapd(dev, apdev):
+ """rfkill block/unblock during and prior to hostapd operations"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+
+ rfk = get_rfkill(hapd)
+
+ try:
+ rfk.block()
+ ev = hapd.wait_event(["INTERFACE-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("INTERFACE-DISABLED event not seen")
+ rfk.unblock()
+ ev = hapd.wait_event(["INTERFACE-ENABLED"], timeout=5)
+ if ev is None:
+ raise Exception("INTERFACE-ENABLED event not seen")
+ # hostapd does not current re-enable beaconing automatically
+ hapd.disable()
+ hapd.enable()
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ rfk.block()
+ ev = hapd.wait_event(["INTERFACE-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("INTERFACE-DISABLED event not seen")
+ dev[0].wait_disconnected(timeout=10)
+ dev[0].request("DISCONNECT")
+ hapd.disable()
+
+ hglobal = HostapdGlobal(apdev[0])
+ hglobal.flush()
+ hglobal.remove(apdev[0]['ifname'])
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open2"},
+ no_enable=True)
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("ENABLE succeeded unexpectedly (rfkill)")
+ finally:
+ rfk.unblock()
+
+def test_rfkill_wpas(dev, apdev):
+ """rfkill block prior to wpa_supplicant start"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ rfk = get_rfkill(wpas)
+ wpas.interface_remove("wlan5")
+ try:
+ rfk.block()
+ wpas.interface_add("wlan5")
+ time.sleep(0.5)
+ state = wpas.get_status_field("wpa_state")
+ if state != "INTERFACE_DISABLED":
+ raise Exception("Unexpected state with rfkill blocked: " + state)
+ rfk.unblock()
+ time.sleep(0.5)
+ state = wpas.get_status_field("wpa_state")
+ if state == "INTERFACE_DISABLED":
+ raise Exception("Unexpected state with rfkill unblocked: " + state)
+ finally:
+ rfk.unblock()
diff --git a/contrib/wpa/tests/hwsim/test_rrm.py b/contrib/wpa/tests/hwsim/test_rrm.py
new file mode 100644
index 000000000000..9111a357eaca
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_rrm.py
@@ -0,0 +1,2142 @@
+# Radio measurement
+# Copyright(c) 2013 - 2016 Intel Mobile Communications GmbH.
+# Copyright(c) 2011 - 2016 Intel Corporation. All rights reserved.
+# Copyright (c) 2017, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import re
+import logging
+logger = logging.getLogger()
+import struct
+import subprocess
+import time
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+from remotehost import remote_compatible
+
+def check_rrm_support(dev):
+ rrm = int(dev.get_driver_status_field("capa.rrm_flags"), 16)
+ if rrm & 0x5 != 0x5 and rrm & 0x10 != 0x10:
+ raise HwsimSkip("Required RRM capabilities are not supported")
+
+def check_tx_power_support(dev):
+ rrm = int(dev.get_driver_status_field("capa.rrm_flags"), 16)
+ if rrm & 0x8 != 0x8:
+ raise HwsimSkip("Required RRM capabilities are not supported")
+
+nr = "00112233445500000000510107"
+lci = "01000800101298c0b512926666f6c2f1001c00004104050000c00012"
+civic = "01000b0011223344556677889900998877665544332211aabbccddeeff"
+
+def check_nr_results(dev, bssids=None, lci=False, civic=False):
+ if bssids is None:
+ ev = dev.wait_event(["RRM-NEIGHBOR-REP-REQUEST-FAILED"], timeout=10)
+ if ev is None:
+ raise Exception("RRM neighbor report failure not received")
+ return
+
+ received = []
+ for bssid in bssids:
+ ev = dev.wait_event(["RRM-NEIGHBOR-REP-RECEIVED"], timeout=10)
+ if ev is None:
+ raise Exception("RRM report result not indicated")
+ received.append(ev)
+
+ for bssid in bssids:
+ found = False
+ for r in received:
+ if "RRM-NEIGHBOR-REP-RECEIVED bssid=" + bssid in r:
+ if lci and "lci=" not in r:
+ raise Exception("LCI data not reported for %s" % bssid)
+ if civic and "civic=" not in r:
+ raise Exception("civic data not reported for %s" % bssid)
+ received.remove(r)
+ found = True
+ break
+ if not found:
+ raise Exception("RRM report result for %s not indicated" % bssid)
+
+def test_rrm_neighbor_db(dev, apdev):
+ """hostapd ctrl_iface SET_NEIGHBOR"""
+ params = {"ssid": "test", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ params = {"ssid": "test2", "rrm_neighbor_report": "1"}
+ hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ res = hapd.request("SHOW_NEIGHBOR")
+ if len(res.splitlines()) != 1:
+ raise Exception("Unexpected SHOW_NEIGHBOR output(1): " + res)
+ if apdev[0]['bssid'] not in res:
+ raise Exception("Own BSS not visible in SHOW_NEIGHBOR output")
+
+ if "OK" not in hapd2.request("SET_NEIGHBOR " + res.strip()):
+ raise Exception("Failed to copy neighbor entry to another hostapd")
+ res2 = hapd2.request("SHOW_NEIGHBOR")
+ if len(res2.splitlines()) != 2:
+ raise Exception("Unexpected SHOW_NEIGHBOR output: " + res2)
+ if res not in res2:
+ raise Exception("Copied entry not visible")
+
+ # Bad BSSID
+ if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:gg ssid=\"test1\" nr=" + nr):
+ raise Exception("Set neighbor succeeded unexpectedly")
+
+ # Bad SSID
+ if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=test1 nr=" + nr):
+ raise Exception("Set neighbor succeeded unexpectedly")
+
+ # Bad SSID end
+ if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1 nr=" + nr):
+ raise Exception("Set neighbor succeeded unexpectedly")
+
+ # No SSID
+ if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 nr=" + nr):
+ raise Exception("Set neighbor succeeded unexpectedly")
+
+ # No NR
+ if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\""):
+ raise Exception("Set neighbor succeeded unexpectedly")
+
+ # Odd length of NR
+ if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr[:-1]):
+ raise Exception("Set neighbor succeeded unexpectedly")
+
+ # Invalid lci
+ if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " lci=1"):
+ raise Exception("Set neighbor succeeded unexpectedly")
+
+ # Invalid civic
+ if "FAIL" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " civic=1"):
+ raise Exception("Set neighbor succeeded unexpectedly")
+
+ # No entry yet in database
+ if "FAIL" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\""):
+ raise Exception("Remove neighbor succeeded unexpectedly")
+
+ # Add a neighbor entry
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " lci=" + lci + " civic=" + civic):
+ raise Exception("Set neighbor failed")
+
+ res = hapd.request("SHOW_NEIGHBOR")
+ if len(res.splitlines()) != 2:
+ raise Exception("Unexpected SHOW_NEIGHBOR output(2): " + res)
+ if apdev[0]['bssid'] not in res:
+ raise Exception("Own BSS not visible in SHOW_NEIGHBOR output")
+ if "00:11:22:33:44:55" not in res:
+ raise Exception("Added BSS not visible in SHOW_NEIGHBOR output")
+
+ # Another BSSID with the same SSID
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:56 ssid=\"test1\" nr=" + nr + " lci=" + lci + " civic=" + civic):
+ raise Exception("Set neighbor failed")
+
+ res = hapd.request("SHOW_NEIGHBOR")
+ if len(res.splitlines()) != 3:
+ raise Exception("Unexpected SHOW_NEIGHBOR output(3): " + res)
+ if apdev[0]['bssid'] not in res:
+ raise Exception("Own BSS not visible in SHOW_NEIGHBOR output")
+ if "00:11:22:33:44:55" not in res:
+ raise Exception("Added BSS not visible in SHOW_NEIGHBOR output")
+ if "00:11:22:33:44:56" not in res:
+ raise Exception("Second added BSS not visible in SHOW_NEIGHBOR output")
+
+ # Fewer parameters
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr):
+ raise Exception("Set neighbor failed")
+
+ # SSID in hex format
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=7465737431 nr=" + nr):
+ raise Exception("Set neighbor failed")
+
+ # With more parameters
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " civic=" + civic):
+ raise Exception("Set neighbor failed")
+
+ # With all parameters
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " lci=" + lci + " civic=" + civic):
+ raise Exception("Set neighbor failed")
+
+ # Another SSID on the same BSSID
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test2\" nr=" + nr + " lci=" + lci):
+ raise Exception("Set neighbor failed")
+
+ if "OK" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\""):
+ raise Exception("Remove neighbor failed")
+
+ if "OK" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:56 ssid=\"test1\""):
+ raise Exception("Remove neighbor failed")
+
+ if "OK" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test2\""):
+ raise Exception("Remove neighbor failed")
+
+ # Double remove
+ if "FAIL" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\""):
+ raise Exception("Remove neighbor succeeded unexpectedly")
+
+ # Stationary AP
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test3\" nr=" + nr + " lci=" + lci + " civic=" + civic + " stat"):
+ raise Exception("Set neighbor failed")
+
+ res = hapd.request("SHOW_NEIGHBOR")
+ if len(res.splitlines()) != 2:
+ raise Exception("Unexpected SHOW_NEIGHBOR output(4): " + res)
+ if "00:11:22:33:44:55" not in res or " stat" not in res:
+ raise Exception("Unexpected SHOW_NEIGHBOR output(4b): " + res)
+
+ if "OK" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test3\""):
+ raise Exception("Remove neighbor failed")
+
+ # Add an entry for following REMOVE_NEIGHBOR tests
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=7465737431 nr=" + nr):
+ raise Exception("Set neighbor failed")
+
+ # Invalid remove - bad BSSID
+ if "FAIL" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:5 ssid=\"test1\""):
+ raise Exception("Remove neighbor succeeded unexpectedly")
+
+ # Invalid remove - bad SSID
+ if "FAIL" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1"):
+ raise Exception("Remove neighbor succeeded unexpectedly")
+
+ # Remove without specifying SSID
+ if "OK" not in hapd.request("REMOVE_NEIGHBOR 00:11:22:33:44:55"):
+ raise Exception("Remove neighbor without SSID failed")
+
+ res = hapd.request("SHOW_NEIGHBOR")
+ if len(res.splitlines()) != 1:
+ raise Exception("Unexpected SHOW_NEIGHBOR output(5): " + res)
+ if apdev[0]['bssid'] not in res:
+ raise Exception("Own BSS not visible in SHOW_NEIGHBOR output")
+
+def test_rrm_neighbor_db_failures(dev, apdev):
+ """hostapd ctrl_iface SET_NEIGHBOR failures"""
+ params = {"ssid": "test", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ cmd = "SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test1\" nr=" + nr + " lci=" + lci + " civic=" + civic
+ tests = [(1, "hostapd_neighbor_add"),
+ (1, "wpabuf_dup;hostapd_neighbor_set"),
+ (2, "wpabuf_dup;hostapd_neighbor_set"),
+ (3, "wpabuf_dup;hostapd_neighbor_set")]
+ for count, func in tests:
+ with alloc_fail(hapd, count, func):
+ if "FAIL" not in hapd.request(cmd):
+ raise Exception("Set neighbor succeeded")
+
+def test_rrm_neighbor_db_disabled(dev, apdev):
+ """hostapd ctrl_iface SHOW_NEIGHBOR while neighbor report disabled"""
+ params = {"ssid": "test"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ if "FAIL" not in hapd.request("SHOW_NEIGHBOR"):
+ raise Exception("SHOW_NEIGHBOR accepted")
+
+def test_rrm_neighbor_rep_req(dev, apdev):
+ """wpa_supplicant ctrl_iface NEIGHBOR_REP_REQUEST"""
+ check_rrm_support(dev[0])
+
+ nr1 = "00112233445500000000510107"
+ nr2 = "00112233445600000000510107"
+ nr3 = "dd112233445500000000510107"
+
+ params = {"ssid": "test"}
+ hostapd.add_ap(apdev[0]['ifname'], params)
+ params = {"ssid": "test2", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ bssid1 = apdev[1]['bssid']
+
+ dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+ if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request succeeded unexpectedly (AP without RRM)")
+ if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"abcdef\""):
+ raise Exception("Request succeeded unexpectedly (AP without RRM 2)")
+ dev[0].request("DISCONNECT")
+
+ dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], [bssid1])
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST lci"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], [bssid1])
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST lci civic"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], [bssid1])
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\""):
+ raise Exception("Request failed")
+ check_nr_results(dev[0])
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\" lci civic"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0])
+
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"test3\" nr=" + nr1 + " lci=" + lci + " civic=" + civic):
+ raise Exception("Set neighbor failed")
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:56 ssid=\"test3\" nr=" + nr2 + " lci=" + lci + " civic=" + civic):
+ raise Exception("Set neighbor failed")
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:56 ssid=\"test4\" nr=" + nr2 + " lci=" + lci + " civic=" + civic):
+ raise Exception("Set neighbor failed")
+ if "OK" not in hapd.request("SET_NEIGHBOR dd:11:22:33:44:55 ssid=\"test5\" nr=" + nr3 + " lci=" + lci):
+ raise Exception("Set neighbor failed")
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\""):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["00:11:22:33:44:55", "00:11:22:33:44:56"])
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\" lci"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["00:11:22:33:44:55", "00:11:22:33:44:56"],
+ lci=True)
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\" civic"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["00:11:22:33:44:55", "00:11:22:33:44:56"],
+ civic=True)
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test3\" lci civic"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["00:11:22:33:44:55", "00:11:22:33:44:56"],
+ lci=True, civic=True)
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test4\""):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["00:11:22:33:44:56"])
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test4\" lci"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["00:11:22:33:44:56"], lci=True)
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test4\" civic"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["00:11:22:33:44:56"], civic=True)
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test4\" lci civic"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["00:11:22:33:44:56"], lci=True, civic=True)
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test5\""):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["dd:11:22:33:44:55"])
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test5\" lci"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["dd:11:22:33:44:55"], lci=True)
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test5\" civic"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["dd:11:22:33:44:55"])
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST ssid=\"test5\" lci civic"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], ["dd:11:22:33:44:55"], lci=True)
+
+def test_rrm_neighbor_rep_oom(dev, apdev):
+ """hostapd neighbor report OOM"""
+ check_rrm_support(dev[0])
+
+ nr1 = "00112233445500000000510107"
+ nr2 = "00112233445600000000510107"
+ nr3 = "dd112233445500000000510107"
+
+ params = {"ssid": "test", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+
+ with alloc_fail(hapd, 1, "hostapd_send_nei_report_resp"):
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request failed")
+ ev = dev[0].wait_event(["RRM-NEIGHBOR-REP-REQUEST-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Neighbor report failure not reported")
+
+def test_rrm_lci_req(dev, apdev):
+ """hostapd lci request"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ # station not specified
+ if "FAIL" not in hapd.request("REQ_LCI "):
+ raise Exception("REQ_LCI with no station succeeded unexpectedly")
+
+ # station that is not connected specified
+ if "FAIL" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
+ raise Exception("REQ_LCI succeeded unexpectedly (station not connected)")
+
+ dev[0].request("SET LCI ")
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+
+ # station connected without LCI
+ if "FAIL" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
+ raise Exception("REQ_LCI succeeded unexpectedly (station without lci)")
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=2)
+
+ dev[0].request("SET LCI " + lci)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+
+ # station connected with LCI
+ if "OK" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
+ raise Exception("REQ_LCI failed unexpectedly")
+
+def test_rrm_lci_req_timeout(dev, apdev):
+ """hostapd lci request timeout"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].request("SET LCI " + lci)
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ if "OK" not in hapd.request("REQ_LCI " + addr):
+ raise Exception("REQ_LCI failed unexpectedly")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("No response seen at the AP")
+ # Ignore response and wait for HOSTAPD_RRM_REQUEST_TIMEOUT
+ time.sleep(5.1)
+ # Process response after timeout
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % ev.split(' ')[1]):
+ raise Exception("MGMT_RX_PROCESS failed")
+ for i in range(257):
+ if "OK" not in hapd.request("REQ_LCI " + addr):
+ raise Exception("REQ_LCI failed unexpectedly")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+ hapd.set("ext_mgmt_frame_handling", "0")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_rrm_lci_req_oom(dev, apdev):
+ """LCI report generation OOM"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].request("SET LCI " + lci)
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+
+ with alloc_fail(dev[0], 1, "wpabuf_resize;wpas_rrm_build_lci_report"):
+ if "OK" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
+ raise Exception("REQ_LCI failed unexpectedly")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ dev[0].request("SET LCI ")
+ # This in in wpas_rrm_build_lci_report(), but backtrace may not always work
+ # for the "reject" label there.
+ with alloc_fail(dev[0], 1, "wpabuf_resize;wpas_rrm_handle_msr_req_element"):
+ if "OK" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
+ raise Exception("REQ_LCI failed unexpectedly")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+def test_rrm_lci_req_ap_oom(dev, apdev):
+ """LCI report generation AP OOM and failure"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].request("SET LCI " + lci)
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+
+ with alloc_fail(hapd, 1, "wpabuf_alloc;hostapd_send_lci_req"):
+ if "FAIL" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
+ raise Exception("REQ_LCI succeeded during OOM")
+
+ with fail_test(hapd, 1, "nl80211_send_frame_cmd;hostapd_send_lci_req"):
+ if "FAIL" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
+ raise Exception("REQ_LCI succeeded during failure testing")
+
+def test_rrm_lci_req_get_reltime_failure(dev, apdev):
+ """LCI report generation and os_get_reltime() failure"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].request("SET LCI " + lci)
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+
+ with fail_test(dev[0], 1, "os_get_reltime;wpas_rrm_build_lci_report"):
+ if "OK" not in hapd.request("REQ_LCI " + dev[0].own_addr()):
+ raise Exception("REQ_LCI failed unexpectedly")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+
+def test_rrm_neighbor_rep_req_from_conf(dev, apdev):
+ """wpa_supplicant ctrl_iface NEIGHBOR_REP_REQUEST and hostapd config"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "test2", "rrm_neighbor_report": "1",
+ "stationary_ap": "1", "lci": lci, "civic": civic}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ bssid = apdev[0]['bssid']
+
+ dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request failed")
+ check_nr_results(dev[0], [bssid])
+
+def test_rrm_neighbor_rep_req_timeout(dev, apdev):
+ """wpa_supplicant behavior on NEIGHBOR_REP_REQUEST response timeout"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "test2", "rrm_neighbor_report": "1",
+ "stationary_ap": "1", "lci": lci, "civic": civic}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request failed")
+ msg = hapd.mgmt_rx()
+ if msg is None:
+ raise Exception("Neighbor report request not seen")
+ check_nr_results(dev[0])
+
+def test_rrm_neighbor_rep_req_oom(dev, apdev):
+ """wpa_supplicant ctrl_iface NEIGHBOR_REP_REQUEST OOM"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "test2", "rrm_neighbor_report": "1",
+ "stationary_ap": "1", "lci": lci, "civic": civic}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;wpas_rrm_process_neighbor_rep"):
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request failed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with fail_test(dev[0], 1,
+ "wpa_driver_nl80211_send_action;wpas_rrm_send_neighbor_rep_request"):
+ if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request succeeded unexpectedly")
+
+ with alloc_fail(dev[0], 1,
+ "wpabuf_alloc;wpas_rrm_send_neighbor_rep_request"):
+ if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request succeeded unexpectedly")
+
+def test_rrm_neighbor_rep_req_disconnect(dev, apdev):
+ """wpa_supplicant behavior on disconnection during NEIGHBOR_REP_REQUEST"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "test2", "rrm_neighbor_report": "1",
+ "stationary_ap": "1", "lci": lci, "civic": civic}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request accepted while disconnected")
+
+ dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request failed")
+ msg = hapd.mgmt_rx()
+ if msg is None:
+ raise Exception("Neighbor report request not seen")
+ dev[0].request("DISCONNECT")
+ check_nr_results(dev[0])
+
+def test_rrm_neighbor_rep_req_not_supported(dev, apdev):
+ """NEIGHBOR_REP_REQUEST for AP not supporting neighbor report"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "test2", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
+
+ if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request accepted unexpectedly")
+
+def test_rrm_neighbor_rep_req_busy(dev, apdev):
+ """wpa_supplicant and concurrent NEIGHBOR_REP_REQUEST commands"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "test2", "rrm_neighbor_report": "1",
+ "stationary_ap": "1", "lci": lci, "civic": civic}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ dev[0].connect("test2", key_mgmt="NONE", scan_freq="2412")
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ if "OK" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request failed")
+ msg = hapd.mgmt_rx()
+ if msg is None:
+ raise Exception("Neighbor report request not seen")
+
+ if "FAIL" not in dev[0].request("NEIGHBOR_REP_REQUEST"):
+ raise Exception("Request accepted while disconnected")
+
+def test_rrm_ftm_range_req(dev, apdev):
+ """hostapd FTM range request command"""
+ check_rrm_support(dev[0])
+ try:
+ run_rrm_ftm_range_req(dev, apdev)
+ finally:
+ dev[1].request("VENDOR_ELEM_REMOVE 13 *")
+
+def run_rrm_ftm_range_req(dev, apdev):
+ params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ # station not specified
+ if "FAIL" not in hapd.request("REQ_RANGE "):
+ raise Exception("REQ_RANGE with no station succeeded unexpectedly")
+
+ # station that is not connected specified
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr()):
+ raise Exception("REQ_RANGE succeeded unexpectedly (station not connected)")
+
+ # No responders specified
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 10"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (no responder)")
+
+ # Bad responder address
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 10 00:11:22:33:44:"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (bad responder address)")
+
+ # Bad responder address
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 10 00:11:22:33:44:55 00:11:22:33:44"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (bad responder address 2)")
+
+ # Bad min_ap value
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 300 00:11:22:33:44:55"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (invalid min_ap value)")
+
+ # Bad rand value
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " -1 10 00:11:22:33:44:55"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (invalid rand value)")
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 65536 10 00:11:22:33:44:55"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (invalid rand value)")
+
+ # Missing min_ap value
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (missing min_ap value)")
+
+ # Too many responders
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 10" + 20*" 00:11:22:33:44:55"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (too many responders)")
+ # Wrong min AP count
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 10 00:11:22:33:44:55"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (responder not in database)")
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ # Override RM capabilities to include FTM range report
+ dev[1].request("VENDOR_ELEM_ADD 13 46057100000004")
+ dev[1].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+
+ # Request range: Destination address is not connected
+ if "FAIL" not in hapd.request("REQ_RANGE 11:22:33:44:55:66 10 1 00:11:22:33:44:55"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (responder not in database)")
+
+ # Responder not in database
+ # Note: this check would pass since the station does not support FTM range
+ # request and not because the responder is not in the database.
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[0].own_addr() + " 10 1 00:11:22:33:44:55"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (responder not in database)")
+
+ # Missing neighbor report for 00:11:22:33:44:55
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[1].own_addr() + " 10 1 00:11:22:33:44:55"):
+ raise Exception("REQ_RANGE succeeded unexpectedly (responder not in database)")
+
+ # Send request
+ if "OK" not in hapd.request("REQ_RANGE " + dev[1].own_addr() + " 10 1 " + bssid):
+ raise Exception("REQ_RANGE failed unexpectedly")
+
+ # Too long range request
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[1].own_addr() + " 10 1" + 16*(" " + bssid)):
+ raise Exception("REQ_RANGE accepted for too long range request")
+
+ time.sleep(0.1)
+ dev[0].request("DISCONNECT")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+
+def test_rrm_ftm_range_req_timeout(dev, apdev):
+ """hostapd FTM range request timeout"""
+ check_rrm_support(dev[0])
+ try:
+ run_rrm_ftm_range_req_timeout(dev, apdev)
+ finally:
+ dev[1].request("VENDOR_ELEM_REMOVE 13 *")
+
+def run_rrm_ftm_range_req_timeout(dev, apdev):
+ params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ # Override RM capabilities to include FTM range report
+ dev[1].request("VENDOR_ELEM_ADD 13 46057100000004")
+ dev[1].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[1].own_addr()
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ if "OK" not in hapd.request("REQ_RANGE " + addr + " 10 1 " + bssid):
+ raise Exception("REQ_RANGE failed")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("No response seen at the AP")
+ # Ignore response and wait for HOSTAPD_RRM_REQUEST_TIMEOUT
+ time.sleep(5.1)
+ # Process response after timeout
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % ev.split(' ')[1]):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ for i in range(257):
+ if "OK" not in hapd.request("REQ_RANGE " + addr + " 10 1 " + bssid):
+ raise Exception("REQ_RANGE failed")
+ dev[1].dump_monitor()
+ hapd.dump_monitor()
+
+ hapd.set("ext_mgmt_frame_handling", "0")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+
+def test_rrm_ftm_range_req_failure(dev, apdev):
+ """hostapd FTM range request failure"""
+ check_rrm_support(dev[0])
+ try:
+ run_rrm_ftm_range_req_failure(dev, apdev)
+ finally:
+ dev[1].request("VENDOR_ELEM_REMOVE 13 *")
+
+def run_rrm_ftm_range_req_failure(dev, apdev):
+ params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ # Override RM capabilities to include FTM range report
+ dev[1].request("VENDOR_ELEM_ADD 13 46057100000004")
+ dev[1].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+
+ with alloc_fail(hapd, 1, "wpabuf_alloc;hostapd_send_range_req"):
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[1].own_addr() + " 10 1 " + bssid):
+ raise Exception("REQ_RANGE succeeded during OOM")
+
+ with fail_test(hapd, 1, "nl80211_send_frame_cmd;hostapd_send_range_req"):
+ if "FAIL" not in hapd.request("REQ_RANGE " + dev[1].own_addr() + " 10 1 " + bssid):
+ raise Exception("REQ_RANGE succeeded during failure testing")
+
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+
+def test_rrm_ftm_capa_indication(dev, apdev):
+ """FTM capability indication"""
+ try:
+ _test_rrm_ftm_capa_indication(dev, apdev)
+ finally:
+ dev[0].request("SET ftm_initiator 0")
+ dev[0].request("SET ftm_responder 0")
+
+def _test_rrm_ftm_capa_indication(dev, apdev):
+ params = {"ssid": "ftm",
+ "ftm_responder": "1",
+ "ftm_initiator": "1",}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ if "OK" not in dev[0].request("SET ftm_initiator 1"):
+ raise Exception("could not set ftm_initiator")
+ if "OK" not in dev[0].request("SET ftm_responder 1"):
+ raise Exception("could not set ftm_responder")
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412, force_scan=True)
+
+class BeaconReport:
+ def __init__(self, report):
+ self.opclass, self.channel, self.start, self.duration, self.frame_info, self.rcpi, self.rsni = struct.unpack("<BBQHBBB", report[0:15])
+ report = report[15:]
+ self.bssid = report[0:6]
+ self.bssid_str = "%02x:%02x:%02x:%02x:%02x:%02x" % (struct.unpack('6B', self.bssid))
+ report = report[6:]
+ self.antenna_id, self.parent_tsf = struct.unpack("<BI", report[0:5])
+ report = report[5:]
+ self.subelems = report
+ self.frame_body = None
+ self.frame_body_fragment_id = None
+ self.last_indication = None
+ while len(report) >= 2:
+ eid, elen = struct.unpack('BB', report[0:2])
+ report = report[2:]
+ if len(report) < elen:
+ raise Exception("Invalid subelement in beacon report")
+ if eid == 1:
+ # Reported Frame Body
+ # Contents depends on the reporting detail request:
+ # 0 = no Reported Frame Body subelement
+ # 1 = all fixed fields and any elements identified in Request
+ # element
+ # 2 = all fixed fields and all elements
+ # Fixed fields: Timestamp[8] BeaconInt[2] CapabInfo[2]
+ self.frame_body = report[0:elen]
+ if eid == 2:
+ self.frame_body_fragment_id = report[0:elen]
+ if eid == 164:
+ self.last_indication = report[0:elen]
+ report = report[elen:]
+ def __str__(self):
+ txt = "opclass={} channel={} start={} duration={} frame_info={} rcpi={} rsni={} bssid={} antenna_id={} parent_tsf={}".format(self.opclass, self.channel, self.start, self.duration, self.frame_info, self.rcpi, self.rsni, self.bssid_str, self.antenna_id, self.parent_tsf)
+ if self.frame_body:
+ txt += " frame_body=" + binascii.hexlify(self.frame_body).decode()
+ if self.frame_body_fragment_id:
+ txt += " fragment_id=" + binascii.hexlify(self.frame_body_fragment_id).decode()
+ if self.last_indication:
+ txt += " last_indication=" + binascii.hexlify(self.last_indication).decode()
+
+ return txt
+
+def run_req_beacon(hapd, addr, request):
+ token = hapd.request("REQ_BEACON " + addr + " " + request)
+ if "FAIL" in token:
+ raise Exception("REQ_BEACON failed")
+
+ ev = hapd.wait_event(["BEACON-REQ-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("No TX status event for beacon request received")
+ fields = ev.split(' ')
+ if fields[1] != addr:
+ raise Exception("Unexpected STA address in TX status: " + fields[1])
+ if fields[2] != token:
+ raise Exception("Unexpected dialog token in TX status: " + fields[2] + " (expected " + token + ")")
+ if fields[3] != "ack=1":
+ raise Exception("Unexected ACK status in TX status: " + fields[3])
+ return token
+
+@remote_compatible
+def test_rrm_beacon_req_table(dev, apdev):
+ """Beacon request - beacon table mode"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another"})
+
+ tests = ["REQ_BEACON ",
+ "REQ_BEACON q",
+ "REQ_BEACON 11:22:33:44:55:66",
+ "REQ_BEACON 11:22:33:44:55:66 req_mode=q",
+ "REQ_BEACON 11:22:33:44:55:66 req_mode=11",
+ "REQ_BEACON 11:22:33:44:55:66 1",
+ "REQ_BEACON 11:22:33:44:55:66 1q",
+ "REQ_BEACON 11:22:33:44:55:66 11223344556677889900aabbccddeeff"]
+ for t in tests:
+ if "FAIL" not in hapd.request(t):
+ raise Exception("Invalid command accepted: " + t)
+
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq=2412)
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff")
+
+ for i in range(1, 3):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ if fields[1] != addr:
+ raise Exception("Unexpected STA address in beacon report response: " + fields[1])
+ if fields[2] != token:
+ raise Exception("Unexpected dialog token in beacon report response: " + fields[2] + " (expected " + token + ")")
+ if fields[3] != "00":
+ raise Exception("Unexpected measurement report mode")
+
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+
+ # Default reporting detail is 2, i.e., all fixed fields and elements.
+ if not report.frame_body:
+ raise Exception("Reported Frame Body subelement missing")
+ if len(report.frame_body) <= 12:
+ raise Exception("Too short Reported Frame Body subelement")
+
+def test_rrm_beacon_req_frame_body_fragmentation(dev, apdev):
+ """Beacon request - beacon table mode - frame body fragmentation"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.set('vendor_elements', ("dd051122330203dd0400137400dd04001374ffdd0511"
+ "22330203dd0400137400dd04001374ffdd051122330203dd0400137400dd04001"
+ "374ffdd051122330203dd0400137400dd04001374ffdd051122330203dd040013"
+ "7400dd04001374ffdd051122330203dd0400137400dd04001374ffdd051122330"
+ "203dd0400137400dd04001374ffdd051122330203dd0400137400dd04001374ff"
+ "dd051122330203dd0400137400dd04001374ff"))
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff")
+
+ # 2 beacon reports elements are expected because of fragmentation
+ for i in range(0, 2):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ if fields[1] != addr:
+ raise Exception("Unexpected STA address in beacon report response: " + fields[1])
+ if fields[2] != token:
+ raise Exception("Unexpected dialog token in beacon report response: " + fields[2] + " (expected " + token + ")")
+ if fields[3] != "00":
+ raise Exception("Unexpected measurement report mode")
+
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+
+ # Default reporting detail is 2, i.e., all fixed fields and elements.
+ if not report.frame_body_fragment_id:
+ raise Exception("Reported Frame Body Fragment ID subelement missing")
+ fragment_id = binascii.hexlify(report.frame_body_fragment_id)
+ frag_number = int(fragment_id[2:], 16) & int(0x7f)
+ if frag_number != i:
+ raise Exception("Incorrect fragment number: %d" % frag_number)
+ more_frags = int(fragment_id[2:], 16) >> 7
+ if i == 0 and more_frags != 1:
+ raise Exception("more fragments bit is not set on first fragment")
+ if i == 1 and more_frags != 0:
+ raise Exception("more fragments bit is set on last fragment")
+
+def test_rrm_beacon_req_last_frame_indication(dev, apdev):
+ """Beacon request - beacon table mode - last frame indication"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ # The request contains the last beacon report indication subelement
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffffa40101")
+
+ for i in range(1, 3):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ if fields[1] != addr:
+ raise Exception("Unexpected STA address in beacon report response: " + fields[1])
+ if fields[2] != token:
+ raise Exception("Unexpected dialog token in beacon report response: " + fields[2] + " (expected " + token + ")")
+ if fields[3] != "00":
+ raise Exception("Unexpected measurement report mode")
+
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+
+ if not report.last_indication:
+ raise Exception("Last Beacon Report Indication subelement missing")
+
+ last = binascii.hexlify(report.last_indication).decode()
+ if (i == 2 and last != '01') or (i != 2 and last != '00'):
+ raise Exception("last beacon report indication is not set on last frame")
+
+ # The request does not contain the last beacon report indication subelement
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff")
+
+ for i in range(1, 3):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ if fields[1] != addr:
+ raise Exception("Unexpected STA address in beacon report response: " + fields[1])
+ if fields[2] != token:
+ raise Exception("Unexpected dialog token in beacon report response: " + fields[2] + " (expected " + token + ")")
+ if fields[3] != "00":
+ raise Exception("Unexpected measurement report mode")
+
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+
+ if report.last_indication:
+ raise Exception("Last Beacon Report Indication subelement present but not requested")
+
+@remote_compatible
+def test_rrm_beacon_req_table_detail(dev, apdev):
+ """Beacon request - beacon table mode - reporting detail"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ logger.info("Reporting Detail 0")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020100")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if report.frame_body:
+ raise Exception("Reported Frame Body subelement included with Reporting Detail 0")
+ hapd.dump_monitor()
+
+ logger.info("Reporting Detail 1")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if not report.frame_body:
+ raise Exception("Reported Frame Body subelement missing")
+ if len(report.frame_body) != 12:
+ raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 1")
+ hapd.dump_monitor()
+
+ logger.info("Reporting Detail 2")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020102")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if not report.frame_body:
+ raise Exception("Reported Frame Body subelement missing")
+ if len(report.frame_body) <= 12:
+ raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 2")
+ hapd.dump_monitor()
+
+ logger.info("Reporting Detail 3 (invalid)")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020103")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response to invalid reporting detail 3")
+ hapd.dump_monitor()
+
+ logger.info("Reporting Detail (too short)")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0200")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response to invalid reporting detail")
+ hapd.dump_monitor()
+
+@remote_compatible
+def test_rrm_beacon_req_table_request(dev, apdev):
+ """Beacon request - beacon table mode - request element"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].flush_scan_cache()
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if not report.frame_body:
+ raise Exception("Reported Frame Body subelement missing")
+ if len(report.frame_body) != 12 + 5 + 10:
+ raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 1 and requested elements SSID + SuppRates")
+ hapd.dump_monitor()
+
+ logger.info("Incorrect reporting detail with request subelement")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020102" + "0a03000106")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response (invalid reporting detail)")
+ hapd.dump_monitor()
+
+ logger.info("Invalid request subelement length")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a00")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response (invalid request subelement length)")
+ hapd.dump_monitor()
+
+ logger.info("Multiple request subelements")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a0100" + "0a0101")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response (multiple request subelements)")
+ hapd.dump_monitor()
+
+@remote_compatible
+def test_rrm_beacon_req_table_request_oom(dev, apdev):
+ """Beacon request - beacon table mode - request element OOM"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ with alloc_fail(dev[0], 1,
+ "bitfield_alloc;wpas_rm_handle_beacon_req_subelem"):
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response received (OOM)")
+
+ with alloc_fail(dev[0], 1,
+ "wpabuf_alloc;wpas_rrm_send_msr_report_mpdu"):
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response received (OOM)")
+
+ with fail_test(dev[0], 1,
+ "wpa_driver_nl80211_send_action;wpas_rrm_send_msr_report_mpdu"):
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response received (OOM)")
+
+ with alloc_fail(dev[0], 1,
+ "wpabuf_resize;wpas_add_beacon_rep"):
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a03000106")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received (OOM -> empty report)")
+ fields = ev.split(' ')
+ if len(fields[4]) > 0:
+ raise Exception("Unexpected beacon report received")
+
+@remote_compatible
+def test_rrm_beacon_req_table_bssid(dev, apdev):
+ """Beacon request - beacon table mode - specific BSSID"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ bssid2 = hapd2.own_addr()
+ token = run_req_beacon(hapd, addr, "51000000000002" + bssid2.replace(':', ''))
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if "bssid=" + bssid2 not in str(report):
+ raise Exception("Report for unexpected BSS")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response")
+
+@remote_compatible
+def test_rrm_beacon_req_table_ssid(dev, apdev):
+ """Beacon request - beacon table mode - specific SSID"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ bssid2 = hapd2.own_addr()
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0007" + binascii.hexlify(b"another").decode())
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if "bssid=" + bssid2 not in str(report):
+ raise Exception("Report for unexpected BSS")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response")
+ hapd.dump_monitor()
+
+ logger.info("Wildcard SSID")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0000")
+ for i in range(2):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ hapd.dump_monitor()
+
+ logger.info("Too long SSID")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0021" + 33*"00")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response (invalid SSID subelement in request)")
+ hapd.dump_monitor()
+
+@remote_compatible
+def test_rrm_beacon_req_table_info(dev, apdev):
+ """Beacon request - beacon table mode - Reporting Information subelement"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ logger.info("Unsupported reporting information 1")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "01020100")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response (incapable) is not received")
+
+ fields = ev.split(' ')
+ if fields[3] != "02":
+ raise Exception("Beacon report response - unexpected mode (" + fields[3] + ")")
+ hapd.dump_monitor()
+
+ logger.info("Invalid reporting information length")
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "010100")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response (invalid reporting information length)")
+ hapd.dump_monitor()
+
+@remote_compatible
+def test_rrm_beacon_req_table_unknown_subelem(dev, apdev):
+ """Beacon request - beacon table mode - unknown subelement"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "330101" + "fe00")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+
+@remote_compatible
+def test_rrm_beacon_req_table_truncated_subelem(dev, apdev):
+ """Beacon request - beacon table mode - Truncated subelement"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "0001")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response (truncated subelement)")
+ hapd.dump_monitor()
+
+@remote_compatible
+def test_rrm_beacon_req_table_rsne(dev, apdev):
+ """Beacon request - beacon table mode - RSNE reporting"""
+ params = hostapd.wpa2_params(ssid="rrm-rsn", passphrase="12345678")
+ params["rrm_beacon_report"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm-rsn", psk="12345678", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51000000000002ffffffffffff" + "020101" + "0a0130")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if not report.frame_body:
+ raise Exception("Reported Frame Body subelement missing")
+ if len(report.frame_body) != 12 + 22:
+ raise Exception("Unexpected Reported Frame Body subelement length with Reporting Detail 1 and requested element RSNE")
+ if binascii.unhexlify("30140100000fac040100000fac040100000fac020c00") not in report.frame_body:
+ raise Exception("Full RSNE not found")
+
+def test_rrm_beacon_req_table_vht(dev, apdev):
+ """Beacon request - beacon table mode - VHT"""
+ clear_scan_cache(apdev[0])
+ try:
+ hapd = None
+ params = {"ssid": "rrm-vht",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "test-vht40",
+ "country_code": "FI",
+ "hw_mode": "a",
+ "channel": "48",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ht_capab": "[HT40-]",
+ "vht_capab": "",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "0",
+ }
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq=5240)
+ dev[0].connect("rrm-vht", key_mgmt="NONE", scan_freq="5180")
+
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "f0000000000002ffffffffffff")
+ for i in range(2):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if report.bssid_str == apdev[0]['bssid']:
+ if report.opclass != 128 or report.channel != 36:
+ raise Exception("Incorrect opclass/channel for AP0")
+ elif report.bssid_str == apdev[1]['bssid']:
+ if report.opclass != 117 or report.channel != 48:
+ raise Exception("Incorrect opclass/channel for AP1")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ dev[0].request("DISCONNECT")
+ disable_hapd(hapd)
+ disable_hapd(hapd2)
+ clear_regdom_dev(dev)
+
+@remote_compatible
+def test_rrm_beacon_req_active(dev, apdev):
+ """Beacon request - active scan mode"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51000000640001ffffffffffff")
+
+ for i in range(1, 3):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if report.bssid_str == apdev[0]['bssid']:
+ if report.opclass != 81 or report.channel != 1:
+ raise Exception("Incorrect opclass/channel for AP0")
+ elif report.bssid_str == apdev[1]['bssid']:
+ if report.opclass != 81 or report.channel != 11:
+ raise Exception("Incorrect opclass/channel for AP1")
+
+@remote_compatible
+def test_rrm_beacon_req_active_ignore_old_result(dev, apdev):
+ """Beacon request - active scan mode and old scan result"""
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another"})
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq=2412)
+ hapd2.disable()
+
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51010000640001ffffffffffff")
+
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if report.bssid_str == apdev[1]['bssid']:
+ raise Exception("Old BSS reported")
+
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response")
+
+def start_ap(dev):
+ id = dev.add_network()
+ dev.set_network(id, "mode", "2")
+ dev.set_network_quoted(id, "ssid", 32*'A')
+ dev.set_network_quoted(id, "psk", "1234567890")
+ dev.set_network(id, "frequency", "2412")
+ dev.set_network(id, "scan_freq", "2412")
+ dev.select_network(id)
+ dev.wait_connected()
+
+def test_rrm_beacon_req_active_many(dev, apdev):
+ """Beacon request - active scan mode and many BSSs"""
+ for i in range(1, 7):
+ ifname = apdev[0]['ifname'] if i == 1 else apdev[0]['ifname'] + "-%d" % i
+ hapd1 = hostapd.add_bss(apdev[0], ifname, 'bss-%i.conf' % i)
+ hapd1.set('vendor_elements', "dd50" + 80*'bb')
+ hapd1.request("UPDATE_BEACON")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.request("SET device_name " + 20*'a')
+ start_ap(wpas)
+ start_ap(dev[1])
+ start_ap(dev[2])
+
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ params['vendor_elements'] = "dd50" + 80*'aa'
+ hapd = hostapd.add_ap(apdev[1]['ifname'], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ ok = False
+ for j in range(3):
+ token = run_req_beacon(hapd, addr, "51010000640001ffffffffffff")
+
+ for i in range(10):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ if len(fields[4]) == 0:
+ break
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if i == 9:
+ ok = True
+ if ok:
+ break
+
+@remote_compatible
+def test_rrm_beacon_req_active_ap_channels(dev, apdev):
+ """Beacon request - active scan mode with AP Channel Report subelement"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51ff0000640001ffffffffffff" + "dd0111" + "330351010b" + "dd0111")
+
+ for i in range(1, 3):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if report.bssid_str == apdev[0]['bssid']:
+ if report.opclass != 81 or report.channel != 1:
+ raise Exception("Incorrect opclass/channel for AP0")
+ elif report.bssid_str == apdev[1]['bssid']:
+ if report.opclass != 81 or report.channel != 11:
+ raise Exception("Incorrect opclass/channel for AP1")
+
+@remote_compatible
+def test_rrm_beacon_req_passive_ap_channels(dev, apdev):
+ """Beacon request - passive scan mode with AP Channel Report subelement"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51ff0000640000ffffffffffff" + "330351010b" + "3300" + "dd00")
+
+ for i in range(1, 3):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if report.bssid_str == apdev[0]['bssid']:
+ if report.opclass != 81 or report.channel != 1:
+ raise Exception("Incorrect opclass/channel for AP0")
+ elif report.bssid_str == apdev[1]['bssid']:
+ if report.opclass != 81 or report.channel != 11:
+ raise Exception("Incorrect opclass/channel for AP1")
+
+@remote_compatible
+def test_rrm_beacon_req_active_single_channel(dev, apdev):
+ """Beacon request - active scan mode with single channel"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "510b0000640001ffffffffffff")
+
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+
+@remote_compatible
+def test_rrm_beacon_req_active_ap_channels_unknown_opclass(dev, apdev):
+ """Beacon request - active scan mode with AP Channel Report subelement and unknown opclass"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51ff0000640001ffffffffffff" + "3303ff010b")
+
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response (refused) not received")
+
+ fields = ev.split(' ')
+ if fields[3] != "04":
+ raise Exception("Unexpected beacon report mode: " + fields[3])
+
+@remote_compatible
+def test_rrm_beacon_req_active_ap_channel_oom(dev, apdev):
+ """Beacon request - AP Channel Report subelement and OOM"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ with alloc_fail(dev[0], 1, "wpas_add_channels"):
+ token = run_req_beacon(hapd, addr, "51ff0000640001ffffffffffff" + "330351010b")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ # allow either not to respond or send refused response
+ if ev is not None:
+ fields = ev.split(' ')
+ if fields[3] != "04":
+ raise Exception("Unexpected Beacon report during OOM with mode: " + fields[3])
+
+@remote_compatible
+def test_rrm_beacon_req_active_scan_fail(dev, apdev):
+ """Beacon request - Active scan failure"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ with alloc_fail(dev[0], 1, "wpa_supplicant_trigger_scan"):
+ token = run_req_beacon(hapd, addr, "51ff0000640001ffffffffffff" + "330351010b")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("No Beacon report")
+ fields = ev.split(' ')
+ if fields[3] != "04":
+ raise Exception("Unexpected Beacon report contents: " + ev)
+
+@remote_compatible
+def test_rrm_beacon_req_active_zero_duration(dev, apdev):
+ """Beacon request - Action scan and zero duration"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51000000000001ffffffffffff")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected Beacon report")
+
+@remote_compatible
+def test_rrm_beacon_req_active_fail_random(dev, apdev):
+ """Beacon request - active scan mode os_get_random failure"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ with fail_test(dev[0], 1, "os_get_random;wpas_rm_handle_beacon_req"):
+ token = run_req_beacon(hapd, addr, "51000000640001ffffffffffff")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+
+@remote_compatible
+def test_rrm_beacon_req_passive(dev, apdev):
+ """Beacon request - passive scan mode"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "another", "channel": "11"})
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51000000640000ffffffffffff")
+
+ for i in range(1, 3):
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if report.bssid_str == apdev[0]['bssid']:
+ if report.opclass != 81 or report.channel != 1:
+ raise Exception("Incorrect opclass/channel for AP0")
+ elif report.bssid_str == apdev[1]['bssid']:
+ if report.opclass != 81 or report.channel != 11:
+ raise Exception("Incorrect opclass/channel for AP1")
+
+@remote_compatible
+def test_rrm_beacon_req_passive_no_match(dev, apdev):
+ """Beacon request - passive scan mode and no matching BSS"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "51010000640000021122334455")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ if len(fields[4]) > 0:
+ raise Exception("Unexpected beacon report BSS")
+
+@remote_compatible
+def test_rrm_beacon_req_passive_no_match_oom(dev, apdev):
+ """Beacon request - passive scan mode and no matching BSS (OOM)"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ with alloc_fail(dev[0], 1, "wpabuf_resize;wpas_beacon_rep_scan_process"):
+ token = run_req_beacon(hapd, addr, "51010000640000021122334455")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected Beacon report response during OOM")
+
+ # verify reporting is still functional
+ token = run_req_beacon(hapd, addr, "51010000640000021122334455")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report %d response not received" % i)
+ fields = ev.split(' ')
+ if len(fields[4]) > 0:
+ raise Exception("Unexpected beacon report BSS")
+
+@remote_compatible
+def test_rrm_beacon_req_active_duration_mandatory(dev, apdev):
+ """Beacon request - Action scan and duration mandatory"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "req_mode=10 51000000640001ffffffffffff")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("No Beacon report response")
+ fields = ev.split(' ')
+ rrm = int(dev[0].get_driver_status_field("capa.rrm_flags"), 16)
+ if rrm & 0x20 == 0x20:
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ else:
+ # Driver does not support scan dwell time setting, so wpa_supplicant
+ # rejects the measurement request due to the mandatory duration using
+ # Measurement Report Mode field Incapable=1.
+ if fields[3] != '02':
+ raise Exception("Unexpected Measurement Report Mode: " + fields[3])
+ if len(fields[4]) > 0:
+ raise Exception("Unexpected beacon report received")
+
+def test_rrm_beacon_req_passive_scan_vht(dev, apdev):
+ """Beacon request - passive scan mode - VHT"""
+ clear_scan_cache(apdev[0])
+ try:
+ hapd = None
+ params = {"ssid": "rrm-vht",
+ "country_code": "FI",
+ 'ieee80211d': '1',
+ "hw_mode": "a",
+ "channel": "36",
+ "ht_capab": "[HT40+]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "1",
+ "vht_oper_centr_freq_seg0_idx": "42",
+ "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=5180)
+ dev[0].connect("rrm-vht", key_mgmt="NONE", scan_freq="5180")
+
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "80000000640000ffffffffffff")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if report.opclass != 128 or report.channel != 36:
+ raise Exception("Incorrect opclass/channel for AP")
+
+ token = run_req_beacon(hapd, addr, "82000000640000ffffffffffff")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if report.opclass != 128 or report.channel != 36:
+ raise Exception("Incorrect opclass/channel for AP")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ if not vht_supported():
+ raise HwsimSkip("80 MHz channel not supported in regulatory information")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_rrm_beacon_req_passive_scan_vht160(dev, apdev):
+ """Beacon request - passive scan mode - VHT160"""
+ clear_scan_cache(apdev[0])
+ try:
+ hapd = None
+ params = {"ssid": "rrm-vht",
+ "country_code": "ZA",
+ 'ieee80211d': '1',
+ "hw_mode": "a",
+ "channel": "104",
+ "ht_capab": "[HT40-]",
+ "vht_capab": "[VHT160]",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "vht_oper_chwidth": "2",
+ "vht_oper_centr_freq_seg0_idx": "114",
+ "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=5520)
+ dev[0].connect("rrm-vht", key_mgmt="NONE", scan_freq="5520")
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=160 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value: " + str(sig))
+
+ addr = dev[0].own_addr()
+
+ token = run_req_beacon(hapd, addr, "81000000640000ffffffffffff")
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received")
+ fields = ev.split(' ')
+ report = BeaconReport(binascii.unhexlify(fields[4]))
+ logger.info("Received beacon report: " + str(report))
+ if report.opclass != 129 or report.channel != 104:
+ raise Exception("Incorrect opclass/channel for AP")
+ except Exception as e:
+ if isinstance(e, Exception) and str(e) == "AP startup failed":
+ raise HwsimSkip("ZA regulatory rule likely did not have DFS requirement removed")
+ raise
+ finally:
+ clear_regdom(hapd, dev)
+
+def test_rrm_beacon_req_ap_errors(dev, apdev):
+ """Beacon request - AP error cases"""
+ try:
+ run_rrm_beacon_req_ap_errors(dev, apdev)
+ finally:
+ dev[1].request("VENDOR_ELEM_REMOVE 13 *")
+
+def run_rrm_beacon_req_ap_errors(dev, apdev):
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+ # Override RM capabilities (remove all)
+ dev[1].request("VENDOR_ELEM_ADD 13 46050000000000")
+ dev[1].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr1 = dev[1].own_addr()
+
+ # Beacon request: Too short request data
+ if "FAIL" not in hapd.request("REQ_BEACON " + addr + " 11"):
+ raise Exception("Invalid REQ_BEACON accepted")
+
+ # Beacon request: 02:00:00:00:01:00 does not support table beacon report
+ if "FAIL" not in hapd.request("REQ_BEACON " + addr1 + " 51000000000002ffffffffffff"):
+ raise Exception("Invalid REQ_BEACON accepted")
+
+ # Beacon request: 02:00:00:00:01:00 does not support active beacon report
+ if "FAIL" not in hapd.request("REQ_BEACON " + addr1 + " 51000000640001ffffffffffff"):
+ raise Exception("Invalid REQ_BEACON accepted")
+
+ # Beacon request: 02:00:00:00:01:00 does not support passive beacon report
+ if "FAIL" not in hapd.request("REQ_BEACON " + addr1 + " 510b0000640000ffffffffffff"):
+ raise Exception("Invalid REQ_BEACON accepted")
+
+ # Beacon request: Unknown measurement mode 3
+ if "FAIL" not in hapd.request("REQ_BEACON " + addr1 + " 510b0000640003ffffffffffff"):
+ raise Exception("Invalid REQ_BEACON accepted")
+
+ for i in range(257):
+ if "FAIL" in hapd.request("REQ_BEACON " + addr + " 510b0000640000ffffffffffff"):
+ raise Exception("REQ_BEACON failed")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+
+ with alloc_fail(hapd, 1, "wpabuf_alloc;hostapd_send_beacon_req"):
+ if "FAIL" not in hapd.request("REQ_BEACON " + addr + " 510b0000640000ffffffffffff"):
+ raise Exception("REQ_BEACON accepted during OOM")
+
+ with fail_test(hapd, 1, "nl80211_send_frame_cmd;hostapd_send_beacon_req"):
+ if "FAIL" not in hapd.request("REQ_BEACON " + addr + " 510b0000640000ffffffffffff"):
+ raise Exception("REQ_BEACON accepted during failure testing")
+
+def test_rrm_req_reject_oom(dev, apdev):
+ """Radio measurement request - OOM while rejecting a request"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hdr = "d0003a01" + addr.replace(':', '') + 2*bssid.replace(':', '') + "1000"
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+
+ with alloc_fail(dev[0], 1, "wpabuf_resize;wpas_rrm_handle_msr_req_element"):
+ # "RRM: Parallel measurements are not supported, reject"
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "05000100002603010105"):
+ raise Exception("MGMT_RX_PROCESS failed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response during OOM")
+
+def test_rrm_req_when_rrm_not_used(dev, apdev):
+ """Radio/link measurement request for non-RRM association"""
+ params = {"ssid": "rrm"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hdr = "d0003a01" + addr.replace(':', '') + 2*bssid.replace(':', '') + "1000"
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "050001000026030100fe"):
+ raise Exception("MGMT_RX_PROCESS failed")
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0502000000"):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response when RRM is disabled")
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "050001000026030100fe"):
+ raise Exception("MGMT_RX_PROCESS failed")
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0502000000"):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+@remote_compatible
+def test_rrm_req_proto(dev, apdev):
+ """Radio measurement request - protocol testing"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].request("SET LCI ")
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hdr = "d0003a01" + addr.replace(':', '') + 2*bssid.replace(':', '') + "1000"
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+
+ tests = []
+ # "RRM: Ignoring too short radio measurement request"
+ tests += ["0500", "050001", "05000100"]
+ # No measurement request element at all
+ tests += ["0500010000"]
+ # "RRM: Truncated element"
+ tests += ["050001000026"]
+ # "RRM: Element length too short"
+ tests += ["05000100002600", "0500010000260111", "050001000026021122"]
+ # "RRM: Element length too long"
+ tests += ["05000100002603", "0500010000260311", "050001000026031122"]
+ # "RRM: Enable bit not supported, ignore"
+ tests += ["05000100002603010200"]
+ # "RRM: Measurement report failed. TX power insertion not supported"
+ # OR
+ # "RRM: Link measurement report failed. Request too short"
+ tests += ["0502"]
+ # Too short LCI request
+ tests += ["05000100002603010008"]
+ # Too short neighbor report response
+ tests += ["0505"]
+ # Unexpected neighbor report response
+ tests += ["050500", "050501", "050502", "050503", "050504", "050505"]
+ # Too short beacon request
+ tests += ["05000100002603010005",
+ "0500010000260f010005112233445566778899aabbcc"]
+ # Unknown beacon report mode
+ tests += ["05000100002610010005112233445566778899aabbccdd"]
+ # "RRM: Expected Measurement Request element, but EID is 0"
+ tests += ["05000100000000"]
+ for t in tests:
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected response seen at the AP: " + ev)
+
+ tests = []
+ # "RRM: Parallel measurements are not supported, reject"
+ tests += ["05000100002603010105"]
+ # "RRM: Unsupported radio measurement type 254"
+ tests += ["050001000026030100fe"]
+ # Reject LCI request
+ tests += ["0500010000260701000811223344"]
+ # Beacon report info subelement; no valid channels
+ tests += ["05000100002614010005112233445566008899aabbccdd01020000"]
+ for t in tests:
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("No response seen at the AP")
+ hapd.dump_monitor()
+
+ dev[0].request("SET LCI " + lci)
+ tests = []
+ # "Not building LCI report - bad location subject"
+ tests += ["0500010000260701000811223344"]
+ for t in tests:
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected response seen at the AP: " + ev)
+
+ tests = []
+ # LCI report or reject
+ tests += ["0500010000260701000801223344",
+ "05000100002607010008010402ff",
+ "05000100002608010008010402ffff"]
+ for t in tests:
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("No response seen at the AP")
+ hapd.dump_monitor()
+
+ # Verify rejection of a group-addressed request frame
+ hdr = "d0003a01" + "ffffffffffff" + 2*bssid.replace(':', '') + "1000"
+ # "RRM: Parallel measurements are not supported, reject"
+ t = "05000100002603010105"
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected response seen at the AP (broadcast request rejected)")
+ hapd.dump_monitor()
+
+ hapd.set("ext_mgmt_frame_handling", "0")
+ dev[0].request("SET ext_mgmt_frame_handling 0")
+ dev[0].request("SET LCI ")
+
+def test_rrm_link_measurement(dev, apdev):
+ """Radio measurement request - link measurement"""
+ check_tx_power_support(dev[0])
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hdr = "d0003a01" + addr.replace(':', '') + 2*bssid.replace(':', '') + "1000"
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0502000000"):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = hapd.wait_event(["MGMT-RX"], timeout=5)
+ if ev is None:
+ raise Exception("No link measurement report seen")
+
+def test_rrm_link_measurement_oom(dev, apdev):
+ """Radio measurement request - link measurement OOM"""
+ check_tx_power_support(dev[0])
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hdr = "d0003a01" + addr.replace(':', '') + 2*bssid.replace(':', '') + "1000"
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;wpas_rrm_handle_link_measurement_request"):
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0502000000"):
+ raise Exception("MGMT_RX_PROCESS failed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ with fail_test(dev[0], 1, "wpas_rrm_handle_link_measurement_request"):
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0502000000"):
+ raise Exception("MGMT_RX_PROCESS failed")
+ wait_fail_trigger(dev[0], "GET_FAIL")
+
+ ev = hapd.wait_event(["MGMT-RX"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected beacon report response during OOM")
+
+def test_rrm_rep_parse_proto(dev, apdev):
+ """hostapd rrm report parsing protocol testing"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "rrm", "rrm_neighbor_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ dev[0].request("SET LCI " + lci)
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ tests = ["0501",
+ "05ff01",
+ "0501012703fffffe2700",
+ "0501012703ffff05",
+ "05010127ffffff05" + 252*"00",
+ "0504012603ffffff2600",
+ "0504012603ffff08",
+ "0504012608ffff08ffffffffff",
+ "0504012608ffff08ff04021234",
+ "0504012608ffff08ff04020100",
+ "0504012608ffff08ff0402ffff"]
+ for t in tests:
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed for " + t)
+
+ if "OK" not in hapd.request("SET_NEIGHBOR 00:11:22:33:44:55 ssid=\"rrm\" nr=" + nr + " lci=" + lci):
+ raise Exception("Set neighbor failed")
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "0504012608ffff08ff04021000"):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+def test_rrm_unexpected(dev, apdev):
+ """hostapd unexpected rrm"""
+ check_rrm_support(dev[0])
+
+ params = {"ssid": "rrm", "rrm_neighbor_report": "0"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ tests = ["050401"]
+ for t in tests:
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed for " + t)
+
+def check_beacon_req(hapd, addr, idx):
+ request = "51000000000002ffffffffffff" + "020100"
+ token = hapd.request("REQ_BEACON " + addr + " " + request)
+ if "FAIL" in token:
+ raise Exception("REQ_BEACON failed (%d)" % idx)
+ ev = hapd.wait_event(["BEACON-RESP-RX"], timeout=10)
+ if ev is None:
+ raise Exception("Beacon report response not received (%d)" % idx)
+
+def test_rrm_reassociation(dev, apdev):
+ """Radio measurement request - reassociation"""
+ params = {"ssid": "rrm", "rrm_beacon_report": "1"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ addr = dev[0].own_addr()
+ dev[0].flush_scan_cache()
+ dev[0].connect("rrm", key_mgmt="NONE", scan_freq="2412")
+ check_beacon_req(hapd, addr, 1)
+
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected()
+ check_beacon_req(hapd, addr, 1)
+
+ hapd2 = hostapd.add_ap(apdev[1]['ifname'], params)
+ bssid2 = hapd2.own_addr()
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].roam(bssid2)
+ check_beacon_req(hapd2, addr, 2)
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].roam(bssid)
+ check_beacon_req(hapd, addr, 3)
diff --git a/contrib/wpa/tests/hwsim/test_sae.py b/contrib/wpa/tests/hwsim/test_sae.py
new file mode 100644
index 000000000000..124dded80ce4
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_sae.py
@@ -0,0 +1,2722 @@
+# Test cases for SAE
+# Copyright (c) 2013-2020, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import os
+import time
+import logging
+logger = logging.getLogger()
+import socket
+import struct
+import subprocess
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+from test_ap_psk import find_wpas_process, read_process_memory, verify_not_present, get_key_locations
+
+@remote_compatible
+def test_sae(dev, apdev):
+ """SAE with default group"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ key_mgmt = hapd.get_config()['key_mgmt']
+ if key_mgmt.split(' ')[0] != "SAE":
+ raise Exception("Unexpected GET_CONFIG(key_mgmt): " + key_mgmt)
+
+ dev[0].request("SET sae_groups ")
+ id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ hapd.wait_sta()
+ if dev[0].get_status_field('sae_group') != '19':
+ raise Exception("Expected default SAE group not used")
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if 'flags' not in bss:
+ raise Exception("Could not get BSS flags from BSS table")
+ if "[WPA2-SAE-CCMP]" not in bss['flags']:
+ raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+ res = hapd.request("STA-FIRST")
+ if "sae_group=19" not in res.splitlines():
+ raise Exception("hostapd STA output did not specify SAE group")
+
+ pmk_h = hapd.request("GET_PMK " + dev[0].own_addr())
+ pmk_w = dev[0].get_pmk(id)
+ if pmk_h != pmk_w:
+ raise Exception("Fetched PMK does not match: hostapd %s, wpa_supplicant %s" % (pmk_h, pmk_w))
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ pmk_h2 = hapd.request("GET_PMK " + dev[0].own_addr())
+ if pmk_h != pmk_h2:
+ raise Exception("Fetched PMK from PMKSA cache does not match: %s, %s" % (pmk_h, pmk_h2))
+ if "FAIL" not in hapd.request("GET_PMK foo"):
+ raise Exception("Invalid GET_PMK did not return failure")
+ if "FAIL" not in hapd.request("GET_PMK 02:ff:ff:ff:ff:ff"):
+ raise Exception("GET_PMK for unknown STA did not return failure")
+
+@remote_compatible
+def test_sae_password_ecc(dev, apdev):
+ """SAE with number of different passwords (ECC)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups 19")
+
+ for i in range(10):
+ password = "12345678-" + str(i)
+ hapd.set("wpa_passphrase", password)
+ dev[0].connect("test-sae", psk=password, key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+@remote_compatible
+def test_sae_password_ffc(dev, apdev):
+ """SAE with number of different passwords (FFC)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = '15'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups 15")
+
+ for i in range(10):
+ password = "12345678-" + str(i)
+ hapd.set("wpa_passphrase", password)
+ dev[0].connect("test-sae", psk=password, key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+@remote_compatible
+def test_sae_pmksa_caching(dev, apdev):
+ """SAE and PMKSA caching"""
+ run_sae_pmksa_caching(dev, apdev)
+
+@remote_compatible
+def test_sae_pmksa_caching_pmkid(dev, apdev):
+ """SAE and PMKSA caching (PMKID in AssocReq after SAE)"""
+ try:
+ dev[0].set("sae_pmkid_in_assoc", "1")
+ run_sae_pmksa_caching(dev, apdev)
+ finally:
+ dev[0].set("sae_pmkid_in_assoc", "0")
+
+def run_sae_pmksa_caching(dev, apdev):
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ sta0 = hapd.get_sta(dev[0].own_addr())
+ if sta0['wpa'] != '2' or sta0['AKMSuiteSelector'] != '00-0f-ac-8':
+ raise Exception("SAE STA(0) AKM suite selector reported incorrectly")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected(timeout=15, error="Reconnect timed out")
+ if dev[0].get_status_field('sae_group') is not None:
+ raise Exception("SAE group claimed to have been used")
+ sta0 = hapd.get_sta(dev[0].own_addr())
+ if sta0['wpa'] != '2' or sta0['AKMSuiteSelector'] != '00-0f-ac-8':
+ raise Exception("SAE STA(0) AKM suite selector reported incorrectly after PMKSA caching")
+
+@remote_compatible
+def test_sae_pmksa_caching_disabled(dev, apdev):
+ """SAE and PMKSA caching disabled"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['disable_pmksa_caching'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected(timeout=15, error="Reconnect timed out")
+ if dev[0].get_status_field('sae_group') != '19':
+ raise Exception("Expected default SAE group not used")
+
+def test_sae_groups(dev, apdev):
+ """SAE with all supported groups"""
+ check_sae_capab(dev[0])
+ # This is the full list of supported groups, but groups 14-16 (2048-4096 bit
+ # MODP) and group 21 (521-bit random ECP group) are a bit too slow on some
+ # VMs and can result in hitting the mac80211 authentication timeout, so
+ # allow them to fail and just report such failures in the debug log.
+ sae_groups = [19, 25, 26, 20, 21, 1, 2, 5, 14, 15, 16, 22, 23, 24]
+ tls = dev[0].request("GET tls_library")
+ if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls:
+ logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+ sae_groups += [27, 28, 29, 30]
+ heavy_groups = [14, 15, 16]
+ suitable_groups = [15, 16, 17, 18, 19, 20, 21]
+ groups = [str(g) for g in sae_groups]
+ params = hostapd.wpa2_params(ssid="test-sae-groups",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = ' '.join(groups)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ for g in groups:
+ logger.info("Testing SAE group " + g)
+ dev[0].request("SET sae_groups " + g)
+ id = dev[0].connect("test-sae-groups", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ if int(g) in heavy_groups:
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=5)
+ if ev is None:
+ logger.info("No connection with heavy SAE group %s did not connect - likely hitting timeout in mac80211" % g)
+ dev[0].remove_network(id)
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ continue
+ logger.info("Connection with heavy SAE group " + g)
+ else:
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ if "BoringSSL" in tls and int(g) in [25]:
+ logger.info("Ignore connection failure with group " + g + " with BoringSSL")
+ dev[0].remove_network(id)
+ dev[0].dump_monitor()
+ continue
+ if int(g) not in suitable_groups:
+ logger.info("Ignore connection failure with unsuitable group " + g)
+ dev[0].remove_network(id)
+ dev[0].dump_monitor()
+ continue
+ raise Exception("Connection timed out with group " + g)
+ if dev[0].get_status_field('sae_group') != g:
+ raise Exception("Expected SAE group not used")
+ pmksa = dev[0].get_pmksa(hapd.own_addr())
+ if not pmksa:
+ raise Exception("No PMKSA cache entry added")
+ if pmksa['pmkid'] == '00000000000000000000000000000000':
+ raise Exception("All zeros PMKID derived for group %s" % g)
+ dev[0].remove_network(id)
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+@remote_compatible
+def test_sae_group_nego(dev, apdev):
+ """SAE group negotiation"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae-group-nego",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = '19'
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups 25 26 20 19")
+ dev[0].connect("test-sae-group-nego", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ if dev[0].get_status_field('sae_group') != '19':
+ raise Exception("Expected SAE group not used")
+
+def test_sae_group_nego_no_match(dev, apdev):
+ """SAE group negotiation (no match)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae-group-nego",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ # None-existing SAE group to force all attempts to be rejected
+ params['sae_groups'] = '0'
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae-group-nego", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ dev[0].request("REMOVE_NETWORK all")
+ if ev is None:
+ raise Exception("Network profile disabling not reported")
+
+@remote_compatible
+def test_sae_anti_clogging(dev, apdev):
+ """SAE anti clogging"""
+ check_sae_capab(dev[0])
+ check_sae_capab(dev[1])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_anti_clogging_threshold'] = '1'
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[1].request("SET sae_groups ")
+ id = {}
+ for i in range(0, 2):
+ dev[i].scan(freq="2412")
+ id[i] = dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
+ for i in range(0, 2):
+ dev[i].select_network(id[i])
+ for i in range(0, 2):
+ dev[i].wait_connected(timeout=10)
+
+def test_sae_forced_anti_clogging(dev, apdev):
+ """SAE anti clogging (forced)"""
+ check_sae_capab(dev[0])
+ check_sae_capab(dev[1])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params['sae_anti_clogging_threshold'] = '0'
+ hostapd.add_ap(apdev[0], params)
+ dev[2].connect("test-sae", psk="12345678", scan_freq="2412")
+ for i in range(0, 2):
+ dev[i].request("SET sae_groups ")
+ dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+
+def test_sae_mixed(dev, apdev):
+ """Mixed SAE and non-SAE network"""
+ check_sae_capab(dev[0])
+ check_sae_capab(dev[1])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params['sae_anti_clogging_threshold'] = '0'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[2].connect("test-sae", psk="12345678", scan_freq="2412")
+ for i in range(0, 2):
+ dev[i].request("SET sae_groups ")
+ dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ sta0 = hapd.get_sta(dev[0].own_addr())
+ sta2 = hapd.get_sta(dev[2].own_addr())
+ if sta0['wpa'] != '2' or sta0['AKMSuiteSelector'] != '00-0f-ac-8':
+ raise Exception("SAE STA(0) AKM suite selector reported incorrectly")
+ if sta2['wpa'] != '2' or sta2['AKMSuiteSelector'] != '00-0f-ac-2':
+ raise Exception("PSK STA(2) AKM suite selector reported incorrectly")
+
+def test_sae_and_psk(dev, apdev):
+ """SAE and PSK enabled in network profile"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE WPA-PSK",
+ scan_freq="2412")
+
+def test_sae_and_psk2(dev, apdev):
+ """SAE and PSK enabled in network profile (use PSK)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-psk", passphrase="12345678")
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-psk", psk="12345678", key_mgmt="SAE WPA-PSK",
+ scan_freq="2412")
+
+def test_sae_mixed_mfp(dev, apdev):
+ """Mixed SAE and non-SAE network and MFP required with SAE"""
+ check_sae_capab(dev[0])
+ check_sae_capab(dev[1])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params["ieee80211w"] = "1"
+ params['sae_require_mfp'] = '1'
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", ieee80211w="2",
+ scan_freq="2412")
+ dev[0].dump_monitor()
+
+ dev[1].request("SET sae_groups ")
+ dev[1].connect("test-sae", psk="12345678", key_mgmt="SAE", ieee80211w="0",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("No connection result reported")
+ if "CTRL-EVENT-ASSOC-REJECT" not in ev:
+ raise Exception("SAE connection without MFP was not rejected")
+ if "status_code=31" not in ev:
+ raise Exception("Unexpected status code in rejection: " + ev)
+ dev[1].request("DISCONNECT")
+ dev[1].dump_monitor()
+
+ dev[2].connect("test-sae", psk="12345678", ieee80211w="0", scan_freq="2412")
+ dev[2].dump_monitor()
+
+def test_sae_and_psk_transition_disable(dev, apdev):
+ """SAE and PSK transition disable indication"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params["ieee80211w"] = "1"
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params['transition_disable'] = '0x01'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE WPA-PSK",
+ ieee80211w="1", scan_freq="2412")
+ ev = dev[0].wait_event(["TRANSITION-DISABLE"], timeout=1)
+ if ev is None:
+ raise Exception("Transition disable not indicated")
+ if ev.split(' ')[1] != "01":
+ raise Exception("Unexpected transition disable bitmap: " + ev)
+
+ val = dev[0].get_network(id, "ieee80211w")
+ if val != "2":
+ raise Exception("Unexpected ieee80211w value: " + val)
+ val = dev[0].get_network(id, "key_mgmt")
+ if val != "SAE":
+ raise Exception("Unexpected key_mgmt value: " + val)
+ val = dev[0].get_network(id, "group")
+ if val != "CCMP":
+ raise Exception("Unexpected group value: " + val)
+ val = dev[0].get_network(id, "proto")
+ if val != "RSN":
+ raise Exception("Unexpected proto value: " + val)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+
+def test_sae_mfp(dev, apdev):
+ """SAE and MFP enabled without sae_require_mfp"""
+ check_sae_capab(dev[0])
+ check_sae_capab(dev[1])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "1"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", ieee80211w="2",
+ scan_freq="2412")
+
+ dev[1].request("SET sae_groups ")
+ dev[1].connect("test-sae", psk="12345678", key_mgmt="SAE", ieee80211w="0",
+ scan_freq="2412")
+
+@remote_compatible
+def test_sae_missing_password(dev, apdev):
+ """SAE and missing password"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ id = dev[0].connect("test-sae",
+ raw_psk="46b4a73b8a951ad53ebd2e0afdb9c5483257edd4c21d12b7710759da70945858",
+ key_mgmt="SAE", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(['CTRL-EVENT-SSID-TEMP-DISABLED'], timeout=10)
+ if ev is None:
+ raise Exception("Invalid network not temporarily disabled")
+
+
+def test_sae_key_lifetime_in_memory(dev, apdev, params):
+ """SAE and key lifetime in memory"""
+ check_sae_capab(dev[0])
+ password = "5ad144a7c1f5a5503baa6fa01dabc15b1843e8c01662d78d16b70b5cd23cf8b"
+ p = hostapd.wpa2_params(ssid="test-sae", passphrase=password)
+ p['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], p)
+
+ pid = find_wpas_process(dev[0])
+
+ dev[0].request("SET sae_groups ")
+ id = dev[0].connect("test-sae", psk=password, key_mgmt="SAE",
+ scan_freq="2412")
+
+ # The decrypted copy of GTK is freed only after the CTRL-EVENT-CONNECTED
+ # event has been delivered, so verify that wpa_supplicant has returned to
+ # eloop before reading process memory.
+ time.sleep(1)
+ dev[0].ping()
+ password = password.encode()
+ buf = read_process_memory(pid, password)
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ dev[0].relog()
+ sae_k = None
+ sae_keyseed = None
+ sae_kck = None
+ pmk = None
+ ptk = None
+ gtk = None
+ with open(os.path.join(params['logdir'], 'log0'), 'r') as f:
+ for l in f.readlines():
+ if "SAE: k - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ sae_k = binascii.unhexlify(val)
+ if "SAE: keyseed - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ sae_keyseed = binascii.unhexlify(val)
+ if "SAE: KCK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ sae_kck = binascii.unhexlify(val)
+ if "SAE: PMK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ pmk = binascii.unhexlify(val)
+ if "WPA: PTK - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ ptk = binascii.unhexlify(val)
+ if "WPA: Group Key - hexdump" in l:
+ val = l.strip().split(':')[3].replace(' ', '')
+ gtk = binascii.unhexlify(val)
+ if not sae_k or not sae_keyseed or not sae_kck or not pmk or not ptk or not gtk:
+ raise Exception("Could not find keys from debug log")
+ if len(gtk) != 16:
+ raise Exception("Unexpected GTK length")
+
+ kck = ptk[0:16]
+ kek = ptk[16:32]
+ tk = ptk[32:48]
+
+ fname = os.path.join(params['logdir'],
+ 'sae_key_lifetime_in_memory.memctx-')
+
+ logger.info("Checking keys in memory while associated")
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ if password not in buf:
+ raise HwsimSkip("Password not found while associated")
+ if pmk not in buf:
+ raise HwsimSkip("PMK not found while associated")
+ if kck not in buf:
+ raise Exception("KCK not found while associated")
+ if kek not in buf:
+ raise Exception("KEK not found while associated")
+ #if tk in buf:
+ # raise Exception("TK found from memory")
+ verify_not_present(buf, sae_k, fname, "SAE(k)")
+ verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)")
+ verify_not_present(buf, sae_kck, fname, "SAE(KCK)")
+
+ logger.info("Checking keys in memory after disassociation")
+ buf = read_process_memory(pid, password)
+
+ # Note: Password is still present in network configuration
+ # Note: PMK is in PMKSA cache
+
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ if gtk in buf:
+ get_key_locations(buf, gtk, "GTK")
+ verify_not_present(buf, gtk, fname, "GTK")
+ verify_not_present(buf, sae_k, fname, "SAE(k)")
+ verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)")
+ verify_not_present(buf, sae_kck, fname, "SAE(KCK)")
+
+ dev[0].request("PMKSA_FLUSH")
+ logger.info("Checking keys in memory after PMKSA cache flush")
+ buf = read_process_memory(pid, password)
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ verify_not_present(buf, pmk, fname, "PMK")
+
+ dev[0].request("REMOVE_NETWORK all")
+
+ logger.info("Checking keys in memory after network profile removal")
+ buf = read_process_memory(pid, password)
+
+ get_key_locations(buf, password, "Password")
+ get_key_locations(buf, pmk, "PMK")
+ verify_not_present(buf, password, fname, "password")
+ verify_not_present(buf, pmk, fname, "PMK")
+ verify_not_present(buf, kck, fname, "KCK")
+ verify_not_present(buf, kek, fname, "KEK")
+ verify_not_present(buf, tk, fname, "TK")
+ verify_not_present(buf, gtk, fname, "GTK")
+ verify_not_present(buf, sae_k, fname, "SAE(k)")
+ verify_not_present(buf, sae_keyseed, fname, "SAE(keyseed)")
+ verify_not_present(buf, sae_kck, fname, "SAE(KCK)")
+
+@remote_compatible
+def test_sae_oom_wpas(dev, apdev):
+ """SAE and OOM in wpa_supplicant"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = '19 25 26 20'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups 20")
+ with alloc_fail(dev[0], 1, "sae_set_group"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+
+ dev[0].request("SET sae_groups ")
+ with alloc_fail(dev[0], 2, "sae_set_group"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;sme_auth_build_sae_commit"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+
+ with alloc_fail(dev[0], 1, "wpabuf_alloc;sme_auth_build_sae_confirm"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+
+ with alloc_fail(dev[0], 1, "=sme_authenticate"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+
+ with alloc_fail(dev[0], 1, "radio_add_work;sme_authenticate"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+
+@remote_compatible
+def test_sae_proto_ecc(dev, apdev):
+ """SAE protocol testing (ECC)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].request("SET sae_groups 19")
+
+ tests = [("Confirm mismatch",
+ "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+ "0000800edebc3f260dc1fe7e0b20888af2b8a3316252ec37388a8504e25b73dc4240"),
+ ("Commit without even full cyclic group field",
+ "13",
+ None),
+ ("Too short commit",
+ "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02",
+ None),
+ ("Invalid commit scalar (0)",
+ "1300" + "0000000000000000000000000000000000000000000000000000000000000000" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+ None),
+ ("Invalid commit scalar (1)",
+ "1300" + "0000000000000000000000000000000000000000000000000000000000000001" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+ None),
+ ("Invalid commit scalar (> r)",
+ "1300" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+ None),
+ ("Commit element not on curve",
+ "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728d0000000000000000000000000000000000000000000000000000000000000000",
+ None),
+ ("Invalid commit element (y coordinate > P)",
+ "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ None),
+ ("Invalid commit element (x coordinate > P)",
+ "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+ None),
+ ("Different group in commit",
+ "1400" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+ None),
+ ("Too short confirm",
+ "1300" + "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03" + "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728dd3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8",
+ "0000800edebc3f260dc1fe7e0b20888af2b8a3316252ec37388a8504e25b73dc42")]
+ for (note, commit, confirm) in tests:
+ logger.info(note)
+ dev[0].scan_for_bss(bssid, freq=2412)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+
+ logger.info("Commit")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (commit)")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (commit) not received")
+
+ hapd.dump_monitor()
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = binascii.unhexlify("030001000000" + commit)
+ hapd.mgmt_tx(resp)
+
+ if confirm:
+ logger.info("Confirm")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (confirm)")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (confirm) not received")
+
+ hapd.dump_monitor()
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = binascii.unhexlify("030002000000" + confirm)
+ hapd.mgmt_tx(resp)
+
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ hapd.set("ext_mgmt_frame_handling", "0")
+ hapd.dump_monitor()
+
+@remote_compatible
+def test_sae_proto_ffc(dev, apdev):
+ """SAE protocol testing (FFC)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].request("SET sae_groups 2")
+
+ tests = [("Confirm mismatch",
+ "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "a8c00117493cdffa5dd671e934bc9cb1a69f39e25e9dd9cd9afd3aea2441a0f5491211c7ba50a753563f9ce943b043557cb71193b28e86ed9544f4289c471bf91b70af5c018cf4663e004165b0fd0bc1d8f3f78adf42eee92bcbc55246fd3ee9f107ab965dc7d4986f23eb71d616ebfe6bfe0a6c1ac5dc1718acee17c9a17486",
+ "0000f3116a9731f1259622e3eb55d4b3b50ba16f8c5f5565b28e609b180c51460251"),
+ ("Too short commit",
+ "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "a8c00117493cdffa5dd671e934bc9cb1a69f39e25e9dd9cd9afd3aea2441a0f5491211c7ba50a753563f9ce943b043557cb71193b28e86ed9544f4289c471bf91b70af5c018cf4663e004165b0fd0bc1d8f3f78adf42eee92bcbc55246fd3ee9f107ab965dc7d4986f23eb71d616ebfe6bfe0a6c1ac5dc1718acee17c9a174",
+ None),
+ ("Invalid element (0) in commit",
+ "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ None),
+ ("Invalid element (1) in commit",
+ "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001",
+ None),
+ ("Invalid element (> P) in commit",
+ "0200" + "0c70519d874e3e4930a917cc5e17ea7a26028211159f217bab28b8d6c56691805e49f03249b2c6e22c7c9f86b30e04ccad2deedd5e5108ae07b737c00001c59cd0eb08b1dfc7f1b06a1542e2b6601a963c066e0c65940983a03917ae57a101ce84b5cbbc76ff33ebb990aac2e54aa0f0ab6ec0a58113d927683502b2cb2347d2" + "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
+ None)]
+ for (note, commit, confirm) in tests:
+ logger.info(note)
+ dev[0].scan_for_bss(bssid, freq=2412)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+
+ logger.info("Commit")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (commit)")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (commit) not received")
+
+ hapd.dump_monitor()
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = binascii.unhexlify("030001000000" + commit)
+ hapd.mgmt_tx(resp)
+
+ if confirm:
+ logger.info("Confirm")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (confirm)")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (confirm) not received")
+
+ hapd.dump_monitor()
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = binascii.unhexlify("030002000000" + confirm)
+ hapd.mgmt_tx(resp)
+
+ time.sleep(0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ hapd.set("ext_mgmt_frame_handling", "0")
+ hapd.dump_monitor()
+
+
+def test_sae_proto_commit_delayed(dev, apdev):
+ """SAE protocol testing - Commit delayed"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].request("SET sae_groups 19")
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+
+ logger.info("Commit")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (commit)")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (commit) not received")
+
+ hapd.dump_monitor()
+ time.sleep(2.5)
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+
+ logger.info("Commit/Confirm")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (confirm)")
+ if req['subtype'] == 11:
+ trans, = struct.unpack('<H', req['payload'][2:4])
+ if trans == 1:
+ logger.info("Extra Commit")
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ continue
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (confirm) not received")
+
+ hapd.dump_monitor()
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+
+ logger.info("Association Request")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (AssocReq)")
+ if req['subtype'] == 0:
+ break
+ req = None
+ if not req:
+ raise Exception("Association Request frame not received")
+
+ hapd.dump_monitor()
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Management frame TX status not reported (1)")
+ if "stype=1 ok=1" not in ev:
+ raise Exception("Unexpected management frame TX status (1): " + ev)
+ cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
+ if "OK" not in hapd.request(cmd):
+ raise Exception("MGMT_TX_STATUS_PROCESS failed")
+
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+ dev[0].wait_connected()
+
+def test_sae_proto_commit_replay(dev, apdev):
+ """SAE protocol testing - Commit replay"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].request("SET sae_groups 19")
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+
+ logger.info("Commit")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (commit)")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (commit) not received")
+
+ hapd.dump_monitor()
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ logger.info("Replay Commit")
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+
+ logger.info("Confirm")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (confirm)")
+ if req['subtype'] == 11:
+ trans, = struct.unpack('<H', req['payload'][2:4])
+ if trans == 1:
+ logger.info("Extra Commit")
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ continue
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (confirm) not received")
+
+ hapd.dump_monitor()
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+
+ logger.info("Association Request")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (AssocReq)")
+ if req['subtype'] == 0:
+ break
+ req = None
+ if not req:
+ raise Exception("Association Request frame not received")
+
+ hapd.dump_monitor()
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ for i in range(0, 10):
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Management frame TX status not reported (1)")
+ if "stype=11 ok=1" in ev:
+ continue
+ if "stype=12 ok=1" in ev:
+ continue
+ if "stype=1 ok=1" not in ev:
+ raise Exception("Unexpected management frame TX status (1): " + ev)
+ cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
+ if "OK" not in hapd.request(cmd):
+ raise Exception("MGMT_TX_STATUS_PROCESS failed")
+ break
+
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+ dev[0].wait_connected()
+
+def test_sae_proto_confirm_replay(dev, apdev):
+ """SAE protocol testing - Confirm replay"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].request("SET sae_groups 19")
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+
+ logger.info("Commit")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (commit)")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (commit) not received")
+
+ hapd.dump_monitor()
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+
+ logger.info("Confirm")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (confirm)")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (confirm) not received")
+
+ hapd.dump_monitor()
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+
+ logger.info("Replay Confirm")
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+
+ logger.info("Association Request")
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (AssocReq)")
+ if req['subtype'] == 0:
+ break
+ req = None
+ if not req:
+ raise Exception("Association Request frame not received")
+
+ hapd.dump_monitor()
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + binascii.hexlify(req['frame']).decode())
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Management frame TX status not reported (1)")
+ if "stype=1 ok=1" not in ev:
+ raise Exception("Unexpected management frame TX status (1): " + ev)
+ cmd = "MGMT_TX_STATUS_PROCESS %s" % (" ".join(ev.split(' ')[1:4]))
+ if "OK" not in hapd.request(cmd):
+ raise Exception("MGMT_TX_STATUS_PROCESS failed")
+
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+ dev[0].wait_connected()
+
+def test_sae_proto_hostapd(dev, apdev):
+ """SAE protocol testing with hostapd"""
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = "19 65535"
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ bssid = hapd.own_addr().replace(':', '')
+ addr = "020000000000"
+ addr2 = "020000000001"
+ hdr = "b0003a01" + bssid + addr + bssid + "1000"
+ hdr2 = "b0003a01" + bssid + addr2 + bssid + "1000"
+ group = "1300"
+ scalar = "f7df19f4a7fef1d3b895ea1de150b7c5a7a705c8ebb31a52b623e0057908bd93"
+ element_x = "21931572027f2e953e2a49fab3d992944102cc95aa19515fc068b394fb25ae3c"
+ element_y = "cb4eeb94d7b0b789abfdb73a67ab9d6d5efa94dd553e0e724a6289821cbce530"
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "030001000000" + group + scalar + element_x + element_y)
+ # "SAE: Not enough data for scalar"
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "030001000000" + group + scalar[:-2])
+ # "SAE: Do not allow group to be changed"
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "030001000000" + "ffff" + scalar[:-2])
+ # "SAE: Unsupported Finite Cyclic Group 65535"
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr2 + "030001000000" + "ffff" + scalar[:-2])
+
+def test_sae_proto_hostapd_ecc(dev, apdev):
+ """SAE protocol testing with hostapd (ECC)"""
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="foofoofoo")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = "19"
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ bssid = hapd.own_addr().replace(':', '')
+ addr = "020000000000"
+ addr2 = "020000000001"
+ hdr = "b0003a01" + bssid + addr + bssid + "1000"
+ hdr2 = "b0003a01" + bssid + addr2 + bssid + "1000"
+ group = "1300"
+ scalar = "9e9a959bf2dda875a4a29ce9b2afef46f2d83060930124cd9e39ddce798cd69a"
+ element_x = "dfc55fd8622b91d362f4d1fc9646474d7fba0ff7cce6ca58b8e96a931e070220"
+ element_y = "dac8a4e80724f167c1349cc9e1f9dd82a7c77b29d49789b63b72b4c849301a28"
+ # sae_parse_commit_element_ecc() failure to parse peer element
+ # (depending on crypto library, either crypto_ec_point_from_bin() failure
+ # or crypto_ec_point_is_on_curve() returning 0)
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "030001000000" + group + scalar + element_x + element_y)
+ # Unexpected continuation of the connection attempt with confirm
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "030002000000" + "0000" + "fd7b081ff4e8676f03612a4140eedcd3c179ab3a13b93863c6f7ca451340b9ae")
+
+def test_sae_proto_hostapd_ffc(dev, apdev):
+ """SAE protocol testing with hostapd (FFC)"""
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="foofoofoo")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = "22"
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ bssid = hapd.own_addr().replace(':', '')
+ addr = "020000000000"
+ addr2 = "020000000001"
+ hdr = "b0003a01" + bssid + addr + bssid + "1000"
+ hdr2 = "b0003a01" + bssid + addr2 + bssid + "1000"
+ group = "1600"
+ scalar = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044cc46a73c07ef479dc66ec1f5e8ccf25131fa40"
+ element = "0f1d67025e12fc874cf718c35b19d1ab2db858215623f1ce661cbd1d7b1d7a09ceda7dba46866cf37044259b5cac4db15e7feb778edc8098854b93a84347c1850c02ee4d7dac46db79c477c731085d5b39f56803cda1eeac4a2fbbccb9a546379e258c00ebe93dfdd0a34cf8ce5c55cf905a89564a590b7e159fb89198e9d5cd"
+ # sae_parse_commit_element_ffc() failure to parse peer element
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "030001000000" + group + scalar + element)
+ # Unexpected continuation of the connection attempt with confirm
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "030002000000" + "0000" + "fd7b081ff4e8676f03612a4140eedcd3c179ab3a13b93863c6f7ca451340b9ae")
+
+def sae_start_ap(apdev, sae_pwe):
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="foofoofoo")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = "19"
+ params['sae_pwe'] = str(sae_pwe)
+ return hostapd.add_ap(apdev, params)
+
+def check_commit_status(hapd, use_status, expect_status):
+ hapd.set("ext_mgmt_frame_handling", "1")
+ bssid = hapd.own_addr().replace(':', '')
+ addr = "020000000000"
+ addr2 = "020000000001"
+ hdr = "b0003a01" + bssid + addr + bssid + "1000"
+ hdr2 = "b0003a01" + bssid + addr2 + bssid + "1000"
+ group = "1300"
+ scalar = "033d3635b39666ed427fd4a3e7d37acec2810afeaf1687f746a14163ff0e6d03"
+ element_x = "559cb8928db4ce4e3cbd6555e837591995e5ebe503ef36b503d9ca519d63728d"
+ element_y = "d3c7c676b8e8081831b6bc3a64bdf136061a7de175e17d1965bfa41983ed02f8"
+ status = binascii.hexlify(struct.pack('<H', use_status)).decode()
+ hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "03000100" + status + group + scalar + element_x + element_y)
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("MGMT-TX-STATUS not seen")
+ msg = ev.split(' ')[3].split('=')[1]
+ body = msg[2 * 24:]
+ status, = struct.unpack('<H', binascii.unhexlify(body[8:12]))
+ if status != expect_status:
+ raise Exception("Unexpected status code: %d" % status)
+
+def test_sae_proto_hostapd_status_126(dev, apdev):
+ """SAE protocol testing with hostapd (status code 126)"""
+ hapd = sae_start_ap(apdev[0], 0)
+ check_commit_status(hapd, 126, 1)
+ check_commit_status(hapd, 0, 0)
+
+def test_sae_proto_hostapd_status_127(dev, apdev):
+ """SAE protocol testing with hostapd (status code 127)"""
+ hapd = sae_start_ap(apdev[0], 2)
+ check_commit_status(hapd, 127, 1)
+ check_commit_status(hapd, 0, 0)
+
+@remote_compatible
+def test_sae_no_ffc_by_default(dev, apdev):
+ """SAE and default groups rejecting FFC"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups 15")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["SME: Trying to authenticate"], timeout=3)
+ if ev is None:
+ raise Exception("Did not try to authenticate")
+ ev = dev[0].wait_event(["SME: Trying to authenticate"], timeout=3)
+ if ev is None:
+ raise Exception("Did not try to authenticate (2)")
+ dev[0].request("REMOVE_NETWORK all")
+
+def sae_reflection_attack(apdev, dev, group):
+ check_sae_capab(dev)
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="no-knowledge-of-passphrase")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev, params)
+ bssid = apdev['bssid']
+
+ dev.scan_for_bss(bssid, freq=2412)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ dev.request("SET sae_groups %d" % group)
+ dev.connect("test-sae", psk="reflection-attack", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+
+ # Commit
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame not received")
+
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = req['payload']
+ hapd.mgmt_tx(resp)
+
+ # Confirm
+ req = hapd.mgmt_rx(timeout=0.5)
+ if req is not None:
+ if req['subtype'] == 11:
+ raise Exception("Unexpected Authentication frame seen")
+
+@remote_compatible
+def test_sae_reflection_attack_ecc(dev, apdev):
+ """SAE reflection attack (ECC)"""
+ sae_reflection_attack(apdev[0], dev[0], 19)
+
+@remote_compatible
+def test_sae_reflection_attack_ffc(dev, apdev):
+ """SAE reflection attack (FFC)"""
+ sae_reflection_attack(apdev[0], dev[0], 15)
+
+def sae_reflection_attack_internal(apdev, dev, group):
+ check_sae_capab(dev)
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="no-knowledge-of-passphrase")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_reflection_attack'] = '1'
+ hapd = hostapd.add_ap(apdev, params)
+ bssid = apdev['bssid']
+
+ dev.scan_for_bss(bssid, freq=2412)
+ dev.request("SET sae_groups %d" % group)
+ dev.connect("test-sae", psk="reflection-attack", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev.wait_event(["SME: Trying to authenticate"], timeout=10)
+ if ev is None:
+ raise Exception("No authentication attempt seen")
+ ev = dev.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+
+@remote_compatible
+def test_sae_reflection_attack_ecc_internal(dev, apdev):
+ """SAE reflection attack (ECC) - internal"""
+ sae_reflection_attack_internal(apdev[0], dev[0], 19)
+
+@remote_compatible
+def test_sae_reflection_attack_ffc_internal(dev, apdev):
+ """SAE reflection attack (FFC) - internal"""
+ sae_reflection_attack_internal(apdev[0], dev[0], 15)
+
+@remote_compatible
+def test_sae_commit_override(dev, apdev):
+ """SAE commit override (hostapd)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_commit_override'] = '13ffbad00d215867a7c5ff37d87bb9bdb7cb116e520f71e8d7a794ca2606d537ddc6c099c40e7a25372b80a8fd443cd7dd222c8ea21b8ef372d4b3e316c26a73fd999cc79ad483eb826e7b3893ea332da68fa13224bcdeb4fb18b0584dd100a2c514'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="test-sae", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+
+@remote_compatible
+def test_sae_commit_override2(dev, apdev):
+ """SAE commit override (wpa_supplicant)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET sae_groups ")
+ dev[0].set('sae_commit_override', '13ffbad00d215867a7c5ff37d87bb9bdb7cb116e520f71e8d7a794ca2606d537ddc6c099c40e7a25372b80a8fd443cd7dd222c8ea21b8ef372d4b3e316c26a73fd999cc79ad483eb826e7b3893ea332da68fa13224bcdeb4fb18b0584dd100a2c514')
+ dev[0].connect("test-sae", psk="test-sae", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+
+def test_sae_commit_invalid_scalar_element_ap(dev, apdev):
+ """SAE commit invalid scalar/element from AP"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_commit_override'] = '1300' + 96*'00'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="test-sae", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+
+def test_sae_commit_invalid_element_ap(dev, apdev):
+ """SAE commit invalid element from AP"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_commit_override'] = '1300' + 31*'00' + '02' + 64*'00'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="test-sae", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+
+def test_sae_commit_invalid_scalar_element_sta(dev, apdev):
+ """SAE commit invalid scalar/element from STA"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET sae_groups ")
+ dev[0].set('sae_commit_override', '1300' + 96*'00')
+ dev[0].connect("test-sae", psk="test-sae", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+
+def test_sae_commit_invalid_element_sta(dev, apdev):
+ """SAE commit invalid element from STA"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].request("SET sae_groups ")
+ dev[0].set('sae_commit_override', '1300' + 31*'00' + '02' + 64*'00')
+ dev[0].connect("test-sae", psk="test-sae", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+
+@remote_compatible
+def test_sae_anti_clogging_proto(dev, apdev):
+ """SAE anti clogging protocol testing"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="no-knowledge-of-passphrase")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="anti-cloggign", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+
+ # Commit
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame not received")
+
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = binascii.unhexlify("030001004c00" + "ffff00")
+ hapd.mgmt_tx(resp)
+
+ # Confirm (not received due to DH group being rejected)
+ req = hapd.mgmt_rx(timeout=0.5)
+ if req is not None:
+ if req['subtype'] == 11:
+ raise Exception("Unexpected Authentication frame seen")
+
+@remote_compatible
+def test_sae_no_random(dev, apdev):
+ """SAE and no random numbers available"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ tests = [(1, "os_get_random;sae_derive_pwe_ecc")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+@remote_compatible
+def test_sae_pwe_failure(dev, apdev):
+ """SAE and pwe failure"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = '19 15'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups 19")
+ with fail_test(dev[0], 1, "hmac_sha256_vector;sae_derive_pwe_ecc"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ with fail_test(dev[0], 1, "sae_test_pwd_seed_ecc"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].request("SET sae_groups 15")
+ with fail_test(dev[0], 1, "hmac_sha256_vector;sae_derive_pwe_ffc"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].request("SET sae_groups 15")
+ with fail_test(dev[0], 1, "sae_test_pwd_seed_ffc"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ with fail_test(dev[0], 2, "sae_test_pwd_seed_ffc"):
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+@remote_compatible
+def test_sae_bignum_failure(dev, apdev):
+ """SAE and bignum failure"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = '19 15 22'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups 19")
+ tests = [(1, "crypto_bignum_init_set;dragonfly_get_rand_1_to_p_1"),
+ (1, "crypto_bignum_init;dragonfly_is_quadratic_residue_blind"),
+ (1, "crypto_bignum_mulmod;dragonfly_is_quadratic_residue_blind"),
+ (2, "crypto_bignum_mulmod;dragonfly_is_quadratic_residue_blind"),
+ (3, "crypto_bignum_mulmod;dragonfly_is_quadratic_residue_blind"),
+ (1, "crypto_bignum_legendre;dragonfly_is_quadratic_residue_blind"),
+ (1, "crypto_bignum_init_set;sae_test_pwd_seed_ecc"),
+ (1, "crypto_ec_point_compute_y_sqr;sae_test_pwd_seed_ecc"),
+ (1, "crypto_bignum_to_bin;sae_derive_pwe_ecc"),
+ (1, "crypto_ec_point_init;sae_derive_pwe_ecc"),
+ (1, "crypto_ec_point_solve_y_coord;sae_derive_pwe_ecc"),
+ (1, "crypto_ec_point_init;sae_derive_commit_element_ecc"),
+ (1, "crypto_ec_point_mul;sae_derive_commit_element_ecc"),
+ (1, "crypto_ec_point_invert;sae_derive_commit_element_ecc"),
+ (1, "crypto_bignum_init;=sae_derive_commit"),
+ (1, "crypto_ec_point_init;sae_derive_k_ecc"),
+ (1, "crypto_ec_point_mul;sae_derive_k_ecc"),
+ (1, "crypto_ec_point_add;sae_derive_k_ecc"),
+ (2, "crypto_ec_point_mul;sae_derive_k_ecc"),
+ (1, "crypto_ec_point_to_bin;sae_derive_k_ecc"),
+ (1, "crypto_bignum_legendre;dragonfly_get_random_qr_qnr"),
+ (1, "sha256_prf;sae_derive_keys"),
+ (1, "crypto_bignum_init;sae_derive_keys"),
+ (1, "crypto_bignum_init_set;sae_parse_commit_scalar"),
+ (1, "crypto_bignum_to_bin;sae_parse_commit_element_ecc"),
+ (1, "crypto_ec_point_from_bin;sae_parse_commit_element_ecc")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ hapd.request("NOTE STA failure testing %d:%s" % (count, func))
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL", timeout=0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+
+ dev[0].request("SET sae_groups 15")
+ tests = [(1, "crypto_bignum_init_set;sae_set_group"),
+ (2, "crypto_bignum_init_set;sae_set_group"),
+ (1, "crypto_bignum_init;sae_derive_commit"),
+ (2, "crypto_bignum_init;sae_derive_commit"),
+ (1, "crypto_bignum_init_set;sae_test_pwd_seed_ffc"),
+ (1, "crypto_bignum_exptmod;sae_test_pwd_seed_ffc"),
+ (1, "crypto_bignum_init;sae_derive_pwe_ffc"),
+ (1, "crypto_bignum_init;sae_derive_commit_element_ffc"),
+ (1, "crypto_bignum_exptmod;sae_derive_commit_element_ffc"),
+ (1, "crypto_bignum_inverse;sae_derive_commit_element_ffc"),
+ (1, "crypto_bignum_init;sae_derive_k_ffc"),
+ (1, "crypto_bignum_exptmod;sae_derive_k_ffc"),
+ (1, "crypto_bignum_mulmod;sae_derive_k_ffc"),
+ (2, "crypto_bignum_exptmod;sae_derive_k_ffc"),
+ (1, "crypto_bignum_to_bin;sae_derive_k_ffc"),
+ (1, "crypto_bignum_init_set;sae_parse_commit_element_ffc"),
+ (1, "crypto_bignum_init;sae_parse_commit_element_ffc"),
+ (2, "crypto_bignum_init_set;sae_parse_commit_element_ffc"),
+ (1, "crypto_bignum_exptmod;sae_parse_commit_element_ffc")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ hapd.request("NOTE STA failure testing %d:%s" % (count, func))
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL", timeout=0.1)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+
+def test_sae_bignum_failure_unsafe_group(dev, apdev):
+ """SAE and bignum failure unsafe group"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = '22'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups 22")
+ tests = [(1, "crypto_bignum_init_set;sae_test_pwd_seed_ffc"),
+ (1, "crypto_bignum_sub;sae_test_pwd_seed_ffc"),
+ (1, "crypto_bignum_div;sae_test_pwd_seed_ffc")]
+ for count, func in tests:
+ with fail_test(dev[0], count, func):
+ hapd.request("NOTE STA failure testing %d:%s" % (count, func))
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+ hapd.dump_monitor()
+
+def test_sae_invalid_anti_clogging_token_req(dev, apdev):
+ """SAE and invalid anti-clogging token request"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ # Beacon more frequently since Probe Request frames are practically ignored
+ # in this test setup (ext_mgmt_frame_handled=1 on hostapd side) and
+ # wpa_supplicant scans may end up getting ignored if no new results are
+ # available due to the missing Probe Response frames.
+ params['beacon_int'] = '20'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ dev[0].request("SET sae_groups 19")
+ dev[0].scan_for_bss(bssid, freq=2412)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["SME: Trying to authenticate"])
+ if ev is None:
+ raise Exception("No authentication attempt seen (1)")
+ dev[0].dump_monitor()
+
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (commit)")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (commit) not received")
+
+ hapd.dump_monitor()
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = binascii.unhexlify("030001004c0013")
+ hapd.mgmt_tx(resp)
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Management frame TX status not reported (1)")
+ if "stype=11 ok=1" not in ev:
+ raise Exception("Unexpected management frame TX status (1): " + ev)
+
+ ev = dev[0].wait_event(["SME: Trying to authenticate"])
+ if ev is None:
+ raise Exception("No authentication attempt seen (2)")
+ dev[0].dump_monitor()
+
+ for i in range(0, 10):
+ req = hapd.mgmt_rx()
+ if req is None:
+ raise Exception("MGMT RX wait timed out (commit) (2)")
+ if req['subtype'] == 11:
+ break
+ req = None
+ if not req:
+ raise Exception("Authentication frame (commit) not received (2)")
+
+ hapd.dump_monitor()
+ resp = {}
+ resp['fc'] = req['fc']
+ resp['da'] = req['sa']
+ resp['sa'] = req['da']
+ resp['bssid'] = req['bssid']
+ resp['payload'] = binascii.unhexlify("030001000100")
+ hapd.mgmt_tx(resp)
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Management frame TX status not reported (1)")
+ if "stype=11 ok=1" not in ev:
+ raise Exception("Unexpected management frame TX status (1): " + ev)
+
+ ev = dev[0].wait_event(["SME: Trying to authenticate"])
+ if ev is None:
+ raise Exception("No authentication attempt seen (3)")
+ dev[0].dump_monitor()
+
+ dev[0].request("DISCONNECT")
+
+def test_sae_password(dev, apdev):
+ """SAE and sae_password in hostapd configuration"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params['sae_password'] = "sae-password"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="sae-password", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[1].connect("test-sae", psk="12345678", scan_freq="2412")
+ dev[2].request("SET sae_groups ")
+ dev[2].connect("test-sae", sae_password="sae-password", key_mgmt="SAE",
+ scan_freq="2412")
+
+def test_sae_password_short(dev, apdev):
+ """SAE and short password"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = "secret"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", sae_password="secret", key_mgmt="SAE",
+ scan_freq="2412")
+
+def test_sae_password_long(dev, apdev):
+ """SAE and long password"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = 100*"A"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", sae_password=100*"A", key_mgmt="SAE",
+ scan_freq="2412")
+
+def test_sae_connect_cmd(dev, apdev):
+ """SAE with connect command"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+ check_sae_capab(wpas)
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas.request("SET sae_groups ")
+ wpas.connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ # mac80211_hwsim does not support SAE offload, so accept both a successful
+ # connection and association rejection.
+ ev = wpas.wait_event(["CTRL-EVENT-CONNECTED", "CTRL-EVENT-ASSOC-REJECT",
+ "Association request to the driver failed"],
+ timeout=15)
+ if ev is None:
+ raise Exception("No connection result reported")
+
+def run_sae_password_id(dev, apdev, groups=None):
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE'
+ if groups:
+ params['sae_groups'] = groups
+ else:
+ groups = ""
+ params['sae_password'] = ['secret|mac=ff:ff:ff:ff:ff:ff|id=pw id',
+ 'foo|mac=02:02:02:02:02:02',
+ 'another secret|mac=ff:ff:ff:ff:ff:ff|id=' + 29*'A']
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups " + groups)
+ dev[0].connect("test-sae", sae_password="secret", sae_password_id="pw id",
+ key_mgmt="SAE", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ # SAE Password Identifier element with the exact same length as the
+ # optional Anti-Clogging Token field
+ dev[0].connect("test-sae", sae_password="another secret",
+ sae_password_id=29*'A',
+ key_mgmt="SAE", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].connect("test-sae", sae_password="secret", sae_password_id="unknown",
+ key_mgmt="SAE", scan_freq="2412", wait_connect=False)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SAE-UNKNOWN-PASSWORD-IDENTIFIER"],
+ timeout=10)
+ if ev is None:
+ raise Exception("Unknown password identifier not reported")
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_sae_password_id(dev, apdev):
+ """SAE and password identifier"""
+ run_sae_password_id(dev, apdev, "")
+
+def test_sae_password_id_ecc(dev, apdev):
+ """SAE and password identifier (ECC)"""
+ run_sae_password_id(dev, apdev, "19")
+
+def test_sae_password_id_ffc(dev, apdev):
+ """SAE and password identifier (FFC)"""
+ run_sae_password_id(dev, apdev, "15")
+
+def test_sae_password_id_only(dev, apdev):
+ """SAE and password identifier (exclusively)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = 'secret|id=pw id'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", sae_password="secret", sae_password_id="pw id",
+ key_mgmt="SAE", scan_freq="2412")
+
+def test_sae_password_id_pwe_looping(dev, apdev):
+ """SAE and password identifier with forced PWE looping"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = 'secret|id=pw id'
+ params['sae_pwe'] = "3"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ try:
+ dev[0].set("sae_pwe", "3")
+ dev[0].connect("test-sae", sae_password="secret",
+ sae_password_id="pw id",
+ key_mgmt="SAE", scan_freq="2412")
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_password_id_pwe_check_ap(dev, apdev):
+ """SAE and password identifier with STA using unexpected PWE derivation"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = 'secret|id=pw id'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ try:
+ dev[0].set("sae_pwe", "3")
+ dev[0].connect("test-sae", sae_password="secret",
+ sae_password_id="pw id",
+ key_mgmt="SAE", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ if ev is None or "CTRL-EVENT-SSID-TEMP-DISABLED" not in ev:
+ raise Exception("Connection failure not reported")
+ finally:
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_password_id_pwe_check_sta(dev, apdev):
+ """SAE and password identifier with AP using unexpected PWE derivation"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_pwe'] = "3"
+ params['sae_password'] = 'secret|id=pw id'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", sae_password="secret",
+ sae_password_id="pw id",
+ key_mgmt="SAE", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if ev is None or "CTRL-EVENT-NETWORK-NOT-FOUND" not in ev:
+ raise Exception("Connection failure not reported")
+
+def test_sae_forced_anti_clogging_pw_id(dev, apdev):
+ """SAE anti clogging (forced and Password Identifier)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_anti_clogging_threshold'] = '0'
+ params['sae_password'] = 'secret|id=' + 29*'A'
+ hostapd.add_ap(apdev[0], params)
+ for i in range(0, 2):
+ dev[i].request("SET sae_groups ")
+ dev[i].connect("test-sae", sae_password="secret",
+ sae_password_id=29*'A', key_mgmt="SAE", scan_freq="2412")
+
+def test_sae_reauth(dev, apdev):
+ """SAE reauthentication"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ ieee80211w="2", scan_freq="2412")
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=10)
+ hapd.set("ext_mgmt_frame_handling", "0")
+ dev[0].request("PMKSA_FLUSH")
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected(timeout=10, error="Timeout on re-connection")
+
+def test_sae_anti_clogging_during_attack(dev, apdev):
+ """SAE anti clogging during an attack"""
+ try:
+ run_sae_anti_clogging_during_attack(dev, apdev)
+ finally:
+ stop_monitor(apdev[1]["ifname"])
+
+def build_sae_commit(bssid, addr, group=21, token=None):
+ if group == 19:
+ scalar = binascii.unhexlify("7332d3ebff24804005ccd8c56141e3ed8d84f40638aa31cd2fac11d4d2e89e7b")
+ element = binascii.unhexlify("954d0f4457066bff3168376a1d7174f4e66620d1792406f613055b98513a7f03a538c13dfbaf2029e2adc6aa96aa0ddcf08ac44887b02f004b7f29b9dbf4b7d9")
+ elif group == 21:
+ scalar = binascii.unhexlify("001eec673111b902f5c8a61c8cb4c1c4793031aeea8c8c319410903bc64bcbaea134ab01c4e016d51436f5b5426f7e2af635759a3033fb4031ea79f89a62a3e2f828")
+ element = binascii.unhexlify("00580eb4b448ea600ea277d5e66e4ed37db82bb04ac90442e9c3727489f366ba4b82f0a472d02caf4cdd142e96baea5915d71374660ee23acbaca38cf3fe8c5fb94b01abbc5278121635d7c06911c5dad8f18d516e1fbe296c179b7c87a1dddfab393337d3d215ed333dd396da6d8f20f798c60d054f1093c24d9c2d98e15c030cc375f0")
+ pass
+ frame = binascii.unhexlify("b0003a01")
+ frame += bssid + addr + bssid
+ frame += binascii.unhexlify("1000")
+ auth_alg = 3
+ transact = 1
+ status = 0
+ frame += struct.pack("<HHHH", auth_alg, transact, status, group)
+ if token:
+ frame += token
+ frame += scalar + element
+ return frame
+
+def sae_rx_commit_token_req(sock, radiotap, send_two=False):
+ msg = sock.recv(1500)
+ ver, pad, length, present = struct.unpack('<BBHL', msg[0:8])
+ frame = msg[length:]
+ if len(frame) < 4:
+ return False
+ fc, duration = struct.unpack('<HH', frame[0:4])
+ if fc != 0xb0:
+ return False
+ frame = frame[4:]
+ da = frame[0:6]
+ if da[0] != 0xf2:
+ return False
+ sa = frame[6:12]
+ bssid = frame[12:18]
+ body = frame[20:]
+
+ alg, seq, status, group = struct.unpack('<HHHH', body[0:8])
+ if alg != 3 or seq != 1 or status != 76:
+ return False
+ token = body[8:]
+
+ frame = build_sae_commit(bssid, da, token=token)
+ sock.send(radiotap + frame)
+ if send_two:
+ sock.send(radiotap + frame)
+ return True
+
+def run_sae_anti_clogging_during_attack(dev, apdev):
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = '21'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].scan_for_bss(hapd.own_addr(), freq=2412)
+ dev[0].request("SET sae_groups 21")
+ dev[1].scan_for_bss(hapd.own_addr(), freq=2412)
+ dev[1].request("SET sae_groups 21")
+
+ sock = start_monitor(apdev[1]["ifname"])
+ radiotap = radiotap_build()
+
+ bssid = binascii.unhexlify(hapd.own_addr().replace(':', ''))
+ for i in range(16):
+ addr = binascii.unhexlify("f2%010x" % i)
+ frame = build_sae_commit(bssid, addr)
+ sock.send(radiotap + frame)
+ sock.send(radiotap + frame)
+
+ count = 0
+ for i in range(150):
+ if sae_rx_commit_token_req(sock, radiotap, send_two=True):
+ count += 1
+ logger.info("Number of token responses sent: %d" % count)
+ if count < 10:
+ raise Exception("Too few token responses seen: %d" % count)
+
+ for i in range(16):
+ addr = binascii.unhexlify("f201%08x" % i)
+ frame = build_sae_commit(bssid, addr)
+ sock.send(radiotap + frame)
+
+ count = 0
+ for i in range(150):
+ if sae_rx_commit_token_req(sock, radiotap):
+ count += 1
+ if count == 10:
+ break
+ if count < 5:
+ raise Exception("Too few token responses in second round: %d" % count)
+
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ dev[1].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+
+ count = 0
+ connected0 = False
+ connected1 = False
+ for i in range(1000):
+ if sae_rx_commit_token_req(sock, radiotap):
+ count += 1
+ addr = binascii.unhexlify("f202%08x" % i)
+ frame = build_sae_commit(bssid, addr)
+ sock.send(radiotap + frame)
+ while dev[0].mon.pending():
+ ev = dev[0].mon.recv()
+ logger.debug("EV0: " + ev)
+ if "CTRL-EVENT-CONNECTED" in ev:
+ connected0 = True
+ while dev[1].mon.pending():
+ ev = dev[1].mon.recv()
+ logger.debug("EV1: " + ev)
+ if "CTRL-EVENT-CONNECTED" in ev:
+ connected1 = True
+ if connected0 and connected1:
+ break
+ time.sleep(0.00000001)
+ if not connected0:
+ raise Exception("Real station(0) did not get connected")
+ if not connected1:
+ raise Exception("Real station(1) did not get connected")
+ if count < 1:
+ raise Exception("Too few token responses in third round: %d" % count)
+
+def test_sae_sync(dev, apdev):
+ """SAE dot11RSNASAESync"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_sync'] = '1'
+ hostapd.add_ap(apdev[0], params)
+
+ # TODO: More complete dot11RSNASAESync testing. For now, this is really only
+ # checking that sae_sync config parameter is accepted.
+ dev[0].request("SET sae_groups ")
+ dev[1].request("SET sae_groups ")
+ id = {}
+ for i in range(0, 2):
+ dev[i].scan(freq="2412")
+ id[i] = dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", only_add_network=True)
+ for i in range(0, 2):
+ dev[i].select_network(id[i])
+ for i in range(0, 2):
+ dev[i].wait_connected(timeout=10)
+
+def test_sae_confirm_immediate(dev, apdev):
+ """SAE and AP sending Confirm message without waiting STA"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_confirm_immediate'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412")
+
+def test_sae_confirm_immediate2(dev, apdev):
+ """SAE and AP sending Confirm message without waiting STA (2)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_confirm_immediate'] = '2'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", scan_freq="2412")
+
+def test_sae_pwe_group_19(dev, apdev):
+ """SAE PWE derivation options with group 19"""
+ run_sae_pwe_group(dev, apdev, 19)
+
+def test_sae_pwe_group_20(dev, apdev):
+ """SAE PWE derivation options with group 20"""
+ run_sae_pwe_group(dev, apdev, 20)
+
+def test_sae_pwe_group_21(dev, apdev):
+ """SAE PWE derivation options with group 21"""
+ run_sae_pwe_group(dev, apdev, 21)
+
+def test_sae_pwe_group_25(dev, apdev):
+ """SAE PWE derivation options with group 25"""
+ run_sae_pwe_group(dev, apdev, 25)
+
+def test_sae_pwe_group_28(dev, apdev):
+ """SAE PWE derivation options with group 28"""
+ run_sae_pwe_group(dev, apdev, 28)
+
+def test_sae_pwe_group_29(dev, apdev):
+ """SAE PWE derivation options with group 29"""
+ run_sae_pwe_group(dev, apdev, 29)
+
+def test_sae_pwe_group_30(dev, apdev):
+ """SAE PWE derivation options with group 30"""
+ run_sae_pwe_group(dev, apdev, 30)
+
+def test_sae_pwe_group_1(dev, apdev):
+ """SAE PWE derivation options with group 1"""
+ run_sae_pwe_group(dev, apdev, 1)
+
+def test_sae_pwe_group_2(dev, apdev):
+ """SAE PWE derivation options with group 2"""
+ run_sae_pwe_group(dev, apdev, 2)
+
+def test_sae_pwe_group_5(dev, apdev):
+ """SAE PWE derivation options with group 5"""
+ run_sae_pwe_group(dev, apdev, 5)
+
+def test_sae_pwe_group_14(dev, apdev):
+ """SAE PWE derivation options with group 14"""
+ run_sae_pwe_group(dev, apdev, 14)
+
+def test_sae_pwe_group_15(dev, apdev):
+ """SAE PWE derivation options with group 15"""
+ run_sae_pwe_group(dev, apdev, 15)
+
+def test_sae_pwe_group_16(dev, apdev):
+ """SAE PWE derivation options with group 16"""
+ run_sae_pwe_group(dev, apdev, 16)
+
+def test_sae_pwe_group_22(dev, apdev):
+ """SAE PWE derivation options with group 22"""
+ run_sae_pwe_group(dev, apdev, 22)
+
+def test_sae_pwe_group_23(dev, apdev):
+ """SAE PWE derivation options with group 23"""
+ run_sae_pwe_group(dev, apdev, 23)
+
+def test_sae_pwe_group_24(dev, apdev):
+ """SAE PWE derivation options with group 24"""
+ run_sae_pwe_group(dev, apdev, 24)
+
+def start_sae_pwe_ap(apdev, group, sae_pwe):
+ params = hostapd.wpa2_params(ssid="sae-pwe", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = str(group)
+ params['sae_pwe'] = str(sae_pwe)
+ return hostapd.add_ap(apdev, params)
+
+def run_sae_pwe_group(dev, apdev, group):
+ check_sae_capab(dev[0])
+ tls = dev[0].request("GET tls_library")
+ if group in [27, 28, 29, 30]:
+ if tls.startswith("OpenSSL") and "run=OpenSSL 1." in tls:
+ logger.info("Add Brainpool EC groups since OpenSSL is new enough")
+ else:
+ raise HwsimSkip("Brainpool curve not supported")
+ start_sae_pwe_ap(apdev[0], group, 2)
+ try:
+ check_sae_pwe_group(dev[0], group, 0)
+ check_sae_pwe_group(dev[0], group, 1)
+ check_sae_pwe_group(dev[0], group, 2)
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def check_sae_pwe_group(dev, group, sae_pwe):
+ dev.set("sae_groups", str(group))
+ dev.set("sae_pwe", str(sae_pwe))
+ dev.connect("sae-pwe", psk="12345678", key_mgmt="SAE", scan_freq="2412")
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+def test_sae_pwe_h2e_only_ap(dev, apdev):
+ """SAE PWE derivation with H2E-only AP"""
+ check_sae_capab(dev[0])
+ start_sae_pwe_ap(apdev[0], 19, 1)
+ try:
+ check_sae_pwe_group(dev[0], 19, 1)
+ check_sae_pwe_group(dev[0], 19, 2)
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("No indication of mismatching network seen")
+
+def test_sae_pwe_h2e_only_ap_sta_forcing_loop(dev, apdev):
+ """SAE PWE derivation with H2E-only AP and STA forcing loop"""
+ check_sae_capab(dev[0])
+ start_sae_pwe_ap(apdev[0], 19, 1)
+ dev[0].set("ignore_sae_h2e_only", "1")
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("No indication of temporary disabled network seen")
+
+def test_sae_pwe_loop_only_ap(dev, apdev):
+ """SAE PWE derivation with loop-only AP"""
+ check_sae_capab(dev[0])
+ start_sae_pwe_ap(apdev[0], 19, 0)
+ try:
+ check_sae_pwe_group(dev[0], 19, 0)
+ check_sae_pwe_group(dev[0], 19, 2)
+ dev[0].set("sae_pwe", "1")
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("No indication of mismatching network seen")
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_h2e_rejected_groups(dev, apdev):
+ """SAE H2E and rejected groups indication"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="sae-pwe", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = "19"
+ params['sae_pwe'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ dev[0].set("sae_groups", "21 20 19")
+ dev[0].set("sae_pwe", "1")
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ addr = dev[0].own_addr()
+ hapd.wait_sta(addr)
+ sta = hapd.get_sta(addr)
+ if 'sae_rejected_groups' not in sta:
+ raise Exception("No sae_rejected_groups")
+ val = sta['sae_rejected_groups']
+ if val != "21 20":
+ raise Exception("Unexpected sae_rejected_groups value: " + val)
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_h2e_rejected_groups_unexpected(dev, apdev):
+ """SAE H2E and rejected groups indication (unexpected group)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="sae-pwe", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_groups'] = "19 20"
+ params['sae_pwe'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ dev[0].set("sae_groups", "21 19")
+ dev[0].set("extra_sae_rejected_groups", "19")
+ dev[0].set("sae_pwe", "1")
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("No indication of temporary disabled network seen")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_h2e_password_id(dev, apdev):
+ """SAE H2E and password identifier"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_pwe'] = '1'
+ params['sae_password'] = 'secret|id=pw id'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ dev[0].request("SET sae_groups ")
+ dev[0].set("sae_pwe", "1")
+ dev[0].connect("test-sae", sae_password="secret",
+ sae_password_id="pw id",
+ key_mgmt="SAE", scan_freq="2412")
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_pwe_in_psk_ap(dev, apdev):
+ """sae_pwe parameter in PSK-only-AP"""
+ params = hostapd.wpa2_params(ssid="test-psk", passphrase="12345678")
+ params['sae_pwe'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-psk", psk="12345678", scan_freq="2412")
+
+def test_sae_auth_restart(dev, apdev):
+ """SAE and authentication restarts with H2E/looping"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_pwe'] = '2'
+ params['sae_password'] = 'secret|id=pw id'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ dev[0].request("SET sae_groups ")
+ for pwe in [1, 0, 1]:
+ dev[0].set("sae_pwe", str(pwe))
+ dev[0].connect("test-sae", sae_password="secret",
+ sae_password_id="pw id",
+ key_mgmt="SAE", scan_freq="2412")
+ # Disconnect without hostapd removing the STA entry so that the
+ # following SAE authentication instance starts with an existing
+ # STA entry that has maintained some SAE state.
+ hapd.set("ext_mgmt_frame_handling", "1")
+ dev[0].request("REMOVE_NETWORK all")
+ req = hapd.mgmt_rx()
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ hapd.set("ext_mgmt_frame_handling", "0")
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_rsne_mismatch(dev, apdev):
+ """SAE and RSNE mismatch in EAPOL-Key msg 2/4"""
+ check_sae_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid="sae-pwe", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ # First, test with matching RSNE to confirm testing capability
+ dev[0].set("rsne_override_eapol",
+ "30140100000fac040100000fac040100000fac080000")
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ # Then, test with modified RSNE
+ tests = ["30140100000fac040100000fac040100000fac080010", "0000"]
+ for ie in tests:
+ dev[0].set("rsne_override_eapol", ie)
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["Associated with"], timeout=10)
+ if ev is None:
+ raise Exception("No indication of association seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=5)
+ dev[0].request("REMOVE_NETWORK all")
+ if ev is None:
+ raise Exception("No disconnection seen")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection")
+ dev[0].dump_monitor()
+
+def test_sae_h2e_rsnxe_mismatch(dev, apdev):
+ """SAE H2E and RSNXE mismatch in EAPOL-Key msg 2/4"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="sae-pwe", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_pwe'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ dev[0].set("sae_groups", "19")
+ dev[0].set("sae_pwe", "1")
+ for rsnxe in ["F40100", "F400", ""]:
+ dev[0].set("rsnxe_override_eapol", rsnxe)
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["Associated with"], timeout=10)
+ if ev is None:
+ raise Exception("No indication of association seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=5)
+ dev[0].request("REMOVE_NETWORK all")
+ if ev is None:
+ raise Exception("No disconnection seen")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection")
+ dev[0].dump_monitor()
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_h2e_rsnxe_mismatch_retries(dev, apdev):
+ """SAE H2E and RSNXE mismatch in EAPOL-Key msg 2/4 retries"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="sae-pwe", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_pwe'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ dev[0].set("sae_groups", "19")
+ dev[0].set("sae_pwe", "1")
+ rsnxe = "F40100"
+ dev[0].set("rsnxe_override_eapol", rsnxe)
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["Associated with"], timeout=10)
+ if ev is None:
+ raise Exception("No indication of association seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No disconnection seen")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("No disconnection seen (2)")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection (2)")
+
+ dev[0].dump_monitor()
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_h2e_rsnxe_mismatch_assoc(dev, apdev):
+ """SAE H2E and RSNXE mismatch in EAPOL-Key msg 2/4 (assoc)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="sae-pwe", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_pwe'] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ dev[0].set("sae_groups", "19")
+ dev[0].set("sae_pwe", "1")
+ for rsnxe in ["F40100", "F400", ""]:
+ dev[0].set("rsnxe_override_assoc", rsnxe)
+ dev[0].set("rsnxe_override_eapol", "F40120")
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["Associated with"], timeout=10)
+ if ev is None:
+ raise Exception("No indication of association seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=5)
+ dev[0].request("REMOVE_NETWORK all")
+ if ev is None:
+ raise Exception("No disconnection seen")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection")
+ dev[0].dump_monitor()
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_h2e_rsnxe_mismatch_ap(dev, apdev):
+ """SAE H2E and RSNXE mismatch in EAPOL-Key msg 3/4"""
+ run_sae_h2e_rsnxe_mismatch_ap(dev, apdev, "F40100")
+
+def test_sae_h2e_rsnxe_mismatch_ap2(dev, apdev):
+ """SAE H2E and RSNXE mismatch in EAPOL-Key msg 3/4"""
+ run_sae_h2e_rsnxe_mismatch_ap(dev, apdev, "F400")
+
+def test_sae_h2e_rsnxe_mismatch_ap3(dev, apdev):
+ """SAE H2E and RSNXE mismatch in EAPOL-Key msg 3/4"""
+ run_sae_h2e_rsnxe_mismatch_ap(dev, apdev, "")
+
+def run_sae_h2e_rsnxe_mismatch_ap(dev, apdev, rsnxe):
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="sae-pwe", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_pwe'] = "1"
+ params['rsnxe_override_eapol'] = rsnxe
+ hapd = hostapd.add_ap(apdev[0], params)
+ try:
+ dev[0].set("sae_groups", "19")
+ dev[0].set("sae_pwe", "1")
+ dev[0].connect("sae-pwe", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["Associated with"], timeout=10)
+ if ev is None:
+ raise Exception("No indication of association seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=5)
+ dev[0].request("REMOVE_NETWORK all")
+ if ev is None:
+ raise Exception("No disconnection seen")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection")
+ finally:
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "0")
+
+def test_sae_forced_anti_clogging_h2e(dev, apdev):
+ """SAE anti clogging (forced, H2E)"""
+ check_sae_capab(dev[0])
+ check_sae_capab(dev[1])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params['sae_pwe'] = "1"
+ params['sae_anti_clogging_threshold'] = '0'
+ hostapd.add_ap(apdev[0], params)
+ dev[2].connect("test-sae", psk="12345678", scan_freq="2412")
+ try:
+ for i in range(2):
+ dev[i].request("SET sae_groups ")
+ dev[i].set("sae_pwe", "1")
+ dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ finally:
+ for i in range(2):
+ dev[i].set("sae_pwe", "0")
+
+def test_sae_forced_anti_clogging_h2e_loop(dev, apdev):
+ """SAE anti clogging (forced, H2E + loop)"""
+ check_sae_capab(dev[0])
+ check_sae_capab(dev[1])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params['sae_pwe'] = "2"
+ params['sae_anti_clogging_threshold'] = '0'
+ hostapd.add_ap(apdev[0], params)
+ dev[2].connect("test-sae", psk="12345678", scan_freq="2412")
+ try:
+ for i in range(2):
+ dev[i].request("SET sae_groups ")
+ dev[i].set("sae_pwe", "2")
+ dev[i].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ finally:
+ for i in range(2):
+ dev[i].set("sae_pwe", "0")
+
+def test_sae_okc(dev, apdev):
+ """SAE and opportunistic key caching"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['okc'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].set("sae_groups", "")
+ id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ okc=True, scan_freq="2412")
+ dev[0].dump_monitor()
+ hapd.wait_sta()
+ if "sae_group" not in dev[0].get_status():
+ raise Exception("SAE authentication not used")
+
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].roam(bssid2)
+ dev[0].dump_monitor()
+ hapd2.wait_sta()
+ if "sae_group" in dev[0].get_status():
+ raise Exception("SAE authentication used during roam to AP2")
+
+ dev[0].roam(bssid)
+ dev[0].dump_monitor()
+ hapd.wait_sta()
+ if "sae_group" in dev[0].get_status():
+ raise Exception("SAE authentication used during roam to AP1")
+
+def test_sae_okc_sta_only(dev, apdev):
+ """SAE and opportunistic key caching only on STA"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].set("sae_groups", "")
+ id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ okc=True, scan_freq="2412")
+ dev[0].dump_monitor()
+ hapd.wait_sta()
+ if "sae_group" not in dev[0].get_status():
+ raise Exception("SAE authentication not used")
+
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].roam(bssid2, assoc_reject_ok=True)
+ dev[0].dump_monitor()
+ hapd2.wait_sta()
+ if "sae_group" not in dev[0].get_status():
+ raise Exception("SAE authentication not used during roam to AP2")
+
+def test_sae_okc_pmk_lifetime(dev, apdev):
+ """SAE and opportunistic key caching and PMK lifetime"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['okc'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].set("sae_groups", "")
+ dev[0].set("dot11RSNAConfigPMKLifetime", "10")
+ dev[0].set("dot11RSNAConfigPMKReauthThreshold", "30")
+ id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ okc=True, scan_freq="2412")
+ dev[0].dump_monitor()
+ hapd.wait_sta()
+ if "sae_group" not in dev[0].get_status():
+ raise Exception("SAE authentication not used")
+
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ time.sleep(5)
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].roam(bssid2)
+ dev[0].dump_monitor()
+ hapd2.wait_sta()
+ if "sae_group" not in dev[0].get_status():
+ raise Exception("SAE authentication not used during roam to AP2 after reauth threshold")
+
+def test_sae_pmk_lifetime(dev, apdev):
+ """SAE and opportunistic key caching and PMK lifetime"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].set("sae_groups", "")
+ dev[0].set("dot11RSNAConfigPMKLifetime", "10")
+ dev[0].set("dot11RSNAConfigPMKReauthThreshold", "50")
+ id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ scan_freq="2412")
+ dev[0].dump_monitor()
+ hapd.wait_sta()
+ if "sae_group" not in dev[0].get_status():
+ raise Exception("SAE authentication not used")
+
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].roam(bssid2)
+ dev[0].dump_monitor()
+ hapd2.wait_sta()
+ if "sae_group" not in dev[0].get_status():
+ raise Exception("SAE authentication not used during roam to AP2")
+
+ dev[0].roam(bssid)
+ dev[0].dump_monitor()
+ hapd.wait_sta()
+ if "sae_group" in dev[0].get_status():
+ raise Exception("SAE authentication used during roam to AP1")
+
+ time.sleep(6)
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].roam(bssid2)
+ dev[0].dump_monitor()
+ hapd2.wait_sta()
+ if "sae_group" not in dev[0].get_status():
+ raise Exception("SAE authentication not used during roam to AP2 after reauth threshold")
+
+ ev = dev[0].wait_event(["PMKSA-CACHE-REMOVED"], 11)
+ if ev is None:
+ raise Exception("PMKSA cache entry did not expire")
+ if bssid2 not in ev:
+ ev = dev[0].wait_event(["PMKSA-CACHE-REMOVED"], 11)
+ if ev is None:
+ raise Exception("PMKSA cache entry did not expire")
+ if bssid2 not in ev:
+ raise Exception("PMKSA cache entry for the current AP did not expire")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], 1)
+ if ev is None:
+ raise Exception("Disconnection not reported after PMKSA cache entry expiration")
+
+ dev[0].wait_connected()
+ if "sae_group" not in dev[0].get_status():
+ raise Exception("SAE authentication not used after PMKSA cache entry expiration")
+
+def test_sae_and_psk_multiple_passwords(dev, apdev, params):
+ """SAE and PSK with multiple passwords/passphrases"""
+ check_sae_capab(dev[0])
+ check_sae_capab(dev[1])
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ psk_file = os.path.join(params['logdir'],
+ 'sae_and_psk_multiple_passwords.wpa_psk')
+ with open(psk_file, 'w') as f:
+ f.write(addr0 + ' passphrase0\n')
+ f.write(addr1 + ' passphrase1\n')
+ params = hostapd.wpa2_params(ssid="test-sae")
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params['sae_password'] = ['passphrase0|mac=' + addr0,
+ 'passphrase1|mac=' + addr1]
+ params['wpa_psk_file'] = psk_file
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].set("sae_groups", "")
+ dev[0].connect("test-sae", sae_password="passphrase0",
+ key_mgmt="SAE", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].connect("test-sae", psk="passphrase0", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[1].set("sae_groups", "")
+ dev[1].connect("test-sae", sae_password="passphrase1",
+ key_mgmt="SAE", scan_freq="2412")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[1].wait_disconnected()
+
+ dev[1].connect("test-sae", psk="passphrase1", scan_freq="2412")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[1].wait_disconnected()
+
+def test_sae_pmf_roam(dev, apdev):
+ """SAE/PMF roam"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae", passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['ieee80211w'] = '2'
+ params['skip_prune_assoc'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].set("sae_groups", "")
+ id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE",
+ ieee80211w="2", scan_freq="2412")
+ dev[0].dump_monitor()
+ hapd.wait_sta()
+
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].roam(bssid2)
+ dev[0].dump_monitor()
+ hapd2.wait_sta()
+
+ dev[0].roam(bssid)
+ dev[0].dump_monitor()
+
+def test_sae_ocv_pmk(dev, apdev):
+ """SAE with OCV and fetching PMK (successful 4-way handshake)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['ieee80211w'] = '2'
+ params['ocv'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].set("sae_groups", "")
+ id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", ocv="1",
+ ieee80211w="2", scan_freq="2412")
+ hapd.wait_sta()
+
+ pmk_h = hapd.request("GET_PMK " + dev[0].own_addr())
+ if "FAIL" in pmk_h or len(pmk_h) == 0:
+ raise Exception("Failed to fetch PMK from hostapd during a successful authentication")
+
+ pmk_w = dev[0].get_pmk(id)
+ if pmk_h != pmk_w:
+ raise Exception("Fetched PMK does not match: hostapd %s, wpa_supplicant %s" % (pmk_h, pmk_w))
+
+def test_sae_ocv_pmk_failure(dev, apdev):
+ """SAE with OCV and fetching PMK (failed 4-way handshake)"""
+ check_sae_capab(dev[0])
+ params = hostapd.wpa2_params(ssid="test-sae",
+ passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params['ieee80211w'] = '2'
+ params['ocv'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].set("sae_groups", "")
+ dev[0].set("oci_freq_override_eapol", "2462")
+ id = dev[0].connect("test-sae", psk="12345678", key_mgmt="SAE", ocv="1",
+ ieee80211w="2", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("No connection result reported")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+
+ pmk_h = hapd.request("GET_PMK " + dev[0].own_addr())
+ if "FAIL" in pmk_h or len(pmk_h) == 0:
+ raise Exception("Failed to fetch PMK from hostapd during a successful authentication")
+
+ res = dev[0].request("PMKSA_GET %d" % id)
+ if not res.startswith(hapd.own_addr()):
+ raise Exception("PMKSA from wpa_supplicant does not have matching BSSID")
+ pmk_w = res.split(' ')[2]
+ if pmk_h != pmk_w:
+ raise Exception("Fetched PMK does not match: hostapd %s, wpa_supplicant %s" % (pmk_h, pmk_w))
+
+ dev[0].request("DISCONNECT")
+ time.sleep(0.1)
+ pmk_h2 = hapd.request("GET_PMK " + dev[0].own_addr())
+ res = dev[0].request("PMKSA_GET %d" % id)
+ pmk_w2 = res.split(' ')[2]
+ if pmk_h2 != pmk_h:
+ raise Exception("hostapd did not report correct PMK after disconnection")
+ if pmk_w2 != pmk_w:
+ raise Exception("wpa_supplicant did not report correct PMK after disconnection")
diff --git a/contrib/wpa/tests/hwsim/test_sae_pk.py b/contrib/wpa/tests/hwsim/test_sae_pk.py
new file mode 100644
index 000000000000..3bbc62ecd0ba
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_sae_pk.py
@@ -0,0 +1,462 @@
+# Test cases for SAE-PK
+# Copyright (c) 2020, The Linux Foundation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hostapd
+from utils import *
+
+SAE_PK_SSID = "SAE-PK test"
+
+SAE_PK_SEC3_PW = "r6cr-6ksa-56og"
+SAE_PK_SEC3_M = "089ec11475d55f0d38403f5117a6d64d"
+SAE_PK_19_PK = "MHcCAQEEIAJIGlfnteonDb7rQyP/SGQjwzrZAnfrXIm4280VWajYoAoGCCqGSM49AwEHoUQDQgAEeRkstKQV+FSAMqBayqFknn2nAQsdsh/MhdX6tiHOTAFin/sUMFRMyspPtIu7YvlKdsexhI0jPVhaYZn1jKWhZg=="
+
+SAE_PK_20_PW = "4zsy-uspe-xbfr-3ifo"
+SAE_PK_20_M = "206902f9f09b62e3fafcd487c65f5c64"
+SAE_PK_20_PK = "MIGkAgEBBDA4wpA6w/fK0g3a2V6QmcoxNoFCVuQPyzWvKYimJkgXsVsXt2ERXQ7dGOVXeycM5DqgBwYFK4EEACKhZANiAARTdszGBNe2PGCnc8Wvs+IDvdVEf4PPBrty0meRZf6UTbGouquTHpy6KKTq5sxrulYzsQFimg4op0UJBGxAzqo0EtTgMlLiBvY0I3Nl3N69MhWo8nvnmguvGGN32AAPXpQ="
+
+SAE_PK_21_PW = "vluk-umpa-3mbw-zrhe-s2n2"
+SAE_PK_21_M = "1c63c1b17e9a999f0693b4341a970a63"
+SAE_PK_21_PK = "MIHcAgEBBEIBnFBjU0ywxo1dLTYcg2aZdMfNY7JHt4GTADRTgJ7RRo9qzRIlfmK7p+BP1c8YM8ia8v7YDTut00rDOfzkdmLOi0WgBwYFK4EEACOhgYkDgYYABAD6n3DHI+qaj/lElhe2sUSKqAe4sweckMlr9bhdmwp8Wsx5lKR/Tt7WPexeqFrA47nChw5WMWy6qJanCKNFvGYG0ADUWnxesYczGtCdUYJQgs3X5tHSapMssz6tP8QL0X9adTI/H3tFYhiVIdor03eZDUVnej78/F31CcHcjGBEyItVfw=="
+
+def run_sae_pk(apdev, dev, ssid, pw, m, pk, ap_groups=None,
+ confirm_immediate=False):
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = ['%s|pk=%s:%s' % (pw, m, pk)]
+ if ap_groups:
+ params['sae_groups'] = ap_groups
+ if confirm_immediate:
+ params['sae_confirm_immediate'] = '1'
+ hapd = hostapd.add_ap(apdev, params)
+ bssid = hapd.own_addr()
+
+ dev.connect(ssid, sae_password=pw, key_mgmt="SAE", scan_freq="2412")
+ bss = dev.get_bss(bssid)
+ if 'flags' not in bss:
+ raise Exception("Could not get BSS flags from BSS table")
+ if "[SAE-H2E]" not in bss['flags'] or "[SAE-PK]" not in bss['flags']:
+ raise Exception("Unexpected BSS flags: " + bss['flags'])
+ status = dev.get_status()
+ if "sae_h2e" not in status or "sae_pk" not in status or \
+ status["sae_h2e"] != "1" or status["sae_pk"] != "1":
+ raise Exception("SAE-PK or H2E not indicated in STATUS")
+ dev.request("REMOVE_NETWORK *")
+ dev.wait_disconnected()
+ hapd.disable()
+
+def test_sae_pk(dev, apdev):
+ """SAE-PK"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "")
+
+ passwords = [SAE_PK_SEC3_PW,
+ "r6cr-6ksa-56oo-5557",
+ "r6cr-6ksa-56oo-555p-wi44",
+ "r6cr-6ksa-56oo-555p-wi4b-vghb",
+ "r6cr-6ksa-56oo-555p-wi4b-vghv-vwro",
+ "r6cr-6ksa-56oo-555p-wi4b-vghv-vwrp-taqj",
+ "r6cr-6ksa-56oo-555p-wi4b-vghv-vwrp-taq5-4zfq",
+ "r6cr-6ksa-56oo-555p-wi4b-vghv-vwrp-taq5-4zfa-ye3x",
+ "r6cr-6ksa-56oo-555p-wi4b-vghv-vwrp-taq5-4zfa-ye35-4rne",
+ "r6cr-6ksa-56oo-555p-wi4b-vghv-vwrp-taq5-4zfa-ye35-4rny-5yqz"]
+ for p in passwords:
+ run_sae_pk(apdev[0], dev[0], SAE_PK_SSID, p, SAE_PK_SEC3_M,
+ SAE_PK_19_PK)
+
+def test_sae_pk_group_negotiation(dev, apdev):
+ """SAE-PK"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "20 19")
+
+ try:
+ run_sae_pk(apdev[0], dev[0], SAE_PK_SSID, SAE_PK_SEC3_PW,
+ SAE_PK_SEC3_M, SAE_PK_19_PK, ap_groups="19 20")
+ finally:
+ dev[0].set("sae_groups", "")
+
+def test_sae_pk_sec_3(dev, apdev):
+ """SAE-PK with Sec 3"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "")
+
+ run_sae_pk(apdev[0], dev[0], SAE_PK_SSID, SAE_PK_SEC3_PW, SAE_PK_SEC3_M,
+ SAE_PK_19_PK)
+
+def test_sae_pk_sec_5(dev, apdev):
+ """SAE-PK with Sec 5"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "")
+
+ pw = "hbbi-f4xq-b457-jjew-muei"
+ m = "d2e5fa27d1be8897f987f2d480d2af6b"
+ run_sae_pk(apdev[0], dev[0], SAE_PK_SSID, pw, m, SAE_PK_19_PK)
+
+def test_sae_pk_group_20(dev, apdev):
+ """SAE-PK with group 20"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "20")
+
+ try:
+ run_sae_pk(apdev[0], dev[0], SAE_PK_SSID, SAE_PK_20_PW,
+ SAE_PK_20_M, SAE_PK_20_PK, ap_groups="20")
+ finally:
+ dev[0].set("sae_groups", "")
+
+def test_sae_pk_group_21(dev, apdev):
+ """SAE-PK with group 21"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "21")
+
+ try:
+ run_sae_pk(apdev[0], dev[0], SAE_PK_SSID, SAE_PK_21_PW,
+ SAE_PK_21_M, SAE_PK_21_PK, ap_groups="21")
+ finally:
+ dev[0].set("sae_groups", "")
+
+def test_sae_pk_group_20_sae_group_19(dev, apdev):
+ """SAE-PK with group 20 with SAE group 19"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "19")
+ try:
+ run_sae_pk(apdev[0], dev[0], SAE_PK_SSID, SAE_PK_20_PW,
+ SAE_PK_20_M, SAE_PK_20_PK, ap_groups="19")
+ finally:
+ dev[0].set("sae_groups", "")
+
+def test_sae_pk_group_20_sae_group_21(dev, apdev):
+ """SAE-PK with group 20 with SAE group 21"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "21")
+ try:
+ run_sae_pk(apdev[0], dev[0], SAE_PK_SSID, SAE_PK_20_PW,
+ SAE_PK_20_M, SAE_PK_20_PK, ap_groups="21")
+ finally:
+ dev[0].set("sae_groups", "")
+
+def test_sae_pk_group_19_sae_group_20(dev, apdev):
+ """SAE-PK with group 19 with SAE group 20"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "20")
+ try:
+ run_sae_pk(apdev[0], dev[0], SAE_PK_SSID, SAE_PK_SEC3_PW,
+ SAE_PK_SEC3_M, SAE_PK_19_PK, ap_groups="20")
+ finally:
+ dev[0].set("sae_groups", "")
+
+def test_sae_pk_password_without_pk(dev, apdev):
+ """SAE-PK password but not SAE-PK on the AP"""
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = SAE_PK_SEC3_PW
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(SAE_PK_SSID, sae_password=SAE_PK_SEC3_PW,
+ key_mgmt="SAE", scan_freq="2412")
+ if dev[0].get_status_field("sae_pk") != "0":
+ raise Exception("Unexpected sae_pk STATUS value")
+
+def test_sae_pk_only(dev, apdev):
+ """SAE-PK only"""
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = SAE_PK_SEC3_PW
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(SAE_PK_SSID, sae_password=SAE_PK_SEC3_PW,
+ key_mgmt="SAE", sae_pk="1",
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if ev is None:
+ raise Exception("No result for the connection attempt")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection without SAE-PK")
+ dev[0].request("DISCONNECT")
+ dev[0].dump_monitor()
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = ['%s|pk=%s:%s' % (SAE_PK_SEC3_PW, SAE_PK_SEC3_M,
+ SAE_PK_19_PK)]
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid2, freq=2412, force_scan=True)
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_connected()
+ if bssid2 not in ev:
+ raise Exception("Unexpected connection BSSID")
+ if dev[0].get_status_field("sae_pk") != "1":
+ raise Exception("SAE-PK was not used")
+
+def test_sae_pk_modes(dev, apdev):
+ """SAE-PK modes"""
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_password'] = ['%s|pk=%s:%s' % (SAE_PK_SEC3_PW, SAE_PK_SEC3_M,
+ SAE_PK_19_PK)]
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ tests = [(2, 0), (1, 1), (0, 1)]
+ for sae_pk, expected in tests:
+ dev[0].connect(SAE_PK_SSID, sae_password=SAE_PK_SEC3_PW,
+ key_mgmt="SAE", sae_pk=str(sae_pk), ieee80211w="2",
+ scan_freq="2412")
+ val = dev[0].get_status_field("sae_pk")
+ if val != str(expected):
+ raise Exception("Unexpected sae_pk=%d result %s" % (sae_pk, val))
+ dev[0].request("REMOVE_NETWORK *")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def test_sae_pk_not_on_ap(dev, apdev):
+ """SAE-PK password, but no PK on AP"""
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = SAE_PK_SEC3_PW
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(SAE_PK_SSID, sae_password=SAE_PK_SEC3_PW,
+ key_mgmt="SAE", scan_freq="2412")
+ if dev[0].get_status_field("sae_pk") == "1":
+ raise Exception("SAE-PK was claimed to be used")
+
+def test_sae_pk_transition_disable(dev, apdev):
+ """SAE-PK transition disable indication"""
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = ['%s|pk=%s:%s' % (SAE_PK_SEC3_PW, SAE_PK_SEC3_M,
+ SAE_PK_19_PK)]
+ params['transition_disable'] = '0x02'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ id = dev[0].connect(SAE_PK_SSID, sae_password=SAE_PK_SEC3_PW,
+ key_mgmt="SAE", scan_freq="2412")
+ ev = dev[0].wait_event(["TRANSITION-DISABLE"], timeout=1)
+ if ev is None:
+ raise Exception("Transition disable not indicated")
+ if ev.split(' ')[1] != "02":
+ raise Exception("Unexpected transition disable bitmap: " + ev)
+
+ val = dev[0].get_network(id, "sae_pk")
+ if val != "1":
+ raise Exception("Unexpected sae_pk value: " + str(val))
+
+def test_sae_pk_mixed(dev, apdev):
+ """SAE-PK mixed deployment"""
+ run_sae_pk_mixed(dev, apdev)
+
+def test_sae_pk_mixed_immediate_confirm(dev, apdev):
+ """SAE-PK mixed deployment with immediate confirm on AP"""
+ run_sae_pk_mixed(dev, apdev, confirm_immediate=True)
+
+def run_sae_pk_mixed(dev, apdev, confirm_immediate=False):
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = SAE_PK_SEC3_PW
+ if confirm_immediate:
+ params['sae_confirm_immediate'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = ['%s|pk=%s:%s' % (SAE_PK_SEC3_PW, SAE_PK_SEC3_M,
+ SAE_PK_19_PK)]
+ # Disable HT from the SAE-PK BSS to make the station prefer the other BSS
+ # by default.
+ params['ieee80211n'] = '0'
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].scan_for_bss(bssid2, freq=2412)
+
+ dev[0].connect(SAE_PK_SSID, sae_password=SAE_PK_SEC3_PW,
+ key_mgmt="SAE", scan_freq="2412")
+
+ if dev[0].get_status_field("sae_pk") != "1":
+ raise Exception("SAE-PK was not used")
+ if dev[0].get_status_field("bssid") != bssid2:
+ raise Exception("Unexpected BSSID selected")
+
+def check_sae_pk_sta_connect_failure(dev):
+ dev.connect(SAE_PK_SSID, sae_password=SAE_PK_SEC3_PW,
+ key_mgmt="SAE", scan_freq="2412", wait_connect=False)
+ ev = dev.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("No result for the connection attempt")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+
+def test_sae_pk_missing_ie(dev, apdev):
+ """SAE-PK and missing SAE-PK IE in confirm"""
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = ['%s|pk=%s:%s' % (SAE_PK_SEC3_PW, SAE_PK_SEC3_M,
+ SAE_PK_19_PK)]
+ params['sae_pk_omit'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_sae_pk_sta_connect_failure(dev[0])
+
+def test_sae_pk_unexpected_status(dev, apdev):
+ """SAE-PK and unexpected status code in commit"""
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = ['%s|pk=%s:%s' % (SAE_PK_SEC3_PW, SAE_PK_SEC3_M,
+ SAE_PK_19_PK)]
+ params['sae_commit_status'] = '126'
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_sae_pk_sta_connect_failure(dev[0])
+
+def test_sae_pk_invalid_signature(dev, apdev):
+ """SAE-PK and invalid signature"""
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ other = "MHcCAQEEILw+nTjFzRyhVea0G6KbwZu18oWrfhzppxj+MceUO3YLoAoGCCqGSM49AwEHoUQDQgAELdou6LuTDNiMVlMB65KsWhQFbPXR9url0EA6luWzUfAuGoDXYJUBTVz6Nv3mz6oQcDrSiDmz/LejndJ0YHGgfQ=="
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = ['%s|pk=%s:%s:%s' % (SAE_PK_SEC3_PW, SAE_PK_SEC3_M,
+ SAE_PK_19_PK, other)]
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_sae_pk_sta_connect_failure(dev[0])
+
+def test_sae_pk_invalid_fingerprint(dev, apdev):
+ """SAE-PK and invalid fingerprint"""
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ other = "431ff8322f93b9dc50ded9f3d14ace21"
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params['sae_password'] = ['%s|pk=%s:%s' % (SAE_PK_SEC3_PW, other,
+ SAE_PK_19_PK)]
+ hapd = hostapd.add_ap(apdev[0], params)
+ check_sae_pk_sta_connect_failure(dev[0])
+
+def test_sae_pk_confirm_immediate(dev, apdev):
+ """SAE-PK with immediate confirm on AP"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "")
+
+ run_sae_pk(apdev[0], dev[0], SAE_PK_SSID, SAE_PK_SEC3_PW,
+ SAE_PK_SEC3_M, SAE_PK_19_PK, confirm_immediate=True)
+
+def test_sae_pk_and_psk(dev, apdev):
+ """SAE-PK and PSK"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+ dev[0].set("sae_groups", "")
+ dev[2].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params['sae_password'] = ['%s|pk=%s:%s' % (SAE_PK_SEC3_PW,
+ SAE_PK_SEC3_M,
+ SAE_PK_19_PK)]
+ params['wpa_passphrase'] = SAE_PK_SEC3_PW
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect(SAE_PK_SSID, sae_password=SAE_PK_SEC3_PW, key_mgmt="SAE",
+ scan_freq="2412")
+ dev[1].connect(SAE_PK_SSID, psk=SAE_PK_SEC3_PW, key_mgmt="WPA-PSK",
+ scan_freq="2412")
+ dev[2].connect(SAE_PK_SSID, sae_password=SAE_PK_SEC3_PW, key_mgmt="SAE",
+ sae_pk="2", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK *")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ dev[0].connect(SAE_PK_SSID, psk=SAE_PK_SEC3_PW, key_mgmt="SAE",
+ scan_freq="2412")
+ status = dev[0].get_status()
+ if "sae_h2e" not in status or "sae_pk" not in status or \
+ status["sae_h2e"] != "1" or status["sae_pk"] != "1":
+ raise Exception("SAE-PK or H2E not indicated in STATUS")
+
+def test_sae_pk_and_psk_invalid_password(dev, apdev):
+ """SAE-PK and PSK using invalid password combination"""
+ check_sae_pk_capab(dev[0])
+ dev[0].flush_scan_cache()
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE WPA-PSK'
+ params['sae_password'] = ['%s|pk=%s:%s' % (SAE_PK_SEC3_PW,
+ SAE_PK_SEC3_M,
+ SAE_PK_19_PK)]
+ params['wpa_passphrase'] = SAE_PK_20_PW
+ hapd = hostapd.add_ap(apdev[0], params, no_enable=True)
+ res = hapd.request("ENABLE")
+ if "FAIL" not in res:
+ raise Exception("Invalid configuration accepted")
+
+def test_sae_pk_invalid_pw(dev, apdev):
+ """SAE-PK with invalid password on AP"""
+ check_sae_pk_capab(dev[0])
+ dev[0].set("sae_groups", "")
+
+ params = hostapd.wpa2_params(ssid=SAE_PK_SSID)
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params["sae_pk_password_check_skip"] = "1"
+ invalid_pw = "r6cr+6ksa+56og"
+ params['sae_password'] = ['%s|pk=%s:%s' % (invalid_pw, SAE_PK_SEC3_M,
+ SAE_PK_19_PK)]
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect(SAE_PK_SSID, sae_password=invalid_pw,
+ key_mgmt="SAE", ieee80211w="2", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK *")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ dev[0].connect(SAE_PK_SSID, sae_password=SAE_PK_SEC3_PW,
+ key_mgmt="SAE", ieee80211w="2", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("No result for the connection attempt")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection with invalid SAE-PK password")
+ dev[0].request("DISCONNECT")
diff --git a/contrib/wpa/tests/hwsim/test_scan.py b/contrib/wpa/tests/hwsim/test_scan.py
new file mode 100644
index 000000000000..24a7903ab217
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_scan.py
@@ -0,0 +1,2025 @@
+# Scanning tests
+# Copyright (c) 2013-2016, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import time
+import logging
+logger = logging.getLogger()
+import os
+import struct
+import subprocess
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+from tshark import run_tshark
+from test_ap_csa import switch_channel, wait_channel_switch
+
+def check_scan(dev, params, other_started=False, test_busy=False):
+ if not other_started:
+ dev.dump_monitor()
+ id = dev.request("SCAN " + params)
+ if "FAIL" in id:
+ raise Exception("Failed to start scan")
+ id = int(id)
+
+ if test_busy:
+ if "FAIL-BUSY" not in dev.request("SCAN"):
+ raise Exception("SCAN command while already scanning not rejected")
+
+ if other_started:
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ if ev is None:
+ raise Exception("Other scan did not start")
+ if "id=" + str(id) in ev:
+ raise Exception("Own scan id unexpectedly included in start event")
+
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Other scan did not complete")
+ if "id=" + str(id) in ev:
+ raise Exception("Own scan id unexpectedly included in completed event")
+
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ if ev is None:
+ raise Exception("Scan did not start")
+ if "id=" + str(id) not in ev:
+ raise Exception("Scan id not included in start event")
+ if test_busy:
+ if "FAIL-BUSY" not in dev.request("SCAN"):
+ raise Exception("SCAN command while already scanning not rejected")
+
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+ if "id=" + str(id) not in ev:
+ raise Exception("Scan id not included in completed event")
+
+def check_scan_retry(dev, params, bssid):
+ for i in range(0, 5):
+ check_scan(dev, "freq=2412-2462,5180 use_id=1")
+ if int(dev.get_bss(bssid)['age']) <= 1:
+ return
+ raise Exception("Unexpectedly old BSS entry")
+
+@remote_compatible
+def test_scan(dev, apdev):
+ """Control interface behavior on scan parameters"""
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+
+ logger.info("Full scan")
+ check_scan(dev[0], "use_id=1", test_busy=True)
+
+ logger.info("Limited channel scan")
+ check_scan_retry(dev[0], "freq=2412-2462,5180 use_id=1", bssid)
+
+ # wait long enough to allow next scans to be verified not to find the AP
+ time.sleep(2)
+
+ logger.info("Passive single-channel scan")
+ check_scan(dev[0], "freq=2457 passive=1 use_id=1")
+ logger.info("Active single-channel scan")
+ check_scan(dev[0], "freq=2452 passive=0 use_id=1")
+ if int(dev[0].get_bss(bssid)['age']) < 2:
+ raise Exception("Unexpectedly updated BSS entry")
+
+ logger.info("Active single-channel scan on AP's operating channel")
+ check_scan_retry(dev[0], "freq=2412 passive=0 use_id=1", bssid)
+
+@remote_compatible
+def test_scan_tsf(dev, apdev):
+ """Scan and TSF updates from Beacon/Probe Response frames"""
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan",
+ 'beacon_int': "100"})
+ bssid = apdev[0]['bssid']
+
+ tsf = []
+ for passive in [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1]:
+ check_scan(dev[0], "freq=2412 passive=%d use_id=1" % passive)
+ bss = dev[0].get_bss(bssid)
+ if bss:
+ tsf.append(int(bss['tsf']))
+ logger.info("TSF: " + bss['tsf'])
+ if tsf[-3] <= tsf[-4]:
+ # For now, only write this in the log without failing the test case
+ # since mac80211_hwsim does not yet update the Timestamp field in
+ # Probe Response frames.
+ logger.info("Probe Response did not update TSF")
+ #raise Exception("Probe Response did not update TSF")
+ if tsf[-1] <= tsf[-3]:
+ raise Exception("Beacon did not update TSF")
+ if 0 in tsf:
+ raise Exception("0 TSF reported")
+
+@remote_compatible
+def test_scan_only(dev, apdev):
+ """Control interface behavior on scan parameters with type=only"""
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+
+ logger.info("Full scan")
+ check_scan(dev[0], "type=only use_id=1")
+
+ logger.info("Limited channel scan")
+ check_scan_retry(dev[0], "type=only freq=2412-2462,5180 use_id=1", bssid)
+
+ # wait long enough to allow next scans to be verified not to find the AP
+ time.sleep(2)
+
+ logger.info("Passive single-channel scan")
+ check_scan(dev[0], "type=only freq=2457 passive=1 use_id=1")
+ logger.info("Active single-channel scan")
+ check_scan(dev[0], "type=only freq=2452 passive=0 use_id=1")
+ if int(dev[0].get_bss(bssid)['age']) < 2:
+ raise Exception("Unexpectedly updated BSS entry")
+
+ logger.info("Active single-channel scan on AP's operating channel")
+ check_scan_retry(dev[0], "type=only freq=2412 passive=0 use_id=1", bssid)
+
+@remote_compatible
+def test_scan_external_trigger(dev, apdev):
+ """Avoid operations during externally triggered scan"""
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+ dev[0].cmd_execute(['iw', dev[0].ifname, 'scan', 'trigger'])
+ check_scan(dev[0], "use_id=1", other_started=True)
+
+def test_scan_bss_expiration_count(dev, apdev):
+ """BSS entry expiration based on scan results without match"""
+ if "FAIL" not in dev[0].request("BSS_EXPIRE_COUNT 0"):
+ raise Exception("Invalid BSS_EXPIRE_COUNT accepted")
+ if "OK" not in dev[0].request("BSS_EXPIRE_COUNT 2"):
+ raise Exception("BSS_EXPIRE_COUNT failed")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+ dev[0].scan(freq="2412", only_new=True)
+ if bssid not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("BSS not found in initial scan")
+ hapd.request("DISABLE")
+ # Try to give enough time for hostapd to have stopped mac80211 from
+ # beaconing before checking a new scan. This is needed with UML time travel
+ # testing.
+ hapd.ping()
+ time.sleep(0.2)
+ dev[0].scan(freq="2412", only_new=True)
+ if bssid not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("BSS not found in first scan without match")
+ dev[0].scan(freq="2412", only_new=True)
+ if bssid in dev[0].request("SCAN_RESULTS"):
+ raise Exception("BSS found after two scans without match")
+
+@remote_compatible
+def test_scan_bss_expiration_age(dev, apdev):
+ """BSS entry expiration based on age"""
+ try:
+ if "FAIL" not in dev[0].request("BSS_EXPIRE_AGE COUNT 9"):
+ raise Exception("Invalid BSS_EXPIRE_AGE accepted")
+ if "OK" not in dev[0].request("BSS_EXPIRE_AGE 10"):
+ raise Exception("BSS_EXPIRE_AGE failed")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+ # Allow couple more retries to avoid reporting errors during heavy load
+ for i in range(5):
+ dev[0].scan(freq="2412")
+ if bssid in dev[0].request("SCAN_RESULTS"):
+ break
+ if bssid not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("BSS not found in initial scan")
+ hapd.request("DISABLE")
+ logger.info("Waiting for BSS entry to expire")
+ time.sleep(7)
+ if bssid not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("BSS expired too quickly")
+ ev = dev[0].wait_event(["CTRL-EVENT-BSS-REMOVED"], timeout=15)
+ if ev is None:
+ raise Exception("BSS entry expiration timed out")
+ if bssid in dev[0].request("SCAN_RESULTS"):
+ raise Exception("BSS not removed after expiration time")
+ finally:
+ dev[0].request("BSS_EXPIRE_AGE 180")
+
+@remote_compatible
+def test_scan_filter(dev, apdev):
+ """Filter scan results based on SSID"""
+ try:
+ if "OK" not in dev[0].request("SET filter_ssids 1"):
+ raise Exception("SET failed")
+ id = dev[0].connect("test-scan", key_mgmt="NONE", only_add_network=True)
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+ hostapd.add_ap(apdev[1], {"ssid": "test-scan2"})
+ bssid2 = apdev[1]['bssid']
+ dev[0].scan(freq="2412", only_new=True)
+ if bssid not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("BSS not found in scan results")
+ if bssid2 in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Unexpected BSS found in scan results")
+ dev[0].set_network_quoted(id, "ssid", "")
+ dev[0].scan(freq="2412")
+ id2 = dev[0].connect("test", key_mgmt="NONE", only_add_network=True)
+ dev[0].scan(freq="2412")
+ finally:
+ dev[0].request("SET filter_ssids 0")
+
+@remote_compatible
+def test_scan_int(dev, apdev):
+ """scan interval configuration"""
+ try:
+ if "FAIL" not in dev[0].request("SCAN_INTERVAL -1"):
+ raise Exception("Accepted invalid scan interval")
+ if "OK" not in dev[0].request("SCAN_INTERVAL 1"):
+ raise Exception("Failed to set scan interval")
+ dev[0].connect("not-used", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ times = {}
+ for i in range(0, 3):
+ logger.info("Waiting for scan to start")
+ start = os.times()[4]
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("did not start a scan")
+ stop = os.times()[4]
+ times[i] = stop - start
+ logger.info("Waiting for scan to complete")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 10)
+ if ev is None:
+ raise Exception("did not complete a scan")
+ logger.info("times=" + str(times))
+ if times[0] > 1 or times[1] < 0.5 or times[1] > 1.5 or times[2] < 0.5 or times[2] > 1.5:
+ raise Exception("Unexpected scan timing: " + str(times))
+ finally:
+ dev[0].request("SCAN_INTERVAL 5")
+
+def test_scan_bss_operations(dev, apdev):
+ """Control interface behavior on BSS parameters"""
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+ hostapd.add_ap(apdev[1], {"ssid": "test2-scan"})
+ bssid2 = apdev[1]['bssid']
+
+ dev[0].scan(freq="2412")
+ dev[0].scan(freq="2412")
+ dev[0].scan(freq="2412")
+
+ id1 = dev[0].request("BSS FIRST MASK=0x1").splitlines()[0].split('=')[1]
+ id2 = dev[0].request("BSS LAST MASK=0x1").splitlines()[0].split('=')[1]
+
+ res = dev[0].request("BSS RANGE=ALL MASK=0x20001")
+ if "id=" + id1 not in res:
+ raise Exception("Missing BSS " + id1)
+ if "id=" + id2 not in res:
+ raise Exception("Missing BSS " + id2)
+ if "====" not in res:
+ raise Exception("Missing delim")
+ if "####" not in res:
+ raise Exception("Missing end")
+
+ res = dev[0].request("BSS RANGE=ALL MASK=0")
+ if "id=" + id1 not in res:
+ raise Exception("Missing BSS " + id1)
+ if "id=" + id2 not in res:
+ raise Exception("Missing BSS " + id2)
+ if "====" in res:
+ raise Exception("Unexpected delim")
+ if "####" in res:
+ raise Exception("Unexpected end delim")
+
+ res = dev[0].request("BSS RANGE=ALL MASK=0x1").splitlines()
+ if len(res) != 2:
+ raise Exception("Unexpected result: " + str(res))
+ res = dev[0].request("BSS FIRST MASK=0x1")
+ if "id=" + id1 not in res:
+ raise Exception("Unexpected result: " + res)
+ res = dev[0].request("BSS LAST MASK=0x1")
+ if "id=" + id2 not in res:
+ raise Exception("Unexpected result: " + res)
+ res = dev[0].request("BSS ID-" + id1 + " MASK=0x1")
+ if "id=" + id1 not in res:
+ raise Exception("Unexpected result: " + res)
+ res = dev[0].request("BSS NEXT-" + id1 + " MASK=0x1")
+ if "id=" + id2 not in res:
+ raise Exception("Unexpected result: " + res)
+ res = dev[0].request("BSS NEXT-" + id2 + " MASK=0x1")
+ if "id=" in res:
+ raise Exception("Unexpected result: " + res)
+
+ if len(dev[0].request("BSS RANGE=" + id2 + " MASK=0x1").splitlines()) != 0:
+ raise Exception("Unexpected RANGE=1 result")
+ if len(dev[0].request("BSS RANGE=" + id1 + "- MASK=0x1").splitlines()) != 2:
+ raise Exception("Unexpected RANGE=0- result")
+ if len(dev[0].request("BSS RANGE=-" + id2 + " MASK=0x1").splitlines()) != 2:
+ raise Exception("Unexpected RANGE=-1 result")
+ if len(dev[0].request("BSS RANGE=" + id1 + "-" + id2 + " MASK=0x1").splitlines()) != 2:
+ raise Exception("Unexpected RANGE=0-1 result")
+ if len(dev[0].request("BSS RANGE=" + id2 + "-" + id2 + " MASK=0x1").splitlines()) != 1:
+ raise Exception("Unexpected RANGE=1-1 result")
+ if len(dev[0].request("BSS RANGE=" + str(int(id2) + 1) + "-" + str(int(id2) + 10) + " MASK=0x1").splitlines()) != 0:
+ raise Exception("Unexpected RANGE=2-10 result")
+ if len(dev[0].request("BSS RANGE=0-" + str(int(id2) + 10) + " MASK=0x1").splitlines()) != 2:
+ raise Exception("Unexpected RANGE=0-10 result")
+ if len(dev[0].request("BSS RANGE=" + id1 + "-" + id1 + " MASK=0x1").splitlines()) != 1:
+ raise Exception("Unexpected RANGE=0-0 result")
+
+ res = dev[0].request("BSS p2p_dev_addr=FOO")
+ if "FAIL" in res or "id=" in res:
+ raise Exception("Unexpected result: " + res)
+ res = dev[0].request("BSS p2p_dev_addr=00:11:22:33:44:55")
+ if "FAIL" in res or "id=" in res:
+ raise Exception("Unexpected result: " + res)
+
+ dev[0].request("BSS_FLUSH 1000")
+ res = dev[0].request("BSS RANGE=ALL MASK=0x1").splitlines()
+ if len(res) != 2:
+ raise Exception("Unexpected result after BSS_FLUSH 1000")
+ dev[0].request("BSS_FLUSH 0")
+ res = dev[0].request("BSS RANGE=ALL MASK=0x1").splitlines()
+ if len(res) != 0:
+ raise Exception("Unexpected result after BSS_FLUSH 0")
+
+@remote_compatible
+def test_scan_and_interface_disabled(dev, apdev):
+ """Scan operation when interface gets disabled"""
+ try:
+ dev[0].request("SCAN")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ if ev is None:
+ raise Exception("Scan did not start")
+ dev[0].request("DRIVER_EVENT INTERFACE_DISABLED")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=7)
+ if ev is not None:
+ raise Exception("Scan completed unexpectedly")
+
+ # verify that scan is rejected
+ if "FAIL" not in dev[0].request("SCAN"):
+ raise Exception("New scan request was accepted unexpectedly")
+
+ dev[0].request("DRIVER_EVENT INTERFACE_ENABLED")
+ dev[0].scan(freq="2412")
+ finally:
+ dev[0].request("DRIVER_EVENT INTERFACE_ENABLED")
+
+@remote_compatible
+def test_scan_for_auth(dev, apdev):
+ """cfg80211 workaround with scan-for-auth"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ # Block sme-connect radio work with an external radio work item, so that
+ # SELECT_NETWORK can decide to use fast associate without a new scan while
+ # cfg80211 still has the matching BSS entry, but the actual connection is
+ # not yet started.
+ id = dev[0].request("RADIO_WORK add block-work")
+ ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ dev[0].dump_monitor()
+ # Clear cfg80211 BSS table.
+ res, data = dev[0].cmd_execute(['iw', dev[0].ifname, 'scan', 'trigger',
+ 'freq', '2457', 'flush'])
+ if res != 0:
+ raise HwsimSkip("iw scan trigger flush not supported")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ if ev is None:
+ raise Exception("External flush scan timed out")
+ # Release blocking radio work to allow connection to go through with the
+ # cfg80211 BSS entry missing.
+ dev[0].request("RADIO_WORK done " + id)
+
+ dev[0].wait_connected(timeout=15)
+
+@remote_compatible
+def test_scan_for_auth_fail(dev, apdev):
+ """cfg80211 workaround with scan-for-auth failing"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ # Block sme-connect radio work with an external radio work item, so that
+ # SELECT_NETWORK can decide to use fast associate without a new scan while
+ # cfg80211 still has the matching BSS entry, but the actual connection is
+ # not yet started.
+ id = dev[0].request("RADIO_WORK add block-work")
+ ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ dev[0].dump_monitor()
+ hapd.disable()
+ # Clear cfg80211 BSS table.
+ res, data = dev[0].cmd_execute(['iw', dev[0].ifname, 'scan', 'trigger',
+ 'freq', '2457', 'flush'])
+ if res != 0:
+ raise HwsimSkip("iw scan trigger flush not supported")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ if ev is None:
+ raise Exception("External flush scan timed out")
+ # Release blocking radio work to allow connection to go through with the
+ # cfg80211 BSS entry missing.
+ dev[0].request("RADIO_WORK done " + id)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS",
+ "CTRL-EVENT-CONNECTED"], 15)
+ if ev is None:
+ raise Exception("Scan event missing")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ dev[0].request("DISCONNECT")
+
+@remote_compatible
+def test_scan_for_auth_wep(dev, apdev):
+ """cfg80211 scan-for-auth workaround with WEP keys"""
+ check_wep_capa(dev[0])
+ dev[0].flush_scan_cache()
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wep", "wep_key0": '"abcde"',
+ "auth_algs": "2"})
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ # Block sme-connect radio work with an external radio work item, so that
+ # SELECT_NETWORK can decide to use fast associate without a new scan while
+ # cfg80211 still has the matching BSS entry, but the actual connection is
+ # not yet started.
+ id = dev[0].request("RADIO_WORK add block-work")
+ ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+ dev[0].connect("wep", key_mgmt="NONE", wep_key0='"abcde"',
+ auth_alg="SHARED", scan_freq="2412", wait_connect=False)
+ dev[0].dump_monitor()
+ # Clear cfg80211 BSS table.
+ res, data = dev[0].cmd_execute(['iw', dev[0].ifname, 'scan', 'trigger',
+ 'freq', '2457', 'flush'])
+ if res != 0:
+ raise HwsimSkip("iw scan trigger flush not supported")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], 5)
+ if ev is None:
+ raise Exception("External flush scan timed out")
+ # Release blocking radio work to allow connection to go through with the
+ # cfg80211 BSS entry missing.
+ dev[0].request("RADIO_WORK done " + id)
+
+ dev[0].wait_connected(timeout=15)
+
+@remote_compatible
+def test_scan_hidden(dev, apdev):
+ """Control interface behavior on scan parameters"""
+ dev[0].flush_scan_cache()
+ ssid = "test-scan"
+ wrong_ssid = "wrong"
+ hapd = hostapd.add_ap(apdev[0], {"ssid": ssid,
+ "ignore_broadcast_ssid": "1"})
+ bssid = apdev[0]['bssid']
+
+ check_scan(dev[0], "freq=2412 use_id=1")
+ try:
+ payload = struct.pack('BB', 0, len(wrong_ssid)) + wrong_ssid.encode()
+ ssid_list = struct.pack('BB', 84, len(payload)) + payload
+ cmd = "VENDOR_ELEM_ADD 14 " + binascii.hexlify(ssid_list).decode()
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ check_scan(dev[0], "freq=2412 use_id=1")
+
+ payload = struct.pack('<L', binascii.crc32(wrong_ssid.encode()))
+ ssid_list = struct.pack('BBB', 255, 1 + len(payload), 58) + payload
+ cmd = "VENDOR_ELEM_ADD 14 " + binascii.hexlify(ssid_list).decode()
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ check_scan(dev[0], "freq=2412 use_id=1")
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 14 *")
+ if "test-scan" in dev[0].request("SCAN_RESULTS"):
+ raise Exception("BSS unexpectedly found in initial scan")
+
+ id1 = dev[0].connect("foo", key_mgmt="NONE", scan_ssid="1",
+ only_add_network=True)
+ id2 = dev[0].connect("test-scan", key_mgmt="NONE", scan_ssid="1",
+ only_add_network=True)
+ id3 = dev[0].connect("bar", key_mgmt="NONE", only_add_network=True)
+
+ check_scan(dev[0], "freq=2412 use_id=1")
+ if "test-scan" in dev[0].request("SCAN_RESULTS"):
+ raise Exception("BSS unexpectedly found in scan")
+
+ # Allow multiple attempts to be more robust under heavy CPU load that can
+ # result in Probe Response frames getting sent only after the station has
+ # already stopped waiting for the response on the channel.
+ found = False
+ for i in range(10):
+ check_scan(dev[0], "scan_id=%d,%d,%d freq=2412 use_id=1" % (id1, id2, id3))
+ if "test-scan" in dev[0].request("SCAN_RESULTS"):
+ found = True
+ break
+ if not found:
+ raise Exception("BSS not found in scan")
+
+ if "FAIL" not in dev[0].request("SCAN scan_id=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17"):
+ raise Exception("Too many scan_id values accepted")
+
+ # Duplicate SSID removal
+ check_scan(dev[0], "scan_id=%d,%d,%d freq=2412 use_id=1" % (id1, id1, id2))
+
+ dev[0].request("REMOVE_NETWORK all")
+ hapd.disable()
+ dev[0].flush_scan_cache(freq=2432)
+ dev[0].flush_scan_cache()
+
+def test_scan_and_bss_entry_removed(dev, apdev):
+ """Last scan result and connect work processing on BSS entry update"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open",
+ "eap_server": "1",
+ "wps_state": "2"})
+ bssid = apdev[0]['bssid']
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+
+ # Add a BSS entry
+ dev[0].scan_for_bss(bssid, freq="2412")
+ wpas.scan_for_bss(bssid, freq="2412")
+
+ # Start a connect radio work with a blocking entry preventing this from
+ # proceeding; this stores a pointer to the selected BSS entry.
+ id = dev[0].request("RADIO_WORK add block-work")
+ w_id = wpas.request("RADIO_WORK add block-work")
+ dev[0].wait_event(["EXT-RADIO-WORK-START"], timeout=1)
+ wpas.wait_event(["EXT-RADIO-WORK-START"], timeout=1)
+ nid = dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ w_nid = wpas.connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ time.sleep(0.1)
+
+ # Remove the BSS entry
+ dev[0].request("BSS_FLUSH 0")
+ wpas.request("BSS_FLUSH 0")
+
+ # Allow the connect radio work to continue. The bss entry stored in the
+ # pending connect work is now stale. This will result in the connection
+ # attempt failing since the BSS entry does not exist.
+ dev[0].request("RADIO_WORK done " + id)
+ wpas.request("RADIO_WORK done " + w_id)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+ dev[0].remove_network(nid)
+ ev = wpas.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+ wpas.remove_network(w_nid)
+ time.sleep(0.5)
+ dev[0].request("BSS_FLUSH 0")
+ wpas.request("BSS_FLUSH 0")
+
+ # Add a BSS entry
+ dev[0].scan_for_bss(bssid, freq="2412")
+ wpas.scan_for_bss(bssid, freq="2412")
+
+ # Start a connect radio work with a blocking entry preventing this from
+ # proceeding; this stores a pointer to the selected BSS entry.
+ id = dev[0].request("RADIO_WORK add block-work")
+ w_id = wpas.request("RADIO_WORK add block-work")
+ dev[0].wait_event(["EXT-RADIO-WORK-START"], timeout=1)
+ wpas.wait_event(["EXT-RADIO-WORK-START"], timeout=1)
+
+ # Schedule a connection based on the current BSS entry.
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ wpas.connect("open", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+
+ # Update scan results with results that have longer set of IEs so that new
+ # memory needs to be allocated for the BSS entry.
+ hapd.request("WPS_PBC")
+ time.sleep(0.1)
+ subprocess.call(['iw', dev[0].ifname, 'scan', 'trigger', 'freq', '2412'])
+ subprocess.call(['iw', wpas.ifname, 'scan', 'trigger', 'freq', '2412'])
+ time.sleep(0.1)
+
+ # Allow the connect radio work to continue. The bss entry stored in the
+ # pending connect work becomes stale during the scan and it must have been
+ # updated for the connection to work.
+ dev[0].request("RADIO_WORK done " + id)
+ wpas.request("RADIO_WORK done " + w_id)
+
+ dev[0].wait_connected(timeout=15, error="No connection (sme-connect)")
+ wpas.wait_connected(timeout=15, error="No connection (connect)")
+ dev[0].request("DISCONNECT")
+ wpas.request("DISCONNECT")
+ dev[0].flush_scan_cache()
+ wpas.flush_scan_cache()
+
+@remote_compatible
+def test_scan_reqs_with_non_scan_radio_work(dev, apdev):
+ """SCAN commands while non-scan radio_work is in progress"""
+ id = dev[0].request("RADIO_WORK add test-work-a")
+ ev = dev[0].wait_event(["EXT-RADIO-WORK-START"])
+ if ev is None:
+ raise Exception("Timeout while waiting radio work to start")
+
+ if "OK" not in dev[0].request("SCAN"):
+ raise Exception("SCAN failed")
+ if "FAIL-BUSY" not in dev[0].request("SCAN"):
+ raise Exception("SCAN accepted while one is already pending")
+ if "FAIL-BUSY" not in dev[0].request("SCAN"):
+ raise Exception("SCAN accepted while one is already pending")
+
+ res = dev[0].request("RADIO_WORK show").splitlines()
+ count = 0
+ for l in res:
+ if "scan" in l:
+ count += 1
+ if count != 1:
+ logger.info(res)
+ raise Exception("Unexpected number of scan radio work items")
+
+ dev[0].dump_monitor()
+ dev[0].request("RADIO_WORK done " + id)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Scan did not start")
+ if "FAIL-BUSY" not in dev[0].request("SCAN"):
+ raise Exception("SCAN accepted while one is already in progress")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected scan started")
+
+def test_scan_setband(dev, apdev):
+ """Band selection for scan operations"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ devs = [ dev[0], dev[1], dev[2], wpas ]
+
+ try:
+ hapd = None
+ hapd2 = None
+ params = {"ssid": "test-setband",
+ "hw_mode": "a",
+ "channel": "36",
+ "country_code": "US"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+
+ params = {"ssid": "test-setband",
+ "hw_mode": "g",
+ "channel": "1"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ if "FAIL" not in dev[0].request("SET setband FOO"):
+ raise Exception("Invalid set setband accepted")
+ if "OK" not in dev[0].request("SET setband AUTO"):
+ raise Exception("Failed to set setband")
+ if "OK" not in dev[1].request("SET setband 5G"):
+ raise Exception("Failed to set setband")
+ if "OK" not in dev[2].request("SET setband 2G"):
+ raise Exception("Failed to set setband")
+ if "OK" not in wpas.request("SET setband 2G,5G"):
+ raise Exception("Failed to set setband")
+
+ # Allow a retry to avoid reporting errors during heavy load
+ for j in range(5):
+ for d in devs:
+ d.request("SCAN only_new=1")
+
+ for d in devs:
+ ev = d.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+ if ev is None:
+ raise Exception("Scan timed out")
+
+ res0 = dev[0].request("SCAN_RESULTS")
+ res1 = dev[1].request("SCAN_RESULTS")
+ res2 = dev[2].request("SCAN_RESULTS")
+ res3 = wpas.request("SCAN_RESULTS")
+ if bssid in res0 and bssid2 in res0 and \
+ bssid in res1 and bssid2 in res2 and \
+ bssid in res3 and bssid2 in res3:
+ break
+
+ res = dev[0].request("SCAN_RESULTS")
+ if bssid not in res or bssid2 not in res:
+ raise Exception("Missing scan result(0)")
+
+ res = dev[1].request("SCAN_RESULTS")
+ if bssid not in res:
+ raise Exception("Missing scan result(1)")
+ if bssid2 in res:
+ raise Exception("Unexpected scan result(1)")
+
+ res = dev[2].request("SCAN_RESULTS")
+ if bssid2 not in res:
+ raise Exception("Missing scan result(2)")
+ if bssid in res:
+ raise Exception("Unexpected scan result(2)")
+
+ res = wpas.request("SCAN_RESULTS")
+ if bssid not in res or bssid2 not in res:
+ raise Exception("Missing scan result(3)")
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ if hapd2:
+ hapd2.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ for d in devs:
+ d.request("SET setband AUTO")
+ d.flush_scan_cache()
+
+@remote_compatible
+def test_scan_hidden_many(dev, apdev):
+ """scan_ssid=1 with large number of profile with hidden SSID"""
+ try:
+ _test_scan_hidden_many(dev, apdev)
+ finally:
+ dev[0].flush_scan_cache(freq=2432)
+ dev[0].flush_scan_cache()
+ dev[0].request("SCAN_INTERVAL 5")
+
+def _test_scan_hidden_many(dev, apdev):
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan-ssid",
+ "ignore_broadcast_ssid": "1"})
+ bssid = apdev[0]['bssid']
+
+ dev[0].request("SCAN_INTERVAL 1")
+
+ for i in range(5):
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", "foo")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "disabled", "0")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "scan_ssid", "1")
+
+ dev[0].set_network_quoted(id, "ssid", "test-scan-ssid")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "disabled", "0")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "scan_ssid", "1")
+
+ for i in range(5):
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", "foo")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "disabled", "0")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "scan_ssid", "1")
+
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected(timeout=30)
+ dev[0].request("REMOVE_NETWORK all")
+ hapd.disable()
+
+def test_scan_random_mac(dev, apdev, params):
+ """Random MAC address in scans"""
+ try:
+ _test_scan_random_mac(dev, apdev, params)
+ finally:
+ dev[0].request("MAC_RAND_SCAN all enable=0")
+
+def _test_scan_random_mac(dev, apdev, params):
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+
+ tests = ["",
+ "addr=foo",
+ "mask=foo",
+ "enable=1",
+ "all enable=1 mask=00:11:22:33:44:55",
+ "all enable=1 addr=00:11:22:33:44:55",
+ "all enable=1 addr=01:11:22:33:44:55 mask=ff:ff:ff:ff:ff:ff",
+ "all enable=1 addr=00:11:22:33:44:55 mask=fe:ff:ff:ff:ff:ff",
+ "enable=2 scan sched pno all",
+ "pno enable=1",
+ "all enable=2",
+ "foo"]
+ for args in tests:
+ if "FAIL" not in dev[0].request("MAC_RAND_SCAN " + args):
+ raise Exception("Invalid MAC_RAND_SCAN accepted: " + args)
+
+ if dev[0].get_driver_status_field('capa.mac_addr_rand_scan_supported') != '1':
+ raise HwsimSkip("Driver does not support random MAC address for scanning")
+
+ tests = ["all enable=1",
+ "all enable=1 addr=f2:11:22:33:44:55 mask=ff:ff:ff:ff:ff:ff",
+ "all enable=1 addr=f2:11:33:00:00:00 mask=ff:ff:ff:00:00:00"]
+ for args in tests:
+ dev[0].request("MAC_RAND_SCAN " + args)
+ dev[0].scan_for_bss(bssid, freq=2412, force_scan=True)
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan.fc.type_subtype == 4", ["wlan.ta"])
+ if out is not None:
+ addr = out.splitlines()
+ logger.info("Probe Request frames seen from: " + str(addr))
+ if dev[0].own_addr() in addr:
+ raise Exception("Real address used to transmit Probe Request frame")
+ if "f2:11:22:33:44:55" not in addr:
+ raise Exception("Fully configured random address not seen")
+ found = False
+ for a in addr:
+ if a.startswith('f2:11:33'):
+ found = True
+ break
+ if not found:
+ raise Exception("Fixed OUI random address not seen")
+
+def test_scan_random_mac_connected(dev, apdev, params):
+ """Random MAC address in scans while connected"""
+ try:
+ _test_scan_random_mac_connected(dev, apdev, params)
+ finally:
+ dev[0].request("MAC_RAND_SCAN all enable=0")
+
+def _test_scan_random_mac_connected(dev, apdev, params):
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+ if dev[0].get_driver_status_field('capa.mac_addr_rand_scan_supported') != '1':
+ raise HwsimSkip("Driver does not support random MAC address for scanning")
+
+ dev[0].connect("test-scan", key_mgmt="NONE", scan_freq="2412")
+
+ hostapd.add_ap(apdev[1], {"ssid": "test-scan-2", "channel": "11"})
+ bssid1 = apdev[1]['bssid']
+
+ # Verify that scanning can be completed while connected even if that means
+ # disabling use of random MAC address.
+ dev[0].request("MAC_RAND_SCAN all enable=1")
+ dev[0].scan_for_bss(bssid1, freq=2462, force_scan=True)
+
+@remote_compatible
+def test_scan_trigger_failure(dev, apdev):
+ """Scan trigger to the driver failing"""
+ if dev[0].get_status_field('wpa_state') == "SCANNING":
+ raise Exception("wpa_state was already SCANNING")
+
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+
+ if "OK" not in dev[0].request("SET test_failure 1"):
+ raise Exception("Failed to set test_failure")
+
+ if "OK" not in dev[0].request("SCAN"):
+ raise Exception("SCAN command failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=10)
+ if ev is None:
+ raise Exception("Did not receive CTRL-EVENT-SCAN-FAILED event")
+ if "retry=1" in ev:
+ raise Exception("Unexpected scan retry indicated")
+ if dev[0].get_status_field('wpa_state') == "SCANNING":
+ raise Exception("wpa_state SCANNING not cleared")
+
+ id = dev[0].connect("test-scan", key_mgmt="NONE", scan_freq="2412",
+ only_add_network=True)
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=10)
+ if ev is None:
+ raise Exception("Did not receive CTRL-EVENT-SCAN-FAILED event")
+ if "retry=1" not in ev:
+ raise Exception("No scan retry indicated for connection")
+ if dev[0].get_status_field('wpa_state') == "SCANNING":
+ raise Exception("wpa_state SCANNING not cleared")
+ dev[0].request("SET test_failure 0")
+ dev[0].wait_connected()
+
+ dev[0].request("SET test_failure 1")
+ if "OK" not in dev[0].request("SCAN"):
+ raise Exception("SCAN command failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=10)
+ if ev is None:
+ raise Exception("Did not receive CTRL-EVENT-SCAN-FAILED event")
+ if "retry=1" in ev:
+ raise Exception("Unexpected scan retry indicated")
+ if dev[0].get_status_field('wpa_state') != "COMPLETED":
+ raise Exception("wpa_state COMPLETED not restored")
+ dev[0].request("SET test_failure 0")
+
+@remote_compatible
+def test_scan_specify_ssid(dev, apdev):
+ """Control interface behavior on scan SSID parameter"""
+ dev[0].flush_scan_cache()
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-hidden",
+ "ignore_broadcast_ssid": "1"})
+ bssid = apdev[0]['bssid']
+ check_scan(dev[0], "freq=2412 use_id=1 ssid 414243")
+ bss = dev[0].get_bss(bssid)
+ if bss is not None and bss['ssid'] == 'test-hidden':
+ raise Exception("BSS entry for hidden AP present unexpectedly")
+ # Allow couple more retries to avoid reporting errors during heavy load
+ for i in range(5):
+ check_scan(dev[0], "freq=2412 ssid 414243 ssid 746573742d68696464656e ssid 616263313233 use_id=1")
+ bss = dev[0].get_bss(bssid)
+ if bss and 'test-hidden' in dev[0].request("SCAN_RESULTS"):
+ break
+ if bss is None:
+ raise Exception("BSS entry for hidden AP not found")
+ if 'test-hidden' not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("Expected SSID not included in the scan results")
+
+ hapd.disable()
+ dev[0].flush_scan_cache(freq=2432)
+ dev[0].flush_scan_cache()
+
+ if "FAIL" not in dev[0].request("SCAN ssid foo"):
+ raise Exception("Invalid SCAN command accepted")
+
+@remote_compatible
+def test_scan_ap_scan_2_ap_mode(dev, apdev):
+ """AP_SCAN 2 AP mode and scan()"""
+ try:
+ _test_scan_ap_scan_2_ap_mode(dev, apdev)
+ finally:
+ dev[0].request("AP_SCAN 1")
+
+def _test_scan_ap_scan_2_ap_mode(dev, apdev):
+ if "OK" not in dev[0].request("AP_SCAN 2"):
+ raise Exception("Failed to set AP_SCAN 2")
+
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "disabled", "0")
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("AP failed to start")
+
+ with fail_test(dev[0], 1, "wpa_driver_nl80211_scan"):
+ if "OK" not in dev[0].request("SCAN freq=2412"):
+ raise Exception("SCAN command failed unexpectedly")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED",
+ "AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("CTRL-EVENT-SCAN-FAILED not seen")
+ if "AP-DISABLED" in ev:
+ raise Exception("Unexpected AP-DISABLED event")
+ if "retry=1" in ev:
+ # Wait for the retry to scan happen
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED",
+ "AP-DISABLED"], timeout=5)
+ if ev is None:
+ raise Exception("CTRL-EVENT-SCAN-FAILED not seen - retry")
+ if "AP-DISABLED" in ev:
+ raise Exception("Unexpected AP-DISABLED event - retry")
+
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_scan_bss_expiration_on_ssid_change(dev, apdev):
+ """BSS entry expiration when AP changes SSID"""
+ dev[0].flush_scan_cache()
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+
+ hapd.request("DISABLE")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ if "OK" not in dev[0].request("BSS_EXPIRE_COUNT 3"):
+ raise Exception("BSS_EXPIRE_COUNT failed")
+ dev[0].scan(freq="2412")
+ dev[0].scan(freq="2412")
+ if "OK" not in dev[0].request("BSS_EXPIRE_COUNT 2"):
+ raise Exception("BSS_EXPIRE_COUNT failed")
+ res = dev[0].request("SCAN_RESULTS")
+ if "test-scan" not in res:
+ raise Exception("The first SSID not in scan results")
+ if "open" not in res:
+ raise Exception("The second SSID not in scan results")
+ dev[0].connect("open", key_mgmt="NONE")
+
+ dev[0].request("BSS_FLUSH 0")
+ res = dev[0].request("SCAN_RESULTS")
+ if "test-scan" in res:
+ raise Exception("The BSS entry with the old SSID was not removed")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_scan_dfs(dev, apdev, params):
+ """Scan on DFS channels"""
+ try:
+ _test_scan_dfs(dev, apdev, params)
+ finally:
+ clear_regdom_dev(dev)
+
+def _test_scan_dfs(dev, apdev, params):
+ subprocess.call(['iw', 'reg', 'set', 'US'])
+ for i in range(2):
+ for j in range(5):
+ ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=US" in ev:
+ break
+ dev[i].dump_monitor()
+
+ if "OK" not in dev[0].request("SCAN"):
+ raise Exception("SCAN command failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+
+ if "OK" not in dev[0].request("SCAN freq=2412,5180,5260,5500,5600,5745"):
+ raise Exception("SCAN command failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+
+ out = run_tshark(os.path.join(params['logdir'], "hwsim0.pcapng"),
+ "wlan.fc.type_subtype == 4", ["radiotap.channel.freq"])
+ if out is not None:
+ freq = out.splitlines()
+ freq = [int(f) for f in freq]
+ freq = list(set(freq))
+ freq.sort()
+ logger.info("Active scan seen on channels: " + str(freq))
+ for f in freq:
+ if (f >= 5260 and f <= 5320) or (f >= 5500 and f <= 5700):
+ raise Exception("Active scan on DFS channel: %d" % f)
+ if f in [2467, 2472]:
+ raise Exception("Active scan on US-disallowed channel: %d" % f)
+
+@remote_compatible
+def test_scan_abort(dev, apdev):
+ """Aborting a full scan"""
+ dev[0].request("SCAN")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ if ev is None:
+ raise Exception("Scan did not start")
+ if "OK" not in dev[0].request("ABORT_SCAN"):
+ raise Exception("ABORT_SCAN command failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=2)
+ if ev is None:
+ raise Exception("Scan did not terminate")
+
+@remote_compatible
+def test_scan_abort_on_connect(dev, apdev):
+ """Aborting a full scan on connection request"""
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ dev[0].request("SCAN")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ if ev is None:
+ raise Exception("Scan did not start")
+ dev[0].connect("test-scan", key_mgmt="NONE")
+
+@remote_compatible
+def test_scan_ext(dev, apdev):
+ """Custom IE in Probe Request frame"""
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+
+ try:
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 14 dd050011223300"):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ check_scan(dev[0], "freq=2412 use_id=1")
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 14 *")
+
+def test_scan_fail(dev, apdev):
+ """Scan failures"""
+ with fail_test(dev[0], 1, "wpa_driver_nl80211_scan"):
+ dev[0].request("DISCONNECT")
+ if "OK" not in dev[0].request("SCAN freq=2412"):
+ raise Exception("SCAN failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Did not see scan failure event")
+ dev[0].dump_monitor()
+
+ for i in range(1, 5):
+ with alloc_fail(dev[0], i,
+ "wpa_scan_clone_params;wpa_supplicant_trigger_scan"):
+ if "OK" not in dev[0].request("SCAN ssid 112233 freq=2412"):
+ raise Exception("SCAN failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Did not see scan failure event")
+ dev[0].dump_monitor()
+
+ with alloc_fail(dev[0], 1, "radio_add_work;wpa_supplicant_trigger_scan"):
+ if "OK" not in dev[0].request("SCAN freq=2412"):
+ raise Exception("SCAN failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Did not see scan failure event")
+ dev[0].dump_monitor()
+
+ try:
+ if "OK" not in dev[0].request("SET filter_ssids 1"):
+ raise Exception("SET failed")
+ id = dev[0].connect("test-scan", key_mgmt="NONE", only_add_network=True)
+ with alloc_fail(dev[0], 1, "wpa_supplicant_build_filter_ssids"):
+ # While the filter list cannot be created due to memory allocation
+ # failure, this scan is expected to be completed without SSID
+ # filtering.
+ if "OK" not in dev[0].request("SCAN freq=2412"):
+ raise Exception("SCAN failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+ dev[0].remove_network(id)
+ finally:
+ dev[0].request("SET filter_ssids 0")
+ dev[0].dump_monitor()
+
+ with alloc_fail(dev[0], 1, "nl80211_get_scan_results"):
+ if "OK" not in dev[0].request("SCAN freq=2412"):
+ raise Exception("SCAN failed")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("Did not see scan started event")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].dump_monitor()
+
+ try:
+ if "OK" not in dev[0].request("SET setband 2G"):
+ raise Exception("SET setband failed")
+ with alloc_fail(dev[0], 1, "=wpa_add_scan_freqs_list"):
+ # While the frequency list cannot be created due to memory
+ # allocation failure, this scan is expected to be completed without
+ # frequency filtering.
+ if "OK" not in dev[0].request("SCAN"):
+ raise Exception("SCAN failed")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("ABORT_SCAN")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+ finally:
+ dev[0].request("SET setband AUTO")
+ dev[0].dump_monitor()
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.request("SET preassoc_mac_addr 1")
+ with fail_test(wpas, 1, "nl80211_set_mac_addr;wpas_trigger_scan_cb"):
+ if "OK" not in wpas.request("SCAN freq=2412"):
+ raise Exception("SCAN failed")
+ ev = wpas.wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Did not see scan failure event")
+ wpas.request("SET preassoc_mac_addr 0")
+ wpas.dump_monitor()
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ with alloc_fail(dev[0], 1, "wpa_bss_add"):
+ dev[0].flush_scan_cache()
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
+
+def test_scan_fail_type_only(dev, apdev):
+ """Scan failures for TYPE=ONLY"""
+ with fail_test(dev[0], 1, "wpa_driver_nl80211_scan"):
+ dev[0].request("SCAN TYPE=ONLY freq=2417")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-FAILED"], timeout=5)
+ if ev is None:
+ raise Exception("Scan trigger failure not reported")
+ # Verify that scan_only_handler() does not get left set as the
+ # wpa_s->scan_res_handler in failure case.
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+@remote_compatible
+def test_scan_freq_list(dev, apdev):
+ """Scan with SET freq_list and scan_cur_freq"""
+ try:
+ if "OK" not in dev[0].request("SET freq_list 2412 2417"):
+ raise Exception("SET freq_list failed")
+ check_scan(dev[0], "use_id=1")
+ finally:
+ dev[0].request("SET freq_list ")
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ dev[0].connect("test-scan", key_mgmt="NONE", scan_freq="2412")
+ try:
+ if "OK" not in dev[0].request("SET scan_cur_freq 1"):
+ raise Exception("SET scan_cur_freq failed")
+ check_scan(dev[0], "use_id=1")
+ finally:
+ dev[0].request("SET scan_cur_freq 0")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_scan_bss_limit(dev, apdev):
+ """Scan and wpa_supplicant BSS entry limit"""
+ try:
+ _test_scan_bss_limit(dev, apdev)
+ finally:
+ dev[0].request("SET bss_max_count 200")
+ pass
+
+def _test_scan_bss_limit(dev, apdev):
+ dev[0].flush_scan_cache()
+ # Trigger 'Increasing the MAX BSS count to 2 because all BSSes are in use.
+ # We should normally not get here!' message by limiting the maximum BSS
+ # count to one so that the second AP would not fit in the BSS list and the
+ # first AP cannot be removed from the list since it is still in use.
+ dev[0].request("SET bss_max_count 1")
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ dev[0].connect("test-scan", key_mgmt="NONE", scan_freq="2412")
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "test-scan-2",
+ "channel": "6"})
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq=2437, force_scan=True)
+
+def run_scan(dev, bssid, exp_freq):
+ for i in range(5):
+ dev.request("SCAN freq=2412,2437,2462")
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+ bss = dev.get_bss(bssid)
+ freq = int(bss['freq']) if bss else 0
+ if freq == exp_freq:
+ break
+ if freq != exp_freq:
+ raise Exception("BSS entry shows incorrect frequency: %d != %d" % (freq, exp_freq))
+
+def test_scan_chan_switch(dev, apdev):
+ """Scanning and AP changing channels"""
+
+ # This test verifies that wpa_supplicant updates its local BSS table based
+ # on the correct cfg80211 scan entry in cases where the cfg80211 BSS table
+ # has multiple (one for each frequency) BSS entries for the same BSS.
+
+ csa_supported(dev[0])
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan", "channel": "1"})
+ csa_supported(hapd)
+ bssid = hapd.own_addr()
+
+ logger.info("AP channel switch while not connected")
+ run_scan(dev[0], bssid, 2412)
+ dev[0].dump_monitor()
+ switch_channel(hapd, 1, 2437)
+ run_scan(dev[0], bssid, 2437)
+ dev[0].dump_monitor()
+ switch_channel(hapd, 1, 2462)
+ run_scan(dev[0], bssid, 2462)
+ dev[0].dump_monitor()
+
+ logger.info("AP channel switch while connected")
+ dev[0].connect("test-scan", key_mgmt="NONE", scan_freq="2412 2437 2462")
+ run_scan(dev[0], bssid, 2462)
+ dev[0].dump_monitor()
+ switch_channel(hapd, 2, 2437)
+ wait_channel_switch(dev[0], 2437)
+ dev[0].dump_monitor()
+ run_scan(dev[0], bssid, 2437)
+ dev[0].dump_monitor()
+ switch_channel(hapd, 2, 2412)
+ wait_channel_switch(dev[0], 2412)
+ dev[0].dump_monitor()
+ run_scan(dev[0], bssid, 2412)
+ dev[0].dump_monitor()
+
+@reset_ignore_old_scan_res
+def test_scan_new_only(dev, apdev):
+ """Scan and only_new=1 multiple times"""
+ dev[0].flush_scan_cache()
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ dev[0].set("ignore_old_scan_res", "1")
+ # Get the BSS added to cfg80211 BSS list
+ bssid = hapd.own_addr()
+ dev[0].scan_for_bss(bssid, freq=2412)
+ bss = dev[0].get_bss(bssid)
+ idx1 = bss['update_idx']
+ dev[0].scan_for_bss(bssid, freq=2412, force_scan=True)
+ dev[0].scan_for_bss(bssid, freq=2412, force_scan=True)
+ bss = dev[0].get_bss(bssid)
+ idx2 = bss['update_idx']
+ if int(idx2) <= int(idx1):
+ raise Exception("Scan result update_idx did not increase")
+ # Disable AP to ensure there are no new scan results after this.
+ hapd.disable()
+
+ # Try to scan multiple times to verify that old scan results do not get
+ # accepted as new.
+ for i in range(10):
+ dev[0].scan(freq=2412)
+ bss = dev[0].get_bss(bssid)
+ if bss:
+ idx = bss['update_idx']
+ if int(idx) > int(idx2):
+ raise Exception("Unexpected update_idx increase")
+
+def test_scan_flush(dev, apdev):
+ """Ongoing scan and FLUSH"""
+ dev[0].flush_scan_cache()
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ dev[0].dump_monitor()
+ dev[0].request("SCAN TYPE=ONLY freq=2412-2472 passive=1")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not start")
+ time.sleep(0.1)
+ dev[0].request("FLUSH")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS",
+ "CTRL-EVENT-SCAN-FAILED",
+ "CTRL-EVENT-BSS-ADDED"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ if "CTRL-EVENT-BSS-ADDED" in ev:
+ raise Exception("Unexpected BSS entry addition after FLUSH")
+
+def test_scan_ies(dev, apdev):
+ """Scan and both Beacon and Probe Response frame IEs"""
+ dev[0].flush_scan_cache()
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan",
+ "beacon_int": "20"})
+ bssid = hapd.own_addr()
+ dev[0].dump_monitor()
+
+ for i in range(10):
+ dev[0].request("SCAN TYPE=ONLY freq=2412 passive=1")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=15)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ if dev[0].get_bss(bssid):
+ break
+
+ for i in range(10):
+ dev[0].scan_for_bss(bssid, freq=2412, force_scan=True)
+ bss = dev[0].get_bss(bssid)
+ if 'beacon_ie' in bss:
+ if bss['ie'] != bss['beacon_ie']:
+ break
+
+ if not bss or 'beacon_ie' not in bss:
+ raise Exception("beacon_ie not present")
+ ie = parse_ie(bss['ie'])
+ logger.info("ie: " + str(list(ie.keys())))
+ beacon_ie = parse_ie(bss['beacon_ie'])
+ logger.info("beacon_ie: " + str(list(ie.keys())))
+ if bss['ie'] == bss['beacon_ie']:
+ raise Exception("Both ie and beacon_ie show same data")
+
+def test_scan_parsing(dev, apdev):
+ """Scan result parsing"""
+ if "OK" not in dev[0].request("DRIVER_EVENT SCAN_RES START"):
+ raise Exception("DRIVER_EVENT SCAN_RES START failed")
+
+ if "FAIL" not in dev[0].request("DRIVER_EVENT SCAN_RES foo "):
+ raise Exception("Invalid DRIVER_EVENT SCAN_RES accepted")
+
+ tests = ["",
+ "flags=ffffffff",
+ "bssid=02:03:04:05:06:07",
+ "freq=1234",
+ "beacon_int=102",
+ "caps=1234",
+ "qual=10",
+ "noise=10",
+ "level=10",
+ "tsf=1122334455667788",
+ "age=123",
+ "est_throughput=100",
+ "snr=10",
+ "parent_tsf=1122334455667788",
+ "tsf_bssid=02:03:04:05:06:07",
+ "ie=00",
+ "beacon_ie=00",
+ # Too long SSID
+ "bssid=02:ff:00:00:00:01 ie=0033" + 33*'FF',
+ # All parameters
+ "flags=ffffffff bssid=02:ff:00:00:00:02 freq=1234 beacon_int=102 caps=1234 qual=10 noise=10 level=10 tsf=1122334455667788 age=123456 est_throughput=100 snr=10 parent_tsf=1122334455667788 tsf_bssid=02:03:04:05:06:07 ie=000474657374 beacon_ie=000474657374",
+ # Beacon IEs truncated
+ "bssid=02:ff:00:00:00:03 ie=0000 beacon_ie=0003ffff",
+ # Probe Response IEs truncated
+ "bssid=02:ff:00:00:00:04 ie=00000101 beacon_ie=0000",
+ # DMG (invalid caps)
+ "bssid=02:ff:00:00:00:05 freq=58320 ie=0003646d67",
+ # DMG (IBSS)
+ "bssid=02:ff:00:00:00:06 freq=60480 caps=0001 ie=0003646d67",
+ # DMG (PBSS)
+ "bssid=02:ff:00:00:00:07 freq=62640 caps=0002 ie=0003646d67",
+ # DMG (AP)
+ "bssid=02:ff:00:00:00:08 freq=64800 caps=0003 ie=0003646d67",
+ # Test BSS for updates
+ "bssid=02:ff:00:00:00:09 freq=2412 caps=0011 level=1 ie=0003757064010182",
+ # Minimal BSS data
+ "bssid=02:ff:00:00:00:00 ie=0000"]
+ for t in tests:
+ if "OK" not in dev[0].request("DRIVER_EVENT SCAN_RES BSS " + t):
+ raise Exception("DRIVER_EVENT SCAN_RES BSS failed")
+
+ if "OK" not in dev[0].request("DRIVER_EVENT SCAN_RES END"):
+ raise Exception("DRIVER_EVENT SCAN_RES END failed")
+
+ res = dev[0].request("SCAN_RESULTS")
+ logger.info("SCAN_RESULTS:\n" + res)
+
+ bss = []
+ res = dev[0].request("BSS FIRST")
+ if "FAIL" in res:
+ raise Exception("BSS FIRST failed")
+ while "\nbssid=" in res:
+ logger.info("BSS output:\n" + res)
+ bssid = None
+ id = None
+ for val in res.splitlines():
+ if val.startswith("id="):
+ id = val.split('=')[1]
+ if val.startswith("bssid="):
+ bssid = val.split('=')[1]
+ if bssid is None or id is None:
+ raise Exception("Missing id or bssid line")
+ bss.append(bssid)
+ res = dev[0].request("BSS NEXT-" + id)
+
+ logger.info("Discovered BSSs: " + str(bss))
+ invalid_bss = ["02:03:04:05:06:07", "02:ff:00:00:00:01"]
+ valid_bss = ["02:ff:00:00:00:00", "02:ff:00:00:00:02",
+ "02:ff:00:00:00:03", "02:ff:00:00:00:04",
+ "02:ff:00:00:00:05", "02:ff:00:00:00:06",
+ "02:ff:00:00:00:07", "02:ff:00:00:00:08",
+ "02:ff:00:00:00:09"]
+ for bssid in invalid_bss:
+ if bssid in bss:
+ raise Exception("Invalid BSS included: " + bssid)
+ for bssid in valid_bss:
+ if bssid not in bss:
+ raise Exception("Valid BSS missing: " + bssid)
+
+ logger.info("Update BSS parameters")
+ if "OK" not in dev[0].request("DRIVER_EVENT SCAN_RES START"):
+ raise Exception("DRIVER_EVENT SCAN_RES START failed")
+ if "OK" not in dev[0].request("DRIVER_EVENT SCAN_RES BSS bssid=02:ff:00:00:00:09 freq=2412 caps=0002 level=2 ie=000375706401028204"):
+ raise Exception("DRIVER_EVENT SCAN_RES BSS failed")
+ if "OK" not in dev[0].request("DRIVER_EVENT SCAN_RES END"):
+ raise Exception("DRIVER_EVENT SCAN_RES END failed")
+ res = dev[0].request("BSS 02:ff:00:00:00:09")
+ logger.info("Updated BSS:\n" + res)
+
+def get_probe_req_ies(hapd):
+ for i in range(10):
+ msg = hapd.mgmt_rx()
+ if msg is None:
+ break
+ if msg['subtype'] != 4:
+ continue
+ return parse_ie(binascii.hexlify(msg['payload']).decode())
+
+ raise Exception("Probe Request not seen")
+
+def test_scan_specific_bssid(dev, apdev):
+ """Scan for a specific BSSID"""
+ dev[0].flush_scan_cache()
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan",
+ "beacon_int": "1000"})
+ bssid = hapd.own_addr()
+
+ time.sleep(0.1)
+ dev[0].request("SCAN TYPE=ONLY freq=2412 bssid=02:ff:ff:ff:ff:ff")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ bss1 = dev[0].get_bss(bssid)
+
+ for i in range(10):
+ dev[0].request("SCAN TYPE=ONLY freq=2412 bssid=" + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ bss2 = dev[0].get_bss(bssid)
+ if bss2:
+ break
+
+ if not bss2:
+ raise Exception("Did not find BSS")
+ if bss1 and 'beacon_ie' in bss1 and 'ie' in bss1 and bss1['beacon_ie'] != bss1['ie']:
+ raise Exception("First scan for unknown BSSID returned unexpected response")
+ if bss2 and 'beacon_ie' in bss2 and 'ie' in bss2 and bss2['beacon_ie'] == bss2['ie']:
+ raise Exception("Second scan did find Probe Response frame")
+
+ hapd.dump_monitor()
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ # With specific SSID in the Probe Request frame
+ dev[0].request("SCAN TYPE=ONLY freq=2412 bssid=" + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ ie = get_probe_req_ies(hapd)
+ if ie[0] != b"test-scan":
+ raise Exception("Specific SSID not seen in Probe Request frame")
+
+ hapd.dump_monitor()
+
+ # Without specific SSID in the Probe Request frame
+ dev[0].request("SCAN TYPE=ONLY freq=2412 wildcard_ssid=1 bssid=" + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ ie = get_probe_req_ies(hapd)
+ if len(ie[0]) != 0:
+ raise Exception("Wildcard SSID not seen in Probe Request frame")
+
+def test_scan_probe_req_events(dev, apdev):
+ """Probe Request frame RX events from hostapd"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ hapd2 = hostapd.Hostapd(apdev[0]['ifname'])
+ if "OK" not in hapd2.mon.request("ATTACH probe_rx_events=1"):
+ raise Exception("Failed to register for events")
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+
+ ev = hapd2.wait_event(["RX-PROBE-REQUEST"], timeout=5)
+ if ev is None:
+ raise Exception("RX-PROBE-REQUEST not reported")
+ if "sa=" + dev[0].own_addr() not in ev:
+ raise Exception("Unexpected event parameters: " + ev)
+
+ ev = hapd.wait_event(["RX-PROBE-REQUEST"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected RX-PROBE-REQUEST")
+
+ if "OK" not in hapd2.mon.request("ATTACH probe_rx_events=0"):
+ raise Exception("Failed to update event registration")
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412", force_scan=True)
+ ev = hapd2.wait_event(["RX-PROBE-REQUEST"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected RX-PROBE-REQUEST")
+
+ tests = ["probe_rx_events", "probe_rx_events=-1", "probe_rx_events=2"]
+ for val in tests:
+ if "FAIL" not in hapd2.mon.request("ATTACH " + val):
+ raise Exception("Invalid ATTACH command accepted")
+
+def elem_capab(capab):
+ # Nontransmitted BSSID Capability element (83 = 0x53)
+ return struct.pack('<BBH', 83, 2, capab)
+
+def elem_ssid(ssid):
+ # SSID element
+ return struct.pack('BB', 0, len(ssid)) + ssid.encode()
+
+def elem_bssid_index(index):
+ # Multiple BSSID-index element (85 = 0x55)
+ return struct.pack('BBB', 85, 1, index)
+
+def elem_multibssid(profiles, max_bssid_indic):
+ # TODO: add support for fragmenting over multiple Multiple BSSID elements
+ if 1 + len(profiles) > 255:
+ raise Exception("Too long Multiple BSSID element")
+ elem = struct.pack('BBB', 71, 1 + len(profiles), max_bssid_indic) + profiles
+ return binascii.hexlify(elem).decode()
+
+def run_scans(dev, check):
+ for i in range(2):
+ dev.request("SCAN TYPE=ONLY freq=2412")
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan did not complete")
+
+ # TODO: Check IEs
+ for (bssid, ssid, capab) in check:
+ bss = dev.get_bss(bssid)
+ if bss is None:
+ raise Exception("AP " + bssid + " missing from scan results")
+ logger.info("AP " + bssid + ": " + str(bss))
+ if bss['ssid'] != ssid:
+ raise Exception("Unexpected AP " + bssid + " SSID")
+ if int(bss['capabilities'], 16) != capab:
+ raise Exception("Unexpected AP " + bssid + " capabilities")
+
+def check_multibss_sta_capa(dev):
+ res = dev.get_capability("multibss")
+ if res is None or 'MULTIBSS-STA' not in res:
+ raise HwsimSkip("Multi-BSS STA functionality not supported")
+
+def test_scan_multi_bssid(dev, apdev):
+ """Scan and Multiple BSSID element"""
+ check_multibss_sta_capa(dev[0])
+ dev[0].flush_scan_cache()
+
+ params = {"ssid": "test-scan"}
+ # Max BSSID Indicator 0 (max 1 BSSID) and no subelements
+ params['vendor_elements'] = elem_multibssid(b'', 0)
+ hostapd.add_ap(apdev[0], params)
+
+ params = {"ssid": "test-scan"}
+ elems = elem_capab(0x0401) + elem_ssid("1") + elem_bssid_index(1)
+ profile1 = struct.pack('BB', 0, len(elems)) + elems
+ params['vendor_elements'] = elem_multibssid(profile1, 1)
+ hostapd.add_ap(apdev[1], params)
+
+ bssid0 = apdev[0]['bssid']
+ bssid1 = apdev[1]['bssid']
+ check = [(bssid0, 'test-scan', 0x401),
+ (bssid1, 'test-scan', 0x401),
+ (bssid1[0:16] + '1', '1', 0x401)]
+ run_scans(dev[0], check)
+
+def test_scan_multi_bssid_2(dev, apdev):
+ """Scan and Multiple BSSID element (2)"""
+ check_multibss_sta_capa(dev[0])
+ dev[0].flush_scan_cache()
+
+ params = {"ssid": "transmitted"}
+
+ # Duplicated entry for the transmitted BSS (not a normal use case)
+ elems = elem_capab(1) + elem_ssid("transmitted") + elem_bssid_index(0)
+ profile1 = struct.pack('BB', 0, len(elems)) + elems
+
+ elems = elem_capab(1) + elem_ssid("nontransmitted") + elem_bssid_index(1)
+ profile2 = struct.pack('BB', 0, len(elems)) + elems
+
+ elems = elem_capab(1) + elem_ssid("nontransmitted_2") + elem_bssid_index(2)
+ profile3 = struct.pack('BB', 0, len(elems)) + elems
+
+ profiles = profile1 + profile2 + profile3
+ params['vendor_elements'] = elem_multibssid(profiles, 4)
+ hostapd.add_ap(apdev[0], params)
+
+ bssid = apdev[0]['bssid']
+ check = [(bssid, 'transmitted', 0x401),
+ (bssid[0:16] + '1', 'nontransmitted', 0x1),
+ (bssid[0:16] + '2', 'nontransmitted_2', 0x1)]
+ run_scans(dev[0], check)
+
+def test_scan_multi_bssid_3(dev, apdev):
+ """Scan and Multiple BSSID element (3)"""
+ check_multibss_sta_capa(dev[0])
+ dev[0].flush_scan_cache()
+
+ params = {"ssid": "transmitted"}
+
+ # Duplicated nontransmitted BSS (not a normal use case)
+ elems = elem_capab(1) + elem_ssid("nontransmitted") + elem_bssid_index(1)
+ profile1 = struct.pack('BB', 0, len(elems)) + elems
+
+ elems = elem_capab(1) + elem_ssid("nontransmitted") + elem_bssid_index(1)
+ profile2 = struct.pack('BB', 0, len(elems)) + elems
+
+ profiles = profile1 + profile2
+ params['vendor_elements'] = elem_multibssid(profiles, 2)
+ hostapd.add_ap(apdev[0], params)
+
+ bssid = apdev[0]['bssid']
+ check = [(bssid, 'transmitted', 0x401),
+ (bssid[0:16] + '1', 'nontransmitted', 0x1)]
+ run_scans(dev[0], check)
+
+def test_scan_multi_bssid_4(dev, apdev):
+ """Scan and Multiple BSSID element (3)"""
+ check_multibss_sta_capa(dev[0])
+ dev[0].flush_scan_cache()
+
+ # Transmitted BSSID is not the first one in the block
+ bssid = apdev[0]['bssid']
+ hapd = None
+ try:
+ params = {"ssid": "transmitted",
+ "bssid": bssid[0:16] + '1'}
+
+ elems = elem_capab(1) + elem_ssid("1") + elem_bssid_index(1)
+ profile1 = struct.pack('BB', 0, len(elems)) + elems
+
+ elems = elem_capab(1) + elem_ssid("2") + elem_bssid_index(2)
+ profile2 = struct.pack('BB', 0, len(elems)) + elems
+
+ elems = elem_capab(1) + elem_ssid("3") + elem_bssid_index(3)
+ profile3 = struct.pack('BB', 0, len(elems)) + elems
+
+ profiles = profile1 + profile2 + profile3
+ params['vendor_elements'] = elem_multibssid(profiles, 2)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ check = [(bssid[0:16] + '1', 'transmitted', 0x401),
+ (bssid[0:16] + '2', '1', 0x1),
+ (bssid[0:16] + '3', '2', 0x1),
+ (bssid[0:16] + '0', '3', 0x1)]
+ run_scans(dev[0], check)
+ finally:
+ if hapd:
+ hapd.disable()
+ hapd.set('bssid', bssid)
+ hapd.enable()
+
+def test_scan_multi_bssid_check_ie(dev, apdev):
+ """Scan and check if nontransmitting BSS inherits IE from transmitting BSS"""
+ check_multibss_sta_capa(dev[0])
+ dev[0].flush_scan_cache()
+
+ params = {"ssid": "transmitted"}
+
+ # Duplicated entry for the transmitted BSS (not a normal use case)
+ elems = elem_capab(1) + elem_ssid("transmitted") + elem_bssid_index(0)
+ profile1 = struct.pack('BB', 0, len(elems)) + elems
+
+ elems = elem_capab(1) + elem_ssid("nontransmitted") + elem_bssid_index(1)
+ profile2 = struct.pack('BB', 0, len(elems)) + elems
+
+ profiles = profile1 + profile2
+ params['vendor_elements'] = elem_multibssid(profiles, 2)
+ hostapd.add_ap(apdev[0], params)
+
+ bssid = apdev[0]['bssid']
+
+ for i in range(10):
+ dev[0].request("SCAN TYPE=ONLY freq=2412 passive=1")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=15)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ if dev[0].get_bss(bssid):
+ break
+
+ for i in range(10):
+ dev[0].scan_for_bss(bssid, freq=2412, force_scan=True)
+ bss = dev[0].get_bss(bssid)
+ if 'beacon_ie' in bss:
+ break
+
+ trans_bss = dev[0].get_bss(bssid)
+ if trans_bss is None:
+ raise Exception("AP " + bssid + " missing from scan results")
+
+ if not trans_bss or 'beacon_ie' not in trans_bss:
+ raise Exception("beacon_ie not present in trans_bss")
+
+ beacon_ie = parse_ie(trans_bss['beacon_ie'])
+ logger.info("trans_bss beacon_ie: " + str(list(beacon_ie.keys())))
+
+ bssid = bssid[0:16] + '1'
+ nontrans_bss1 = dev[0].get_bss(bssid)
+ if nontrans_bss1 is None:
+ raise Exception("AP " + bssid + " missing from scan results")
+
+ if not trans_bss or 'beacon_ie' not in nontrans_bss1:
+ raise Exception("beacon_ie not present in nontrans_bss1")
+
+ nontx_beacon_ie = parse_ie(nontrans_bss1['beacon_ie'])
+ logger.info("nontrans_bss1 beacon_ie: " + str(list(nontx_beacon_ie.keys())))
+
+ if 71 in list(beacon_ie.keys()):
+ ie_list = list(beacon_ie.keys())
+ ie_list.remove(71)
+ nontx_ie_list = list(nontx_beacon_ie.keys())
+ try:
+ nontx_ie_list.remove(85)
+ except ValueError:
+ pass
+ if sorted(ie_list) != sorted(nontx_ie_list):
+ raise Exception("check IE failed")
+
+def elem_fms1():
+ # this FMS IE has 1 FMS counter
+ fms_counters = struct.pack('B', 0x39)
+ fms_ids = struct.pack('B', 0x01)
+ return struct.pack('BBB', 86, 3, 1) + fms_counters + fms_ids
+
+def elem_fms2():
+ # this FMS IE has 2 FMS counters
+ fms_counters = struct.pack('BB', 0x29, 0x32)
+ fms_ids = struct.pack('BB', 0x01, 0x02)
+ return struct.pack('BBB', 86, 5, 2) + fms_counters + fms_ids
+
+def test_scan_multi_bssid_fms(dev, apdev):
+ """Non-transmitting BSS has different FMS IE from transmitting BSS"""
+ check_multibss_sta_capa(dev[0])
+ dev[0].flush_scan_cache()
+
+ params = {"ssid": "transmitted"}
+
+ # construct transmitting BSS Beacon with FMS IE
+ elems = elem_capab(1) + elem_ssid("transmitted") + elem_bssid_index(0) + elem_fms1()
+ profile1 = struct.pack('BB', 0, len(elems)) + elems
+
+ elems = elem_capab(1) + elem_ssid("nontransmitted") + elem_bssid_index(1) + elem_fms2()
+ profile2 = struct.pack('BB', 0, len(elems)) + elems
+
+ profiles = profile1 + profile2
+ params['vendor_elements'] = elem_multibssid(profiles, 2) + binascii.hexlify(elem_fms1()).decode()
+ hostapd.add_ap(apdev[0], params)
+
+ bssid = apdev[0]['bssid']
+
+ for i in range(10):
+ dev[0].request("SCAN TYPE=ONLY freq=2412 passive=1")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=15)
+ if ev is None:
+ raise Exception("Scan did not complete")
+ if dev[0].get_bss(bssid):
+ break
+
+ for i in range(10):
+ dev[0].scan_for_bss(bssid, freq=2412, force_scan=True)
+ bss = dev[0].get_bss(bssid)
+ if 'beacon_ie' in bss:
+ break
+
+ trans_bss = dev[0].get_bss(bssid)
+ if trans_bss is None:
+ raise Exception("AP " + bssid + " missing from scan results")
+
+ if not trans_bss or 'beacon_ie' not in trans_bss:
+ raise Exception("beacon_ie not present in trans_bss")
+
+ beacon_ie = parse_ie(trans_bss['beacon_ie'])
+ trans_bss_fms = beacon_ie[86]
+ logger.info("trans_bss fms ie: " + binascii.hexlify(trans_bss_fms).decode())
+
+ bssid = bssid[0:16] + '1'
+ nontrans_bss1 = dev[0].get_bss(bssid)
+ if nontrans_bss1 is None:
+ raise Exception("AP " + bssid + " missing from scan results")
+
+ if not nontrans_bss1 or 'beacon_ie' not in nontrans_bss1:
+ raise Exception("beacon_ie not present in nontrans_bss1")
+
+ nontrans_beacon_ie = parse_ie(nontrans_bss1['beacon_ie'])
+ nontrans_bss_fms = nontrans_beacon_ie[86]
+ logger.info("nontrans_bss fms ie: " + binascii.hexlify(nontrans_bss_fms).decode())
+
+ if binascii.hexlify(trans_bss_fms) == binascii.hexlify(nontrans_bss_fms):
+ raise Exception("Nontrans BSS has the same FMS IE as trans BSS")
+
+def test_scan_multiple_mbssid_ie(dev, apdev):
+ """Transmitting BSS has 2 MBSSID IE"""
+ check_multibss_sta_capa(dev[0])
+ dev[0].flush_scan_cache()
+
+ bssid = apdev[0]['bssid']
+ logger.info("bssid: " + bssid)
+ hapd = None
+
+ # construct 2 MBSSID IEs, each MBSSID IE contains 1 profile
+ params = {"ssid": "transmitted",
+ "bssid": bssid}
+
+ elems = elem_capab(1) + elem_ssid("1") + elem_bssid_index(1)
+ profile1 = struct.pack('BB', 0, len(elems)) + elems
+
+ elems = elem_capab(2) + elem_ssid("2") + elem_bssid_index(2)
+ profile2 = struct.pack('BB', 0, len(elems)) + elems
+
+ params['vendor_elements'] = elem_multibssid(profile1, 2) + elem_multibssid(profile2, 2)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ check = [(bssid, 'transmitted', 0x401),
+ (bssid[0:16] + '1', '1', 0x1),
+ (bssid[0:16] + '2', '2', 0x2)]
+ run_scans(dev[0], check)
+
+def test_scan_mbssid_hidden_ssid(dev, apdev):
+ """Non-transmitting BSS has hidden SSID"""
+ check_multibss_sta_capa(dev[0])
+ dev[0].flush_scan_cache()
+
+ bssid = apdev[0]['bssid']
+ logger.info("bssid: " + bssid)
+ hapd = None
+
+ # construct 2 MBSSID IEs, each MBSSID IE contains 1 profile
+ params = {"ssid": "transmitted",
+ "bssid": bssid}
+
+ elems = elem_capab(1) + elem_ssid("") + elem_bssid_index(1)
+ profile1 = struct.pack('BB', 0, len(elems)) + elems
+
+ elems = elem_capab(2) + elem_ssid("2") + elem_bssid_index(2)
+ profile2 = struct.pack('BB', 0, len(elems)) + elems
+
+ profiles = profile1 + profile2
+ params['vendor_elements'] = elem_multibssid(profiles, 2)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ check = [(bssid, 'transmitted', 0x401),
+ (bssid[0:16] + '1', '', 0x1),
+ (bssid[0:16] + '2', '2', 0x2)]
+ run_scans(dev[0], check)
+
+def test_connect_mbssid_open_1(dev, apdev):
+ """Connect to transmitting and nontransmitting BSS in open mode"""
+ check_multibss_sta_capa(dev[0])
+ dev[0].flush_scan_cache()
+
+ bssid = apdev[0]['bssid']
+ params = {"ssid": "transmitted"}
+
+ elems = elem_capab(1) + elem_ssid("nontransmitted") + elem_bssid_index(1)
+ profile1 = struct.pack('BB', 0, len(elems)) + elems
+
+ elems = elem_capab(1) + elem_ssid("nontransmitted_2") + elem_bssid_index(2)
+ profile2 = struct.pack('BB', 0, len(elems)) + elems
+
+ profiles = profile1 + profile2
+ params['vendor_elements'] = elem_multibssid(profiles, 4)
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("transmitted", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ dev[0].connect("nontransmitted", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["SME: Trying to authenticate"], timeout=10)
+ if ev is None:
+ raise Exception("Connection attempt to nontransmitted BSS not started")
+ if "02:00:00:00:03:01 (SSID='nontransmitted'" not in ev:
+ raise Exception("Unexpected authentication target")
+ # hostapd does not yet support Multiple-BSSID, so only verify that STA is
+ # able to start connection attempt.
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+ dev[0].connect("nontransmitted_2", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["SME: Trying to authenticate"], timeout=10)
+ if ev is None:
+ raise Exception("Connection attempt to nontransmitted BSS not started")
+ if "02:00:00:00:03:02 (SSID='nontransmitted_2'" not in ev:
+ raise Exception("Unexpected authentication target")
+ # hostapd does not yet support Multiple-BSSID, so only verify that STA is
+ # able to start connection attempt.
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].dump_monitor()
+
+def test_scan_only_one(dev, apdev):
+ """Test that scanning with a single active AP only returns that one"""
+ dev[0].flush_scan_cache()
+ hostapd.add_ap(apdev[0], {"ssid": "test-scan"})
+ bssid = apdev[0]['bssid']
+
+ check_scan(dev[0], "use_id=1", test_busy=True)
+ dev[0].scan_for_bss(bssid, freq="2412")
+
+ status, stdout = hostapd.cmd_execute(dev[0], ['iw', dev[0].ifname, 'scan', 'dump'])
+ if status != 0:
+ raise Exception("iw scan dump failed with code %d" % status)
+ lines = stdout.split('\n')
+ entries = len(list(filter(lambda x: x.startswith('BSS '), lines)))
+ if entries != 1:
+ raise Exception("expected to find 1 BSS entry, got %d" % entries)
+
+def test_scan_ssid_list(dev, apdev):
+ """Scan using SSID List element"""
+ dev[0].flush_scan_cache()
+ ssid = "test-ssid-list"
+ hapd = hostapd.add_ap(apdev[0], {"ssid": ssid,
+ "ignore_broadcast_ssid": "1"})
+ bssid = apdev[0]['bssid']
+ found = False
+ try:
+ payload = struct.pack('BB', 0, len(ssid)) + ssid.encode()
+ ssid_list = struct.pack('BB', 84, len(payload)) + payload
+ cmd = "VENDOR_ELEM_ADD 14 " + binascii.hexlify(ssid_list).decode()
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ for i in range(10):
+ check_scan(dev[0], "freq=2412 use_id=1")
+ if ssid in dev[0].request("SCAN_RESULTS"):
+ found = True
+ break
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 14 *")
+ hapd.disable()
+ dev[0].flush_scan_cache(freq=2432)
+ dev[0].flush_scan_cache()
+
+ if not found:
+ raise Exception("AP not found in scan results")
+
+def test_scan_short_ssid_list(dev, apdev):
+ """Scan using Short SSID List element"""
+ dev[0].flush_scan_cache()
+ ssid = "test-short-ssid-list"
+ hapd = hostapd.add_ap(apdev[0], {"ssid": ssid,
+ "ignore_broadcast_ssid": "1"})
+ bssid = apdev[0]['bssid']
+ found = False
+ try:
+ payload = struct.pack('<L', binascii.crc32(ssid.encode()))
+ ssid_list = struct.pack('BBB', 255, 1 + len(payload), 58) + payload
+ cmd = "VENDOR_ELEM_ADD 14 " + binascii.hexlify(ssid_list).decode()
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ for i in range(10):
+ check_scan(dev[0], "freq=2412 use_id=1")
+ if ssid in dev[0].request("SCAN_RESULTS"):
+ found = True
+ break
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 14 *")
+ hapd.disable()
+ dev[0].flush_scan_cache(freq=2432)
+ dev[0].flush_scan_cache()
+
+ if not found:
+ raise Exception("AP not found in scan results")
diff --git a/contrib/wpa/tests/hwsim/test_sigma_dut.py b/contrib/wpa/tests/hwsim/test_sigma_dut.py
new file mode 100644
index 000000000000..5450c337e6a3
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_sigma_dut.py
@@ -0,0 +1,5264 @@
+# Test cases for sigma_dut
+# Copyright (c) 2017, Qualcomm Atheros, Inc.
+# Copyright (c) 2018-2019, The Linux Foundation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import errno
+import fcntl
+import hashlib
+import logging
+logger = logging.getLogger()
+import os
+import socket
+import struct
+import subprocess
+import threading
+import time
+
+import hostapd
+from utils import *
+from hwsim import HWSimRadio
+import hwsim_utils
+from wlantest import Wlantest
+from tshark import run_tshark
+from test_dpp import check_dpp_capab, update_hapd_config, wait_auth_success
+from test_suite_b import check_suite_b_192_capa, suite_b_as_params, suite_b_192_rsa_ap_params
+from test_ap_eap import check_eap_capa, int_eap_server_params, check_domain_match, check_domain_suffix_match
+from test_ap_hs20 import hs20_ap_params
+from test_ap_pmf import check_mac80211_bigtk
+from test_ocv import check_ocv_failure
+
+def check_sigma_dut():
+ if not os.path.exists("./sigma_dut"):
+ raise HwsimSkip("sigma_dut not available")
+
+def to_hex(s):
+ return binascii.hexlify(s.encode()).decode()
+
+def from_hex(s):
+ return binascii.unhexlify(s).decode()
+
+def sigma_log_output(cmd):
+ try:
+ out = cmd.stdout.read()
+ if out:
+ logger.debug("sigma_dut stdout: " + str(out.decode()))
+ except IOError as e:
+ if e.errno != errno.EAGAIN:
+ raise
+ try:
+ out = cmd.stderr.read()
+ if out:
+ logger.debug("sigma_dut stderr: " + str(out.decode()))
+ except IOError as e:
+ if e.errno != errno.EAGAIN:
+ raise
+
+sigma_prog = None
+
+def sigma_dut_cmd(cmd, port=9000, timeout=2):
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM,
+ socket.IPPROTO_TCP)
+ sock.settimeout(timeout)
+ addr = ('127.0.0.1', port)
+ sock.connect(addr)
+ sock.send(cmd.encode() + b"\r\n")
+ try:
+ res = sock.recv(1000).decode()
+ running = False
+ done = False
+ for line in res.splitlines():
+ if line.startswith("status,RUNNING"):
+ running = True
+ elif line.startswith("status,INVALID"):
+ done = True
+ elif line.startswith("status,ERROR"):
+ done = True
+ elif line.startswith("status,COMPLETE"):
+ done = True
+ if running and not done:
+ # Read the actual response
+ res = sock.recv(1000).decode()
+ except:
+ res = ''
+ pass
+ sock.close()
+ res = res.rstrip()
+ logger.debug("sigma_dut: '%s' --> '%s'" % (cmd, res))
+ global sigma_prog
+ if sigma_prog:
+ sigma_log_output(sigma_prog)
+ return res
+
+def sigma_dut_cmd_check(cmd, port=9000, timeout=2):
+ res = sigma_dut_cmd(cmd, port=port, timeout=timeout)
+ if "COMPLETE" not in res:
+ raise Exception("sigma_dut command failed: " + cmd)
+ return res
+
+def start_sigma_dut(ifname, hostapd_logdir=None, cert_path=None,
+ bridge=None, sae_h2e=False, owe_ptk_workaround=False):
+ check_sigma_dut()
+ cmd = ['./sigma_dut',
+ '-d',
+ '-M', ifname,
+ '-S', ifname,
+ '-F', '../../hostapd/hostapd',
+ '-G',
+ '-w', '/var/run/wpa_supplicant/',
+ '-j', ifname]
+ if hostapd_logdir:
+ cmd += ['-H', hostapd_logdir]
+ if cert_path:
+ cmd += ['-C', cert_path]
+ if bridge:
+ cmd += ['-b', bridge]
+ if sae_h2e:
+ cmd += ['-2']
+ if owe_ptk_workaround:
+ cmd += ['-3']
+ sigma = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ for stream in [sigma.stdout, sigma.stderr]:
+ fd = stream.fileno()
+ fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+
+ global sigma_prog
+ sigma_prog = sigma
+ res = None
+ for i in range(20):
+ try:
+ res = sigma_dut_cmd("HELLO")
+ break
+ except:
+ time.sleep(0.05)
+ if res is None or "errorCode,Unknown command" not in res:
+ raise Exception("Failed to start sigma_dut")
+ return {'cmd': sigma, 'ifname': ifname}
+
+def stop_sigma_dut(sigma):
+ global sigma_prog
+ sigma_prog = None
+ cmd = sigma['cmd']
+ sigma_log_output(cmd)
+ logger.debug("Terminating sigma_dut process")
+ cmd.terminate()
+ cmd.wait()
+ out, err = cmd.communicate()
+ logger.debug("sigma_dut stdout: " + str(out.decode()))
+ logger.debug("sigma_dut stderr: " + str(err.decode()))
+ subprocess.call(["ip", "addr", "del", "dev", sigma['ifname'],
+ "127.0.0.11/24"],
+ stderr=open('/dev/null', 'w'))
+
+def sigma_dut_wait_connected(ifname):
+ for i in range(50):
+ res = sigma_dut_cmd("sta_is_connected,interface," + ifname)
+ if "connected,1" in res:
+ break
+ time.sleep(0.2)
+ if i == 49:
+ raise Exception("Connection did not complete")
+
+def test_sigma_dut_basic(dev, apdev):
+ """sigma_dut basic functionality"""
+ sigma = start_sigma_dut(dev[0].ifname)
+
+ tests = [("ca_get_version", "status,COMPLETE,version,1.0"),
+ ("device_get_info", "status,COMPLETE,vendor"),
+ ("device_list_interfaces,interfaceType,foo", "status,ERROR"),
+ ("device_list_interfaces,interfaceType,802.11",
+ "status,COMPLETE,interfaceType,802.11,interfaceID," + dev[0].ifname)]
+ try:
+ res = sigma_dut_cmd("UNKNOWN")
+ if "status,INVALID,errorCode,Unknown command" not in res:
+ raise Exception("Unexpected sigma_dut response to unknown command")
+
+ for cmd, response in tests:
+ res = sigma_dut_cmd(cmd)
+ if response not in res:
+ raise Exception("Unexpected %s response: %s" % (cmd, res))
+ finally:
+ stop_sigma_dut(sigma)
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_open(dev, apdev):
+ """sigma_dut controlled open network association"""
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_encryption,interface,%s,ssid,%s,encpType,none" % (ifname, "open"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s" % (ifname, "open"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_psk_pmf(dev, apdev):
+ """sigma_dut controlled PSK+PMF association"""
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-pmf-required"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,PMF" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_psk,interface,%s,ssid,%s,passphrase,%s,encpType,aes-ccmp,keymgmttype,wpa2,PMF,Required" % (ifname, "test-pmf-required", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-pmf-required"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_psk_pmf_bip_cmac_128(dev, apdev):
+ """sigma_dut controlled PSK+PMF association with BIP-CMAC-128"""
+ run_sigma_dut_psk_pmf_cipher(dev, apdev, "BIP-CMAC-128", "AES-128-CMAC")
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_psk_pmf_bip_cmac_256(dev, apdev):
+ """sigma_dut controlled PSK+PMF association with BIP-CMAC-256"""
+ run_sigma_dut_psk_pmf_cipher(dev, apdev, "BIP-CMAC-256", "BIP-CMAC-256")
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_psk_pmf_bip_gmac_128(dev, apdev):
+ """sigma_dut controlled PSK+PMF association with BIP-GMAC-128"""
+ run_sigma_dut_psk_pmf_cipher(dev, apdev, "BIP-GMAC-128", "BIP-GMAC-128")
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_psk_pmf_bip_gmac_256(dev, apdev):
+ """sigma_dut controlled PSK+PMF association with BIP-GMAC-256"""
+ run_sigma_dut_psk_pmf_cipher(dev, apdev, "BIP-GMAC-256", "BIP-GMAC-256")
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_psk_pmf_bip_gmac_256_mismatch(dev, apdev):
+ """sigma_dut controlled PSK+PMF association with BIP-GMAC-256 mismatch"""
+ run_sigma_dut_psk_pmf_cipher(dev, apdev, "BIP-GMAC-256", "AES-128-CMAC",
+ failure=True)
+
+def run_sigma_dut_psk_pmf_cipher(dev, apdev, sigma_cipher, hostapd_cipher,
+ failure=False):
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-pmf-required"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ params["group_mgmt_cipher"] = hostapd_cipher
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,PMF" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_psk,interface,%s,ssid,%s,passphrase,%s,encpType,aes-ccmp,keymgmttype,wpa2,PMF,Required,GroupMgntCipher,%s" % (ifname, "test-pmf-required", "12345678", sigma_cipher))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-pmf-required"),
+ timeout=2 if failure else 10)
+ if failure:
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Network selection result not indicated")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ res = sigma_dut_cmd("sta_is_connected,interface," + ifname)
+ if "connected,1" in res:
+ raise Exception("Connection reported")
+ else:
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_sae(dev, apdev):
+ """sigma_dut controlled SAE association"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_groups'] = '19 20 21'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ if dev[0].get_status_field('sae_group') != '19':
+ raise Exception("Expected default SAE group not used")
+ res = sigma_dut_cmd_check("sta_get_parameter,interface,%s,Parameter,PMK" % ifname)
+ logger.info("Reported PMK: " + res)
+ if ",PMK," not in res:
+ raise Exception("PMK not reported");
+ if hapd.request("GET_PMK " + dev[0].own_addr()) != res.split(',')[3]:
+ raise Exception("Mismatch in reported PMK")
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2,ECGroupID,20" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ if dev[0].get_status_field('sae_group') != '20':
+ raise Exception("Expected SAE group not used")
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_sae_groups(dev, apdev):
+ """sigma_dut controlled SAE association with group negotiation"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_groups'] = '19'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2,ECGroupID,21 20 19" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ if dev[0].get_status_field('sae_group') != '19':
+ raise Exception("Expected default SAE group not used")
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_sae_pmkid_include(dev, apdev):
+ """sigma_dut controlled SAE association with PMKID"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params["sae_confirm_immediate"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2,PMKID_Include,enable" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_sae_password(dev, apdev):
+ """sigma_dut controlled SAE association and long password"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['sae_password'] = 100*'B'
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2" % (ifname, "test-sae", 100*'B'))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_sae_pw_id(dev, apdev):
+ """sigma_dut controlled SAE association with Password Identifier"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_password'] = 'secret|id=pw id'
+ params['sae_groups'] = '19'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,AKMSuiteType,8;9,PasswordID,pw id" % (ifname, "test-sae", "secret"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_sae_pw_id_pwe_loop(dev, apdev):
+ """sigma_dut controlled SAE association with Password Identifier and forced PWE looping"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_password'] = 'secret|id=pw id'
+ params['sae_groups'] = '19'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,AKMSuiteType,8;9,PasswordID,pw id,sae_pwe,looping" % (ifname, "test-sae", "secret"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ for i in range(3):
+ ev = dev[0].wait_event(["SME: Trying to authenticate",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Network selection result not indicated")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ res = sigma_dut_cmd("sta_is_connected,interface," + ifname)
+ if "connected,1" in res:
+ raise Exception("Connection reported")
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_sae_pw_id_ft(dev, apdev):
+ """sigma_dut controlled SAE association with Password Identifier and FT"""
+ run_sigma_dut_sae_pw_id_ft(dev, apdev)
+
+def test_sigma_dut_sae_pw_id_ft_over_ds(dev, apdev):
+ """sigma_dut controlled SAE association with Password Identifier and FT-over-DS"""
+ run_sigma_dut_sae_pw_id_ft(dev, apdev, over_ds=True)
+
+def run_sigma_dut_sae_pw_id_ft(dev, apdev, over_ds=False):
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_key_mgmt'] = 'SAE FT-SAE'
+ params["ieee80211w"] = "2"
+ params['sae_password'] = ['pw1|id=id1', 'pw2|id=id2', 'pw3', 'pw4|id=id4']
+ params['mobility_domain'] = 'aabb'
+ params['ft_over_ds'] = '1' if over_ds else '0'
+ bssid = apdev[0]['bssid'].replace(':', '')
+ params['nas_identifier'] = bssid + '.nas.example.com'
+ params['r1_key_holder'] = bssid
+ params['pmk_r1_push'] = '0'
+ params['r0kh'] = 'ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff'
+ params['r1kh'] = '00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ if over_ds:
+ sigma_dut_cmd_check("sta_preset_testparameters,interface,%s,FT_DS,Enable" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,AKMSuiteType,8;9,PasswordID,id2" % (ifname, "test-sae", "pw2"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+
+ bssid = apdev[1]['bssid'].replace(':', '')
+ params['nas_identifier'] = bssid + '.nas.example.com'
+ params['r1_key_holder'] = bssid
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid = hapd2.own_addr()
+ sigma_dut_cmd_check("sta_reassoc,interface,%s,Channel,1,bssid,%s" % (ifname, bssid))
+ dev[0].wait_connected()
+
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_sta_override_rsne(dev, apdev):
+ """sigma_dut and RSNE override on STA"""
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-psk"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+
+ tests = ["30120100000fac040100000fac040100000fac02",
+ "30140100000fac040100000fac040100000fac02ffff"]
+ for test in tests:
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,type,PSK,passphrase,%s,EncpType,aes-ccmp,KeyMgmtType,wpa2" % (ifname, "test-psk", "12345678"))
+ sigma_dut_cmd_check("dev_configure_ie,interface,%s,IE_Name,RSNE,Contents,%s" % (ifname, test))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-psk"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,type,PSK,passphrase,%s,EncpType,aes-ccmp,KeyMgmtType,wpa2" % (ifname, "test-psk", "12345678"))
+ sigma_dut_cmd_check("dev_configure_ie,interface,%s,IE_Name,RSNE,Contents,300101" % ifname)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-psk"),
+ timeout=10)
+
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"])
+ if ev is None:
+ raise Exception("Association rejection not reported")
+ if "status_code=40" not in ev:
+ raise Exception("Unexpected status code: " + ev)
+
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_psk(dev, apdev):
+ """sigma_dut controlled AP"""
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-psk,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-PSK,PSK,12345678")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-psk", psk="12345678", scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_pskhex(dev, apdev, params):
+ """sigma_dut controlled AP and PSKHEX"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_pskhex.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ psk = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-psk,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-PSK,PSKHEX," + psk)
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-psk", raw_psk=psk, scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_psk_sha256(dev, apdev, params):
+ """sigma_dut controlled AP PSK SHA256"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_psk_sha256.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-psk,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-PSK-256,PSK,12345678")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-psk", key_mgmt="WPA-PSK-SHA256",
+ psk="12345678", scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_psk_deauth(dev, apdev, params):
+ """sigma_dut controlled AP and deauth commands"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_psk_deauth.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-psk,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-PSK,PSK,12345678,PMF,Required")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-psk", key_mgmt="WPA-PSK-SHA256",
+ psk="12345678", ieee80211w="2", scan_freq="2412")
+ addr = dev[0].own_addr()
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("ap_deauth_sta,NAME,AP,sta_mac_address," + addr)
+ ev = dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+ if "locally_generated=1" in ev:
+ raise Exception("Unexpected disconnection reason")
+ dev[0].wait_connected()
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("ap_deauth_sta,NAME,AP,sta_mac_address," + addr + ",disconnect,silent")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev and "locally_generated=1" not in ev:
+ raise Exception("Unexpected disconnection")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_eap_ttls(dev, apdev, params):
+ """sigma_dut controlled STA and EAP-TTLS parameters"""
+ check_domain_match(dev[0])
+ logdir = params['logdir']
+
+ with open("auth_serv/ca.pem", "r") as f:
+ with open(os.path.join(logdir, "sigma_dut_eap_ttls.ca.pem"), "w") as f2:
+ f2.write(f.read())
+
+ src = "auth_serv/server.pem"
+ dst = os.path.join(logdir, "sigma_dut_eap_ttls.server.der")
+ hashdst = os.path.join(logdir, "sigma_dut_eap_ttls.server.pem.sha256")
+ subprocess.check_call(["openssl", "x509", "-in", src, "-out", dst,
+ "-outform", "DER"],
+ stderr=open('/dev/null', 'w'))
+ with open(dst, "rb") as f:
+ der = f.read()
+ hash = hashlib.sha256(der).digest()
+ with open(hashdst, "w") as f:
+ f.write(binascii.hexlify(hash).decode())
+
+ dst = os.path.join(logdir, "sigma_dut_eap_ttls.incorrect.pem.sha256")
+ with open(dst, "w") as f:
+ f.write(32*"00")
+
+ ssid = "test-wpa2-eap"
+ params = hostapd.wpa2_eap_params(ssid=ssid)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, cert_path=logdir)
+
+ cmd = "sta_set_security,type,eapttls,interface,%s,ssid,%s,keymgmttype,wpa2,encType,AES-CCMP,PairwiseCipher,AES-CCMP-128,trustedRootCA,sigma_dut_eap_ttls.ca.pem,username,DOMAIN\mschapv2 user,password,password" % (ifname, ssid)
+
+ try:
+ tests = ["",
+ ",Domain,server.w1.fi",
+ ",DomainSuffix,w1.fi",
+ ",DomainSuffix,server.w1.fi",
+ ",ServerCert,sigma_dut_eap_ttls.server.pem"]
+ for extra in tests:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check(cmd + extra)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, ssid),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ dev[0].dump_monitor()
+
+ tests = [",Domain,w1.fi",
+ ",DomainSuffix,example.com",
+ ",ServerCert,sigma_dut_eap_ttls.incorrect.pem"]
+ for extra in tests:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check(cmd + extra)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, ssid),
+ timeout=10)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
+ if ev is None:
+ raise Exception("Server certificate error not reported")
+ res = sigma_dut_cmd("sta_is_connected,interface," + ifname)
+ if "connected,1" in res:
+ raise Exception("Unexpected connection reported")
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ dev[0].dump_monitor()
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_suite_b(dev, apdev, params):
+ """sigma_dut controlled STA Suite B"""
+ check_suite_b_192_capa(dev)
+ logdir = params['logdir']
+
+ with open("auth_serv/ec2-ca.pem", "r") as f:
+ with open(os.path.join(logdir, "suite_b_ca.pem"), "w") as f2:
+ f2.write(f.read())
+
+ with open("auth_serv/ec2-user.pem", "r") as f:
+ with open("auth_serv/ec2-user.key", "r") as f2:
+ with open(os.path.join(logdir, "suite_b.pem"), "w") as f3:
+ f3.write(f.read())
+ f3.write(f2.read())
+
+ dev[0].flush_scan_cache()
+ params = suite_b_as_params()
+ params['ca_cert'] = 'auth_serv/ec2-ca.pem'
+ params['server_cert'] = 'auth_serv/ec2-server.pem'
+ params['private_key'] = 'auth_serv/ec2-server.key'
+ params['openssl_ciphers'] = 'SUITEB192'
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-suite-b",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP-SUITE-B-192",
+ "rsn_pairwise": "GCMP-256",
+ "group_mgmt_cipher": "BIP-GMAC-256",
+ "ieee80211w": "2",
+ "ieee8021x": "1",
+ 'auth_server_addr': "127.0.0.1",
+ 'auth_server_port': "18129",
+ 'auth_server_shared_secret': "radius",
+ 'nas_identifier': "nas.w1.fi"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, cert_path=logdir)
+
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,PMF" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,type,eaptls,interface,%s,ssid,%s,PairwiseCipher,AES-GCMP-256,GroupCipher,AES-GCMP-256,GroupMgntCipher,BIP-GMAC-256,keymgmttype,SuiteB,clientCertificate,suite_b.pem,trustedRootCA,suite_b_ca.pem,CertType,ECC" % (ifname, "test-suite-b"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-suite-b"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_suite_b_rsa(dev, apdev, params):
+ """sigma_dut controlled STA Suite B (RSA)"""
+ check_suite_b_192_capa(dev)
+ logdir = params['logdir']
+
+ with open("auth_serv/rsa3072-ca.pem", "r") as f:
+ with open(os.path.join(logdir, "suite_b_ca_rsa.pem"), "w") as f2:
+ f2.write(f.read())
+
+ with open("auth_serv/rsa3072-user.pem", "r") as f:
+ with open("auth_serv/rsa3072-user.key", "r") as f2:
+ with open(os.path.join(logdir, "suite_b_rsa.pem"), "w") as f3:
+ f3.write(f.read())
+ f3.write(f2.read())
+
+ dev[0].flush_scan_cache()
+ params = suite_b_192_rsa_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, cert_path=logdir)
+
+ cmd = "sta_set_security,type,eaptls,interface,%s,ssid,%s,PairwiseCipher,AES-GCMP-256,GroupCipher,AES-GCMP-256,GroupMgntCipher,BIP-GMAC-256,keymgmttype,SuiteB,clientCertificate,suite_b_rsa.pem,trustedRootCA,suite_b_ca_rsa.pem,CertType,RSA" % (ifname, "test-suite-b")
+
+ try:
+ tests = ["",
+ ",TLSCipher,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
+ ",TLSCipher,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"]
+ for extra in tests:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,PMF" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check(cmd + extra)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-suite-b"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_suite_b(dev, apdev, params):
+ """sigma_dut controlled AP Suite B"""
+ check_suite_b_192_capa(dev)
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_suite_b.sigma-hostapd")
+ params = suite_b_as_params()
+ params['ca_cert'] = 'auth_serv/ec2-ca.pem'
+ params['server_cert'] = 'auth_serv/ec2-server.pem'
+ params['private_key'] = 'auth_serv/ec2-server.key'
+ params['openssl_ciphers'] = 'SUITEB192'
+ hostapd.add_ap(apdev[1], params)
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-suite-b,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_radius,NAME,AP,IPADDR,127.0.0.1,PORT,18129,PASSWORD,radius")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,SuiteB")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user.pem",
+ private_key="auth_serv/ec2-user.key",
+ pairwise="GCMP-256", group="GCMP-256",
+ scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_cipher_gcmp_128(dev, apdev, params):
+ """sigma_dut controlled AP with GCMP-128/BIP-GMAC-128 cipher"""
+ run_sigma_dut_ap_cipher(dev, apdev, params, "AES-GCMP-128", "BIP-GMAC-128",
+ "GCMP")
+
+def test_sigma_dut_ap_cipher_gcmp_256(dev, apdev, params):
+ """sigma_dut controlled AP with GCMP-256/BIP-GMAC-256 cipher"""
+ run_sigma_dut_ap_cipher(dev, apdev, params, "AES-GCMP-256", "BIP-GMAC-256",
+ "GCMP-256")
+
+def test_sigma_dut_ap_cipher_ccmp_128(dev, apdev, params):
+ """sigma_dut controlled AP with CCMP-128/BIP-CMAC-128 cipher"""
+ run_sigma_dut_ap_cipher(dev, apdev, params, "AES-CCMP-128", "BIP-CMAC-128",
+ "CCMP")
+
+def test_sigma_dut_ap_cipher_ccmp_256(dev, apdev, params):
+ """sigma_dut controlled AP with CCMP-256/BIP-CMAC-256 cipher"""
+ run_sigma_dut_ap_cipher(dev, apdev, params, "AES-CCMP-256", "BIP-CMAC-256",
+ "CCMP-256")
+
+def test_sigma_dut_ap_cipher_ccmp_gcmp_1(dev, apdev, params):
+ """sigma_dut controlled AP with CCMP-128+GCMP-256 ciphers (1)"""
+ run_sigma_dut_ap_cipher(dev, apdev, params, "AES-CCMP-128 AES-GCMP-256",
+ "BIP-GMAC-256", "CCMP")
+
+def test_sigma_dut_ap_cipher_ccmp_gcmp_2(dev, apdev, params):
+ """sigma_dut controlled AP with CCMP-128+GCMP-256 ciphers (2)"""
+ run_sigma_dut_ap_cipher(dev, apdev, params, "AES-CCMP-128 AES-GCMP-256",
+ "BIP-GMAC-256", "GCMP-256", "CCMP")
+
+def test_sigma_dut_ap_cipher_gcmp_256_group_ccmp(dev, apdev, params):
+ """sigma_dut controlled AP with GCMP-256/CCMP/BIP-GMAC-256 cipher"""
+ run_sigma_dut_ap_cipher(dev, apdev, params, "AES-GCMP-256", "BIP-GMAC-256",
+ "GCMP-256", "CCMP", "AES-CCMP-128")
+
+def run_sigma_dut_ap_cipher(dev, apdev, params, ap_pairwise, ap_group_mgmt,
+ sta_cipher, sta_cipher_group=None, ap_group=None):
+ check_suite_b_192_capa(dev)
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_cipher.sigma-hostapd")
+ params = suite_b_as_params()
+ params['ca_cert'] = 'auth_serv/ec2-ca.pem'
+ params['server_cert'] = 'auth_serv/ec2-server.pem'
+ params['private_key'] = 'auth_serv/ec2-server.key'
+ params['openssl_ciphers'] = 'SUITEB192'
+ hostapd.add_ap(apdev[1], params)
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-suite-b,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_radius,NAME,AP,IPADDR,127.0.0.1,PORT,18129,PASSWORD,radius")
+ cmd = "ap_set_security,NAME,AP,KEYMGNT,SuiteB,PMF,Required,PairwiseCipher,%s,GroupMgntCipher,%s" % (ap_pairwise, ap_group_mgmt)
+ if ap_group:
+ cmd += ",GroupCipher,%s" % ap_group
+ sigma_dut_cmd_check(cmd)
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ if sta_cipher_group is None:
+ sta_cipher_group = sta_cipher
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user.pem",
+ private_key="auth_serv/ec2-user.key",
+ pairwise=sta_cipher, group=sta_cipher_group,
+ scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_override_rsne(dev, apdev):
+ """sigma_dut controlled AP overriding RSNE"""
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-psk,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-PSK,PSK,12345678")
+ sigma_dut_cmd_check("dev_configure_ie,NAME,AP,interface,%s,IE_Name,RSNE,Contents,30180100000fac040200ffffffff000fac040100000fac020c00" % iface)
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-psk", psk="12345678", scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_sae(dev, apdev, params):
+ """sigma_dut controlled AP with SAE"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae.sigma-hostapd")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].request("SET sae_groups ")
+ id = dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+ if dev[0].get_status_field('sae_group') != '19':
+ raise Exception("Expected default SAE group not used")
+
+ res = sigma_dut_cmd_check("ap_get_parameter,name,AP,STA_MAC_Address,%s,Parameter,PMK" % dev[0].own_addr())
+ logger.info("Reported PMK: " + res)
+ if ",PMK," not in res:
+ raise Exception("PMK not reported");
+ if dev[0].get_pmk(id) != res.split(',')[3]:
+ raise Exception("Mismatch in reported PMK")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_sae_confirm_immediate(dev, apdev, params):
+ """sigma_dut controlled AP with SAE Confirm immediate"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_confirm_immediate.sigma-hostapd")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678,SAE_Confirm_Immediate,enable")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+ if dev[0].get_status_field('sae_group') != '19':
+ raise Exception("Expected default SAE group not used")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_sae_password(dev, apdev, params):
+ """sigma_dut controlled AP with SAE and long password"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_password.sigma-hostapd")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK," + 100*'C')
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", key_mgmt="SAE", sae_password=100*'C',
+ ieee80211w="2", scan_freq="2412")
+ if dev[0].get_status_field('sae_group') != '19':
+ raise Exception("Expected default SAE group not used")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_sae_pw_id(dev, apdev, params):
+ """sigma_dut controlled AP with SAE Password Identifier"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_pw_id.sigma-hostapd")
+ conffile = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_pw_id.sigma-conf")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,AKMSuiteType,8,SAEPasswords,pw1:id1;pw2:id2;pw3;pw4:id4,PMF,Required")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(conffile, "wb") as f2:
+ f2.write(f.read())
+
+ dev[0].request("SET sae_groups ")
+ tests = [("pw1", "id1"),
+ ("pw2", "id2"),
+ ("pw3", None),
+ ("pw4", "id4")]
+ for pw, pw_id in tests:
+ dev[0].connect("test-sae", key_mgmt="SAE", sae_password=pw,
+ sae_password_id=pw_id,
+ ieee80211w="2", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_sae_pw_id_pwe_loop(dev, apdev, params):
+ """sigma_dut controlled AP with SAE Password Identifier and forced PWE looping"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_pw_id_pwe_loop.sigma-hostapd")
+ conffile = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_pw_id_pwe_loop.sigma-conf")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,AKMSuiteType,8,SAEPasswords,12345678:pwid,PMF,Required,sae_pwe,looping")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(conffile, "wb") as f2:
+ f2.write(f.read())
+
+ dev[0].set("sae_groups", "")
+ dev[0].connect("test-sae", key_mgmt="SAE", sae_password="12345678",
+ sae_password_id="pwid",
+ ieee80211w="2", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-NETWORK-NOT-FOUND",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Network selection result not indicated")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ dev[0].request("REMOVE_NETWORK all")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_sae_pw_id_ft(dev, apdev, params):
+ """sigma_dut controlled AP with SAE Password Identifier and FT"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_pw_id_ft.sigma-hostapd")
+ conffile = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_pw_id_ft.sigma-conf")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng,DOMAIN,aabb")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,AKMSuiteType,8;9,SAEPasswords,pw1:id1;pw2:id2;pw3;pw4:id4,PMF,Required")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(conffile, "wb") as f2:
+ f2.write(f.read())
+
+ dev[0].request("SET sae_groups ")
+ tests = [("pw1", "id1", "SAE"),
+ ("pw2", "id2", "FT-SAE"),
+ ("pw3", None, "FT-SAE"),
+ ("pw4", "id4", "SAE")]
+ for pw, pw_id, key_mgmt in tests:
+ dev[0].connect("test-sae", key_mgmt=key_mgmt, sae_password=pw,
+ sae_password_id=pw_id,
+ ieee80211w="2", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_sae_group(dev, apdev, params):
+ """sigma_dut controlled AP with SAE and specific group"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_group.sigma-hostapd")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678,ECGroupID,20")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+ if dev[0].get_status_field('sae_group') != '20':
+ raise Exception("Expected SAE group not used")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_psk_sae(dev, apdev, params):
+ """sigma_dut controlled AP with PSK+SAE"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_psk_sae.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-PSK-SAE,PSK,12345678")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[2].request("SET sae_groups ")
+ dev[2].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ scan_freq="2412", ieee80211w="0", wait_connect=False)
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ scan_freq="2412", ieee80211w="2")
+ dev[1].connect("test-sae", psk="12345678", scan_freq="2412")
+
+ ev = dev[2].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+ dev[2].request("DISCONNECT")
+ if ev is not None:
+ raise Exception("Unexpected connection without PMF")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_psk_sae_ft(dev, apdev, params):
+ """sigma_dut controlled AP with PSK, SAE, FT"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_psk_sae_ft.sigma-hostapd")
+ conffile = os.path.join(params['logdir'],
+ "sigma_dut_ap_psk_sae_ft.sigma-conf")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default,NAME,AP,Program,WPA3")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae-psk,MODE,11ng,DOMAIN,aabb")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,AKMSuiteType,2;4;6;8;9,PSK,12345678,PairwiseCipher,AES-CCMP-128,GroupCipher,AES-CCMP-128")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,DOMAIN,0101,FT_OA,Enable")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,FT_BSS_LIST," + apdev[1]['bssid'])
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(conffile, "wb") as f2:
+ f2.write(f.read())
+
+ dev[0].request("SET sae_groups ")
+ dev[0].connect("test-sae-psk", key_mgmt="SAE FT-SAE",
+ sae_password="12345678", scan_freq="2412")
+ dev[1].connect("test-sae-psk", key_mgmt="WPA-PSK FT-PSK",
+ psk="12345678", scan_freq="2412")
+ dev[2].connect("test-sae-psk", key_mgmt="WPA-PSK",
+ psk="12345678", scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_owe(dev, apdev):
+ """sigma_dut controlled OWE station"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "ieee80211w": "2",
+ "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,owe,Type,OWE" % ifname)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,owe,channel,1" % ifname,
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ res = sigma_dut_cmd_check("sta_get_parameter,interface,%s,Parameter,PMK" % ifname)
+ logger.info("Reported PMK: " + res)
+ if ",PMK," not in res:
+ raise Exception("PMK not reported");
+ if hapd.request("GET_PMK " + dev[0].own_addr()) != res.split(',')[3]:
+ raise Exception("Mismatch in reported PMK")
+
+ dev[0].dump_monitor()
+ sigma_dut_cmd("sta_reassoc,interface,%s,Channel,1,bssid,%s" % (ifname, bssid))
+ dev[0].wait_connected()
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,owe,Type,OWE,ECGroupID,20" % ifname)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,owe,channel,1" % ifname,
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,owe,Type,OWE,ECGroupID,0" % ifname)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,owe,channel,1" % ifname,
+ timeout=10)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ if ev is None:
+ raise Exception("Association not rejected")
+ if "status_code=77" not in ev:
+ raise Exception("Unexpected rejection reason: " + ev)
+
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_owe_ptk_workaround(dev, apdev):
+ """sigma_dut controlled OWE station with PTK workaround"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+
+ params = {"ssid": "owe",
+ "wpa": "2",
+ "wpa_key_mgmt": "OWE",
+ "owe_ptk_workaround": "1",
+ "owe_groups": "20",
+ "ieee80211w": "2",
+ "rsn_pairwise": "CCMP"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, owe_ptk_workaround=True)
+
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,owe,Type,OWE,ECGroupID,20" % ifname)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,owe,channel,1" % ifname,
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_owe(dev, apdev, params):
+ """sigma_dut controlled AP with OWE"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_owe.sigma-hostapd")
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default,NAME,AP,Program,WPA3")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,owe,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,OWE")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ id = dev[0].connect("owe", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="2412")
+
+ res = sigma_dut_cmd_check("ap_get_parameter,name,AP,STA_MAC_Address,%s,Parameter,PMK" % dev[0].own_addr())
+ logger.info("Reported PMK: " + res)
+ if ",PMK," not in res:
+ raise Exception("PMK not reported");
+ if dev[0].get_pmk(id) != res.split(',')[3]:
+ raise Exception("Mismatch in reported PMK")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_owe_ecgroupid(dev, apdev):
+ """sigma_dut controlled AP with OWE and ECGroupID"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface)
+ try:
+ sigma_dut_cmd_check("ap_reset_default,NAME,AP,Program,WPA3")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,owe,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,OWE,ECGroupID,20 21,PMF,Required")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("owe", key_mgmt="OWE", ieee80211w="2",
+ owe_group="20", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].connect("owe", key_mgmt="OWE", ieee80211w="2",
+ owe_group="21", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ dev[0].connect("owe", key_mgmt="OWE", ieee80211w="2",
+ owe_group="19", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Association not rejected")
+ if "status_code=77" not in ev:
+ raise Exception("Unexpected rejection reason: " + ev)
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_owe_ptk_workaround(dev, apdev):
+ """sigma_dut controlled AP with OWE PTK workaround"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, owe_ptk_workaround=True)
+ try:
+ sigma_dut_cmd_check("ap_reset_default,NAME,AP,Program,WPA3")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,owe,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,OWE,ECGroupID,20,PMF,Required")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("owe", key_mgmt="OWE", ieee80211w="2",
+ owe_group="20", owe_ptk_workaround="1",
+ scan_freq="2412")
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_owe_transition_mode(dev, apdev, params):
+ """sigma_dut controlled AP with OWE and transition mode"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_owe_transition_mode.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default,NAME,AP,Program,WPA3")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,WLAN_TAG,1,CHANNEL,1,SSID,owe,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,WLAN_TAG,1,KEYMGNT,OWE")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,WLAN_TAG,2,CHANNEL,1,SSID,owe,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,WLAN_TAG,2,KEYMGNT,NONE")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ res1 = sigma_dut_cmd_check("ap_get_mac_address,NAME,AP,WLAN_TAG,1,Interface,24G")
+ res2 = sigma_dut_cmd_check("ap_get_mac_address,NAME,AP,WLAN_TAG,2,Interface,24G")
+
+ dev[0].connect("owe", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="2412")
+ dev[1].connect("owe", key_mgmt="NONE", scan_freq="2412")
+ if dev[0].get_status_field('bssid') not in res1:
+ raise Exception("Unexpected ap_get_mac_address WLAN_TAG,1: " + res1)
+ if dev[1].get_status_field('bssid') not in res2:
+ raise Exception("Unexpected ap_get_mac_address WLAN_TAG,2: " + res2)
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_owe_transition_mode_2(dev, apdev, params):
+ """sigma_dut controlled AP with OWE and transition mode (2)"""
+ if "OWE" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("OWE not supported")
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_owe_transition_mode_2.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default,NAME,AP,Program,WPA3")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,WLAN_TAG,1,CHANNEL,1,SSID,owe,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,WLAN_TAG,1,KEYMGNT,NONE")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,WLAN_TAG,2,CHANNEL,1,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,WLAN_TAG,2,KEYMGNT,OWE")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ res1 = sigma_dut_cmd_check("ap_get_mac_address,NAME,AP,WLAN_TAG,1,Interface,24G")
+ res2 = sigma_dut_cmd_check("ap_get_mac_address,NAME,AP,WLAN_TAG,2,Interface,24G")
+
+ dev[0].connect("owe", key_mgmt="OWE", ieee80211w="2",
+ scan_freq="2412")
+ dev[1].connect("owe", key_mgmt="NONE", scan_freq="2412")
+ if dev[0].get_status_field('bssid') not in res2:
+ raise Exception("Unexpected ap_get_mac_address WLAN_TAG,2: " + res1)
+ if dev[1].get_status_field('bssid') not in res1:
+ raise Exception("Unexpected ap_get_mac_address WLAN_TAG,1: " + res2)
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def dpp_init_enrollee(dev, id1, enrollee_role):
+ logger.info("Starting DPP initiator/enrollee in a thread")
+ time.sleep(1)
+ cmd = "DPP_AUTH_INIT peer=%d role=enrollee" % id1
+ if enrollee_role == "Configurator":
+ cmd += " netrole=configurator"
+ if "OK" not in dev.request(cmd):
+ raise Exception("Failed to initiate DPP Authentication")
+ ev = dev.wait_event(["DPP-CONF-RECEIVED"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Enrollee)")
+ logger.info("DPP initiator/enrollee done")
+
+def test_sigma_dut_dpp_qr_resp_1(dev, apdev):
+ """sigma_dut DPP/QR responder (conf index 1)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 1)
+
+def test_sigma_dut_dpp_qr_resp_2(dev, apdev):
+ """sigma_dut DPP/QR responder (conf index 2)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 2)
+
+def test_sigma_dut_dpp_qr_resp_3(dev, apdev):
+ """sigma_dut DPP/QR responder (conf index 3)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 3)
+
+def test_sigma_dut_dpp_qr_resp_4(dev, apdev):
+ """sigma_dut DPP/QR responder (conf index 4)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 4)
+
+def test_sigma_dut_dpp_qr_resp_5(dev, apdev):
+ """sigma_dut DPP/QR responder (conf index 5)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 5)
+
+def test_sigma_dut_dpp_qr_resp_6(dev, apdev):
+ """sigma_dut DPP/QR responder (conf index 6)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 6)
+
+def test_sigma_dut_dpp_qr_resp_7(dev, apdev):
+ """sigma_dut DPP/QR responder (conf index 7)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 7)
+
+def test_sigma_dut_dpp_qr_resp_8(dev, apdev):
+ """sigma_dut DPP/QR responder (conf index 8)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 8)
+
+def test_sigma_dut_dpp_qr_resp_9(dev, apdev):
+ """sigma_dut DPP/QR responder (conf index 9)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 9)
+
+def test_sigma_dut_dpp_qr_resp_10(dev, apdev):
+ """sigma_dut DPP/QR responder (conf index 10)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 10)
+
+def test_sigma_dut_dpp_qr_resp_11(dev, apdev, params):
+ """sigma_dut DPP/QR responder (conf index 11)"""
+ if not os.path.exists("./dpp-ca.py"):
+ raise HwsimSkip("dpp-ca.py not available")
+ logdir = params['logdir']
+ with open("auth_serv/ec-ca.pem", "rb") as f:
+ res = f.read()
+ with open(os.path.join(logdir, "dpp-ca.pem"), "wb") as f:
+ f.write(res)
+ with open("auth_serv/ec-ca.key", "rb") as f:
+ res = f.read()
+ with open(os.path.join(logdir, "dpp-ca.key"), "wb") as f:
+ f.write(res)
+ with open(os.path.join(logdir, "dpp-ca-csrattrs"), "wb") as f:
+ f.write(b'MAsGCSqGSIb3DQEJBw==')
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 11, cert_path=logdir)
+
+def test_sigma_dut_dpp_qr_resp_chan_list(dev, apdev):
+ """sigma_dut DPP/QR responder (channel list override)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 1, chan_list='81/2 81/6 81/1',
+ listen_chan=2)
+
+def test_sigma_dut_dpp_qr_resp_status_query(dev, apdev):
+ """sigma_dut DPP/QR responder status query"""
+ check_dpp_capab(dev[1])
+ params = hostapd.wpa2_params(ssid="DPPNET01",
+ passphrase="ThisIsDppPassphrase")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ try:
+ dev[1].set("dpp_config_processing", "2")
+ run_sigma_dut_dpp_qr_resp(dev, apdev, 3, status_query=True)
+ finally:
+ dev[1].set("dpp_config_processing", "0", allow_fail=True)
+
+def test_sigma_dut_dpp_qr_resp_configurator(dev, apdev):
+ """sigma_dut DPP/QR responder (configurator provisioning)"""
+ run_sigma_dut_dpp_qr_resp(dev, apdev, -1, enrollee_role="Configurator")
+
+def run_sigma_dut_dpp_qr_resp(dev, apdev, conf_idx, chan_list=None,
+ listen_chan=None, status_query=False,
+ enrollee_role="STA", cert_path=None):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ sigma = start_sigma_dut(dev[0].ifname, cert_path=cert_path)
+ try:
+ cmd = "dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR"
+ if chan_list:
+ cmd += ",DPPChannelList," + chan_list
+ res = sigma_dut_cmd(cmd)
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ id1 = dev[1].dpp_qr_code(uri)
+
+ t = threading.Thread(target=dpp_init_enrollee, args=(dev[1], id1,
+ enrollee_role))
+ t.start()
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Single,DPPProvisioningRole,Configurator,DPPConfEnrolleeRole,%s,DPPSigningKeyECC,P-256,DPPBS,QR,DPPTimeout,6" % enrollee_role
+ if conf_idx is not None:
+ cmd += ",DPPConfIndex,%d" % conf_idx
+ if listen_chan:
+ cmd += ",DPPListenChannel," + str(listen_chan)
+ if status_query:
+ cmd += ",DPPStatusQuery,Yes"
+ res = sigma_dut_cmd(cmd, timeout=10)
+ t.join()
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ if status_query and "StatusResult,0" not in res:
+ raise Exception("Status query did not succeed: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+
+csign = "30770201010420768240a3fc89d6662d9782f120527fe7fb9edc6366ab0b9c7dde96125cfd250fa00a06082a8648ce3d030107a144034200042908e1baf7bf413cc66f9e878a03e8bb1835ba94b033dbe3d6969fc8575d5eb5dfda1cb81c95cee21d0cd7d92ba30541ffa05cb6296f5dd808b0c1c2a83c0708"
+csign_pub = "3059301306072a8648ce3d020106082a8648ce3d030107034200042908e1baf7bf413cc66f9e878a03e8bb1835ba94b033dbe3d6969fc8575d5eb5dfda1cb81c95cee21d0cd7d92ba30541ffa05cb6296f5dd808b0c1c2a83c0708"
+ap_connector = "eyJ0eXAiOiJkcHBDb24iLCJraWQiOiJwYWtZbXVzd1dCdWpSYTl5OEsweDViaTVrT3VNT3dzZHRlaml2UG55ZHZzIiwiYWxnIjoiRVMyNTYifQ.eyJncm91cHMiOlt7Imdyb3VwSWQiOiIqIiwibmV0Um9sZSI6ImFwIn1dLCJuZXRBY2Nlc3NLZXkiOnsia3R5IjoiRUMiLCJjcnYiOiJQLTI1NiIsIngiOiIybU5vNXZuRkI5bEw3d1VWb1hJbGVPYzBNSEE1QXZKbnpwZXZULVVTYzVNIiwieSI6IlhzS3dqVHJlLTg5WWdpU3pKaG9CN1haeUttTU05OTl3V2ZaSVl0bi01Q3MifX0.XhjFpZgcSa7G2lHy0OCYTvaZFRo5Hyx6b7g7oYyusLC7C_73AJ4_BxEZQVYJXAtDuGvb3dXSkHEKxREP9Q6Qeg"
+ap_netaccesskey = "30770201010420ceba752db2ad5200fa7bc565b9c05c69b7eb006751b0b329b0279de1c19ca67ca00a06082a8648ce3d030107a14403420004da6368e6f9c507d94bef0515a1722578e73430703902f267ce97af4fe51273935ec2b08d3adefbcf588224b3261a01ed76722a630cf7df7059f64862d9fee42b"
+
+def start_dpp_ap(apdev):
+ params = {"ssid": "DPPNET01",
+ "wpa": "2",
+ "ieee80211w": "2",
+ "wpa_key_mgmt": "DPP",
+ "rsn_pairwise": "CCMP",
+ "dpp_connector": ap_connector,
+ "dpp_csign": csign_pub,
+ "dpp_netaccesskey": ap_netaccesskey}
+ try:
+ hapd = hostapd.add_ap(apdev, params)
+ except:
+ raise HwsimSkip("DPP not supported")
+ return hapd
+
+def test_sigma_dut_dpp_qr_init_enrollee(dev, apdev):
+ """sigma_dut DPP/QR initiator as Enrollee"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+
+ cmd = "DPP_CONFIGURATOR_ADD key=" + csign
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-dpp ssid=%s configurator=%d" % (to_hex("DPPNET01"), conf_id))
+ cmd = "DPP_LISTEN 2437 role=configurator"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6,DPPWaitForConnect,Yes", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_qr_init_enrollee_configurator(dev, apdev):
+ """sigma_dut DPP/QR initiator as Enrollee (to become Configurator)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ dev[1].set("dpp_configurator_params",
+ " conf=configurator ssid=%s configurator=%d" % (to_hex("DPPNET01"), conf_id))
+ cmd = "DPP_LISTEN 2437 role=configurator"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPNetworkRole,Configurator,DPPBS,QR,DPPTimeout,6", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_qr_mutual_init_enrollee(dev, apdev):
+ """sigma_dut DPP/QR (mutual) initiator as Enrollee"""
+ run_sigma_dut_dpp_qr_mutual_init_enrollee_check(dev, apdev)
+
+def test_sigma_dut_dpp_qr_mutual_init_enrollee_check(dev, apdev):
+ """sigma_dut DPP/QR (mutual) initiator as Enrollee (extra check)"""
+ run_sigma_dut_dpp_qr_mutual_init_enrollee_check(dev, apdev,
+ extra="DPPAuthDirection,Mutual,")
+
+def test_sigma_dut_dpp_qr_mutual_init_enrollee_mud_url(dev, apdev):
+ """sigma_dut DPP/QR (mutual) initiator as Enrollee (MUD URL)"""
+ run_sigma_dut_dpp_qr_mutual_init_enrollee_check(dev, apdev,
+ mud_url="https://example.com/mud")
+
+def run_sigma_dut_dpp_qr_mutual_init_enrollee_check(dev, apdev, extra='',
+ mud_url=None):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,DPP" % ifname)
+
+ cmd = "DPP_CONFIGURATOR_ADD key=" + csign
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-dpp ssid=%s configurator=%d" % (to_hex("DPPNET01"), conf_id))
+ cmd = "DPP_LISTEN 2437 role=configurator qr=mutual"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR")
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ id1 = dev[1].dpp_qr_code(uri)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,%sDPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6,DPPWaitForConnect,Yes" % extra
+ if mud_url:
+ cmd += ",MUDURL," + mud_url
+ res = sigma_dut_cmd_check(cmd, timeout=10)
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,DPP" % ifname)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+
+ if mud_url:
+ ev = dev[1].wait_event(["DPP-MUD-URL"], timeout=1)
+ if ev is None:
+ raise Exception("DPP MUD URL not reported")
+ if ev.split(' ')[1] != mud_url:
+ raise Exception("Unexpected MUD URL value: " + ev)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def dpp_init_conf_mutual(dev, id1, conf_id, own_id=None):
+ time.sleep(1)
+ logger.info("Starting DPP initiator/configurator in a thread")
+ cmd = "DPP_AUTH_INIT peer=%d conf=sta-dpp ssid=%s configurator=%d" % (id1, to_hex("DPPNET01"), conf_id)
+ if own_id is not None:
+ cmd += " own=%d" % own_id
+ if "OK" not in dev.request(cmd):
+ raise Exception("Failed to initiate DPP Authentication")
+ ev = dev.wait_event(["DPP-CONF-SENT"], timeout=10)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Configurator)")
+ logger.info("DPP initiator/configurator done")
+
+def test_sigma_dut_dpp_qr_mutual_resp_enrollee(dev, apdev):
+ """sigma_dut DPP/QR (mutual) responder as Enrollee"""
+ run_sigma_dut_dpp_qr_mutual_resp_enrollee(dev, apdev)
+
+def test_sigma_dut_dpp_qr_mutual_resp_enrollee_pending(dev, apdev):
+ """sigma_dut DPP/QR (mutual) responder as Enrollee (response pending)"""
+ run_sigma_dut_dpp_qr_mutual_resp_enrollee(dev, apdev, ',DPPDelayQRResponse,1')
+
+def run_sigma_dut_dpp_qr_mutual_resp_enrollee(dev, apdev, extra=None):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+
+ cmd = "DPP_CONFIGURATOR_ADD key=" + csign
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR")
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ id1 = dev[1].dpp_qr_code(uri)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ t = threading.Thread(target=dpp_init_conf_mutual,
+ args=(dev[1], id1, conf_id, id0))
+ t.start()
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Mutual,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,20,DPPWaitForConnect,Yes"
+ if extra:
+ cmd += extra
+ res = sigma_dut_cmd(cmd, timeout=25)
+ t.join()
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def dpp_resp_conf_mutual(dev, conf_id, uri):
+ logger.info("Starting DPP responder/configurator in a thread")
+ dev.set("dpp_configurator_params",
+ " conf=sta-dpp ssid=%s configurator=%d" % (to_hex("DPPNET01"),
+ conf_id))
+ cmd = "DPP_LISTEN 2437 role=configurator qr=mutual"
+ if "OK" not in dev.request(cmd):
+ raise Exception("Failed to initiate DPP listen")
+ if uri:
+ ev = dev.wait_event(["DPP-SCAN-PEER-QR-CODE"], timeout=10)
+ if ev is None:
+ raise Exception("QR Code scan for mutual authentication not requested")
+ dev.dpp_qr_code(uri)
+ ev = dev.wait_event(["DPP-CONF-SENT"], timeout=10)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Configurator)")
+ logger.info("DPP responder/configurator done")
+
+def test_sigma_dut_dpp_qr_mutual_init_enrollee(dev, apdev):
+ """sigma_dut DPP/QR (mutual) initiator as Enrollee"""
+ run_sigma_dut_dpp_qr_mutual_init_enrollee(dev, apdev, False)
+
+def test_sigma_dut_dpp_qr_mutual_init_enrollee_pending(dev, apdev):
+ """sigma_dut DPP/QR (mutual) initiator as Enrollee (response pending)"""
+ run_sigma_dut_dpp_qr_mutual_init_enrollee(dev, apdev, True)
+
+def run_sigma_dut_dpp_qr_mutual_init_enrollee(dev, apdev, resp_pending):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+
+ cmd = "DPP_CONFIGURATOR_ADD key=" + csign
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR")
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ if not resp_pending:
+ dev[1].dpp_qr_code(uri)
+ uri = None
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ t = threading.Thread(target=dpp_resp_conf_mutual,
+ args=(dev[1], conf_id, uri))
+ t.start()
+
+ time.sleep(1)
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,10,DPPWaitForConnect,Yes"
+ res = sigma_dut_cmd(cmd, timeout=15)
+ t.join()
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_qr_init_enrollee_psk(dev, apdev):
+ """sigma_dut DPP/QR initiator as Enrollee (PSK)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ params = hostapd.wpa2_params(ssid="DPPNET01",
+ passphrase="ThisIsDppPassphrase")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-psk ssid=%s pass=%s configurator=%d" % (to_hex("DPPNET01"), to_hex("ThisIsDppPassphrase"), conf_id))
+ cmd = "DPP_LISTEN 2437 role=configurator"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6,DPPWaitForConnect,Yes", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_qr_init_enrollee_sae(dev, apdev):
+ """sigma_dut DPP/QR initiator as Enrollee (SAE)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ params = hostapd.wpa2_params(ssid="DPPNET01",
+ passphrase="ThisIsDppPassphrase")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+ dev[0].set("sae_groups", "")
+
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-sae ssid=%s pass=%s configurator=%d" % (to_hex("DPPNET01"), to_hex("ThisIsDppPassphrase"), conf_id))
+ cmd = "DPP_LISTEN 2437 role=configurator"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6,DPPWaitForConnect,Yes", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_qr_init_configurator_1(dev, apdev):
+ """sigma_dut DPP/QR initiator as Configurator (conf index 1)"""
+ run_sigma_dut_dpp_qr_init_configurator(dev, apdev, 1)
+
+def test_sigma_dut_dpp_qr_init_configurator_2(dev, apdev):
+ """sigma_dut DPP/QR initiator as Configurator (conf index 2)"""
+ run_sigma_dut_dpp_qr_init_configurator(dev, apdev, 2)
+
+def test_sigma_dut_dpp_qr_init_configurator_3(dev, apdev):
+ """sigma_dut DPP/QR initiator as Configurator (conf index 3)"""
+ run_sigma_dut_dpp_qr_init_configurator(dev, apdev, 3)
+
+def test_sigma_dut_dpp_qr_init_configurator_4(dev, apdev):
+ """sigma_dut DPP/QR initiator as Configurator (conf index 4)"""
+ run_sigma_dut_dpp_qr_init_configurator(dev, apdev, 4)
+
+def test_sigma_dut_dpp_qr_init_configurator_5(dev, apdev):
+ """sigma_dut DPP/QR initiator as Configurator (conf index 5)"""
+ run_sigma_dut_dpp_qr_init_configurator(dev, apdev, 5)
+
+def test_sigma_dut_dpp_qr_init_configurator_6(dev, apdev):
+ """sigma_dut DPP/QR initiator as Configurator (conf index 6)"""
+ run_sigma_dut_dpp_qr_init_configurator(dev, apdev, 6)
+
+def test_sigma_dut_dpp_qr_init_configurator_7(dev, apdev):
+ """sigma_dut DPP/QR initiator as Configurator (conf index 7)"""
+ run_sigma_dut_dpp_qr_init_configurator(dev, apdev, 7)
+
+def test_sigma_dut_dpp_qr_init_configurator_both(dev, apdev):
+ """sigma_dut DPP/QR initiator as Configurator or Enrollee (conf index 1)"""
+ run_sigma_dut_dpp_qr_init_configurator(dev, apdev, 1, "Both")
+
+def test_sigma_dut_dpp_qr_init_configurator_neg_freq(dev, apdev):
+ """sigma_dut DPP/QR initiator as Configurator (neg_freq)"""
+ run_sigma_dut_dpp_qr_init_configurator(dev, apdev, 1, extra='DPPSubsequentChannel,81/11')
+
+def test_sigma_dut_dpp_qr_init_configurator_mud_url(dev, apdev):
+ """sigma_dut DPP/QR initiator as Configurator (MUD URL)"""
+ run_sigma_dut_dpp_qr_init_configurator(dev, apdev, 1,
+ mud_url="https://example.com/mud")
+
+def run_sigma_dut_dpp_qr_init_configurator(dev, apdev, conf_idx,
+ prov_role="Configurator",
+ extra=None, mud_url=None):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ if mud_url:
+ dev[1].set("dpp_mud_url", mud_url)
+ cmd = "DPP_LISTEN 2437 role=enrollee"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,%s,DPPConfIndex,%d,DPPSigningKeyECC,P-256,DPPConfEnrolleeRole,STA,DPPBS,QR,DPPTimeout,6" % (prov_role, conf_idx)
+ if extra:
+ cmd += "," + extra
+ res = sigma_dut_cmd(cmd)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ if mud_url and ",MUDURL," + mud_url not in res:
+ raise Exception("Unexpected result (missing MUD URL): " + res)
+ finally:
+ dev[1].set("dpp_mud_url", "")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_incompatible_roles_init(dev, apdev):
+ """sigma_dut DPP roles incompatible (Initiator)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR")
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ id1 = dev[1].dpp_qr_code(uri)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ cmd = "DPP_LISTEN 2437 role=enrollee"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Mutual,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6"
+ res = sigma_dut_cmd(cmd)
+ if "BootstrapResult,OK,AuthResult,ROLES_NOT_COMPATIBLE" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+
+def dpp_init_enrollee_mutual(dev, id1, own_id):
+ logger.info("Starting DPP initiator/enrollee in a thread")
+ time.sleep(1)
+ cmd = "DPP_AUTH_INIT peer=%d own=%d role=enrollee" % (id1, own_id)
+ if "OK" not in dev.request(cmd):
+ raise Exception("Failed to initiate DPP Authentication")
+ ev = dev.wait_event(["DPP-CONF-RECEIVED",
+ "DPP-NOT-COMPATIBLE"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Enrollee)")
+ logger.info("DPP initiator/enrollee done")
+
+def test_sigma_dut_dpp_incompatible_roles_resp(dev, apdev):
+ """sigma_dut DPP roles incompatible (Responder)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ cmd = "dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR"
+ res = sigma_dut_cmd(cmd)
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ id1 = dev[1].dpp_qr_code(uri)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ t = threading.Thread(target=dpp_init_enrollee_mutual, args=(dev[1], id1, id0))
+ t.start()
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Mutual,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6"
+ res = sigma_dut_cmd(cmd, timeout=10)
+ t.join()
+ if "BootstrapResult,OK,AuthResult,ROLES_NOT_COMPATIBLE" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_qr_enrollee_chirp(dev, apdev):
+ """sigma_dut DPP/QR as chirping Enrollee"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,DPP" % ifname)
+ cmd = "dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR"
+ res = sigma_dut_cmd_check(cmd)
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ conf_id = dev[1].dpp_configurator_add(key=csign)
+ idc = dev[1].dpp_qr_code(uri)
+ dev[1].dpp_bootstrap_set(idc, conf="sta-dpp", configurator=conf_id,
+ ssid="DPPNET01")
+ dev[1].dpp_listen(2437)
+
+ res = sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,16,DPPWaitForConnect,Yes,DPPChirp,Enable", timeout=20)
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,DPP" % ifname)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def dpp_enrollee_chirp(dev, id1):
+ logger.info("Starting chirping Enrollee in a thread")
+ time.sleep(0.1)
+ cmd = "DPP_CHIRP own=%d" % id1
+ if "OK" not in dev.request(cmd):
+ raise Exception("Failed to initiate DPP chirping")
+ ev = dev.wait_event(["DPP-CONF-RECEIVED"], timeout=15)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Enrollee)")
+ logger.info("DPP enrollee done")
+
+def test_sigma_dut_dpp_qr_configurator_chirp(dev, apdev):
+ """sigma_dut DPP/QR as Configurator waiting for chirp"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,DPP" % ifname)
+
+ id1 = dev[1].dpp_bootstrap_gen(chan="81/1")
+ uri = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ res = sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ t = threading.Thread(target=dpp_enrollee_chirp, args=(dev[1], id1))
+ t.start()
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Configurator,DPPConfIndex,1,DPPConfEnrolleeRole,STA,DPPBS,QR,DPPTimeout,16,DPPChirp,Enable,DPPChirpChannel,6", timeout=20)
+ t.join()
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,DPP" % ifname)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_dpp_qr_enrollee_chirp(dev, apdev, params):
+ """sigma_dut DPP/QR AP as chirping Enrollee"""
+ check_dpp_capab(dev[0], min_ver=2)
+ check_dpp_capab(dev[1])
+ logdir = params['prefix'] + ".sigma-hostapd"
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default,program,DPP")
+ cmd = "dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR"
+ res = sigma_dut_cmd_check(cmd)
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ conf_id = dev[0].dpp_configurator_add(key=csign)
+ idc = dev[0].dpp_qr_code(uri)
+ dev[0].dpp_bootstrap_set(idc, conf="ap-dpp", configurator=conf_id,
+ ssid="DPPNET01")
+ dev[0].dpp_listen(2437)
+
+ res = sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,16,DPPChirp,Enable", timeout=20)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+
+ dev[1].set("dpp_config_processing", "2")
+ id = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ dev[1].dpp_listen(2437)
+ dev[0].dpp_auth_init(uri=uri, conf="sta-dpp", ssid="DPPNET01",
+ configurator=conf_id)
+ dev[1].wait_connected(timeout=20)
+
+ sigma_dut_cmd_check("ap_reset_default,program,DPP")
+ finally:
+ dev[1].set("dpp_config_processing", "0", allow_fail=True)
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_pkex_init_configurator(dev, apdev):
+ """sigma_dut DPP/PKEX initiator as Configurator"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ id1 = dev[1].dpp_bootstrap_gen(type="pkex")
+ cmd = "DPP_PKEX_ADD own=%d identifier=test code=secret" % (id1)
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to set PKEX data (responder)")
+ cmd = "DPP_LISTEN 2437 role=enrollee"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPProvisioningRole,Configurator,DPPConfIndex,1,DPPSigningKeyECC,P-256,DPPConfEnrolleeRole,STA,DPPBS,PKEX,DPPPKEXCodeIdentifier,test,DPPPKEXCode,secret,DPPTimeout,6")
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+
+def dpp_init_conf(dev, id1, conf, conf_id, extra):
+ logger.info("Starting DPP initiator/configurator in a thread")
+ cmd = "DPP_AUTH_INIT peer=%d conf=%s %s configurator=%d" % (id1, conf, extra, conf_id)
+ if "OK" not in dev.request(cmd):
+ raise Exception("Failed to initiate DPP Authentication")
+ ev = dev.wait_event(["DPP-CONF-SENT"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Configurator)")
+ logger.info("DPP initiator/configurator done")
+
+def test_sigma_dut_ap_dpp_qr(dev, apdev, params):
+ """sigma_dut controlled AP (DPP)"""
+ run_sigma_dut_ap_dpp_qr(dev, apdev, params, "ap-dpp", "sta-dpp")
+
+def test_sigma_dut_ap_dpp_qr_legacy(dev, apdev, params):
+ """sigma_dut controlled AP (legacy)"""
+ run_sigma_dut_ap_dpp_qr(dev, apdev, params, "ap-psk", "sta-psk",
+ extra="pass=%s" % to_hex("qwertyuiop"))
+
+def test_sigma_dut_ap_dpp_qr_legacy_psk(dev, apdev, params):
+ """sigma_dut controlled AP (legacy)"""
+ run_sigma_dut_ap_dpp_qr(dev, apdev, params, "ap-psk", "sta-psk",
+ extra="psk=%s" % (32*"12"))
+
+def run_sigma_dut_ap_dpp_qr(dev, apdev, params, ap_conf, sta_conf, extra=""):
+ check_dpp_capab(dev[0])
+ logdir = os.path.join(params['logdir'], "sigma_dut_ap_dpp_qr.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default,program,DPP")
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR")
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev[0].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id1 = dev[0].dpp_qr_code(uri)
+
+ t = threading.Thread(target=dpp_init_conf,
+ args=(dev[0], id1, ap_conf, conf_id, extra))
+ t.start()
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6")
+ t.join()
+ if "ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+
+ id1 = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ id0b = dev[0].dpp_qr_code(uri1)
+
+ dev[1].set("dpp_config_processing", "2")
+ cmd = "DPP_LISTEN 2412"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+ cmd = "DPP_AUTH_INIT peer=%d conf=%s %s configurator=%d" % (id0b, sta_conf, extra, conf_id)
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("Failed to initiate DPP Authentication")
+ dev[1].wait_connected(timeout=20)
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ dev[1].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_dpp_offchannel(dev, apdev, params):
+ """sigma_dut controlled AP doing DPP on offchannel"""
+ check_dpp_capab(dev[0])
+ logdir = params['prefix'] + ".sigma-hostapd"
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default,program,DPP")
+ sigma_dut_cmd_check("ap_preset_testparameters,Program,DPP,Oper_Chn,3")
+ res = sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR")
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+ if "C:81/3;" not in uri:
+ raise Exception("Unexpected channel in AP's URI: " + uri)
+
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev[0].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id0 = dev[0].dpp_bootstrap_gen(chan="81/7", mac=True)
+ uri0 = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ dev[0].set("dpp_configurator_params",
+ "conf=ap-dpp ssid=%s configurator=%d" % (to_hex("DPPNET01"), conf_id))
+ dev[0].dpp_listen(2442)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6")
+ if "ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+
+ id1 = dev[1].dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id1)
+
+ id0b = dev[0].dpp_qr_code(uri1)
+
+ dev[1].set("dpp_config_processing", "2")
+ cmd = "DPP_LISTEN 2412"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+ cmd = "DPP_AUTH_INIT peer=%d conf=sta-dpp ssid=%s configurator=%d" % (id0b, to_hex("DPPNET01"), conf_id)
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("Failed to initiate DPP Authentication")
+ dev[1].wait_connected(timeout=20)
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ dev[1].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_dpp_pkex_responder(dev, apdev, params):
+ """sigma_dut controlled AP as DPP PKEX responder"""
+ check_dpp_capab(dev[0])
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_dpp_pkex_responder.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ run_sigma_dut_ap_dpp_pkex_responder(dev, apdev)
+ finally:
+ stop_sigma_dut(sigma)
+
+def dpp_init_conf_pkex(dev, conf_id, check_config=True):
+ logger.info("Starting DPP PKEX initiator/configurator in a thread")
+ time.sleep(1.5)
+ id = dev.dpp_bootstrap_gen(type="pkex")
+ cmd = "DPP_PKEX_ADD own=%d init=1 conf=ap-dpp configurator=%d code=password" % (id, conf_id)
+ res = dev.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to initiate DPP PKEX")
+ if not check_config:
+ return
+ ev = dev.wait_event(["DPP-CONF-SENT"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Configurator)")
+ logger.info("DPP initiator/configurator done")
+
+def run_sigma_dut_ap_dpp_pkex_responder(dev, apdev):
+ sigma_dut_cmd_check("ap_reset_default,program,DPP")
+
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev[0].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ t = threading.Thread(target=dpp_init_conf_pkex, args=(dev[0], conf_id))
+ t.start()
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Mutual,DPPProvisioningRole,Enrollee,DPPBS,PKEX,DPPPKEXCode,password,DPPTimeout,6,DPPWaitForConnect,No", timeout=10)
+ t.join()
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+
+ sigma_dut_cmd_check("ap_reset_default")
+
+def test_sigma_dut_dpp_pkex_responder_proto(dev, apdev):
+ """sigma_dut controlled STA as DPP PKEX responder and error case"""
+ check_dpp_capab(dev[0])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ run_sigma_dut_dpp_pkex_responder_proto(dev, apdev)
+ finally:
+ stop_sigma_dut(sigma)
+
+def run_sigma_dut_dpp_pkex_responder_proto(dev, apdev):
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ dev[1].set("dpp_test", "44")
+
+ t = threading.Thread(target=dpp_init_conf_pkex, args=(dev[1], conf_id,
+ False))
+ t.start()
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPProvisioningRole,Enrollee,DPPBS,PKEX,DPPPKEXCode,password,DPPTimeout,6", timeout=10)
+ t.join()
+ if "BootstrapResult,Timeout" not in res:
+ raise Exception("Unexpected result: " + res)
+
+def dpp_proto_init(dev, id1):
+ time.sleep(1)
+ logger.info("Starting DPP initiator/configurator in a thread")
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ cmd = "DPP_AUTH_INIT peer=%d conf=sta-dpp configurator=%d" % (id1, conf_id)
+ if "OK" not in dev.request(cmd):
+ raise Exception("Failed to initiate DPP Authentication")
+
+def test_sigma_dut_dpp_proto_initiator(dev, apdev):
+ """sigma_dut DPP protocol testing - Initiator"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ tests = [("InvalidValue", "AuthenticationRequest", "WrappedData",
+ "BootstrapResult,OK,AuthResult,Errorsent",
+ None),
+ ("InvalidValue", "AuthenticationConfirm", "WrappedData",
+ "BootstrapResult,OK,AuthResult,Errorsent",
+ None),
+ ("MissingAttribute", "AuthenticationRequest", "InitCapabilities",
+ "BootstrapResult,OK,AuthResult,Errorsent",
+ "Missing or invalid I-capabilities"),
+ ("InvalidValue", "AuthenticationConfirm", "InitAuthTag",
+ "BootstrapResult,OK,AuthResult,Errorsent",
+ "Mismatching Initiator Authenticating Tag"),
+ ("MissingAttribute", "ConfigurationResponse", "EnrolleeNonce",
+ "BootstrapResult,OK,AuthResult,OK,ConfResult,Errorsent",
+ "Missing or invalid Enrollee Nonce attribute")]
+ for step, frame, attr, result, fail in tests:
+ dev[0].request("FLUSH")
+ dev[1].request("FLUSH")
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ run_sigma_dut_dpp_proto_initiator(dev, step, frame, attr, result,
+ fail)
+ finally:
+ stop_sigma_dut(sigma)
+
+def run_sigma_dut_dpp_proto_initiator(dev, step, frame, attr, result, fail):
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ cmd = "DPP_LISTEN 2437 role=enrollee"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Configurator,DPPConfIndex,1,DPPSigningKeyECC,P-256,DPPConfEnrolleeRole,STA,DPPBS,QR,DPPTimeout,6,DPPStep,%s,DPPFrameType,%s,DPPIEAttribute,%s" % (step, frame, attr),
+ timeout=10)
+ if result not in res:
+ raise Exception("Unexpected result: " + res)
+ if fail:
+ ev = dev[1].wait_event(["DPP-FAIL"], timeout=5)
+ if ev is None or fail not in ev:
+ raise Exception("Failure not reported correctly: " + str(ev))
+
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_sigma_dut_dpp_proto_responder(dev, apdev):
+ """sigma_dut DPP protocol testing - Responder"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ tests = [("MissingAttribute", "AuthenticationResponse", "DPPStatus",
+ "BootstrapResult,OK,AuthResult,Errorsent",
+ "Missing or invalid required DPP Status attribute"),
+ ("MissingAttribute", "ConfigurationRequest", "EnrolleeNonce",
+ "BootstrapResult,OK,AuthResult,OK,ConfResult,Errorsent",
+ "Missing or invalid Enrollee Nonce attribute")]
+ for step, frame, attr, result, fail in tests:
+ dev[0].request("FLUSH")
+ dev[1].request("FLUSH")
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ run_sigma_dut_dpp_proto_responder(dev, step, frame, attr, result,
+ fail)
+ finally:
+ stop_sigma_dut(sigma)
+
+def run_sigma_dut_dpp_proto_responder(dev, step, frame, attr, result, fail):
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR")
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ id1 = dev[1].dpp_qr_code(uri)
+
+ t = threading.Thread(target=dpp_proto_init, args=(dev[1], id1))
+ t.start()
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPConfIndex,1,DPPSigningKeyECC,P-256,DPPConfEnrolleeRole,STA,DPPBS,QR,DPPTimeout,6,DPPStep,%s,DPPFrameType,%s,DPPIEAttribute,%s" % (step, frame, attr), timeout=10)
+ t.join()
+ if result not in res:
+ raise Exception("Unexpected result: " + res)
+ if fail:
+ ev = dev[1].wait_event(["DPP-FAIL"], timeout=5)
+ if ev is None or fail not in ev:
+ raise Exception("Failure not reported correctly:" + str(ev))
+
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_sigma_dut_dpp_proto_stop_at_initiator(dev, apdev):
+ """sigma_dut DPP protocol testing - Stop at RX on Initiator"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ tests = [("AuthenticationResponse",
+ "BootstrapResult,OK,AuthResult,Errorsent",
+ None),
+ ("ConfigurationRequest",
+ "BootstrapResult,OK,AuthResult,OK,ConfResult,Errorsent",
+ None)]
+ for frame, result, fail in tests:
+ dev[0].request("FLUSH")
+ dev[1].request("FLUSH")
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ run_sigma_dut_dpp_proto_stop_at_initiator(dev, frame, result, fail)
+ finally:
+ stop_sigma_dut(sigma)
+
+def run_sigma_dut_dpp_proto_stop_at_initiator(dev, frame, result, fail):
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ cmd = "DPP_LISTEN 2437 role=enrollee"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Configurator,DPPConfIndex,1,DPPSigningKeyECC,P-256,DPPConfEnrolleeRole,STA,DPPBS,QR,DPPTimeout,6,DPPStep,Timeout,DPPFrameType,%s" % (frame))
+ if result not in res:
+ raise Exception("Unexpected result: " + res)
+ if fail:
+ ev = dev[1].wait_event(["DPP-FAIL"], timeout=5)
+ if ev is None or fail not in ev:
+ raise Exception("Failure not reported correctly: " + str(ev))
+
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_sigma_dut_dpp_proto_stop_at_initiator_enrollee(dev, apdev):
+ """sigma_dut DPP protocol testing - Stop at TX on Initiator/Enrollee"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ tests = [("AuthenticationConfirm",
+ "BootstrapResult,OK,AuthResult,Errorsent,LastFrameReceived,AuthenticationResponse",
+ None)]
+ for frame, result, fail in tests:
+ dev[0].request("FLUSH")
+ dev[1].request("FLUSH")
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ run_sigma_dut_dpp_proto_stop_at_initiator_enrollee(dev, frame,
+ result, fail)
+ finally:
+ stop_sigma_dut(sigma)
+
+def run_sigma_dut_dpp_proto_stop_at_initiator_enrollee(dev, frame, result,
+ fail):
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ cmd = "DPP_LISTEN 2437 role=configurator"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6,DPPStep,Timeout,DPPFrameType,%s" % (frame), timeout=10)
+ if result not in res:
+ raise Exception("Unexpected result: " + res)
+ if fail:
+ ev = dev[1].wait_event(["DPP-FAIL"], timeout=5)
+ if ev is None or fail not in ev:
+ raise Exception("Failure not reported correctly: " + str(ev))
+
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_sigma_dut_dpp_proto_stop_at_responder(dev, apdev):
+ """sigma_dut DPP protocol testing - Stop at RX on Responder"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ tests = [("AuthenticationRequest",
+ "BootstrapResult,OK,AuthResult,Errorsent",
+ None),
+ ("AuthenticationConfirm",
+ "BootstrapResult,OK,AuthResult,Errorsent",
+ None)]
+ for frame, result, fail in tests:
+ dev[0].request("FLUSH")
+ dev[1].request("FLUSH")
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ run_sigma_dut_dpp_proto_stop_at_responder(dev, frame, result, fail)
+ finally:
+ stop_sigma_dut(sigma)
+
+def run_sigma_dut_dpp_proto_stop_at_responder(dev, frame, result, fail):
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR")
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ id1 = dev[1].dpp_qr_code(uri)
+
+ t = threading.Thread(target=dpp_proto_init, args=(dev[1], id1))
+ t.start()
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPConfIndex,1,DPPSigningKeyECC,P-256,DPPConfEnrolleeRole,STA,DPPBS,QR,DPPTimeout,6,DPPStep,Timeout,DPPFrameType,%s" % (frame), timeout=10)
+ t.join()
+ if result not in res:
+ raise Exception("Unexpected result: " + res)
+ if fail:
+ ev = dev[1].wait_event(["DPP-FAIL"], timeout=5)
+ if ev is None or fail not in ev:
+ raise Exception("Failure not reported correctly:" + str(ev))
+
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def dpp_proto_init_pkex(dev):
+ time.sleep(1)
+ logger.info("Starting DPP PKEX initiator/configurator in a thread")
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id = dev.dpp_bootstrap_gen(type="pkex")
+
+ cmd = "DPP_PKEX_ADD own=%d init=1 conf=sta-dpp configurator=%d code=secret" % (id, conf_id)
+ if "FAIL" in dev.request(cmd):
+ raise Exception("Failed to initiate DPP PKEX")
+
+def test_sigma_dut_dpp_proto_initiator_pkex(dev, apdev):
+ """sigma_dut DPP protocol testing - Initiator (PKEX)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ tests = [("InvalidValue", "PKEXCRRequest", "WrappedData",
+ "BootstrapResult,Errorsent",
+ None),
+ ("MissingAttribute", "PKEXExchangeRequest", "FiniteCyclicGroup",
+ "BootstrapResult,Errorsent",
+ "Missing or invalid Finite Cyclic Group attribute"),
+ ("MissingAttribute", "PKEXCRRequest", "BSKey",
+ "BootstrapResult,Errorsent",
+ "No valid peer bootstrapping key found")]
+ for step, frame, attr, result, fail in tests:
+ dev[0].request("FLUSH")
+ dev[1].request("FLUSH")
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ run_sigma_dut_dpp_proto_initiator_pkex(dev, step, frame, attr,
+ result, fail)
+ finally:
+ stop_sigma_dut(sigma)
+
+def run_sigma_dut_dpp_proto_initiator_pkex(dev, step, frame, attr, result, fail):
+ id1 = dev[1].dpp_bootstrap_gen(type="pkex")
+
+ cmd = "DPP_PKEX_ADD own=%d code=secret" % (id1)
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to set PKEX data (responder)")
+
+ cmd = "DPP_LISTEN 2437 role=enrollee"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Configurator,DPPConfIndex,1,DPPSigningKeyECC,P-256,DPPConfEnrolleeRole,STA,DPPBS,PKEX,DPPPKEXCode,secret,DPPTimeout,6,DPPStep,%s,DPPFrameType,%s,DPPIEAttribute,%s" % (step, frame, attr))
+ if result not in res:
+ raise Exception("Unexpected result: " + res)
+ if fail:
+ ev = dev[1].wait_event(["DPP-FAIL"], timeout=5)
+ if ev is None or fail not in ev:
+ raise Exception("Failure not reported correctly: " + str(ev))
+
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_sigma_dut_dpp_proto_responder_pkex(dev, apdev):
+ """sigma_dut DPP protocol testing - Responder (PKEX)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ tests = [("InvalidValue", "PKEXCRResponse", "WrappedData",
+ "BootstrapResult,Errorsent",
+ None),
+ ("MissingAttribute", "PKEXExchangeResponse", "DPPStatus",
+ "BootstrapResult,Errorsent",
+ "No DPP Status attribute"),
+ ("MissingAttribute", "PKEXCRResponse", "BSKey",
+ "BootstrapResult,Errorsent",
+ "No valid peer bootstrapping key found")]
+ for step, frame, attr, result, fail in tests:
+ dev[0].request("FLUSH")
+ dev[1].request("FLUSH")
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ run_sigma_dut_dpp_proto_responder_pkex(dev, step, frame, attr,
+ result, fail)
+ finally:
+ stop_sigma_dut(sigma)
+
+def run_sigma_dut_dpp_proto_responder_pkex(dev, step, frame, attr, result, fail):
+ t = threading.Thread(target=dpp_proto_init_pkex, args=(dev[1],))
+ t.start()
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPConfIndex,1,DPPSigningKeyECC,P-256,DPPConfEnrolleeRole,STA,DPPBS,PKEX,DPPPKEXCode,secret,DPPTimeout,6,DPPStep,%s,DPPFrameType,%s,DPPIEAttribute,%s" % (step, frame, attr), timeout=10)
+ t.join()
+ if result not in res:
+ raise Exception("Unexpected result: " + res)
+ if fail:
+ ev = dev[1].wait_event(["DPP-FAIL"], timeout=5)
+ if ev is None or fail not in ev:
+ raise Exception("Failure not reported correctly:" + str(ev))
+
+ dev[1].request("DPP_STOP_LISTEN")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def init_sigma_dut_dpp_proto_peer_disc_req(dev, apdev):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ dev[0].set("dpp_config_processing", "2")
+
+ cmd = "DPP_CONFIGURATOR_ADD key=" + csign
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-dpp ssid=%s configurator=%d" % (to_hex("DPPNET01"),
+ conf_id))
+ cmd = "DPP_LISTEN 2437 role=configurator"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+def test_sigma_dut_dpp_proto_peer_disc_req(dev, apdev):
+ """sigma_dut DPP protocol testing - Peer Discovery Request"""
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ init_sigma_dut_dpp_proto_peer_disc_req(dev, apdev)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6,DPPWaitForConnect,Yes,DPPStep,MissingAttribute,DPPFrameType,PeerDiscoveryRequest,DPPIEAttribute,TransactionID", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,Errorsent" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_self_config(dev, apdev):
+ """sigma_dut DPP Configurator enrolling an AP and using self-configuration"""
+ check_dpp_capab(dev[0])
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "unconfigured"})
+ check_dpp_capab(hapd)
+
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+ id = hapd.dpp_bootstrap_gen(chan="81/1", mac=True)
+ uri = hapd.request("DPP_BOOTSTRAP_GET_URI %d" % id)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Configurator,DPPConfIndex,1,DPPSigningKeyECC,P-256,DPPConfEnrolleeRole,AP,DPPBS,QR,DPPTimeout,6")
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ update_hapd_config(hapd)
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPCryptoIdentifier,P-256,DPPBS,QR,DPPAuthRole,Initiator,DPPProvisioningRole,Configurator,DPPAuthDirection,Single,DPPConfIndex,1,DPPTimeout,6,DPPWaitForConnect,Yes,DPPSelfConfigure,Yes"
+ res = sigma_dut_cmd(cmd, timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("dpp_config_processing", "0")
+
+def test_sigma_dut_ap_dpp_self_config(dev, apdev, params):
+ """sigma_dut DPP AP Configurator using self-configuration"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_dpp_self_config.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ run_sigma_dut_ap_dpp_self_config(dev, apdev)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("dpp_config_processing", "0", allow_fail=True)
+
+def run_sigma_dut_ap_dpp_self_config(dev, apdev):
+ check_dpp_capab(dev[0])
+
+ sigma_dut_cmd_check("ap_reset_default,program,DPP")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Configurator,DPPConfEnrolleeRole,AP,DPPBS,QR,DPPConfIndex,1,DPPSelfConfigure,Yes,DPPTimeout,6", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+
+ dev[0].set("dpp_config_processing", "2")
+
+ id = dev[0].dpp_bootstrap_gen(chan="81/11", mac=True)
+ uri = dev[0].request("DPP_BOOTSTRAP_GET_URI %d" % id)
+ cmd = "DPP_LISTEN 2462 role=enrollee"
+ if "OK" not in dev[0].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Configurator,DPPConfIndex,1,DPPSigningKeyECC,P-256,DPPConfEnrolleeRole,STA,DPPBS,QR,DPPTimeout,6"
+ res = sigma_dut_cmd(cmd)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ dev[0].wait_connected(timeout=20)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ sigma_dut_cmd_check("ap_reset_default")
+
+
+def test_sigma_dut_ap_dpp_relay(dev, apdev, params):
+ """sigma_dut DPP AP as Relay to Controller"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_dpp_relay.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ run_sigma_dut_ap_dpp_relay(dev, apdev)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def run_sigma_dut_ap_dpp_relay(dev, apdev):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+
+ # Controller
+ conf_id = dev[1].dpp_configurator_add()
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-dpp configurator=%d" % conf_id)
+ id_c = dev[1].dpp_bootstrap_gen()
+ uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ res = dev[1].request("DPP_BOOTSTRAP_INFO %d" % id_c)
+ pkhash = None
+ for line in res.splitlines():
+ name, value = line.split('=')
+ if name == "pkhash":
+ pkhash = value
+ break
+ if not pkhash:
+ raise Exception("Could not fetch public key hash from Controller")
+ if "OK" not in dev[1].request("DPP_CONTROLLER_START"):
+ raise Exception("Failed to start Controller")
+
+ sigma_dut_cmd_check("ap_reset_default,program,DPP")
+ sigma_dut_cmd_check("ap_preset_testparameters,program,DPP,DPPConfiguratorAddress,127.0.0.1,DPPConfiguratorPKHash," + pkhash)
+ res = sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR")
+
+ dev[0].dpp_auth_init(uri=uri_c, role="enrollee")
+ wait_auth_success(dev[1], dev[0], configurator=dev[1], enrollee=dev[0])
+
+ sigma_dut_cmd_check("ap_reset_default")
+
+def dpp_init_tcp_enrollee(dev, id1):
+ logger.info("Starting DPP initiator/enrollee (TCP) in a thread")
+ time.sleep(1)
+ cmd = "DPP_AUTH_INIT peer=%d role=enrollee tcp_addr=127.0.0.1" % id1
+ if "OK" not in dev.request(cmd):
+ raise Exception("Failed to initiate DPP Authentication")
+ ev = dev.wait_event(["DPP-CONF-RECEIVED"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Enrollee)")
+ logger.info("DPP initiator/enrollee done")
+
+def test_sigma_dut_dpp_tcp_conf_resp(dev, apdev):
+ """sigma_dut DPP TCP Configurator (Controller) as responder"""
+ run_sigma_dut_dpp_tcp_conf_resp(dev)
+
+def run_sigma_dut_dpp_tcp_conf_resp(dev, status_query=False):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ cmd = "dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR"
+ res = sigma_dut_cmd(cmd)
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ id1 = dev[1].dpp_qr_code(uri)
+
+ t = threading.Thread(target=dpp_init_tcp_enrollee, args=(dev[1], id1))
+ t.start()
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPConfIndex,1,DPPAuthDirection,Single,DPPProvisioningRole,Configurator,DPPConfEnrolleeRole,STA,DPPSigningKeyECC,P-256,DPPBS,QR,DPPOverTCP,yes,DPPTimeout,6"
+ if status_query:
+ cmd += ",DPPStatusQuery,Yes"
+ res = sigma_dut_cmd(cmd, timeout=10)
+ t.join()
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ if status_query and "StatusResult,0" not in res:
+ raise Exception("Status query did not succeed: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+
+def dpp_init_tcp_configurator(dev, id1, conf_id):
+ logger.info("Starting DPP initiator/configurator (TCP) in a thread")
+ time.sleep(1)
+ cmd = "DPP_AUTH_INIT peer=%d role=configurator conf=sta-dpp configurator=%d tcp_addr=127.0.0.1" % (id1, conf_id)
+ if "OK" not in dev.request(cmd):
+ raise Exception("Failed to initiate DPP Authentication")
+ ev = dev.wait_event(["DPP-CONF-SENT"], timeout=5)
+ if ev is None:
+ raise Exception("DPP configuration not completed (Configurator)")
+ logger.info("DPP initiator/configurator done")
+
+def test_sigma_dut_dpp_tcp_enrollee_resp(dev, apdev):
+ """sigma_dut DPP TCP Enrollee (Controller) as responder"""
+ run_sigma_dut_dpp_tcp_enrollee_resp(dev)
+
+def run_sigma_dut_dpp_tcp_enrollee_resp(dev, status_query=False):
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ cmd = "dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR"
+ res = sigma_dut_cmd(cmd)
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+
+ cmd = "DPP_CONFIGURATOR_ADD"
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id1 = dev[1].dpp_qr_code(uri)
+
+ t = threading.Thread(target=dpp_init_tcp_configurator, args=(dev[1], id1, conf_id))
+ t.start()
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPSigningKeyECC,P-256,DPPBS,QR,DPPOverTCP,yes,DPPTimeout,6"
+ if status_query:
+ cmd += ",DPPStatusQuery,Yes"
+ res = sigma_dut_cmd(cmd, timeout=10)
+ t.join()
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ if status_query and "StatusResult,0" not in res:
+ raise Exception("Status query did not succeed: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_tcp_enrollee_init(dev, apdev):
+ """sigma_dut DPP TCP Enrollee as initiator"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ # Controller
+ conf_id = dev[1].dpp_configurator_add()
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-dpp configurator=%d" % conf_id)
+ id_c = dev[1].dpp_bootstrap_gen()
+ uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ if "OK" not in dev[1].request("DPP_CONTROLLER_START"):
+ raise Exception("Failed to start Controller")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri_c))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPConfEnrolleeRole,STA,DPPBS,QR,DPPOverTCP,127.0.0.1,DPPTimeout,6"
+ res = sigma_dut_cmd(cmd, timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def test_sigma_dut_ap_dpp_tcp_enrollee_init(dev, apdev, params):
+ """sigma_dut DPP AP as TCP Enrollee/initiator"""
+ logdir = params['prefix'] + ".sigma-hostapd"
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ run_sigma_dut_ap_dpp_tcp_enrollee_init(dev, apdev)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def run_sigma_dut_ap_dpp_tcp_enrollee_init(dev, apdev):
+ check_dpp_capab(dev[1])
+ # Controller
+ conf_id = dev[1].dpp_configurator_add()
+ dev[1].set("dpp_configurator_params",
+ "conf=ap-dpp configurator=%d" % conf_id)
+ id_c = dev[1].dpp_bootstrap_gen()
+ uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ if "OK" not in dev[1].request("DPP_CONTROLLER_START"):
+ raise Exception("Failed to start Controller")
+
+ sigma_dut_cmd_check("ap_reset_default,program,DPP")
+ sigma_dut_cmd_check("ap_preset_testparameters,Program,DPP,NAME,AP,oper_chn,6")
+ sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri_c))
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPOverTCP,127.0.0.1,DPPTimeout,6"
+ res = sigma_dut_cmd(cmd, timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ sigma_dut_cmd_check("ap_reset_default")
+
+def test_sigma_dut_dpp_tcp_enrollee_init_mutual(dev, apdev):
+ """sigma_dut DPP TCP Enrollee as initiator with mutual authentication"""
+ check_dpp_capab(dev[0], min_ver=2)
+ check_dpp_capab(dev[1], min_ver=2)
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ # Controller
+ conf_id = dev[1].dpp_configurator_add()
+ dev[1].set("dpp_configurator_params",
+ "conf=sta-dpp configurator=%d" % conf_id)
+ id_c = dev[1].dpp_bootstrap_gen()
+ uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ if "OK" not in dev[1].request("DPP_CONTROLLER_START"):
+ raise Exception("Failed to start Controller")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri_c))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR"
+ res = sigma_dut_cmd_check(cmd)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+ id1 = dev[1].dpp_qr_code(uri)
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Mutual,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPOverTCP,127.0.0.1,DPPTimeout,6"
+ res = sigma_dut_cmd(cmd, timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def test_sigma_dut_dpp_tcp_configurator_init_mutual(dev, apdev):
+ """sigma_dut DPP TCP Configurator as initiator with mutual authentication"""
+ check_dpp_capab(dev[0], min_ver=2)
+ check_dpp_capab(dev[1], min_ver=2)
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ id_c = dev[1].dpp_bootstrap_gen()
+ uri_c = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_c)
+ if "OK" not in dev[1].request("DPP_CONTROLLER_START role=enrollee"):
+ raise Exception("Failed to start Controller")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri_c))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPCryptoIdentifier,P-256,DPPBS,QR"
+ res = sigma_dut_cmd_check(cmd)
+ hex = res.split(',')[3]
+ uri = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri)
+ id1 = dev[1].dpp_qr_code(uri)
+
+ cmd = "dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Mutual,DPPProvisioningRole,Configurator,DPPConfIndex,1,DPPConfEnrolleeRole,STA,DPPBS,QR,DPPOverTCP,127.0.0.1,DPPTimeout,6"
+ res = sigma_dut_cmd(cmd, timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[1].request("DPP_CONTROLLER_STOP")
+
+def test_sigma_dut_dpp_nfc_handover_requestor_enrollee(dev, apdev):
+ """sigma_dut DPP/NFC handover requestor as Enrollee"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+
+ cmd = "DPP_CONFIGURATOR_ADD key=" + csign
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-dpp ssid=%s configurator=%d" % (to_hex("DPPNET01"), conf_id))
+
+ id_own = dev[1].dpp_bootstrap_gen(type="nfc-uri", chan="81/1,6,11",
+ mac=True)
+ uri_own = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_own)
+
+ res = sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPBS,NFC")
+ hex = res.split(',')[3]
+ uri_peer = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri_peer)
+
+ sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,NFC" % to_hex(uri_own))
+
+ res = dev[1].request("DPP_NFC_HANDOVER_REQ own=%d uri=%s" % (id_own,
+ uri_peer))
+ if "FAIL" in res:
+ raise Exception("Failed to process NFC Handover Request")
+ info = dev[1].request("DPP_BOOTSTRAP_INFO %d" % id_own)
+ logger.info("Updated local bootstrapping info:\n" + info)
+ freq = None
+ for line in info.splitlines():
+ if line.startswith("use_freq="):
+ freq = int(line.split('=')[1])
+ if freq is None:
+ raise Exception("Selected channel not indicated")
+ uri1 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_own)
+ logger.info("Updated URI[1]: " + uri1)
+ dev[1].dpp_listen(freq, role="configurator")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPProvisioningRole,Enrollee,DPPBS,NFC,DPPNFCHandover,Negotiated_Requestor,DPPTimeout,6,DPPWaitForConnect,Yes", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_nfc_handover_selector_enrollee(dev, apdev):
+ """sigma_dut DPP/NFC handover selector as Enrollee"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+
+ cmd = "DPP_CONFIGURATOR_ADD key=" + csign
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-dpp ssid=%s configurator=%d" % (to_hex("DPPNET01"), conf_id))
+
+ id_own = dev[1].dpp_bootstrap_gen(type="nfc-uri", chan="81/1,6,11",
+ mac=True)
+ uri_own = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_own)
+
+ res = sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPBS,NFC")
+ hex = res.split(',')[3]
+ uri_peer = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri_peer)
+
+ sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,NFC" % to_hex(uri_own))
+
+ res = dev[1].request("DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (id_own,
+ uri_peer))
+ if "FAIL" in res:
+ raise Exception("Failed to process NFC Handover Select")
+ peer = int(res)
+ dev[1].dpp_auth_init(peer=peer, own=id_own, configurator=conf_id,
+ conf="sta-dpp", ssid="DPPNET01")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPProvisioningRole,Enrollee,DPPBS,NFC,DPPNFCHandover,Negotiated_Selector,DPPTimeout,6,DPPWaitForConnect,Yes", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_nfc_static_read_enrollee(dev, apdev):
+ """sigma_dut DPP/NFC read tag as Enrollee"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+
+ cmd = "DPP_CONFIGURATOR_ADD key=" + csign
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-dpp ssid=%s configurator=%d" % (to_hex("DPPNET01"), conf_id))
+
+ id_own = dev[1].dpp_bootstrap_gen(type="nfc-uri", chan="81/6", mac=True)
+ uri_own = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id_own)
+
+ sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,NFC" % to_hex(uri_own))
+ dev[1].dpp_listen(2437, role="configurator")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPProvisioningRole,Enrollee,DPPBS,NFC,DPPNFCHandover,Static,DPPTimeout,6,DPPWaitForConnect,Yes", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_nfc_static_write_enrollee(dev, apdev):
+ """sigma_dut DPP/NFC write tag as Enrollee"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[0].set("dpp_config_processing", "2")
+
+ cmd = "DPP_CONFIGURATOR_ADD key=" + csign
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-dpp ssid=%s configurator=%d" % (to_hex("DPPNET01"), conf_id))
+
+ res = sigma_dut_cmd_check("dev_exec_action,program,DPP,DPPActionType,GetLocalBootstrap,DPPBS,NFC")
+ hex = res.split(',')[3]
+ uri_peer = from_hex(hex)
+ logger.info("URI from sigma_dut: " + uri_peer)
+
+ dev[1].dpp_auth_init(nfc_uri=uri_peer, configurator=conf_id,
+ conf="sta-dpp", ssid="DPPNET01")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Responder,DPPProvisioningRole,Enrollee,DPPBS,NFC,DPPNFCHandover,Static,DPPTimeout,6,DPPWaitForConnect,Yes", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_reconfig_enrollee(dev, apdev):
+ """sigma_dut DPP reconfiguration (Enrollee)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ hapd = start_dpp_ap(apdev[0])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ cmd = "DPP_CONFIGURATOR_ADD key=" + csign
+ res = dev[1].request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ conf_id = int(res)
+
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+
+ dev[1].set("dpp_configurator_params",
+ " conf=sta-dpp ssid=%s configurator=%d" % (to_hex("DPPNET01"), conf_id))
+ cmd = "DPP_LISTEN 2437 role=configurator"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ ifname = dev[0].ifname
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,DPP" % ifname)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Enrollee,DPPBS,QR,DPPTimeout,6,DPPWaitForConnect,Yes", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK,NetworkIntroResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+
+ hapd.disable()
+ dev[0].dump_monitor()
+
+ ssid = "reconfig"
+ passphrase = "secret passphrase"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[1].set("dpp_configurator_params",
+ "conf=sta-psk ssid=%s pass=%s conn_status=1" % (binascii.hexlify(ssid.encode()).decode(), binascii.hexlify(passphrase.encode()).decode()))
+ cmd = "DPP_LISTEN 2437 role=configurator"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+ dev[1].dump_monitor()
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,DPPReconfigure,DPPTimeout,6,DPPWaitForConnect,Yes", timeout=10)
+ if "status,COMPLETE,ReconfigAuthResult,OK,ConfResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected reconfiguration result: " + res)
+
+ ev = dev[1].wait_event(["DPP-CONF-SENT"], timeout=15)
+ if ev is None:
+ raise Exception("DPP Config Response (reconfig) not transmitted")
+
+ dev[0].wait_connected(timeout=20)
+ ev = dev[1].wait_event(["DPP-CONN-STATUS-RESULT"], timeout=20)
+ if ev is None:
+ raise Exception("No connection status reported")
+ if "result=0" not in ev:
+ raise Exception("Connection status did not report success: " + ev)
+
+ time.sleep(1)
+ cmd = "DPP_LISTEN 2437 role=configurator"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,DPPReconfigure,DPPTimeout,6,DPPWaitForConnect,Yes", timeout=30)
+ if "status,COMPLETE,ReconfigAuthResult,OK,ConfResult,OK,NetworkConnectResult,OK" not in res:
+ raise Exception("Unexpected reconfiguration [2] result: " + res)
+
+ ev = dev[1].wait_event(["DPP-CONF-SENT"], timeout=5)
+ if ev is None:
+ raise Exception("DPP Config Response (reconfig) not transmitted [2]")
+
+ dev[0].wait_connected(timeout=20)
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_dpp_reconfig_configurator(dev, apdev):
+ """sigma_dut DPP reconfiguration (Configurator)"""
+ check_dpp_capab(dev[0])
+ check_dpp_capab(dev[1])
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ dev[1].set("dpp_config_processing", "1")
+ id0 = dev[1].dpp_bootstrap_gen(chan="81/6", mac=True)
+ uri0 = dev[1].request("DPP_BOOTSTRAP_GET_URI %d" % id0)
+ cmd = "DPP_LISTEN 2437"
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ ifname = dev[0].ifname
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,DPP" % ifname)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,SetPeerBootstrap,DPPBootstrappingdata,%s,DPPBS,QR" % to_hex(uri0))
+ if "status,COMPLETE" not in res:
+ raise Exception("dev_exec_action did not succeed: " + res)
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,AutomaticDPP,DPPAuthRole,Initiator,DPPAuthDirection,Single,DPPProvisioningRole,Configurator,DPPConfEnrolleeRole,STA,DPPSigningKeyECC,P-256,DPPConfIndex,1,DPPBS,QR,DPPTimeout,6", timeout=10)
+ if "BootstrapResult,OK,AuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected result: " + res)
+
+ dev[0].dump_monitor()
+
+ ev = dev[1].wait_event(["DPP-NETWORK-ID"], timeout=1)
+ if ev is None:
+ raise Exception("No network profile created")
+ id = int(ev.split(' ')[1])
+
+ ev = dev[1].wait_event(["DPP-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Configuration Result not sent")
+ dev[1].dump_monitor()
+ cmd = "DPP_RECONFIG %d" % id
+ if "OK" not in dev[1].request(cmd):
+ raise Exception("Failed to start reconfiguration")
+
+ res = sigma_dut_cmd("dev_exec_action,program,DPP,DPPActionType,DPPReconfigure,DPPProvisioningRole,Configurator,DPPConfEnrolleeRole,STA,DPPSigningKeyECC,P-256,DPPConfIndex,2,DPPListenChannel,6,DPPTimeout,6", timeout=10)
+ if "status,COMPLETE,ReconfigAuthResult,OK,ConfResult,OK" not in res:
+ raise Exception("Unexpected reconfiguration result: " + res)
+
+ ev = dev[1].wait_event(["DPP-CONF-RECEIVED"], timeout=15)
+ if ev is None:
+ raise Exception("DPP Config Response (reconfig) not received")
+ finally:
+ dev[0].set("dpp_config_processing", "0")
+ dev[1].set("dpp_config_processing", "0")
+ stop_sigma_dut(sigma)
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_preconfigured_profile(dev, apdev):
+ """sigma_dut controlled connection using preconfigured profile"""
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ params = hostapd.wpa2_params(ssid="test-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-psk", psk="12345678", scan_freq="2412",
+ only_add_network=True)
+
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s" % (ifname, "test-psk"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_wps_pbc(dev, apdev):
+ """sigma_dut and WPS PBC Enrollee"""
+ ssid = "test-wps-conf"
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wps", "eap_server": "1", "wps_state": "2",
+ "wpa_passphrase": "12345678", "wpa": "2",
+ "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+ hapd.request("WPS_PBC")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ cmd = "start_wps_registration,interface,%s" % ifname
+ cmd += ",WpsRole,Enrollee"
+ cmd += ",WpsConfigMethod,PBC"
+ sigma_dut_cmd_check(cmd, timeout=15)
+
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ hapd.disable()
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].flush_scan_cache()
+
+def test_sigma_dut_sta_scan_bss(dev, apdev):
+ """sigma_dut sta_scan_bss"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test"})
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ cmd = "sta_scan_bss,Interface,%s,BSSID,%s" % (dev[0].ifname, \
+ hapd.own_addr())
+ res = sigma_dut_cmd(cmd, timeout=10)
+ if "ssid,test,bsschannel,1" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_sta_scan_ssid_bssid(dev, apdev):
+ """sigma_dut sta_scan GetParameter,SSID_BSSID"""
+ hostapd.add_ap(apdev[0], {"ssid": "abcdef"})
+ hostapd.add_ap(apdev[1], {"ssid": "qwerty"})
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ cmd = "sta_scan,Interface,%s,GetParameter,SSID_BSSID" % dev[0].ifname
+ res = sigma_dut_cmd(cmd, timeout=10)
+ if "abcdef" not in res or "qwerty" not in res:
+ raise Exception("Unexpected result: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_sta_scan_short_ssid(dev, apdev):
+ """sigma_dut sta_scan ShortSSID"""
+ dev[0].flush_scan_cache()
+ ssid = "test-short-ssid-list"
+ hapd = hostapd.add_ap(apdev[0], {"ssid": ssid,
+ "ignore_broadcast_ssid": "1"})
+ bssid = apdev[0]['bssid']
+ payload = struct.pack('>L', binascii.crc32(ssid.encode()))
+ val = binascii.hexlify(payload).decode()
+ sigma = start_sigma_dut(dev[0].ifname)
+ found = False
+ try:
+ cmd = "sta_scan,Interface,%s,ChnlFreq,2412,ShortSSID,%s" % (dev[0].ifname, val)
+ for i in range(10):
+ sigma_dut_cmd_check(cmd, timeout=5)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+ if bssid in dev[0].request("SCAN_RESULTS"):
+ found = True
+ break
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].request("VENDOR_ELEM_REMOVE 14 *")
+
+ if not found:
+ raise Exception("AP not found in scan results")
+
+def test_sigma_dut_sta_scan_wait_completion(dev, apdev):
+ """sigma_dut sta_scan WaitCompletion,1"""
+ sigma = start_sigma_dut(dev[0].ifname)
+ try:
+ cmd = "sta_scan,Interface,%s,ChnlFreq,2412,WaitCompletion,1" % dev[0].ifname
+ res = sigma_dut_cmd(cmd, timeout=10)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_osen(dev, apdev, params):
+ """sigma_dut controlled AP with OSEN"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_osen.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-hs20,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_radius,NAME,AP,IPADDR,127.0.0.1,PORT,1812,PASSWORD,radius")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,OSEN,PMF,Optional")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ # RSN-OSEN (for OSU)
+ dev[0].connect("test-hs20", proto="OSEN", key_mgmt="OSEN",
+ pairwise="CCMP", group="GTK_NOT_USED",
+ eap="WFA-UNAUTH-TLS", identity="osen@example.com",
+ ca_cert="auth_serv/ca.pem", scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_eap_osen(dev, apdev, params):
+ """sigma_dut controlled AP with EAP+OSEN"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_eap_osen.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, bridge="ap-br0", hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-hs20,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_radius,NAME,AP,IPADDR,127.0.0.1,PORT,1812,PASSWORD,radius")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-ENT-OSEN,PMF,Optional")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+ # RSN-OSEN (for OSU)
+ dev[0].connect("test-hs20", proto="OSEN", key_mgmt="OSEN",
+ pairwise="CCMP",
+ eap="WFA-UNAUTH-TLS", identity="osen@example.com",
+ ca_cert="auth_serv/ca.pem", ieee80211w='2',
+ scan_freq="2412")
+ # RSN-EAP (for data connection)
+ dev[1].connect("test-hs20", key_mgmt="WPA-EAP", eap="TTLS",
+ identity="hs20-test", password="password",
+ ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+ ieee80211w='2', scan_freq="2412")
+
+ hwsim_utils.test_connectivity(dev[0], dev[1], broadcast=False,
+ success_expected=False, timeout=1)
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+ subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'down'],
+ stderr=open('/dev/null', 'w'))
+ subprocess.call(['brctl', 'delbr', 'ap-br0'],
+ stderr=open('/dev/null', 'w'))
+
+def test_sigma_dut_ap_eap(dev, apdev, params):
+ """sigma_dut controlled AP WPA2-Enterprise"""
+ logdir = os.path.join(params['logdir'], "sigma_dut_ap_eap.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-eap,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_radius,NAME,AP,IPADDR,127.0.0.1,PORT,1812,PASSWORD,radius")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-ENT")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-eap", key_mgmt="WPA-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_eap_sha256(dev, apdev, params):
+ """sigma_dut controlled AP WPA2-Enterprise SHA256"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_eap_sha256.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-eap,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_radius,NAME,AP,IPADDR,127.0.0.1,PORT,1812,PASSWORD,radius")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-ENT-256")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-eap", key_mgmt="WPA-EAP-SHA256", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_ft_eap(dev, apdev, params):
+ """sigma_dut controlled AP FT-EAP"""
+ logdir = os.path.join(params['logdir'], "sigma_dut_ap_ft_eap.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-ft-eap,MODE,11ng,DOMAIN,0101,FT_OA,Enable")
+ sigma_dut_cmd_check("ap_set_radius,NAME,AP,IPADDR,127.0.0.1,PORT,1812,PASSWORD,radius")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,FT-EAP")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-ft-eap", key_mgmt="FT-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_ft_psk(dev, apdev, params):
+ """sigma_dut controlled AP FT-PSK"""
+ logdir = os.path.join(params['logdir'], "sigma_dut_ap_ft_psk.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-ft-psk,MODE,11ng,DOMAIN,0101,FT_OA,Enable")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,FT-PSK,PSK,12345678")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-ft-psk", key_mgmt="FT-PSK", psk="12345678",
+ scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_ft_over_ds_psk(dev, apdev, params):
+ """sigma_dut controlled AP FT-PSK (over-DS)"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_ft_over_ds_psk.sigma-hostapd")
+ conffile = os.path.join(params['logdir'],
+ "sigma_dut_ap_ft_over_ds_psk.sigma-conf")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-ft-psk,MODE,11ng,DOMAIN,0101,FT_DS,Enable")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,FT-PSK,PSK,12345678")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(conffile, "wb") as f2:
+ f2.write(f.read())
+
+ dev[0].connect("test-ft-psk", key_mgmt="FT-PSK", psk="12345678",
+ scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_ent_ft_eap(dev, apdev, params):
+ """sigma_dut controlled AP WPA-EAP and FT-EAP"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_ent_ft_eap.sigma-hostapd")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-ent-ft-eap,MODE,11ng,DOMAIN,0101,FT_OA,Enable")
+ sigma_dut_cmd_check("ap_set_radius,NAME,AP,IPADDR,127.0.0.1,PORT,1812,PASSWORD,radius")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-ENT-FT-EAP")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].connect("test-ent-ft-eap", key_mgmt="FT-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ dev[1].connect("test-ent-ft-eap", key_mgmt="WPA-EAP", eap="GPSK",
+ identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_venue_url(dev, apdev):
+ """sigma_dut controlled Venue URL fetch"""
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "venue"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+
+ venue_group = 1
+ venue_type = 13
+ venue_info = struct.pack('BB', venue_group, venue_type)
+ lang1 = "eng"
+ name1 = "Example venue"
+ lang2 = "fin"
+ name2 = "Esimerkkipaikka"
+ venue1 = struct.pack('B', len(lang1 + name1)) + lang1.encode() + name1.encode()
+ venue2 = struct.pack('B', len(lang2 + name2)) + lang2.encode() + name2.encode()
+ venue_name = binascii.hexlify(venue_info + venue1 + venue2)
+
+ url1 = "http://example.com/venue"
+ url2 = "https://example.org/venue-info/"
+ params["venue_group"] = str(venue_group)
+ params["venue_type"] = str(venue_type)
+ params["venue_name"] = [lang1 + ":" + name1, lang2 + ":" + name2]
+ params["venue_url"] = ["1:" + url1, "2:" + url2]
+
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,PMF" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_psk,interface,%s,ssid,%s,passphrase,%s,encpType,aes-ccmp,keymgmttype,wpa2,PMF,Required" % (ifname, "venue", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "venue"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_hs2_venue_info,interface," + ifname + ",Display,Yes")
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_hs20_assoc_24(dev, apdev):
+ """sigma_dut controlled Hotspot 2.0 connection (2.4 GHz)"""
+ run_sigma_dut_hs20_assoc(dev, apdev, True)
+
+def test_sigma_dut_hs20_assoc_5(dev, apdev):
+ """sigma_dut controlled Hotspot 2.0 connection (5 GHz)"""
+ run_sigma_dut_hs20_assoc(dev, apdev, False)
+
+def run_sigma_dut_hs20_assoc(dev, apdev, band24):
+ hapd0 = None
+ hapd1 = None
+ try:
+ bssid0 = apdev[0]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid0
+ hapd0 = hostapd.add_ap(apdev[0], params)
+
+ bssid1 = apdev[1]['bssid']
+ params = hs20_ap_params()
+ params['hessid'] = bssid0
+ params["hw_mode"] = "a"
+ params["channel"] = "36"
+ params["country_code"] = "US"
+ hapd1 = hostapd.add_ap(apdev[1], params)
+
+ band = "2.4" if band24 else "5"
+ exp_bssid = bssid0 if band24 else bssid1
+ run_sigma_dut_hs20_assoc_2(dev, apdev, band, exp_bssid)
+ finally:
+ dev[0].request("DISCONNECT")
+ if hapd0:
+ hapd0.request("DISABLE")
+ if hapd1:
+ hapd1.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+
+def run_sigma_dut_hs20_assoc_2(dev, apdev, band, expect_bssid):
+ check_eap_capa(dev[0], "MSCHAPV2")
+ dev[0].flush_scan_cache()
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,HS2-R3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_add_credential,interface,%s,type,uname_pwd,realm,example.com,username,hs20-test,password,password" % ifname)
+ res = sigma_dut_cmd_check("sta_hs2_associate,interface,%s,band,%s" % (ifname, band),
+ timeout=15)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+ if "BSSID," + expect_bssid not in res:
+ raise Exception("Unexpected BSSID: " + res)
+
+def test_sigma_dut_ap_hs20(dev, apdev, params):
+ """sigma_dut controlled AP with Hotspot 2.0 parameters"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_hs20.sigma-hostapd")
+ conffile = os.path.join(params['logdir'],
+ "sigma_dut_ap_hs20.sigma-conf")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default,NAME,AP,program,HS2-R3")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,WLAN_TAG,1,CHANNEL,1,SSID,test-hs20,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_radius,NAME,AP,WLAN_TAG,1,IPADDR,127.0.0.1,PORT,1812,PASSWORD,radius")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,WLAN_TAG,1,KEYMGNT,WPA2-ENT")
+ sigma_dut_cmd_check("ap_set_hs2,NAME,AP,WLAN_TAG,1,HESSID,02:12:34:56:78:9a,NAI_REALM_LIST,1,OPER_NAME,1")
+ sigma_dut_cmd_check("ap_set_hs2,NAME,AP,WLAN_TAG,1,OSU_SERVER_URI,https://example.com/ https://example.org/,OSU_SSID,test-osu,OSU_METHOD,SOAP SOAP,OSU_PROVIDER_LIST,10,OSU_PROVIDER_NAI_LIST,4")
+ sigma_dut_cmd_check("ap_set_hs2,NAME,AP,WLAN_TAG,1,NET_AUTH_TYPE,2")
+ sigma_dut_cmd_check("ap_set_hs2,NAME,AP,WLAN_TAG,1,VENUE_NAME,1")
+ sigma_dut_cmd_check("ap_set_hs2,NAME,AP,WLAN_TAG,1,DOMAIN_LIST,example.com")
+ sigma_dut_cmd_check("ap_set_hs2,NAME,AP,WLAN_TAG,1,OPERATOR_ICON_METADATA,1")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,WLAN_TAG,2,CHANNEL,1,SSID,test-osu,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,WLAN_TAG,2,KEYMGNT,NONE")
+ sigma_dut_cmd_check("ap_set_hs2,NAME,AP,WLAN_TAG,2,OSU,1")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(conffile, "wb") as f2:
+ f2.write(f.read())
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_eap_ttls_uosc(dev, apdev, params):
+ """sigma_dut controlled STA and EAP-TTLS with UOSC"""
+ logdir = params['logdir']
+
+ with open("auth_serv/ca.pem", "r") as f:
+ with open(os.path.join(logdir, "sigma_dut_eap_ttls_uosc.ca.pem"),
+ "w") as f2:
+ f2.write(f.read())
+
+ src = "auth_serv/server.pem"
+ dst = os.path.join(logdir, "sigma_dut_eap_ttls_uosc.server.der")
+ hashdst = os.path.join(logdir, "sigma_dut_eap_ttls_uosc.server.pem.sha256")
+ subprocess.check_call(["openssl", "x509", "-in", src, "-out", dst,
+ "-outform", "DER"],
+ stderr=open('/dev/null', 'w'))
+ with open(dst, "rb") as f:
+ der = f.read()
+ hash = hashlib.sha256(der).digest()
+ with open(hashdst, "w") as f:
+ f.write(binascii.hexlify(hash).decode())
+
+ dst = os.path.join(logdir, "sigma_dut_eap_ttls_uosc.incorrect.pem.sha256")
+ with open(dst, "w") as f:
+ f.write(32*"00")
+
+ ssid = "test-wpa2-eap"
+ params = hostapd.wpa2_eap_params(ssid=ssid)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, cert_path=logdir)
+
+ try:
+ cmd = "sta_set_security,type,eapttls,interface,%s,ssid,%s,keymgmttype,wpa2,encType,AES-CCMP,PairwiseCipher,AES-CCMP-128,username,DOMAIN\mschapv2 user,password,password,ServerCert,sigma_dut_eap_ttls_uosc.incorrect.pem" % (ifname, ssid)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check(cmd)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, ssid),
+ timeout=10)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
+ if ev is None:
+ raise Exception("Server certificate error not reported")
+
+ res = sigma_dut_cmd_check("dev_exec_action,program,WPA3,interface,%s,ServerCertTrust,Accept" % ifname)
+ if "ServerCertTrustResult,Accepted" not in res:
+ raise Exception("Server certificate trust was not accepted")
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ dev[0].dump_monitor()
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_eap_ttls_uosc_tod(dev, apdev, params):
+ """sigma_dut controlled STA and EAP-TTLS with UOSC/TOD-STRICT"""
+ run_sigma_dut_eap_ttls_uosc_tod(dev, apdev, params, False)
+
+def test_sigma_dut_eap_ttls_uosc_tod_tofu(dev, apdev, params):
+ """sigma_dut controlled STA and EAP-TTLS with UOSC/TOD-TOFU"""
+ run_sigma_dut_eap_ttls_uosc_tod(dev, apdev, params, True)
+
+def run_sigma_dut_eap_ttls_uosc_tod(dev, apdev, params, tofu):
+ check_tls_tod(dev[0])
+ logdir = params['logdir']
+
+ name = "sigma_dut_eap_ttls_uosc_tod"
+ if tofu:
+ name += "_tofu"
+ with open("auth_serv/ca.pem", "r") as f:
+ with open(os.path.join(logdir, name + ".ca.pem"), "w") as f2:
+ f2.write(f.read())
+
+ if tofu:
+ src = "auth_serv/server-certpol2.pem"
+ else:
+ src = "auth_serv/server-certpol.pem"
+ dst = os.path.join(logdir, name + ".server.der")
+ hashdst = os.path.join(logdir, name + ".server.pem.sha256")
+ subprocess.check_call(["openssl", "x509", "-in", src, "-out", dst,
+ "-outform", "DER"],
+ stderr=open('/dev/null', 'w'))
+ with open(dst, "rb") as f:
+ der = f.read()
+ hash = hashlib.sha256(der).digest()
+ with open(hashdst, "w") as f:
+ f.write(binascii.hexlify(hash).decode())
+
+ ssid = "test-wpa2-eap"
+ params = int_eap_server_params()
+ params["ssid"] = ssid
+ if tofu:
+ params["server_cert"] = "auth_serv/server-certpol2.pem"
+ params["private_key"] = "auth_serv/server-certpol2.key"
+ else:
+ params["server_cert"] = "auth_serv/server-certpol.pem"
+ params["private_key"] = "auth_serv/server-certpol.key"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, cert_path=logdir)
+
+ try:
+ cmd = ("sta_set_security,type,eapttls,interface,%s,ssid,%s,keymgmttype,wpa2,encType,AES-CCMP,PairwiseCipher,AES-CCMP-128,trustedRootCA," + name + ".ca.pem,username,DOMAIN\mschapv2 user,password,password,ServerCert," + name + ".server.pem") % (ifname, ssid)
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check(cmd)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, ssid),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname + ",maintain_profile,1")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ hapd.disable()
+ params = hostapd.wpa2_eap_params(ssid=ssid)
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, ssid),
+ timeout=10)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
+ if ev is None:
+ raise Exception("Server certificate error not reported")
+
+ res = sigma_dut_cmd_check("dev_exec_action,program,WPA3,interface,%s,ServerCertTrust,Accept" % ifname)
+ if "ServerCertTrustResult,Accepted" in res:
+ raise Exception("Server certificate trust override was accepted unexpectedly")
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ dev[0].dump_monitor()
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_eap_ttls_uosc_initial_tod_strict(dev, apdev, params):
+ """sigma_dut controlled STA and EAP-TTLS with initial UOSC/TOD-STRICT"""
+ run_sigma_dut_eap_ttls_uosc_initial_tod(dev, apdev, params, False)
+
+def test_sigma_dut_eap_ttls_uosc_initial_tod_tofu(dev, apdev, params):
+ """sigma_dut controlled STA and EAP-TTLS with initial UOSC/TOD-TOFU"""
+ run_sigma_dut_eap_ttls_uosc_initial_tod(dev, apdev, params, True)
+
+def run_sigma_dut_eap_ttls_uosc_initial_tod(dev, apdev, params, tofu):
+ check_tls_tod(dev[0])
+ logdir = params['logdir']
+ name = params['name']
+ with open("auth_serv/rsa3072-ca.pem", "r") as f:
+ with open(params['prefix'] + ".ca.pem", "w") as f2:
+ f2.write(f.read())
+
+ if tofu:
+ src = "auth_serv/server-certpol2.pem"
+ else:
+ src = "auth_serv/server-certpol.pem"
+ dst = params['prefix'] + ".server.der"
+ hashdst = params['prefix'] + ".server.pem.sha256"
+ subprocess.check_call(["openssl", "x509", "-in", src, "-out", dst,
+ "-outform", "DER"],
+ stderr=open('/dev/null', 'w'))
+ with open(dst, "rb") as f:
+ der = f.read()
+ hash = hashlib.sha256(der).digest()
+ with open(hashdst, "w") as f:
+ f.write(binascii.hexlify(hash).decode())
+
+ ssid = "test-wpa2-eap"
+ params = int_eap_server_params()
+ params["ssid"] = ssid
+ if tofu:
+ params["server_cert"] = "auth_serv/server-certpol2.pem"
+ params["private_key"] = "auth_serv/server-certpol2.key"
+ else:
+ params["server_cert"] = "auth_serv/server-certpol.pem"
+ params["private_key"] = "auth_serv/server-certpol.key"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, cert_path=logdir)
+
+ try:
+ cmd = ("sta_set_security,type,eapttls,interface,%s,ssid,%s,keymgmttype,wpa2,encType,AES-CCMP,PairwiseCipher,AES-CCMP-128,trustedRootCA," + name + ".ca.pem,username,DOMAIN\mschapv2 user,password,password") % (ifname, ssid)
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check(cmd)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, ssid),
+ timeout=10)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=15)
+ if ev is None:
+ raise Exception("Server certificate validation failure not reported")
+
+ res = sigma_dut_cmd_check("dev_exec_action,program,WPA3,interface,%s,ServerCertTrust,Accept" % ifname)
+ if not tofu and "ServerCertTrustResult,Accepted" in res:
+ raise Exception("Server certificate trust override was accepted unexpectedly")
+ if tofu and "ServerCertTrustResult,Accepted" not in res:
+ raise Exception("Server certificate trust override was not accepted")
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ dev[0].dump_monitor()
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_eap_ttls_uosc_ca_mistrust(dev, apdev, params):
+ """sigma_dut controlled STA and EAP-TTLS with UOSC when CA is not trusted"""
+ check_domain_suffix_match(dev[0])
+ logdir = params['logdir']
+
+ with open("auth_serv/ca.pem", "r") as f:
+ with open(os.path.join(logdir,
+ "sigma_dut_eap_ttls_uosc_ca_mistrust.ca.pem"),
+ "w") as f2:
+ f2.write(f.read())
+
+ ssid = "test-wpa2-eap"
+ params = int_eap_server_params()
+ params["ssid"] = ssid
+ params["ca_cert"] = "auth_serv/rsa3072-ca.pem"
+ params["server_cert"] = "auth_serv/rsa3072-server.pem"
+ params["private_key"] = "auth_serv/rsa3072-server.key"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, cert_path=logdir)
+
+ try:
+ cmd = "sta_set_security,type,eapttls,interface,%s,ssid,%s,keymgmttype,wpa2,encType,AES-CCMP,PairwiseCipher,AES-CCMP-128,trustedRootCA,sigma_dut_eap_ttls_uosc_ca_mistrust.ca.pem,username,DOMAIN\mschapv2 user,password,password,domainSuffix,w1.fi" % (ifname, ssid)
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check(cmd)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, ssid),
+ timeout=10)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
+ if ev is None:
+ raise Exception("Server certificate error not reported")
+
+ res = sigma_dut_cmd_check("dev_exec_action,program,WPA3,interface,%s,ServerCertTrust,Accept" % ifname)
+ if "ServerCertTrustResult,Accepted" not in res:
+ raise Exception("Server certificate trust was not accepted")
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ dev[0].dump_monitor()
+ finally:
+ stop_sigma_dut(sigma)
+
+def start_sae_pwe_ap(apdev, sae_pwe):
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_groups'] = '19'
+ params['sae_pwe'] = str(sae_pwe)
+ return hostapd.add_ap(apdev, params)
+
+def connect_sae_pwe_sta(dev, ifname, extra=None):
+ dev.dump_monitor()
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ cmd = "sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2" % (ifname, "test-sae", "12345678")
+ if extra:
+ cmd += "," + extra
+ sigma_dut_cmd_check(cmd)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ dev.wait_disconnected()
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ dev.dump_monitor()
+
+def no_connect_sae_pwe_sta(dev, ifname, extra=None):
+ dev.dump_monitor()
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ cmd = "sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2" % (ifname, "test-sae", "12345678")
+ if extra:
+ cmd += "," + extra
+ sigma_dut_cmd_check(cmd)
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ ev = dev.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ if ev is None or "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection result")
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ dev.dump_monitor()
+
+def test_sigma_dut_sae_h2e(dev, apdev):
+ """sigma_dut controlled SAE H2E association (AP using loop+H2E)"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ start_sae_pwe_ap(apdev[0], 2)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, sae_h2e=True)
+ try:
+ connect_sae_pwe_sta(dev[0], ifname)
+ connect_sae_pwe_sta(dev[0], ifname, extra="sae_pwe,h2e")
+ connect_sae_pwe_sta(dev[0], ifname, extra="sae_pwe,loop")
+ res = sigma_dut_cmd("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2,sae_pwe,unknown" % (ifname, "test-sae", "12345678"))
+ if res != "status,ERROR,errorCode,Unsupported sae_pwe value":
+ raise Exception("Unexpected error result: " + res)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_sae_h2e_ap_loop(dev, apdev):
+ """sigma_dut controlled SAE H2E association (AP using loop-only)"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ start_sae_pwe_ap(apdev[0], 0)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, sae_h2e=True)
+ try:
+ connect_sae_pwe_sta(dev[0], ifname)
+ connect_sae_pwe_sta(dev[0], ifname, extra="sae_pwe,loop")
+ no_connect_sae_pwe_sta(dev[0], ifname, extra="sae_pwe,h2e")
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_sae_h2e_ap_h2e(dev, apdev):
+ """sigma_dut controlled SAE H2E association (AP using H2E-only)"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ start_sae_pwe_ap(apdev[0], 1)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, sae_h2e=True)
+ try:
+ connect_sae_pwe_sta(dev[0], ifname)
+ no_connect_sae_pwe_sta(dev[0], ifname, extra="sae_pwe,loop")
+ connect_sae_pwe_sta(dev[0], ifname, extra="sae_pwe,h2e")
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_ap_sae_h2e(dev, apdev, params):
+ """sigma_dut controlled AP with SAE H2E"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_h2e.sigma-hostapd")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, sae_h2e=True, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ for sae_pwe in [0, 1, 2]:
+ dev[0].request("SET sae_groups ")
+ dev[0].set("sae_pwe", str(sae_pwe))
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_ap_sae_h2e_only(dev, apdev, params):
+ """sigma_dut controlled AP with SAE H2E-only"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_h2e.sigma-hostapd")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, sae_h2e=True, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678,sae_pwe,h2e")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].request("SET sae_groups ")
+ dev[0].set("sae_pwe", "1")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ dev[0].set("sae_pwe", "0")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None or "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection result")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_ap_sae_loop_only(dev, apdev, params):
+ """sigma_dut controlled AP with SAE looping-only"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_h2e.sigma-hostapd")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, sae_h2e=True, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678,sae_pwe,loop")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].request("SET sae_groups ")
+ dev[0].set("sae_pwe", "0")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ dev[0].set("sae_pwe", "1")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-NETWORK-NOT-FOUND"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None or "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection result")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_sae_h2e_loop_forcing(dev, apdev):
+ """sigma_dut controlled SAE H2E misbehavior with looping forced"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_pwe'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2,IgnoreH2E_RSNXE_BSSMemSel,1" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ ev = dev[0].wait_event(["SME: Trying to authenticate with"], timeout=10)
+ if ev is None:
+ raise Exception("No authentication attempt reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected connection reported")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_sae_h2e_enabled_group_rejected(dev, apdev):
+ """sigma_dut controlled SAE H2E misbehavior with rejected groups"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_groups'] = "19 20"
+ params['sae_pwe'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, sae_h2e=True)
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2,ECGroupID_RGE,19 123" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ ev = dev[0].wait_event(["SME: Trying to authenticate with"], timeout=10)
+ if ev is None:
+ raise Exception("No authentication attempt reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected connection reported")
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_sae_h2e_rsnxe_mismatch(dev, apdev):
+ """sigma_dut controlled SAE H2E misbehavior with RSNXE"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_groups'] = "19"
+ params['sae_pwe'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname, sae_h2e=True)
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2,RSNXE_Content,EapolM2:F40100" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ ev = dev[0].wait_event(["SME: Trying to authenticate with"], timeout=10)
+ if ev is None:
+ raise Exception("No authentication attempt reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected connection reported")
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_ap_sae_h2e_rsnxe_mismatch(dev, apdev, params):
+ """sigma_dut controlled SAE H2E AP misbehavior with RSNXE"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_h2e_rsnxe_mismatch.sigma-hostapd")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, sae_h2e=True, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678,sae_pwe,h2e,RSNXE_Content,EapolM3:F40100")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].request("SET sae_groups ")
+ dev[0].set("sae_pwe", "1")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["Associated with"], timeout=10)
+ if ev is None:
+ raise Exception("No indication of association seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("No disconnection seen")
+ if "CTRL-EVENT-DISCONNECTED" not in ev:
+ raise Exception("Unexpected connection")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_ap_sae_h2e_group_rejection(dev, apdev, params):
+ """sigma_dut controlled AP with SAE H2E-only and group rejection"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_h2e_group_rejection.sigma-hostapd")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, sae_h2e=True, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678,sae_pwe,h2e")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].request("SET sae_groups 21 20 19")
+ dev[0].set("sae_pwe", "1")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+ addr = dev[0].own_addr()
+ res = sigma_dut_cmd_check("dev_exec_action,program,WPA3,Dest_MAC,%s,Rejected_DH_Groups,1" % addr)
+ if "DHGroupVerResult,21 20" not in res:
+ raise Exception("Unexpected dev_exec_action response: " + res)
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_ap_sae_h2e_anti_clogging(dev, apdev, params):
+ """sigma_dut controlled AP with SAE H2E and anti-clogging token"""
+ logdir = os.path.join(params['logdir'],
+ "sigma_dut_ap_sae_h2e_anti_clogging.sigma-hostapd")
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, sae_h2e=True, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,SAE,PSK,12345678,AntiCloggingThreshold,0")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].set("sae_groups", "")
+ dev[0].set("sae_pwe", "2")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("sae_pwe", "0")
+
+def test_sigma_dut_ap_5ghz(dev, apdev, params):
+ """sigma_dut controlled AP on 5 GHz"""
+ run_sigma_dut_ap_channel(dev, apdev, params, 36, '11na', 5180,
+ check_signal="WIDTH=20 MHz")
+
+def test_sigma_dut_ap_ht40plus(dev, apdev, params):
+ """sigma_dut controlled AP and HT40+"""
+ run_sigma_dut_ap_channel(dev, apdev, params, 36, '11na', 5180,
+ extra="width,40", check_signal="WIDTH=40 MHz")
+
+def test_sigma_dut_ap_ht40minus(dev, apdev, params):
+ """sigma_dut controlled AP and HT40-"""
+ run_sigma_dut_ap_channel(dev, apdev, params, 40, '11na', 5200,
+ extra="width,40", check_signal="WIDTH=40 MHz")
+
+def test_sigma_dut_ap_vht40(dev, apdev, params):
+ """sigma_dut controlled AP and VHT40"""
+ run_sigma_dut_ap_channel(dev, apdev, params, 36, '11ac', 5180,
+ extra="width,40", check_signal="WIDTH=40 MHz",
+ program="VHT")
+
+def test_sigma_dut_ap_vht80(dev, apdev, params):
+ """sigma_dut controlled AP and VHT80"""
+ run_sigma_dut_ap_channel(dev, apdev, params, 36, '11ac', 5180,
+ extra="width,80", check_signal="WIDTH=80 MHz",
+ program="VHT")
+
+def run_sigma_dut_ap_channel(dev, apdev, params, channel, mode, scan_freq,
+ extra=None, check_signal=None, program=None):
+ logdir = params['prefix'] + ".sigma-hostapd"
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ subprocess.call(['iw', 'reg', 'set', 'US'])
+ cmd = "ap_reset_default"
+ if program:
+ cmd += ",program," + program
+ sigma_dut_cmd_check(cmd)
+ cmd = "ap_set_wireless,NAME,AP,CHANNEL,%d,SSID,test-psk,MODE,%s" % (channel, mode)
+ if extra:
+ cmd += "," + extra
+ sigma_dut_cmd_check(cmd)
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-PSK,PSK,12345678")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(params['prefix'] + ".sigma-conf", "wb") as f2:
+ f2.write(f.read())
+
+ dev[0].connect("test-psk", psk="12345678", scan_freq=str(scan_freq))
+ sig = dev[0].request("SIGNAL_POLL")
+ logger.info("SIGNAL_POLL:\n" + sig.strip())
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ sigma_dut_cmd_check("ap_reset_default")
+
+ if check_signal and check_signal not in sig:
+ raise Exception("Unexpected SIGNAL_POLL data")
+ finally:
+ stop_sigma_dut(sigma)
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].flush_scan_cache()
+
+@reset_ignore_old_scan_res
+def test_sigma_dut_beacon_prot(dev, apdev):
+ """sigma_dut controlled STA and beacon protection"""
+ ssid = "test-pmf-required"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ params["beacon_prot"] = "1"
+ try:
+ hapd = hostapd.add_ap(apdev[0], params)
+ except Exception as e:
+ if "Failed to enable hostapd interface" in str(e):
+ raise HwsimSkip("Beacon protection not supported")
+ raise
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,PMF" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,type,PSK,passphrase,%s,encpType,aes-ccmp,keymgmttype,wpa2,PMF,Required,BeaconProtection,1" % (ifname, "test-pmf-required", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-pmf-required"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+
+ time.sleep(1)
+ check_mac80211_bigtk(dev[0], hapd)
+
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_beacon_prot(dev, apdev, params):
+ """sigma_dut controlled AP and beacon protection"""
+ logdir = params['prefix'] + ".sigma-hostapd"
+
+ Wlantest.setup(None)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-psk,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-PSK,PSK,12345678,PMF,Required,BeaconProtection,1")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+ bssid = sigma_dut_cmd_check("ap_get_mac_address,NAME,AP")
+ bssid = bssid.split(',')[3]
+
+ dev[0].connect("test-psk", key_mgmt="WPA-PSK-SHA256",
+ psk="12345678", scan_freq="2412",
+ ieee80211w="2", beacon_prot="1")
+ time.sleep(1)
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+ valid_bip = wt.get_bss_counter('valid_bip_mmie', bssid)
+ invalid_bip = wt.get_bss_counter('invalid_bip_mmie', bssid)
+ missing_bip = wt.get_bss_counter('missing_bip_mmie', bssid)
+ logger.info("wlantest BIP counters: valid=%d invalid=%d missing=%d" % (valid_bip, invalid_bip, missing_bip))
+ if valid_bip < 0 or invalid_bip > 0 or missing_bip > 0:
+ raise Exception("Unexpected wlantest BIP counters: valid=%d invalid=%d missing=%d" % (valid_bip, invalid_bip, missing_bip))
+
+def test_sigma_dut_ap_transition_disable(dev, apdev, params):
+ """sigma_dut controlled AP and transition disabled indication"""
+ check_sae_capab(dev[0])
+ logdir = params['prefix'] + ".sigma-hostapd"
+
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678,PMF,Required,Transition_Disable,1,Transition_Disable_Index,0")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].set("sae_groups", "")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+ ev = dev[0].wait_event(["TRANSITION-DISABLE"], timeout=1)
+ if ev is None:
+ raise Exception("Transition disable not indicated")
+ if ev.split(' ')[1] != "01":
+ raise Exception("Unexpected transition disable bitmap: " + ev)
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_transition_disable_change(dev, apdev, params):
+ """sigma_dut controlled AP and transition disabled indication change"""
+ check_sae_capab(dev[0])
+ logdir = params['prefix'] + ".sigma-hostapd"
+
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678,PMF,Required")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+ dev[0].set("sae_groups", "")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+ ev = dev[0].wait_event(["TRANSITION-DISABLE"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected transition disable indication")
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("ap_set_rfeature,NAME,AP,Transition_Disable,1,Transition_Disable_Index,0")
+ dev[0].request("RECONNECT")
+ dev[0].wait_connected()
+ ev = dev[0].wait_event(["TRANSITION-DISABLE"], timeout=1)
+ if ev is None:
+ raise Exception("Transition disable not indicated")
+ if ev.split(' ')[1] != "01":
+ raise Exception("Unexpected transition disable bitmap: " + ev)
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ft_rsnxe_used_mismatch(dev, apdev):
+ """sigma_dut controlled FT protocol with RSNXE Used mismatch"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid)
+ params['wpa_key_mgmt'] = 'SAE FT-SAE'
+ params["ieee80211w"] = "2"
+ params['sae_password'] = "hello"
+ params['sae_pwe'] = "2"
+ params['mobility_domain'] = 'aabb'
+ bssid = apdev[0]['bssid'].replace(':', '')
+ params['nas_identifier'] = bssid + '.nas.example.com'
+ params['r1_key_holder'] = bssid
+ params['pmk_r1_push'] = '0'
+ params['r0kh'] = 'ff:ff:ff:ff:ff:ff * 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff'
+ params['r1kh'] = '00:00:00:00:00:00 00:00:00:00:00:00 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff'
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,AKMSuiteType,8;9" % (ifname, "test-sae", "hello"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ dev[0].dump_monitor()
+
+ bssid2 = apdev[1]['bssid'].replace(':', '')
+ params['nas_identifier'] = bssid2 + '.nas.example.com'
+ params['r1_key_holder'] = bssid2
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+
+ sigma_dut_cmd_check("sta_reassoc,interface,%s,Channel,1,bssid,%s" % (ifname, bssid2))
+ count = 0
+ for i in range(5):
+ ev = dev[0].wait_event(["Trying to associate",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection timed out")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ break
+ count += 1
+ dev[0].dump_monitor()
+ if count != 1:
+ raise Exception("Unexpected number of association attempts for the first FT protocol exchange (expecting success)")
+
+ sigma_dut_cmd_check("sta_set_rfeature,interface,%s,prog,WPA3,ReassocReq_RSNXE_Used,1" % ifname)
+ sigma_dut_cmd_check("sta_reassoc,interface,%s,Channel,1,bssid,%s" % (ifname, bssid))
+ count = 0
+ for i in range(5):
+ ev = dev[0].wait_event(["Trying to associate",
+ "CTRL-EVENT-CONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("Connection timed out")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ break
+ count += 1
+ dev[0].dump_monitor()
+ if count != 2:
+ raise Exception("Unexpected number of association attempts for the second FT protocol exchange (expecting failure)")
+
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_ft_rsnxe_used_mismatch(dev, apdev, params):
+ """sigma_dut controlled AP with FT and RSNXE Used mismatch"""
+ logdir = params['prefix'] + ".sigma-hostapd"
+ conffile = params['prefix'] + ".sigma-conf"
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng,DOMAIN,aabb")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,AKMSuiteType,8;9,SAEPasswords,hello,PMF,Required")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(conffile, "wb") as f2:
+ f2.write(f.read())
+
+ dev[0].connect("test-sae", key_mgmt="FT-SAE", sae_password="hello",
+ ieee80211w="2", scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_set_rfeature,NAME,AP,type,WPA3,ReassocResp_RSNXE_Used,1")
+ # This would need to be followed by FT protocol roaming test, but
+ # that is not currently convenient to implement, so for now, this
+ # test is based on manual inspection of hostapd getting configured
+ # properly.
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ocv(dev, apdev):
+ """sigma_dut controlled STA using OCV"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_groups'] = '19'
+ params['ocv'] = '1'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_wireless,interface,%s,program,WPA3,ocvc,1" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_wireless,interface,%s,program,WPA3,ocvc,1" % ifname)
+ sigma_dut_cmd_check("sta_set_rfeature,interface,%s,prog,WPA3,OCIFrameType,eapolM2,OCIChannel,11" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"))
+ ev = hapd.wait_event(["OCV-FAILURE"], timeout=1)
+ if ev is None:
+ raise Exception("OCV failure for EAPOL-Key msg 2/4 not reported")
+ if "addr=" + dev[0].own_addr() not in ev:
+ raise Exception("Unexpected OCV failure addr: " + ev)
+ if "frame=eapol-key-m2" not in ev:
+ raise Exception("Unexpected OCV failure frame: " + ev)
+ if "error=primary channel mismatch" not in ev:
+ raise Exception("Unexpected OCV failure error: " + ev)
+
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_ocv(dev, apdev, params):
+ """sigma_dut controlled AP using OCV"""
+ logdir = params['prefix'] + ".sigma-hostapd"
+ conffile = params['prefix'] + ".sigma-conf"
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,ocvc,1")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+ bssid = sigma_dut_cmd_check("ap_get_mac_address,NAME,AP")
+ bssid = bssid.split(',')[3]
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(conffile, "wb") as f2:
+ f2.write(f.read())
+
+ dev[0].set("sae_groups", "")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", ocv="1", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("ap_set_rfeature,NAME,AP,type,WPA3,OCIFrameType,eapolM3,OCIChannel,3")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", ocv="1", scan_freq="2412",
+ wait_connect=False)
+ check_ocv_failure(dev[0], "EAPOL-Key msg 3/4", "eapol-key-m3",
+ bssid)
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_gtk_rekey(dev, apdev):
+ """sigma_dut controlled STA requesting GTK rekeying"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_groups'] = '19'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_wireless,interface,%s,program,WPA3,ocvc,1" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+
+ dev[0].dump_monitor()
+ sigma_dut_cmd_check("dev_exec_action,interface,%s,program,WPA3,KeyRotation,1" % ifname)
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=5)
+ if ev is None:
+ raise Exception("GTK rekeying not seen")
+
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_gtk_rekey(dev, apdev, params):
+ """sigma_dut controlled AP and requested GTK rekeying"""
+ logdir = params['prefix'] + ".sigma-hostapd"
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,test-sae,MODE,11ng")
+ sigma_dut_cmd_check("ap_set_security,NAME,AP,KEYMGNT,WPA2-SAE,PSK,12345678")
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+
+ dev[0].set("sae_groups", "")
+ dev[0].connect("test-sae", key_mgmt="SAE", psk="12345678",
+ ieee80211w="2", scan_freq="2412")
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("dev_exec_action,name,AP,interface,%s,program,WPA3,KeyRotation,1" % iface)
+
+ ev = dev[0].wait_event(["WPA: Group rekeying completed"], timeout=5)
+ if ev is None:
+ raise Exception("GTK rekeying not seen")
+
+ sigma_dut_cmd_check("ap_reset_default")
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_sae_pk(dev, apdev):
+ """sigma_dut controlled STA using SAE-PK"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ ssid = "SAE-PK test"
+ pw = "hbbi-f4xq-b45g"
+ m = "d2e5fa27d1be8897f987f2d480d2af6b"
+ pk = "MHcCAQEEIAJIGlfnteonDb7rQyP/SGQjwzrZAnfrXIm4280VWajYoAoGCCqGSM49AwEHoUQDQgAEeRkstKQV+FSAMqBayqFknn2nAQsdsh/MhdX6tiHOTAFin/sUMFRMyspPtIu7YvlKdsexhI0jPVhaYZn1jKWhZg=="
+
+ try:
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params['sae_groups'] = '19'
+ params['sae_password'] = ['%s|pk=%s:%s' % (pw, m, pk)]
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_wireless,interface,%s,program,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2,sae_pk,1" % (ifname, ssid, pw))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, ssid),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ dev[0].dump_monitor()
+
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+
+def run_sigma_dut_ap_sae_pk(conffile, dev, ssid, pw, keypair, m, failure,
+ status=None, omit=False, immediate=False, sig=None):
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,%s,MODE,11ng" % ssid)
+ cmd = "ap_set_security,NAME,AP,AKMSuiteType,8,PairwiseCipher,AES-CCMP-128,GroupCipher,AES-CCMP-128,GroupMgntCipher,BIP-CMAC-128,PMF,Required,PSK,%s,sae_pk,1,Transition_Disable,1,Transition_Disable_Index,0,SAE_PK_KeyPair,%s,SAE_PK_Modifier,%s" % (pw, keypair, m)
+ if status is not None:
+ cmd += ",SAE_Commit_StatusCode,%d" % status
+ if omit:
+ cmd += ",SAE_PK_Omit,1"
+ if immediate:
+ cmd += ",SAE_Confirm_Immediate,1"
+ if sig:
+ cmd += ",SAE_PK_KeyPairSigOverride," + sig
+ sigma_dut_cmd_check(cmd)
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+ bssid = sigma_dut_cmd_check("ap_get_mac_address,NAME,AP")
+ bssid = bssid.split(',')[3]
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(conffile, "ab") as f2:
+ f2.write(f.read())
+ f2.write('\n'.encode())
+
+ dev.set("sae_groups", "")
+ dev.connect(ssid, key_mgmt="SAE", sae_password=pw, ieee80211w="2",
+ scan_freq="2412", wait_connect=False)
+
+ ev = dev.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SSID-TEMP-DISABLED"], timeout=15)
+ if ev is None:
+ raise Exception("No connection result reported")
+
+ bss = dev.get_bss(bssid)
+ if 'flags' not in bss:
+ raise Exception("Could not get BSS flags from BSS table")
+ if "[SAE-H2E]" not in bss['flags'] or "[SAE-PK]" not in bss['flags']:
+ raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+ if failure:
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ dev.request("REMOVE_NETWORK all")
+ else:
+ if "CTRL-EVENT-CONNECTED" not in ev:
+ raise Exception("Connection failed")
+ dev.request("REMOVE_NETWORK all")
+ dev.wait_disconnected()
+ dev.dump_monitor()
+
+ sigma_dut_cmd_check("ap_reset_default")
+
+def test_sigma_dut_ap_sae_pk(dev, apdev, params):
+ """sigma_dut controlled AP using SAE-PK"""
+ logdir = params['prefix'] + ".sigma-hostapd"
+ conffile = params['prefix'] + ".sigma-conf"
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ tests = [("SAEPK-4.7.1.1", "ya3o-zvm2-r4so", "saepk1.pem",
+ "faa1ef5094bdb4cb2836332ca2c09839", False),
+ ("SAEPK-4.7.1.2", "xcc2-qwru-yg23", "saepk1.pem",
+ "b1b30107eb74de2f25afd079bb4196c1", False),
+ ("SAEPK-4.7.1.3", "skqz-6scq-zcqv", "saepk1.pem",
+ "4c0ff61465e0f298510254ff54916c71", False),
+ ("SAEPK-4.7.1.4", "r6em-rya4-tqfa", "saepkP384.pem",
+ "fb811655209e9edf347a675ddd3e9c82", False),
+ ("SAEPK-4.7.1.5", "6kjo-umvi-7x3w", "saepkP521.pem",
+ "cccb76bc0f113ab754826ba9538d66f5", False),
+ ("SAEPK-5.7.1.1", "sw4h-re63-wgqg", "saepk1.pem",
+ "0d126f302d85ac809a6a4229dbbe3c75", False),
+ ("SAEPK-5.7.1.2", "wewq-r4kg-4ioz-xb2p", "saepk1.pem",
+ "d6b1d8924b1a462677e67b3bbfe73977", False),
+ ("SAEPK-5.7.1.3", "vb3v-5skk-5eft-v4hu-w2c5", "saepk1.pem",
+ "41f8cfceb96ebc5c8af9677d22749fad", False),
+ ("SAEPK-5.7.1.4", "2qsw-6tgy-xnwa-s7lo-75tq-qggr", "saepk1.pem",
+ "089e8d4a3a79ec637c54dd7bd61972f2", False),
+ ("SAE-PK test", "hbbi-f4xq-b45g", "saepkP256.pem",
+ "d2e5fa27d1be8897f987f2d480d2af6b", False),
+ ("SAE-PK test", "hbbi-f4xq-b457-jje4", "saepkP256.pem",
+ "d2e5fa27d1be8897f987f2d480d2af6b", False),
+ ("SAE-PK test", "hbbi-f4xq-b457-jjew-muei", "saepkP256.pem",
+ "d2e5fa27d1be8897f987f2d480d2af6b", False),
+ ("SAE-PK test", "hbbi-f4xq-b457-jjew-muey-fod3", "saepkP256.pem",
+ "d2e5fa27d1be8897f987f2d480d2af6b", False),
+ ("SAEPK-5.7.1.1", "sw4h-re63-wgqg", "saepk1.pem",
+ "0d126f302d85ac809a6a4229dbbe3c75", False),
+ ("SAEPK-5.7.1.10", "tkor-7nb3-r7tv", "saepkP384.pem",
+ "af1a3df913fc0103f65f105ed1472277", False),
+ ("SAEPK-5.7.1.11", "yjl3-vfvu-w6r3", "saepkP521.pem",
+ "24dadf9d253c4169c9647a21cb54fc57", False),
+ ("SAEPK-5.7.2.1", "rntm-tkrp-xgke", "saepk1.pem",
+ "cd38ccce3baff627d09bee7b9530d6ce", False),
+ ("SAEPK-5.7.2.2", "7lt7-7dqt-6abk", "saepk1.pem",
+ "a22fc8489932597c9e83de62dec02b21", False),
+ ("SAEPK-5.7.2.3", "sw4h-re63-wgqg", "saepk2.pem",
+ "1f4a4c7d290d97e0b6ab0cbbbfa0726d", True),
+ ("SAEPK-5.7.2.4", "rmj3-ya7b-42k4", "saepk1.pem",
+ "5f65e2bc37f8494de7a605ff615c8b6a", False),
+ ("SAEPK-5.7.2.4", "rmj3-ya7b-42k4", "saepk2.pem",
+ "5f65e2bc37f8494de7a605ff615c8b6a", True),
+ ("SAEPK-5.7.3", "4322-ufus-4bhm", "saepk1.pem",
+ "21ede99abc46679646693cafe4677d4e", False)]
+
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ for ssid, pw, keypair, m, failure in tests:
+ run_sigma_dut_ap_sae_pk(conffile, dev[0], ssid, pw, keypair, m,
+ failure)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_ap_sae_pk_misbehavior(dev, apdev, params):
+ """sigma_dut controlled AP using SAE-PK misbehavior"""
+ logdir = params['prefix'] + ".sigma-hostapd"
+ conffile = params['prefix'] + ".sigma-conf"
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ ssid = "SAEPK-4.7.1.1"
+ pw = "rmj3-ya7b-42k4"
+ keypair = "saepk1.pem"
+ m = "faa1ef5094bdb4cb2836332ca2c09839"
+
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ run_sigma_dut_ap_sae_pk(conffile, dev[0], ssid, pw, keypair, m,
+ True, status=126)
+ run_sigma_dut_ap_sae_pk(conffile, dev[0], ssid, pw, keypair, m,
+ True, omit=True)
+ run_sigma_dut_ap_sae_pk(conffile, dev[0], ssid, pw, keypair, m,
+ True, status=126, omit=True, immediate=True)
+ run_sigma_dut_ap_sae_pk(conffile, dev[0], ssid, pw, keypair, m,
+ True, sig="saepk2.pem")
+ finally:
+ stop_sigma_dut(sigma)
+
+def run_sigma_dut_ap_sae_pk_mixed(conffile, dev, ssid, pw, keypair, m, failure):
+ sigma_dut_cmd_check("ap_reset_default")
+ sigma_dut_cmd_check("ap_set_wireless,NAME,AP,CHANNEL,1,SSID,%s,MODE,11ng" % ssid)
+ cmd = "ap_set_security,NAME,AP,AKMSuiteType,2;8,PairwiseCipher,AES-CCMP-128,GroupCipher,AES-CCMP-128,GroupMgntCipher,BIP-CMAC-128,PMF,Required,PSK,%s,sae_pk,0,Transition_Disable,0" % (pw)
+ sigma_dut_cmd_check(cmd)
+ sigma_dut_cmd_check("ap_config_commit,NAME,AP")
+ bssid = sigma_dut_cmd_check("ap_get_mac_address,NAME,AP")
+ bssid = bssid.split(',')[3]
+
+ with open("/tmp/sigma_dut-ap.conf", "rb") as f:
+ with open(conffile, "ab") as f2:
+ f2.write(f.read())
+ f2.write('\n'.encode())
+
+ sigma_dut_cmd_check("ap_set_rfeature,NAME,AP,type,WPA3,Transition_Disable,1,Transition_Disable_Index,0")
+
+ dev[0].set("sae_groups", "")
+ dev[0].connect(ssid, key_mgmt="SAE", sae_password=pw, ieee80211w="2",
+ scan_freq="2412")
+ dev[1].connect(ssid, key_mgmt="WPA-PSK", psk=pw, ieee80211w="2",
+ scan_freq="2412")
+
+ sigma_dut_cmd_check("ap_reset_default")
+
+def test_sigma_dut_ap_sae_pk_mixed(dev, apdev, params):
+ """sigma_dut controlled AP using SAE-PK(disabled) and PSK"""
+ logdir = params['prefix'] + ".sigma-hostapd"
+ conffile = params['prefix'] + ".sigma-conf"
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ ssid = "SAEPK-5.7.3"
+ pw = "4322-ufus-4bhm"
+ keypair = "saepk1.pem"
+ m = "21ede99abc46679646693cafe4677d4e"
+
+ with HWSimRadio() as (radio, iface):
+ sigma = start_sigma_dut(iface, hostapd_logdir=logdir)
+ try:
+ run_sigma_dut_ap_sae_pk_mixed(conffile, dev, ssid, pw, keypair,
+ m, False)
+ finally:
+ stop_sigma_dut(sigma)
+
+def test_sigma_dut_client_privacy(dev, apdev, params):
+ """sigma_dut client privacy"""
+ logdir = params['logdir']
+
+ ssid = "test"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ ifname = dev[0].ifname
+ addr = dev[0].own_addr()
+ sigma = start_sigma_dut(ifname)
+ try:
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_wireless,interface,%s,program,WPA3,ClientPrivacy,1" % ifname)
+ cmd = "sta_scan,Interface,%s,ChnlFreq,2412,WaitCompletion,1" % dev[0].ifname
+ sigma_dut_cmd_check(cmd, timeout=10)
+ time.sleep(2)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_psk,interface,%s,ssid,%s,passphrase,%s,encpType,aes-ccmp,keymgmttype,wpa2" % (ifname, ssid, "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, ssid),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd_check("sta_get_ip_config,interface," + ifname)
+ sigma_dut_cmd_check("sta_disconnect,interface," + ifname)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
+ dev[0].set("mac_addr", "0", allow_fail=True)
+ dev[0].set("rand_addr_lifetime", "60", allow_fail=True)
+ dev[0].request("MAC_RAND_SCAN enable=0 all")
+ dev[0].set("preassoc_mac_addr", "0", allow_fail=True)
+ dev[0].set("gas_rand_mac_addr", "0", allow_fail=True)
+ dev[0].set("gas_rand_addr_lifetime", "60", allow_fail=True)
+
+ out = run_tshark(os.path.join(logdir, "hwsim0.pcapng"),
+ "wlan.addr == " + addr,
+ display=["wlan.ta"])
+ res = out.splitlines()
+ if len(res) > 0:
+ raise Exception("Permanent address used unexpectedly")
+
+def test_sigma_dut_wpa3_inject_frame(dev, apdev):
+ """sigma_dut and WPA3 frame inject"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+ ifname = dev[0].ifname
+ sigma = start_sigma_dut(ifname)
+
+ try:
+ ssid = "test-sae"
+ params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+ params['wpa_key_mgmt'] = 'SAE'
+ params["ieee80211w"] = "2"
+ params["ocv"] = "1"
+ params['sae_groups'] = '19 20 21'
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ sigma_dut_cmd_check("sta_reset_default,interface,%s,prog,WPA3" % ifname)
+ sigma_dut_cmd_check("sta_set_ip_config,interface,%s,dhcp,0,ip,127.0.0.11,mask,255.255.255.0" % ifname)
+ sigma_dut_cmd_check("sta_set_wireless,interface,%s,program,WPA3,ocvc,1" % ifname)
+ sigma_dut_cmd_check("sta_set_security,interface,%s,ssid,%s,passphrase,%s,type,SAE,encpType,aes-ccmp,keymgmttype,wpa2" % (ifname, "test-sae", "12345678"))
+ sigma_dut_cmd_check("sta_associate,interface,%s,ssid,%s,channel,1" % (ifname, "test-sae"),
+ timeout=10)
+ sigma_dut_wait_connected(ifname)
+ sigma_dut_cmd("dev_send_frame,interface,%s,program,WPA3,framename,SAQueryReq,OCIChannel,2" % ifname)
+ sigma_dut_cmd("dev_send_frame,interface,%s,program,WPA3,framename,SAQueryReq,OCIChannel,1" % ifname)
+ sigma_dut_cmd("dev_send_frame,interface,%s,program,WPA3,framename,ReassocReq" % ifname)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ sigma_dut_cmd_check("sta_reset_default,interface," + ifname)
+ finally:
+ stop_sigma_dut(sigma)
diff --git a/contrib/wpa/tests/hwsim/test_ssid.py b/contrib/wpa/tests/hwsim/test_ssid.py
new file mode 100644
index 000000000000..faee75d5ff77
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_ssid.py
@@ -0,0 +1,127 @@
+# -*- coding: utf-8 -*-
+# SSID contents and encoding tests
+# Copyright (c) 2013-2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+
+import hostapd
+
+@remote_compatible
+def test_ssid_hex_encoded(dev, apdev):
+ """SSID configuration using hex encoded version"""
+ hostapd.add_ap(apdev[0], {"ssid2": '68656c6c6f'})
+ dev[0].connect("hello", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect(ssid2="68656c6c6f", key_mgmt="NONE", scan_freq="2412")
+
+def test_ssid_printf_encoded(dev, apdev):
+ """SSID configuration using printf encoded version"""
+ hostapd.add_ap(apdev[0], {"ssid2": 'P"\\0hello\\nthere"'})
+ dev[0].connect(ssid2="0068656c6c6f0a7468657265", key_mgmt="NONE",
+ scan_freq="2412")
+ dev[1].connect(ssid2='P"\\x00hello\\nthere"', key_mgmt="NONE",
+ scan_freq="2412")
+ ssid = dev[0].get_status_field("ssid")
+ bss = dev[1].get_bss(apdev[0]['bssid'])
+ if ssid != bss['ssid']:
+ raise Exception("Unexpected difference in SSID")
+ dev[2].connect(ssid2='P"' + ssid + '"', key_mgmt="NONE", scan_freq="2412")
+
+@remote_compatible
+def test_ssid_1_octet(dev, apdev):
+ """SSID with one octet"""
+ hostapd.add_ap(apdev[0], {"ssid": '1'})
+ dev[0].connect("1", key_mgmt="NONE", scan_freq="2412")
+
+@remote_compatible
+def test_ssid_32_octets(dev, apdev):
+ """SSID with 32 octets"""
+ hostapd.add_ap(apdev[0],
+ {"ssid": '1234567890abcdef1234567890ABCDEF'})
+ dev[0].connect("1234567890abcdef1234567890ABCDEF", key_mgmt="NONE",
+ scan_freq="2412")
+
+def test_ssid_32_octets_nul_term(dev, apdev):
+ """SSID with 32 octets with nul at the end"""
+ ssid = 'P"1234567890abcdef1234567890ABCDE\\x00"'
+ hostapd.add_ap(apdev[0],
+ {"ssid2": ssid})
+ dev[0].connect(ssid2=ssid, key_mgmt="NONE", scan_freq="2412")
+
+@remote_compatible
+def test_ssid_utf8(dev, apdev):
+ """SSID with UTF8 encoding"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'testi-åäöÅÄÖ-testi',
+ "utf8_ssid": "1"})
+ dev[0].connect("testi-åäöÅÄÖ-testi", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect(ssid2="74657374692dc3a5c3a4c3b6c385c384c3962d7465737469",
+ key_mgmt="NONE", scan_freq="2412")
+ # verify ctrl_iface for coverage
+ addrs = [dev[0].p2p_interface_addr(), dev[1].p2p_interface_addr()]
+ sta = hapd.get_sta(None)
+ if sta['addr'] not in addrs:
+ raise Exception("Unexpected STA address")
+ sta2 = hapd.get_sta(sta['addr'], next=True)
+ if sta2['addr'] not in addrs:
+ raise Exception("Unexpected STA2 address")
+ sta3 = hapd.get_sta(sta2['addr'], next=True)
+ if len(sta3) != 0:
+ raise Exception("Unexpected STA iteration result (did not stop)")
+
+ if "[UTF-8]" not in dev[0].get_bss(hapd.own_addr())['flags']:
+ raise Exception("[UTF-8] flag not included in BSS")
+ if "[UTF-8]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("[UTF-8] flag not included in SCAN_RESULTS")
+
+def clear_scan_cache2(hapd, dev):
+ # clear BSS table to avoid issues in following test cases
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ hapd.disable()
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+@remote_compatible
+def test_ssid_hidden(dev, apdev):
+ """Hidden SSID"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'secret',
+ "ignore_broadcast_ssid": "1"})
+ dev[1].connect("secret", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ dev[0].connect("secret", key_mgmt="NONE", scan_freq="2412", scan_ssid="1")
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+ clear_scan_cache2(hapd, dev)
+
+@remote_compatible
+def test_ssid_hidden2(dev, apdev):
+ """Hidden SSID using zero octets as payload"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": 'secret2',
+ "ignore_broadcast_ssid": "2"})
+ dev[1].connect("secret2", key_mgmt="NONE", scan_freq="2412",
+ wait_connect=False)
+ dev[0].connect("secret2", key_mgmt="NONE", scan_freq="2412", scan_ssid="1")
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+ clear_scan_cache2(hapd, dev)
+
+@remote_compatible
+def test_ssid_hidden_wpa2(dev, apdev):
+ """Hidden SSID with WPA2-PSK"""
+ params = hostapd.wpa2_params(ssid="secret", passphrase="12345678")
+ params["ignore_broadcast_ssid"] = "1"
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[1].connect("secret", psk="12345678", scan_freq="2412",
+ wait_connect=False)
+ dev[0].connect("secret", psk="12345678", scan_freq="2412", scan_ssid="1")
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+ clear_scan_cache2(hapd, dev)
diff --git a/contrib/wpa/tests/hwsim/test_sta_dynamic.py b/contrib/wpa/tests/hwsim/test_sta_dynamic.py
new file mode 100644
index 000000000000..357bc9583dab
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_sta_dynamic.py
@@ -0,0 +1,329 @@
+# Dynamic wpa_supplicant interface
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+import time
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+
+def test_sta_dynamic(dev, apdev):
+ """Dynamically added wpa_supplicant interface"""
+ params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+ hostapd.add_ap(apdev[0], params)
+
+ logger.info("Create a dynamic wpa_supplicant interface and connect")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+
+ wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
+
+def test_sta_ap_scan_0(dev, apdev):
+ """Dynamically added wpa_supplicant interface with AP_SCAN 0 connection"""
+ hostapd.add_ap(apdev[0], {"ssid": "test"})
+ bssid = apdev[0]['bssid']
+
+ logger.info("Create a dynamic wpa_supplicant interface and connect")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+
+ if "OK" not in wpas.request("AP_SCAN 0"):
+ raise Exception("Failed to set AP_SCAN 2")
+
+ id = wpas.connect("", key_mgmt="NONE", bssid=bssid,
+ only_add_network=True)
+ wpas.request("ENABLE_NETWORK " + str(id) + " no-connect")
+ wpas.request("SCAN")
+ time.sleep(0.5)
+ subprocess.call(['iw', wpas.ifname, 'connect', 'test', '2412'])
+ wpas.wait_connected(timeout=10)
+ wpas.request("SCAN")
+ wpas.wait_connected(timeout=5)
+
+def test_sta_ap_scan_2(dev, apdev):
+ """Dynamically added wpa_supplicant interface with AP_SCAN 2 connection"""
+ hostapd.add_ap(apdev[0], {"ssid": "test"})
+ bssid = apdev[0]['bssid']
+
+ logger.info("Create a dynamic wpa_supplicant interface and connect")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+
+ if "FAIL" not in wpas.request("AP_SCAN -1"):
+ raise Exception("Invalid AP_SCAN -1 accepted")
+ if "FAIL" not in wpas.request("AP_SCAN 3"):
+ raise Exception("Invalid AP_SCAN 3 accepted")
+ if "OK" not in wpas.request("AP_SCAN 2"):
+ raise Exception("Failed to set AP_SCAN 2")
+
+ id = wpas.connect("", key_mgmt="NONE", bssid=bssid,
+ only_add_network=True)
+ wpas.request("ENABLE_NETWORK " + str(id) + " no-connect")
+ subprocess.call(['iw', wpas.ifname, 'scan', 'trigger', 'freq', '2412'])
+ time.sleep(1)
+ subprocess.call(['iw', wpas.ifname, 'connect', 'test', '2412'])
+ wpas.wait_connected(timeout=10)
+
+ wpas.request("SET disallow_aps bssid " + bssid)
+ wpas.wait_disconnected(timeout=10)
+
+ subprocess.call(['iw', wpas.ifname, 'connect', 'test', '2412'])
+ ev = wpas.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection reported")
+
+def test_sta_ap_scan_2b(dev, apdev):
+ """Dynamically added wpa_supplicant interface with AP_SCAN 2 operation"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test"})
+ bssid = apdev[0]['bssid']
+
+ logger.info("Create a dynamic wpa_supplicant interface and connect")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+
+ if "OK" not in wpas.request("AP_SCAN 2"):
+ raise Exception("Failed to set AP_SCAN 2")
+
+ id = wpas.connect("test", key_mgmt="NONE", bssid=bssid)
+ wpas.request("DISCONNECT")
+ wpas.set_network(id, "disabled", "1")
+ id2 = wpas.add_network()
+ wpas.set_network_quoted(id2, "ssid", "test2")
+ wpas.set_network(id2, "key_mgmt", "NONE")
+ wpas.set_network(id2, "disabled", "0")
+ wpas.request("REASSOCIATE")
+ ev = wpas.wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=15)
+ if ev is None:
+ raise Exception("Association rejection not reported")
+ hapd.disable()
+ wpas.set_network(id, "disabled", "0")
+ wpas.set_network(id2, "disabled", "1")
+ for i in range(3):
+ ev = wpas.wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=15)
+ if ev is None:
+ raise Exception("Association rejection not reported")
+ wpas.request("DISCONNECT")
+
+def test_sta_dynamic_down_up(dev, apdev):
+ """Dynamically added wpa_supplicant interface down/up"""
+ params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ logger.info("Create a dynamic wpa_supplicant interface and connect")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(wpas, hapd)
+ subprocess.call(['ifconfig', wpas.ifname, 'down'])
+ wpas.wait_disconnected(timeout=10)
+ if wpas.get_status_field("wpa_state") != "INTERFACE_DISABLED":
+ raise Exception("Unexpected wpa_state")
+ subprocess.call(['ifconfig', wpas.ifname, 'up'])
+ wpas.wait_connected(timeout=15, error="Reconnection not reported")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(wpas, hapd)
+
+def test_sta_dynamic_ext_mac_addr_change(dev, apdev):
+ """Dynamically added wpa_supplicant interface with external MAC address change"""
+ params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ logger.info("Create a dynamic wpa_supplicant interface and connect")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(wpas, hapd)
+ subprocess.call(['ifconfig', wpas.ifname, 'down'])
+ wpas.wait_disconnected(timeout=10)
+ if wpas.get_status_field("wpa_state") != "INTERFACE_DISABLED":
+ raise Exception("Unexpected wpa_state")
+ prev_addr = wpas.p2p_interface_addr()
+ new_addr = '02:11:22:33:44:55'
+ try:
+ subprocess.call(['ip', 'link', 'set', 'dev', wpas.ifname,
+ 'address', new_addr])
+ subprocess.call(['ifconfig', wpas.ifname, 'up'])
+ wpas.wait_connected(timeout=15, error="Reconnection not reported")
+ if wpas.get_driver_status_field('addr') != new_addr:
+ raise Exception("Address change not reported")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(wpas, hapd)
+ sta = hapd.get_sta(new_addr)
+ if sta['addr'] != new_addr:
+ raise Exception("STA association with new address not found")
+ finally:
+ subprocess.call(['ifconfig', wpas.ifname, 'down'])
+ subprocess.call(['ip', 'link', 'set', 'dev', wpas.ifname,
+ 'address', prev_addr])
+ subprocess.call(['ifconfig', wpas.ifname, 'up'])
+
+def test_sta_dynamic_ext_mac_addr_change_for_connection(dev, apdev):
+ """Dynamically added wpa_supplicant interface with external MAC address change for connection"""
+ params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['ifname']
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.scan_for_bss(bssid, freq=2412)
+ subprocess.call(['ifconfig', wpas.ifname, 'down'])
+ if wpas.get_status_field("wpa_state") != "INTERFACE_DISABLED":
+ raise Exception("Unexpected wpa_state")
+ prev_addr = wpas.own_addr()
+ new_addr = '02:11:22:33:44:55'
+ try:
+ subprocess.call(['ip', 'link', 'set', 'dev', wpas.ifname,
+ 'address', new_addr])
+ subprocess.call(['ifconfig', wpas.ifname, 'up'])
+ wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412",
+ wait_connect=False)
+ ev = wpas.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if "CTRL-EVENT-SCAN-RESULTS" in ev:
+ raise Exception("Unexpected scan after MAC address change")
+ hapd.wait_sta()
+ hwsim_utils.test_connectivity(wpas, hapd)
+ sta = hapd.get_sta(new_addr)
+ if sta['addr'] != new_addr:
+ raise Exception("STA association with new address not found")
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ wpas.dump_monitor()
+ subprocess.call(['ifconfig', wpas.ifname, 'down'])
+ time.sleep(0.1)
+ res = wpas.get_bss(bssid)
+ if res is None:
+ raise Exception("BSS entry not maintained after interface disabling")
+ ev = wpas.wait_event(["CTRL-EVENT-BSS-REMOVED"], timeout=5.5)
+ if ev is None:
+ raise Exception("BSS entry not removed after interface has been disabled for a while")
+ res2 = wpas.get_bss(bssid)
+ if res2 is not None:
+ raise Exception("Unexpected BSS entry found on a disabled interface")
+ finally:
+ subprocess.call(['ifconfig', wpas.ifname, 'down'])
+ subprocess.call(['ip', 'link', 'set', 'dev', wpas.ifname,
+ 'address', prev_addr])
+ subprocess.call(['ifconfig', wpas.ifname, 'up'])
+
+def test_sta_dynamic_random_mac_addr(dev, apdev):
+ """Dynamically added wpa_supplicant interface and random MAC address"""
+ params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ addr0 = wpas.get_driver_status_field("addr")
+ wpas.request("SET preassoc_mac_addr 1")
+ wpas.request("SET rand_addr_lifetime 0")
+
+ id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="1",
+ scan_freq="2412")
+ addr1 = wpas.get_driver_status_field("addr")
+
+ if addr0 == addr1:
+ raise Exception("Random MAC address not used")
+
+ sta = hapd.get_sta(addr0)
+ if sta['addr'] != "FAIL":
+ raise Exception("Unexpected STA association with permanent address")
+ sta = hapd.get_sta(addr1)
+ if sta['addr'] != addr1:
+ raise Exception("STA association with random address not found")
+
+ wpas.request("DISCONNECT")
+ wpas.connect_network(id)
+ addr2 = wpas.get_driver_status_field("addr")
+ if addr1 != addr2:
+ raise Exception("Random MAC address changed unexpectedly")
+
+ wpas.remove_network(id)
+ id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="1",
+ scan_freq="2412")
+ addr2 = wpas.get_driver_status_field("addr")
+ if addr1 == addr2:
+ raise Exception("Random MAC address did not change")
+
+def test_sta_dynamic_random_mac_addr_keep_oui(dev, apdev):
+ """Dynamically added wpa_supplicant interface and random MAC address (keep OUI)"""
+ params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ addr0 = wpas.get_driver_status_field("addr")
+ wpas.request("SET preassoc_mac_addr 2")
+ wpas.request("SET rand_addr_lifetime 0")
+
+ id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="2",
+ scan_freq="2412")
+ addr1 = wpas.get_driver_status_field("addr")
+
+ if addr0 == addr1:
+ raise Exception("Random MAC address not used")
+ if addr1[3:8] != addr0[3:8]:
+ raise Exception("OUI was not kept")
+
+ sta = hapd.get_sta(addr0)
+ if sta['addr'] != "FAIL":
+ raise Exception("Unexpected STA association with permanent address")
+ sta = hapd.get_sta(addr1)
+ if sta['addr'] != addr1:
+ raise Exception("STA association with random address not found")
+
+ wpas.request("DISCONNECT")
+ wpas.connect_network(id)
+ addr2 = wpas.get_driver_status_field("addr")
+ if addr1 != addr2:
+ raise Exception("Random MAC address changed unexpectedly")
+
+ wpas.remove_network(id)
+ id = wpas.connect("sta-dynamic", psk="12345678", mac_addr="2",
+ scan_freq="2412")
+ addr2 = wpas.get_driver_status_field("addr")
+ if addr1 == addr2:
+ raise Exception("Random MAC address did not change")
+ if addr2[3:8] != addr0[3:8]:
+ raise Exception("OUI was not kept")
+
+def test_sta_dynamic_random_mac_addr_scan(dev, apdev):
+ """Dynamically added wpa_supplicant interface and random MAC address for scan"""
+ params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ addr0 = wpas.get_driver_status_field("addr")
+ wpas.request("SET preassoc_mac_addr 1")
+ wpas.request("SET rand_addr_lifetime 0")
+
+ id = wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
+ addr1 = wpas.get_driver_status_field("addr")
+
+ if addr0 != addr1:
+ raise Exception("Random MAC address used unexpectedly")
+
+def test_sta_dynamic_random_mac_addr_scan_keep_oui(dev, apdev):
+ """Dynamically added wpa_supplicant interface and random MAC address for scan (keep OUI)"""
+ params = hostapd.wpa2_params(ssid="sta-dynamic", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ addr0 = wpas.get_driver_status_field("addr")
+ wpas.request("SET preassoc_mac_addr 2")
+ wpas.request("SET rand_addr_lifetime 0")
+
+ id = wpas.connect("sta-dynamic", psk="12345678", scan_freq="2412")
+ addr1 = wpas.get_driver_status_field("addr")
+
+ if addr0 != addr1:
+ raise Exception("Random MAC address used unexpectedly")
diff --git a/contrib/wpa/tests/hwsim/test_suite_b.py b/contrib/wpa/tests/hwsim/test_suite_b.py
new file mode 100644
index 000000000000..7065b18bd65f
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_suite_b.py
@@ -0,0 +1,739 @@
+# Suite B tests
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import logging
+logger = logging.getLogger()
+
+import hostapd
+from utils import HwsimSkip, fail_test
+
+def check_suite_b_capa(dev):
+ if "GCMP" not in dev[0].get_capability("pairwise"):
+ raise HwsimSkip("GCMP not supported")
+ if "BIP-GMAC-128" not in dev[0].get_capability("group_mgmt"):
+ raise HwsimSkip("BIP-GMAC-128 not supported")
+ if "WPA-EAP-SUITE-B" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("WPA-EAP-SUITE-B not supported")
+ check_suite_b_tls_lib(dev, level128=True)
+
+def check_suite_b_tls_lib(dev, dhe=False, level128=False):
+ tls = dev[0].request("GET tls_library")
+ if tls.startswith("GnuTLS"):
+ return
+ if not tls.startswith("OpenSSL"):
+ raise HwsimSkip("TLS library not supported for Suite B: " + tls)
+ supported = False
+ for ver in ['1.0.2', '1.1.0', '1.1.1']:
+ if "build=OpenSSL " + ver in tls and "run=OpenSSL " + ver in tls:
+ supported = True
+ break
+ if not dhe and not level128 and "build=OpenSSL " + ver in tls and "run=BoringSSL" in tls:
+ supported = True
+ break
+ if not supported:
+ raise HwsimSkip("OpenSSL version not supported for Suite B: " + tls)
+
+def suite_b_ap_params():
+ params = {"ssid": "test-suite-b",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP-SUITE-B",
+ "rsn_pairwise": "GCMP",
+ "group_mgmt_cipher": "BIP-GMAC-128",
+ "ieee80211w": "2",
+ "ieee8021x": "1",
+ "openssl_ciphers": "SUITEB128",
+ #"dh_file": "auth_serv/dh.conf",
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ec-ca.pem",
+ "server_cert": "auth_serv/ec-server.pem",
+ "private_key": "auth_serv/ec-server.key"}
+ return params
+
+def test_suite_b(dev, apdev):
+ """WPA2/GCMP connection at Suite B 128-bit level"""
+ check_suite_b_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B", ieee80211w="2",
+ openssl_ciphers="SUITEB128",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec-ca.pem",
+ client_cert="auth_serv/ec-user.pem",
+ private_key="auth_serv/ec-user.key",
+ pairwise="GCMP", group="GCMP", scan_freq="2412")
+ hapd.wait_sta()
+ tls_cipher = dev[0].get_status_field("EAP TLS cipher")
+ if tls_cipher != "ECDHE-ECDSA-AES128-GCM-SHA256" and \
+ tls_cipher != "ECDHE-ECDSA-AES-128-GCM-AEAD":
+ raise Exception("Unexpected TLS cipher: " + tls_cipher)
+
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if 'flags' not in bss:
+ raise Exception("Could not get BSS flags from BSS table")
+ if "[WPA2-EAP-SUITE-B-GCMP]" not in bss['flags']:
+ raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=20)
+ dev[0].dump_monitor()
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+
+ conf = hapd.get_config()
+ if conf['key_mgmt'] != 'WPA-EAP-SUITE-B':
+ raise Exception("Unexpected config key_mgmt: " + conf['key_mgmt'])
+
+ hapd.wait_sta()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=20)
+ dev[0].dump_monitor()
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out (2)")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange (2)")
+
+def suite_b_as_params():
+ params = {}
+ params['ssid'] = 'as'
+ params['beacon_int'] = '2000'
+ params['radius_server_clients'] = 'auth_serv/radius_clients.conf'
+ params['radius_server_auth_port'] = '18129'
+ params['eap_server'] = '1'
+ params['eap_user_file'] = 'auth_serv/eap_user.conf'
+ params['ca_cert'] = 'auth_serv/ec-ca.pem'
+ params['server_cert'] = 'auth_serv/ec-server.pem'
+ params['private_key'] = 'auth_serv/ec-server.key'
+ params['openssl_ciphers'] = 'SUITEB128'
+ return params
+
+def test_suite_b_radius(dev, apdev):
+ """WPA2/GCMP (RADIUS) connection at Suite B 128-bit level"""
+ check_suite_b_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_as_params()
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-suite-b",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP-SUITE-B",
+ "rsn_pairwise": "GCMP",
+ "group_mgmt_cipher": "BIP-GMAC-128",
+ "ieee80211w": "2",
+ "ieee8021x": "1",
+ 'auth_server_addr': "127.0.0.1",
+ 'auth_server_port': "18129",
+ 'auth_server_shared_secret': "radius",
+ 'nas_identifier': "nas.w1.fi"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B", ieee80211w="2",
+ openssl_ciphers="SUITEB128",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec-ca.pem",
+ client_cert="auth_serv/ec-user.pem",
+ private_key="auth_serv/ec-user.key",
+ pairwise="GCMP", group="GCMP", scan_freq="2412")
+
+def check_suite_b_192_capa(dev, dhe=False):
+ if "GCMP-256" not in dev[0].get_capability("pairwise"):
+ raise HwsimSkip("GCMP-256 not supported")
+ if "BIP-GMAC-256" not in dev[0].get_capability("group_mgmt"):
+ raise HwsimSkip("BIP-GMAC-256 not supported")
+ if "WPA-EAP-SUITE-B-192" not in dev[0].get_capability("key_mgmt"):
+ raise HwsimSkip("WPA-EAP-SUITE-B-192 not supported")
+ check_suite_b_tls_lib(dev, dhe=dhe)
+
+def suite_b_192_ap_params():
+ params = {"ssid": "test-suite-b",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP-SUITE-B-192",
+ "rsn_pairwise": "GCMP-256",
+ "group_mgmt_cipher": "BIP-GMAC-256",
+ "ieee80211w": "2",
+ "ieee8021x": "1",
+ "openssl_ciphers": "SUITEB192",
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/ec2-ca.pem",
+ "server_cert": "auth_serv/ec2-server.pem",
+ "private_key": "auth_serv/ec2-server.key"}
+ return params
+
+def test_suite_b_192(dev, apdev):
+ """WPA2/GCMP-256 connection at Suite B 192-bit level"""
+ check_suite_b_192_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_192_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user.pem",
+ private_key="auth_serv/ec2-user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412")
+ tls_cipher = dev[0].get_status_field("EAP TLS cipher")
+ if tls_cipher != "ECDHE-ECDSA-AES256-GCM-SHA384" and \
+ tls_cipher != "ECDHE-ECDSA-AES-256-GCM-AEAD":
+ raise Exception("Unexpected TLS cipher: " + tls_cipher)
+ cipher = dev[0].get_status_field("mgmt_group_cipher")
+ if cipher != "BIP-GMAC-256":
+ raise Exception("Unexpected mgmt_group_cipher: " + cipher)
+
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if 'flags' not in bss:
+ raise Exception("Could not get BSS flags from BSS table")
+ if "[WPA2-EAP-SUITE-B-192-GCMP-256]" not in bss['flags']:
+ raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+ hapd.wait_sta()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=20)
+ dev[0].dump_monitor()
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+
+ conf = hapd.get_config()
+ if conf['key_mgmt'] != 'WPA-EAP-SUITE-B-192':
+ raise Exception("Unexpected config key_mgmt: " + conf['key_mgmt'])
+
+ hapd.wait_sta()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=20)
+ dev[0].dump_monitor()
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out (2)")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange (2)")
+
+def test_suite_b_192_radius(dev, apdev):
+ """WPA2/GCMP-256 (RADIUS) connection at Suite B 192-bit level"""
+ check_suite_b_192_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_as_params()
+ params['ca_cert'] = 'auth_serv/ec2-ca.pem'
+ params['server_cert'] = 'auth_serv/ec2-server.pem'
+ params['private_key'] = 'auth_serv/ec2-server.key'
+ params['openssl_ciphers'] = 'SUITEB192'
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-suite-b",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP-SUITE-B-192",
+ "rsn_pairwise": "GCMP-256",
+ "group_mgmt_cipher": "BIP-GMAC-256",
+ "ieee80211w": "2",
+ "ieee8021x": "1",
+ 'auth_server_addr': "127.0.0.1",
+ 'auth_server_port': "18129",
+ 'auth_server_shared_secret': "radius",
+ 'nas_identifier': "nas.w1.fi"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user.pem",
+ private_key="auth_serv/ec2-user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412")
+
+def test_suite_b_192_radius_and_p256_cert(dev, apdev):
+ """Suite B 192-bit level and p256 client cert"""
+ check_suite_b_192_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_as_params()
+ params['ca_cert'] = 'auth_serv/ec2-ca.pem'
+ params['server_cert'] = 'auth_serv/ec2-server.pem'
+ params['private_key'] = 'auth_serv/ec2-server.key'
+ params['openssl_ciphers'] = 'SUITEB192'
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-suite-b",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP-SUITE-B-192",
+ "rsn_pairwise": "GCMP-256",
+ "group_mgmt_cipher": "BIP-GMAC-256",
+ "ieee80211w": "2",
+ "ieee8021x": "1",
+ 'auth_server_addr': "127.0.0.1",
+ 'auth_server_port': "18129",
+ 'auth_server_shared_secret': "radius",
+ 'nas_identifier': "nas.w1.fi"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ #openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user-p256.pem",
+ private_key="auth_serv/ec2-user-p256.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP-Failure not reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnection not reported")
+ if "reason=23" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+def test_suite_b_pmkid_failure(dev, apdev):
+ """WPA2/GCMP connection at Suite B 128-bit level and PMKID derivation failure"""
+ check_suite_b_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with fail_test(dev[0], 1, "rsn_pmkid_suite_b"):
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB128",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec-ca.pem",
+ client_cert="auth_serv/ec-user.pem",
+ private_key="auth_serv/ec-user.key",
+ pairwise="GCMP", group="GCMP", scan_freq="2412")
+
+def test_suite_b_192_pmkid_failure(dev, apdev):
+ """WPA2/GCMP-256 connection at Suite B 192-bit level and PMKID derivation failure"""
+ check_suite_b_192_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_192_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with fail_test(dev[0], 1, "rsn_pmkid_suite_b"):
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user.pem",
+ private_key="auth_serv/ec2-user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412")
+
+def test_suite_b_mic_failure(dev, apdev):
+ """WPA2/GCMP connection at Suite B 128-bit level and MIC derivation failure"""
+ check_suite_b_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with fail_test(dev[0], 1, "wpa_eapol_key_mic"):
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB128",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec-ca.pem",
+ client_cert="auth_serv/ec-user.pem",
+ private_key="auth_serv/ec-user.key",
+ pairwise="GCMP", group="GCMP", scan_freq="2412",
+ wait_connect=False)
+ dev[0].wait_disconnected()
+
+def test_suite_b_192_mic_failure(dev, apdev):
+ """WPA2/GCMP connection at Suite B 192-bit level and MIC derivation failure"""
+ check_suite_b_192_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_192_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ with fail_test(dev[0], 1, "wpa_eapol_key_mic"):
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user.pem",
+ private_key="auth_serv/ec2-user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412",
+ wait_connect=False)
+ dev[0].wait_disconnected()
+
+def suite_b_192_rsa_ap_params():
+ params = {"ssid": "test-suite-b",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP-SUITE-B-192",
+ "rsn_pairwise": "GCMP-256",
+ "group_mgmt_cipher": "BIP-GMAC-256",
+ "ieee80211w": "2",
+ "ieee8021x": "1",
+ "tls_flags": "[SUITEB]",
+ "dh_file": "auth_serv/dh_param_3072.pem",
+ "eap_server": "1",
+ "eap_user_file": "auth_serv/eap_user.conf",
+ "ca_cert": "auth_serv/rsa3072-ca.pem",
+ "server_cert": "auth_serv/rsa3072-server.pem",
+ "private_key": "auth_serv/rsa3072-server.key"}
+ return params
+
+def test_suite_b_192_rsa(dev, apdev):
+ """WPA2/GCMP-256 connection at Suite B 192-bit level and RSA"""
+ run_suite_b_192_rsa(dev, apdev)
+
+def test_suite_b_192_rsa_ecdhe(dev, apdev):
+ """WPA2/GCMP-256 connection at Suite B 192-bit level and RSA (ECDHE)"""
+ run_suite_b_192_rsa(dev, apdev, no_dhe=True)
+
+def test_suite_b_192_rsa_dhe(dev, apdev):
+ """WPA2/GCMP-256 connection at Suite B 192-bit level and RSA (DHE)"""
+ run_suite_b_192_rsa(dev, apdev, no_ecdh=True)
+
+def run_suite_b_192_rsa(dev, apdev, no_ecdh=False, no_dhe=False):
+ check_suite_b_192_capa(dev, dhe=no_ecdh)
+ dev[0].flush_scan_cache()
+ params = suite_b_192_rsa_ap_params()
+ if no_ecdh:
+ params["tls_flags"] = "[SUITEB-NO-ECDH]"
+ if no_dhe:
+ del params["dh_file"]
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ phase1="tls_suiteb=1",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/rsa3072-ca.pem",
+ client_cert="auth_serv/rsa3072-user.pem",
+ private_key="auth_serv/rsa3072-user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412")
+ tls_cipher = dev[0].get_status_field("EAP TLS cipher")
+ if tls_cipher != "ECDHE-RSA-AES256-GCM-SHA384" and \
+ tls_cipher != "DHE-RSA-AES256-GCM-SHA384" and \
+ tls_cipher != "ECDHE-RSA-AES-256-GCM-AEAD" and \
+ tls_cipher != "DHE-RSA-AES-256-GCM-AEAD":
+ raise Exception("Unexpected TLS cipher: " + tls_cipher)
+ cipher = dev[0].get_status_field("mgmt_group_cipher")
+ if cipher != "BIP-GMAC-256":
+ raise Exception("Unexpected mgmt_group_cipher: " + cipher)
+
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if 'flags' not in bss:
+ raise Exception("Could not get BSS flags from BSS table")
+ if "[WPA2-EAP-SUITE-B-192-GCMP-256]" not in bss['flags']:
+ raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+ hapd.wait_sta()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected(timeout=20)
+ dev[0].dump_monitor()
+ dev[0].request("RECONNECT")
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+
+ conf = hapd.get_config()
+ if conf['key_mgmt'] != 'WPA-EAP-SUITE-B-192':
+ raise Exception("Unexpected config key_mgmt: " + conf['key_mgmt'])
+
+def test_suite_b_192_rsa_insufficient_key(dev, apdev):
+ """WPA2/GCMP-256 connection at Suite B 192-bit level and RSA with insufficient key length"""
+ check_suite_b_192_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_192_rsa_ap_params()
+ params["ca_cert"] = "auth_serv/ca.pem"
+ params["server_cert"] = "auth_serv/server.pem"
+ params["private_key"] = "auth_serv/server.key"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ phase1="tls_suiteb=1",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ca.pem",
+ client_cert="auth_serv/user.pem",
+ private_key="auth_serv/user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-TLS-CERT-ERROR"], timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("Certificate error not reported")
+ if "reason=11" in ev and "err='Insufficient RSA modulus size'" in ev:
+ return
+ if "reason=7" in ev and "err='certificate uses insecure algorithm'" in ev:
+ return
+ raise Exception("Unexpected error reason: " + ev)
+
+def test_suite_b_192_rsa_insufficient_dh(dev, apdev):
+ """WPA2/GCMP-256 connection at Suite B 192-bit level and RSA with insufficient DH key length"""
+ check_suite_b_192_capa(dev, dhe=True)
+ dev[0].flush_scan_cache()
+ params = suite_b_192_rsa_ap_params()
+ params["tls_flags"] = "[SUITEB-NO-ECDH]"
+ params["dh_file"] = "auth_serv/dh.conf"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ phase1="tls_suiteb=1",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/rsa3072-ca.pem",
+ client_cert="auth_serv/rsa3072-user.pem",
+ private_key="auth_serv/rsa3072-user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STATUS status='local TLS alert'",
+ "CTRL-EVENT-CONNECTED"],
+ timeout=10)
+ dev[0].request("DISCONNECT")
+ if ev is None:
+ raise Exception("DH error not reported")
+ if "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection")
+ if "insufficient security" not in ev and "internal error" not in ev:
+ raise Exception("Unexpected error reason: " + ev)
+
+def test_suite_b_192_rsa_radius(dev, apdev):
+ """WPA2/GCMP-256 (RADIUS) connection at Suite B 192-bit level and RSA (ECDHE)"""
+ check_suite_b_192_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_as_params()
+ params['ca_cert'] = 'auth_serv/rsa3072-ca.pem'
+ params['server_cert'] = 'auth_serv/rsa3072-server.pem'
+ params['private_key'] = 'auth_serv/rsa3072-server.key'
+ del params['openssl_ciphers']
+ params["tls_flags"] = "[SUITEB]"
+
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-suite-b",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP-SUITE-B-192",
+ "rsn_pairwise": "GCMP-256",
+ "group_mgmt_cipher": "BIP-GMAC-256",
+ "ieee80211w": "2",
+ "ieee8021x": "1",
+ 'auth_server_addr': "127.0.0.1",
+ 'auth_server_port': "18129",
+ 'auth_server_shared_secret': "radius",
+ 'nas_identifier': "nas.w1.fi"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ openssl_ciphers="ECDHE-RSA-AES256-GCM-SHA384",
+ phase1="tls_suiteb=1",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/rsa3072-ca.pem",
+ client_cert="auth_serv/rsa3072-user.pem",
+ private_key="auth_serv/rsa3072-user.key",
+ pairwise="GCMP-256", group="GCMP-256",
+ group_mgmt="BIP-GMAC-256", scan_freq="2412")
+ tls_cipher = dev[0].get_status_field("EAP TLS cipher")
+ if tls_cipher != "ECDHE-RSA-AES256-GCM-SHA384" and \
+ tls_cipher != "ECDHE-RSA-AES-256-GCM-AEAD":
+ raise Exception("Unexpected TLS cipher: " + tls_cipher)
+
+def test_suite_b_192_rsa_ecdhe_radius_rsa2048_client(dev, apdev):
+ """Suite B 192-bit level and RSA (ECDHE) and RSA2048 client"""
+ run_suite_b_192_rsa_radius_rsa2048_client(dev, apdev, True)
+
+def test_suite_b_192_rsa_dhe_radius_rsa2048_client(dev, apdev):
+ """Suite B 192-bit level and RSA (DHE) and RSA2048 client"""
+ run_suite_b_192_rsa_radius_rsa2048_client(dev, apdev, False)
+
+def run_suite_b_192_rsa_radius_rsa2048_client(dev, apdev, ecdhe):
+ check_suite_b_192_capa(dev, dhe=not ecdhe)
+ dev[0].flush_scan_cache()
+ params = suite_b_as_params()
+ params['ca_cert'] = 'auth_serv/rsa3072-ca.pem'
+ params['server_cert'] = 'auth_serv/rsa3072-server.pem'
+ params['private_key'] = 'auth_serv/rsa3072-server.key'
+ del params['openssl_ciphers']
+ if ecdhe:
+ params["tls_flags"] = "[SUITEB]"
+ ciphers = "ECDHE-RSA-AES256-GCM-SHA384"
+ else:
+ params["tls_flags"] = "[SUITEB-NO-ECDH]"
+ params["dh_file"] = "auth_serv/dh_param_3072.pem"
+ ciphers = "DHE-RSA-AES256-GCM-SHA384"
+
+ hostapd.add_ap(apdev[1], params)
+
+ params = {"ssid": "test-suite-b",
+ "wpa": "2",
+ "wpa_key_mgmt": "WPA-EAP-SUITE-B-192",
+ "rsn_pairwise": "GCMP-256",
+ "group_mgmt_cipher": "BIP-GMAC-256",
+ "ieee80211w": "2",
+ "ieee8021x": "1",
+ 'auth_server_addr': "127.0.0.1",
+ 'auth_server_port': "18129",
+ 'auth_server_shared_secret': "radius",
+ 'nas_identifier': "nas.w1.fi"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ openssl_ciphers=ciphers,
+ phase1="tls_suiteb=1",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/rsa3072-ca.pem",
+ client_cert="auth_serv/rsa3072-user-rsa2048.pem",
+ private_key="auth_serv/rsa3072-user-rsa2048.key",
+ pairwise="GCMP-256", group="GCMP-256",
+ group_mgmt="BIP-GMAC-256", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP-Failure not reported")
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("Disconnection not reported")
+ if "reason=23" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+def test_openssl_ecdh_curves(dev, apdev):
+ """OpenSSL ECDH curve configuration"""
+ check_suite_b_192_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_192_ap_params()
+ params['wpa_key_mgmt'] = "WPA-EAP"
+ del params['openssl_ciphers']
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user.pem",
+ private_key="auth_serv/ec2-user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ hapd.disable()
+ hapd.set('openssl_ecdh_curves', 'foo')
+ if "FAIL" not in hapd.request("ENABLE"):
+ raise Exception("Invalid openssl_ecdh_curves value accepted")
+ hapd.set('openssl_ecdh_curves', 'P-384')
+ hapd.enable()
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user.pem",
+ private_key="auth_serv/ec2-user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ # Check with server enforcing P-256 and client allowing only P-384
+ hapd.disable()
+ hapd.set('openssl_ecdh_curves', 'P-256')
+ hapd.enable()
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user.pem",
+ private_key="auth_serv/ec2-user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412",
+ wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-FAILURE"], timeout=10)
+ if ev is None:
+ raise Exception("EAP failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_suite_b_192_pmksa_caching_roam(dev, apdev):
+ """WPA2/GCMP-256 connection at Suite B 192-bit level using PMKSA caching and roaming"""
+ check_suite_b_192_capa(dev)
+ dev[0].flush_scan_cache()
+ params = suite_b_192_ap_params()
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = hapd.own_addr()
+
+ dev[0].connect("test-suite-b", key_mgmt="WPA-EAP-SUITE-B-192",
+ ieee80211w="2",
+ openssl_ciphers="SUITEB192",
+ eap="TLS", identity="tls user",
+ ca_cert="auth_serv/ec2-ca.pem",
+ client_cert="auth_serv/ec2-user.pem",
+ private_key="auth_serv/ec2-user.key",
+ pairwise="GCMP-256", group="GCMP-256", scan_freq="2412")
+ ev = dev[0].wait_event(["PMKSA-CACHE-ADDED"], timeout=5)
+ if ev is None:
+ raise Exception("PMKSA cache entry not added for AP1")
+ hapd.wait_sta()
+ dev[0].dump_monitor()
+
+ hapd2 = hostapd.add_ap(apdev[1], params)
+ bssid2 = hapd2.own_addr()
+ dev[0].scan_for_bss(bssid2, freq=2412)
+ dev[0].request("ROAM " + bssid2)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" not in ev:
+ raise Exception("EAP exchange not seen")
+ ev = dev[0].wait_connected()
+ if bssid2 not in ev:
+ raise Exception("Roam to AP2 connected back to AP1")
+ ev = dev[0].wait_event(["PMKSA-CACHE-ADDED"], timeout=5)
+ if ev is None:
+ raise Exception("PMKSA cache entry not added for AP2")
+ hapd2.wait_sta()
+ dev[0].dump_monitor()
+
+ dev[0].request("ROAM " + bssid)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if bssid not in ev:
+ raise Exception("Roam to AP1 connected back to AP2")
+ hapd.wait_sta()
+ dev[0].dump_monitor()
+
+ dev[0].request("ROAM " + bssid2)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=20)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ if bssid2 not in ev:
+ raise Exception("Second roam to AP2 connected back to AP1")
+ hapd2.wait_sta()
+ dev[0].dump_monitor()
diff --git a/contrib/wpa/tests/hwsim/test_tnc.py b/contrib/wpa/tests/hwsim/test_tnc.py
new file mode 100644
index 000000000000..0c444bb7ce5e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_tnc.py
@@ -0,0 +1,194 @@
+# -*- coding: utf-8 -*-
+# TNC tests
+# Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os.path
+
+import hostapd
+from utils import HwsimSkip, alloc_fail, fail_test, wait_fail_trigger
+from test_ap_eap import int_eap_server_params, check_eap_capa
+
+def test_tnc_peap_soh(dev, apdev):
+ """TNC PEAP-SoH"""
+ params = int_eap_server_params()
+ params["tnc"] = "1"
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PEAP", identity="user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peapver=0 tnc=soh cryptobinding=0",
+ phase2="auth=MSCHAPV2",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected(timeout=10)
+
+ dev[1].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PEAP", identity="user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peapver=0 tnc=soh1 cryptobinding=1",
+ phase2="auth=MSCHAPV2",
+ scan_freq="2412", wait_connect=False)
+ dev[1].wait_connected(timeout=10)
+
+ dev[2].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PEAP", identity="user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peapver=0 tnc=soh2 cryptobinding=2",
+ phase2="auth=MSCHAPV2",
+ scan_freq="2412", wait_connect=False)
+ dev[2].wait_connected(timeout=10)
+
+def test_tnc_peap_soh_errors(dev, apdev):
+ """TNC PEAP-SoH local error cases"""
+ params = int_eap_server_params()
+ params["tnc"] = "1"
+ hostapd.add_ap(apdev[0], params)
+
+ tests = [(1, "tncc_build_soh"),
+ (1, "eap_msg_alloc;=eap_peap_phase2_request")]
+ for count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PEAP", identity="user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peapver=0 tnc=soh cryptobinding=0",
+ phase2="auth=MSCHAPV2",
+ scan_freq="2412", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ with fail_test(dev[0], 1, "os_get_random;tncc_build_soh"):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="PEAP", identity="user", password="password",
+ ca_cert="auth_serv/ca.pem",
+ phase1="peapver=0 tnc=soh cryptobinding=0",
+ phase2="auth=MSCHAPV2",
+ scan_freq="2412", wait_connect=False)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+def test_tnc_ttls(dev, apdev):
+ """TNC TTLS"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_eap_server_params()
+ params["tnc"] = "1"
+ hostapd.add_ap(apdev[0], params)
+
+ if not os.path.exists("tnc/libhostap_imc.so"):
+ raise HwsimSkip("No IMC installed")
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="DOMAIN\mschapv2 user",
+ anonymous_identity="ttls", password="password",
+ phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected(timeout=10)
+
+def test_tnc_ttls_fragmentation(dev, apdev):
+ """TNC TTLS with fragmentation"""
+ check_eap_capa(dev[0], "MSCHAPV2")
+ params = int_eap_server_params()
+ params["tnc"] = "1"
+ params["fragment_size"] = "150"
+ hostapd.add_ap(apdev[0], params)
+
+ if not os.path.exists("tnc/libhostap_imc.so"):
+ raise HwsimSkip("No IMC installed")
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="TTLS", identity="DOMAIN\mschapv2 user",
+ anonymous_identity="ttls", password="password",
+ phase2="auth=MSCHAPV2",
+ ca_cert="auth_serv/ca.pem",
+ fragment_size="150",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected(timeout=10)
+
+def test_tnc_ttls_errors(dev, apdev):
+ """TNC TTLS local error cases"""
+ if not os.path.exists("tnc/libhostap_imc.so"):
+ raise HwsimSkip("No IMC installed")
+ check_eap_capa(dev[0], "MSCHAPV2")
+
+ params = int_eap_server_params()
+ params["tnc"] = "1"
+ params["fragment_size"] = "150"
+ hostapd.add_ap(apdev[0], params)
+
+ tests = [(1, "eap_ttls_process_phase2_eap;eap_ttls_process_tnc_start",
+ "DOMAIN\mschapv2 user", "auth=MSCHAPV2"),
+ (1, "eap_ttls_process_phase2_eap;eap_ttls_process_tnc_start",
+ "mschap user", "auth=MSCHAP"),
+ (1, "=eap_tnc_init", "chap user", "auth=CHAP"),
+ (1, "tncc_init;eap_tnc_init", "pap user", "auth=PAP"),
+ (1, "eap_msg_alloc;eap_tnc_build_frag_ack",
+ "pap user", "auth=PAP"),
+ (1, "eap_msg_alloc;eap_tnc_build_msg",
+ "pap user", "auth=PAP"),
+ (1, "wpabuf_alloc;=eap_tnc_process_fragment",
+ "pap user", "auth=PAP"),
+ (1, "eap_msg_alloc;=eap_tnc_process", "pap user", "auth=PAP"),
+ (1, "wpabuf_alloc;=eap_tnc_process", "pap user", "auth=PAP"),
+ (1, "dup_binstr;tncc_process_if_tnccs", "pap user", "auth=PAP"),
+ (1, "tncc_get_base64;tncc_process_if_tnccs",
+ "pap user", "auth=PAP"),
+ (1, "tncc_if_tnccs_start", "pap user", "auth=PAP"),
+ (1, "tncc_if_tnccs_end", "pap user", "auth=PAP"),
+ (1, "tncc_parse_imc", "pap user", "auth=PAP"),
+ (2, "tncc_parse_imc", "pap user", "auth=PAP"),
+ (3, "tncc_parse_imc", "pap user", "auth=PAP"),
+ (1, "os_readfile;tncc_read_config", "pap user", "auth=PAP"),
+ (1, "tncc_init", "pap user", "auth=PAP"),
+ (1, "TNC_TNCC_ReportMessageTypes", "pap user", "auth=PAP"),
+ (1, "base64_gen_encode;?base64_encode;TNC_TNCC_SendMessage",
+ "pap user", "auth=PAP"),
+ (1, "=TNC_TNCC_SendMessage", "pap user", "auth=PAP"),
+ (1, "tncc_get_base64;tncc_process_if_tnccs",
+ "pap user", "auth=PAP")]
+ for count, func, identity, phase2 in tests:
+ with alloc_fail(dev[0], count, func):
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ scan_freq="2412",
+ eap="TTLS", anonymous_identity="ttls",
+ identity=identity, password="password",
+ ca_cert="auth_serv/ca.pem", phase2=phase2,
+ fragment_size="150", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-EAP-PROPOSED-METHOD"],
+ timeout=15)
+ if ev is None:
+ raise Exception("Timeout on EAP start")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL",
+ note="Allocation failure not triggered for: %d:%s" % (count, func))
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+ dev[0].dump_monitor()
+
+def test_tnc_fast(dev, apdev):
+ """TNC FAST"""
+ check_eap_capa(dev[0], "FAST")
+ params = int_eap_server_params()
+ params["tnc"] = "1"
+ params["pac_opaque_encr_key"] = "000102030405060708090a0b0c0d0e00"
+ params["eap_fast_a_id"] = "101112131415161718191a1b1c1d1e00"
+ params["eap_fast_a_id_info"] = "test server2"
+
+ hostapd.add_ap(apdev[0], params)
+
+ if not os.path.exists("tnc/libhostap_imc.so"):
+ raise HwsimSkip("No IMC installed")
+
+ dev[0].connect("test-wpa2-eap", key_mgmt="WPA-EAP",
+ eap="FAST", identity="user",
+ anonymous_identity="FAST", password="password",
+ phase2="auth=GTC",
+ phase1="fast_provisioning=2",
+ pac_file="blob://fast_pac_auth_tnc",
+ ca_cert="auth_serv/ca.pem",
+ scan_freq="2412", wait_connect=False)
+ dev[0].wait_connected(timeout=10)
diff --git a/contrib/wpa/tests/hwsim/test_wep.py b/contrib/wpa/tests/hwsim/test_wep.py
new file mode 100644
index 000000000000..5c1fc9adb490
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_wep.py
@@ -0,0 +1,172 @@
+# WEP tests
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import subprocess
+
+from remotehost import remote_compatible
+import hostapd
+import hwsim_utils
+from utils import *
+
+@remote_compatible
+def test_wep_open_auth(dev, apdev):
+ """WEP Open System authentication"""
+ check_wep_capa(dev[0])
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wep-open",
+ "wep_key0": '"hello"'})
+ dev[0].flush_scan_cache()
+ dev[0].connect("wep-open", key_mgmt="NONE", wep_key0='"hello"',
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ if "[WEP]" not in dev[0].request("SCAN_RESULTS"):
+ raise Exception("WEP flag not indicated in scan results")
+
+ bss = dev[0].get_bss(apdev[0]['bssid'])
+ if 'flags' not in bss:
+ raise Exception("Could not get BSS flags from BSS table")
+ if "[WEP]" not in bss['flags']:
+ raise Exception("Unexpected BSS flags: " + bss['flags'])
+
+@remote_compatible
+def test_wep_shared_key_auth(dev, apdev):
+ """WEP Shared Key authentication"""
+ check_wep_capa(dev[0])
+ check_wep_capa(dev[1])
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wep-shared-key",
+ "wep_key0": '"hello12345678"',
+ "auth_algs": "2"})
+ dev[0].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+ wep_key0='"hello12345678"',
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[1].connect("wep-shared-key", key_mgmt="NONE", auth_alg="OPEN SHARED",
+ wep_key0='"hello12345678"',
+ scan_freq="2412")
+
+@remote_compatible
+def test_wep_shared_key_auth_not_allowed(dev, apdev):
+ """WEP Shared Key authentication not allowed"""
+ check_wep_capa(dev[0])
+ hostapd.add_ap(apdev[0],
+ {"ssid": "wep-shared-key",
+ "wep_key0": '"hello12345678"',
+ "auth_algs": "1"})
+ dev[0].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+ wep_key0='"hello12345678"',
+ scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected association")
+
+def test_wep_shared_key_auth_multi_key(dev, apdev):
+ """WEP Shared Key authentication with multiple keys"""
+ check_wep_capa(dev[0])
+ check_wep_capa(dev[1])
+ check_wep_capa(dev[2])
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wep-shared-key",
+ "wep_key0": '"hello12345678"',
+ "wep_key1": '"other12345678"',
+ "auth_algs": "2"})
+ dev[0].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+ wep_key0='"hello12345678"',
+ scan_freq="2412")
+ dev[1].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+ wep_key0='"hello12345678"',
+ wep_key1='"other12345678"',
+ wep_tx_keyidx="1",
+ scan_freq="2412")
+ id = dev[2].connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+ wep_key0='"hello12345678"',
+ wep_key1='"other12345678"',
+ wep_tx_keyidx="0",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ hwsim_utils.test_connectivity(dev[1], hapd)
+ hwsim_utils.test_connectivity(dev[2], hapd)
+
+ dev[2].set_network(id, "wep_tx_keyidx", "1")
+ dev[2].request("REASSOCIATE")
+ dev[2].wait_connected(timeout=10, error="Reassociation timed out")
+ hwsim_utils.test_connectivity(dev[2], hapd)
+
+def test_wep_ht_vht(dev, apdev):
+ """WEP and HT/VHT"""
+ check_wep_capa(dev[0])
+ dev[0].flush_scan_cache()
+ try:
+ hapd = None
+ params = {"ssid": "test-vht40-wep",
+ "country_code": "SE",
+ "hw_mode": "a",
+ "channel": "36",
+ "ieee80211n": "1",
+ "ieee80211ac": "1",
+ "ht_capab": "[HT40+]",
+ "vht_capab": "",
+ "vht_oper_chwidth": "0",
+ "vht_oper_centr_freq_seg0_idx": "0",
+ "wep_key0": '"hello"'}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-vht40-wep", scan_freq="5180", key_mgmt="NONE",
+ wep_key0='"hello"')
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ status = hapd.get_status()
+ logger.info("hostapd STATUS: " + str(status))
+ if status["ieee80211n"] != "0":
+ raise Exception("Unexpected STATUS ieee80211n value")
+ if status["ieee80211ac"] != "0":
+ raise Exception("Unexpected STATUS ieee80211ac value")
+ if status["secondary_channel"] != "0":
+ raise Exception("Unexpected STATUS secondary_channel value")
+ finally:
+ dev[0].request("DISCONNECT")
+ clear_regdom(hapd, dev)
+
+def test_wep_he(dev, apdev):
+ """WEP and HE"""
+ check_wep_capa(dev[0])
+ dev[0].flush_scan_cache()
+ params = {"ssid": "test-he-wep",
+ "ieee80211ax": "1",
+ "wep_key0": '"hello"'}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("test-he-wep", scan_freq="2412", key_mgmt="NONE",
+ wep_key0='"hello"')
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ status = hapd.get_status()
+ logger.info("hostapd STATUS: " + str(status))
+ if status["ieee80211ax"] != "0":
+ raise Exception("Unexpected STATUS ieee80211ax value")
+
+def test_wep_ifdown(dev, apdev):
+ """AP with WEP and external ifconfig down"""
+ check_wep_capa(dev[0])
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wep-open",
+ "wep_key0": '"hello"'})
+ dev[0].flush_scan_cache()
+ id = dev[0].connect("wep-open", key_mgmt="NONE", wep_key0='"hello"',
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+ hapd.cmd_execute(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'down'])
+ ev = hapd.wait_event(["INTERFACE-DISABLED"], timeout=10)
+ if ev is None:
+ raise Exception("No INTERFACE-DISABLED event")
+ hapd.cmd_execute(['ip', 'link', 'set', 'dev', apdev[0]['ifname'], 'up'])
+ ev = hapd.wait_event(["INTERFACE-ENABLED"], timeout=10)
+ if ev is None:
+ raise Exception("No INTERFACE-ENABLED event")
+ dev[0].select_network(id, freq=2412)
+ dev[0].wait_connected()
+ hwsim_utils.test_connectivity(dev[0], hapd)
diff --git a/contrib/wpa/tests/hwsim/test_wext.py b/contrib/wpa/tests/hwsim/test_wext.py
new file mode 100644
index 000000000000..e14eecedeb1a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_wext.py
@@ -0,0 +1,254 @@
+# Deprecated WEXT driver interface in wpa_supplicant
+# Copyright (c) 2013-2015, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os
+
+import hostapd
+import hwsim_utils
+from wpasupplicant import WpaSupplicant
+from utils import *
+from test_rfkill import get_rfkill
+
+def get_wext_interface():
+ if not os.path.exists("/proc/net/wireless"):
+ raise HwsimSkip("WEXT support not included in the kernel")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ try:
+ wpas.interface_add("wlan5", driver="wext")
+ except Exception as e:
+ wpas.close_ctrl()
+ raise HwsimSkip("WEXT driver support not included in wpa_supplicant")
+ return wpas
+
+def test_wext_open(dev, apdev):
+ """WEXT driver interface with open network"""
+ wpas = get_wext_interface()
+
+ params = {"ssid": "wext-open"}
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas.connect("wext-open", key_mgmt="NONE")
+ hwsim_utils.test_connectivity(wpas, hapd)
+
+def test_wext_wpa2_psk(dev, apdev):
+ """WEXT driver interface with WPA2-PSK"""
+ wpas = get_wext_interface()
+
+ params = hostapd.wpa2_params(ssid="wext-wpa2-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas.connect("wext-wpa2-psk", psk="12345678")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ if "RSSI=" not in wpas.request("SIGNAL_POLL"):
+ raise Exception("Missing RSSI from SIGNAL_POLL")
+
+ wpas.dump_monitor()
+ hapd.request("DEAUTHENTICATE " + wpas.p2p_interface_addr())
+ wpas.wait_disconnected(timeout=15)
+
+def test_wext_wpa_psk(dev, apdev):
+ """WEXT driver interface with WPA-PSK"""
+ skip_with_fips(dev[0])
+ skip_without_tkip(dev[0])
+ wpas = get_wext_interface()
+
+ params = hostapd.wpa_params(ssid="wext-wpa-psk", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+ testfile = "/sys/kernel/debug/ieee80211/%s/netdev:%s/tkip_mic_test" % (hapd.get_driver_status_field("phyname"), apdev[0]['ifname'])
+ if not os.path.exists(testfile):
+ wpas.close_ctrl()
+ raise HwsimSkip("tkip_mic_test not supported in mac80211")
+
+ wpas.connect("wext-wpa-psk", psk="12345678")
+ hwsim_utils.test_connectivity(wpas, hapd)
+
+ with open(testfile, "w") as f:
+ f.write(wpas.p2p_interface_addr())
+ ev = wpas.wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected disconnection on first Michael MIC failure")
+
+ with open(testfile, "w") as f:
+ f.write("ff:ff:ff:ff:ff:ff")
+ ev = wpas.wait_disconnected(timeout=10,
+ error="No disconnection after two Michael MIC failures")
+ if "reason=14 locally_generated=1" not in ev:
+ raise Exception("Unexpected disconnection reason: " + ev)
+
+def test_wext_pmksa_cache(dev, apdev):
+ """PMKSA caching with WEXT"""
+ wpas = get_wext_interface()
+
+ params = hostapd.wpa2_eap_params(ssid="test-pmksa-cache")
+ hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ wpas.connect("test-pmksa-cache", proto="RSN", key_mgmt="WPA-EAP",
+ eap="GPSK", identity="gpsk user",
+ password="abcdefghijklmnop0123456789abcdef",
+ scan_freq="2412")
+ pmksa = wpas.get_pmksa(bssid)
+ if pmksa is None:
+ raise Exception("No PMKSA cache entry created")
+ if pmksa['opportunistic'] != '0':
+ raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+ hostapd.add_ap(apdev[1], params)
+ bssid2 = apdev[1]['bssid']
+
+ wpas.dump_monitor()
+ logger.info("Roam to AP2")
+ # It can take some time for the second AP to become ready to reply to Probe
+ # Request frames especially under heavy CPU load, so allow couple of rounds
+ # of scanning to avoid reporting errors incorrectly just because of scans
+ # not having seen the target AP.
+ for i in range(3):
+ wpas.scan()
+ if wpas.get_bss(bssid2) is not None:
+ break
+ logger.info("Scan again to find target AP")
+ wpas.request("ROAM " + bssid2)
+ ev = wpas.wait_event(["CTRL-EVENT-EAP-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("EAP success timed out")
+ wpas.wait_connected(timeout=10, error="Roaming timed out")
+ pmksa2 = wpas.get_pmksa(bssid2)
+ if pmksa2 is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa2['opportunistic'] != '0':
+ raise Exception("Unexpected opportunistic PMKSA cache entry")
+
+ wpas.dump_monitor()
+ logger.info("Roam back to AP1")
+ wpas.scan()
+ wpas.request("ROAM " + bssid)
+ ev = wpas.wait_event(["CTRL-EVENT-EAP-STARTED",
+ "CTRL-EVENT-CONNECTED"], timeout=15)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-EAP-STARTED" in ev:
+ raise Exception("Unexpected EAP exchange")
+ pmksa1b = wpas.get_pmksa(bssid)
+ if pmksa1b is None:
+ raise Exception("No PMKSA cache entry found")
+ if pmksa['pmkid'] != pmksa1b['pmkid']:
+ raise Exception("Unexpected PMKID change for AP1")
+
+ wpas.dump_monitor()
+ if "FAIL" in wpas.request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+ if wpas.get_pmksa(bssid) is not None or wpas.get_pmksa(bssid2) is not None:
+ raise Exception("PMKSA_FLUSH did not remove PMKSA entries")
+ wpas.wait_disconnected(timeout=5)
+ wpas.wait_connected(timeout=15, error="Reconnection timed out")
+
+def test_wext_wep_open_auth(dev, apdev):
+ """WEP Open System authentication"""
+ wpas = get_wext_interface()
+ check_wep_capa(wpas)
+
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wep-open",
+ "wep_key0": '"hello"'})
+ wpas.connect("wep-open", key_mgmt="NONE", wep_key0='"hello"',
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ if "[WEP]" not in wpas.request("SCAN_RESULTS"):
+ raise Exception("WEP flag not indicated in scan results")
+
+def test_wext_wep_shared_key_auth(dev, apdev):
+ """WEP Shared Key authentication"""
+ wpas = get_wext_interface()
+ check_wep_capa(wpas)
+
+ hapd = hostapd.add_ap(apdev[0],
+ {"ssid": "wep-shared-key",
+ "wep_key0": '"hello12345678"',
+ "auth_algs": "2"})
+ wpas.connect("wep-shared-key", key_mgmt="NONE", auth_alg="SHARED",
+ wep_key0='"hello12345678"', scan_freq="2412")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ wpas.request("REMOVE_NETWORK all")
+ wpas.wait_disconnected(timeout=5)
+ wpas.connect("wep-shared-key", key_mgmt="NONE", auth_alg="OPEN SHARED",
+ wep_key0='"hello12345678"', scan_freq="2412")
+
+def test_wext_pmf(dev, apdev):
+ """WEXT driver interface with WPA2-PSK and PMF"""
+ wpas = get_wext_interface()
+
+ params = hostapd.wpa2_params(ssid="wext-wpa2-psk", passphrase="12345678")
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ hapd = hostapd.add_ap(apdev[0], params)
+
+ wpas.connect("wext-wpa2-psk", psk="12345678", ieee80211w="1",
+ key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(wpas, hapd)
+
+ addr = wpas.p2p_interface_addr()
+ hapd.request("DEAUTHENTICATE " + addr)
+ wpas.wait_disconnected(timeout=5)
+
+def test_wext_scan_hidden(dev, apdev):
+ """WEXT with hidden SSID"""
+ wpas = get_wext_interface()
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "test-scan",
+ "ignore_broadcast_ssid": "1"})
+ hapd2 = hostapd.add_ap(apdev[1], {"ssid": "test-scan2",
+ "ignore_broadcast_ssid": "1"})
+
+ id1 = wpas.connect("test-scan", key_mgmt="NONE", scan_ssid="1",
+ only_add_network=True)
+
+ wpas.request("SCAN scan_id=%d" % id1)
+
+ ev = wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=15)
+ if ev is None:
+ raise Exception("Scan did not complete")
+
+ if "test-scan" not in wpas.request("SCAN_RESULTS"):
+ raise Exception("Did not find hidden SSID in scan")
+
+ id = wpas.connect("test-scan2", key_mgmt="NONE", scan_ssid="1",
+ only_add_network=True)
+ wpas.connect_network(id, timeout=30)
+ wpas.request("DISCONNECT")
+ hapd2.disable()
+ hapd.disable()
+ wpas.interface_remove("wlan5")
+ wpas.interface_add("wlan5")
+ wpas.flush_scan_cache(freq=2412)
+ wpas.flush_scan_cache()
+
+def test_wext_rfkill(dev, apdev):
+ """WEXT and rfkill block/unblock"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ rfk = get_rfkill(wpas)
+ wpas.interface_remove("wlan5")
+
+ wpas = get_wext_interface()
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+ try:
+ logger.info("rfkill block")
+ rfk.block()
+ wpas.wait_disconnected(timeout=10,
+ error="Missing disconnection event on rfkill block")
+
+ logger.info("rfkill unblock")
+ rfk.unblock()
+ wpas.wait_connected(timeout=20,
+ error="Missing connection event on rfkill unblock")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ finally:
+ rfk.unblock()
diff --git a/contrib/wpa/tests/hwsim/test_wmediumd.py b/contrib/wpa/tests/hwsim/test_wmediumd.py
new file mode 100644
index 000000000000..ad38f03ced82
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_wmediumd.py
@@ -0,0 +1,480 @@
+# wmediumd sanity checks
+# Copyright (c) 2015, Intel Deutschland GmbH
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import tempfile, os, subprocess, errno, hwsim_utils, time
+from utils import HwsimSkip
+from wpasupplicant import WpaSupplicant
+from tshark import run_tshark
+from test_ap_open import _test_ap_open
+from test_scan import test_scan_only_one as _test_scan_only_one
+from test_wpas_mesh import check_mesh_support, check_mesh_group_added
+from test_wpas_mesh import check_mesh_peer_connected, add_open_mesh_network
+from test_wpas_mesh import check_mesh_group_removed
+
+class LocalVariables:
+ revs = []
+
+CFG = """
+ifaces :
+{
+ ids = ["%s", "%s"]
+ links = (
+ (0, 1, 30)
+ )
+}
+"""
+
+CFG2 = """
+ifaces :
+{
+ ids = ["%s", "%s", "%s"]
+}
+
+model:
+{
+ type = "prob"
+
+ links = (
+ (0, 1, 0.000000),
+ (0, 2, 0.000000),
+ (1, 2, 1.000000)
+ )
+}
+"""
+
+CFG3 = """
+ifaces :
+{
+ ids = ["%s", "%s", "%s", "%s", "%s"]
+}
+
+model:
+{
+ type = "prob"
+
+ default_prob = 1.0
+ links = (
+ (0, 1, 0.000000),
+ (1, 2, 0.000000),
+ (2, 3, 0.000000),
+ (3, 4, 0.000000)
+ )
+}
+"""
+
+def get_wmediumd_version():
+ if len(LocalVariables.revs) > 0:
+ return LocalVariables.revs
+
+ try:
+ verstr = subprocess.check_output(['wmediumd', '-V']).decode()
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ raise HwsimSkip('wmediumd not available')
+ raise
+
+ vernum = verstr.split(' ')[1][1:]
+ LocalVariables.revs = vernum.split('.')
+ for i in range(0, len(LocalVariables.revs)):
+ LocalVariables.revs[i] = int(LocalVariables.revs[i])
+ while len(LocalVariables.revs) < 3:
+ LocalVariables.revs += [0]
+
+ return LocalVariables.revs
+
+def require_wmediumd_version(major, minor, patch):
+ revs = get_wmediumd_version()
+ if revs[0] < major or revs[1] < minor or revs[2] < patch:
+ raise HwsimSkip('wmediumd v%s.%s.%s is too old for this test' %
+ (revs[0], revs[1], revs[2]))
+
+def output_wmediumd_log(p, params, data):
+ log_file = open(os.path.abspath(os.path.join(params['logdir'],
+ 'wmediumd.log')), 'a')
+ log_file.write(data)
+ log_file.close()
+
+def start_wmediumd(fn, params):
+ try:
+ p = subprocess.Popen(['wmediumd', '-c', fn],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ except OSError as e:
+ if e.errno == errno.ENOENT:
+ raise HwsimSkip('wmediumd not available')
+ raise
+
+ logs = ''
+ while True:
+ line = p.stdout.readline().decode()
+ if not line:
+ output_wmediumd_log(p, params, logs)
+ raise Exception('wmediumd was terminated unexpectedly')
+ if line.find('REGISTER SENT!') > -1:
+ break
+ logs += line
+ return p
+
+def stop_wmediumd(p, params):
+ p.terminate()
+ p.wait()
+ stdoutdata, stderrdata = p.communicate()
+ output_wmediumd_log(p, params, stdoutdata.decode())
+
+def test_wmediumd_simple(dev, apdev, params):
+ """test a simple wmediumd configuration"""
+ fd, fn = tempfile.mkstemp()
+ try:
+ f = os.fdopen(fd, 'w')
+ f.write(CFG % (apdev[0]['bssid'], dev[0].own_addr()))
+ f.close()
+ p = start_wmediumd(fn, params)
+ try:
+ _test_ap_open(dev, apdev)
+ finally:
+ stop_wmediumd(p, params)
+ # test that releasing hwsim works correctly
+ _test_ap_open(dev, apdev)
+ finally:
+ os.unlink(fn)
+
+def test_wmediumd_path_simple(dev, apdev, params):
+ """test a mesh path"""
+ # 0 and 1 is connected
+ # 0 and 2 is connected
+ # 1 and 2 is not connected
+ # 1 --- 0 --- 2
+ # | |
+ # +-----X-----+
+ # This tests if 1 and 2 can communicate each other via 0.
+ require_wmediumd_version(0, 3, 1)
+ fd, fn = tempfile.mkstemp()
+ try:
+ f = os.fdopen(fd, 'w')
+ f.write(CFG2 % (dev[0].own_addr(), dev[1].own_addr(),
+ dev[2].own_addr()))
+ f.close()
+ p = start_wmediumd(fn, params)
+ try:
+ _test_wmediumd_path_simple(dev, apdev)
+ finally:
+ stop_wmediumd(p, params)
+ finally:
+ os.unlink(fn)
+
+def _test_wmediumd_path_simple(dev, apdev):
+ for i in range(0, 3):
+ check_mesh_support(dev[i])
+ add_open_mesh_network(dev[i], freq="2462", basic_rates="60 120 240")
+
+ # Check for mesh joined
+ for i in range(0, 3):
+ check_mesh_group_added(dev[i])
+
+ state = dev[i].get_status_field("wpa_state")
+ if state != "COMPLETED":
+ raise Exception("Unexpected wpa_state on dev" + str(i) + ": " + state)
+
+ mode = dev[i].get_status_field("mode")
+ if mode != "mesh":
+ raise Exception("Unexpected mode: " + mode)
+
+ # Check for peer connected
+ check_mesh_peer_connected(dev[0])
+ check_mesh_peer_connected(dev[0])
+ check_mesh_peer_connected(dev[1])
+ check_mesh_peer_connected(dev[2])
+
+ # Test connectivity 1->2 and 2->1
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+
+ # Check mpath table on 0
+ res, data = dev[0].cmd_execute(['iw', dev[0].ifname, 'mpath', 'dump'])
+ if res != 0:
+ raise Exception("iw command failed on dev0")
+ if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) == -1 or \
+ data.find(dev[2].own_addr() + ' ' + dev[2].own_addr()) == -1:
+ raise Exception("mpath not found on dev0:\n" + data)
+ if data.find(dev[0].own_addr()) > -1:
+ raise Exception("invalid mpath found on dev0:\n" + data)
+
+ # Check mpath table on 1
+ res, data = dev[1].cmd_execute(['iw', dev[1].ifname, 'mpath', 'dump'])
+ if res != 0:
+ raise Exception("iw command failed on dev1")
+ if data.find(dev[0].own_addr() + ' ' + dev[0].own_addr()) == -1 or \
+ data.find(dev[2].own_addr() + ' ' + dev[0].own_addr()) == -1:
+ raise Exception("mpath not found on dev1:\n" + data)
+ if data.find(dev[2].own_addr() + ' ' + dev[2].own_addr()) > -1 or \
+ data.find(dev[1].own_addr()) > -1:
+ raise Exception("invalid mpath found on dev1:\n" + data)
+
+ # Check mpath table on 2
+ res, data = dev[2].cmd_execute(['iw', dev[2].ifname, 'mpath', 'dump'])
+ if res != 0:
+ raise Exception("iw command failed on dev2")
+ if data.find(dev[0].own_addr() + ' ' + dev[0].own_addr()) == -1 or \
+ data.find(dev[1].own_addr() + ' ' + dev[0].own_addr()) == -1:
+ raise Exception("mpath not found on dev2:\n" + data)
+ if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) > -1 or \
+ data.find(dev[2].own_addr()) > -1:
+ raise Exception("invalid mpath found on dev2:\n" + data)
+
+ # remove mesh groups
+ for i in range(0, 3):
+ dev[i].mesh_group_remove()
+ check_mesh_group_removed(dev[i])
+ dev[i].dump_monitor()
+
+def test_wmediumd_path_ttl(dev, apdev, params):
+ """Mesh path request TTL"""
+ # 0 --- 1 --- 2 --- 3 --- 4
+ # Test the TTL of mesh path request.
+ # If the TTL is shorter than path, the mesh path request should be dropped.
+ require_wmediumd_version(0, 3, 1)
+
+ local_dev = []
+ for i in range(0, 3):
+ local_dev.append(dev[i])
+
+ for i in range(5, 7):
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan" + str(i))
+ check_mesh_support(wpas)
+ temp_dev = wpas.request("MESH_INTERFACE_ADD ifname=mesh" + str(i))
+ if "FAIL" in temp_dev:
+ raise Exception("MESH_INTERFACE_ADD failed")
+ local_dev.append(WpaSupplicant(ifname=temp_dev))
+
+ fd, fn = tempfile.mkstemp()
+ try:
+ f = os.fdopen(fd, 'w')
+ f.write(CFG3 % (local_dev[0].own_addr(), local_dev[1].own_addr(),
+ local_dev[2].own_addr(), local_dev[3].own_addr(),
+ local_dev[4].own_addr()))
+ f.close()
+ p = start_wmediumd(fn, params)
+ try:
+ _test_wmediumd_path_ttl(local_dev, True)
+ _test_wmediumd_path_ttl(local_dev, False)
+ finally:
+ stop_wmediumd(p, params)
+ finally:
+ os.unlink(fn)
+ for i in range(5, 7):
+ wpas.interface_remove("wlan" + str(i))
+
+def _test_wmediumd_path_ttl(dev, ok):
+ for i in range(0, 5):
+ check_mesh_support(dev[i])
+ add_open_mesh_network(dev[i], freq="2462", basic_rates="60 120 240")
+
+ # Check for mesh joined
+ for i in range(0, 5):
+ check_mesh_group_added(dev[i])
+
+ state = dev[i].get_status_field("wpa_state")
+ if state != "COMPLETED":
+ raise Exception("Unexpected wpa_state on dev" + str(i) + ": " + state)
+
+ mode = dev[i].get_status_field("mode")
+ if mode != "mesh":
+ raise Exception("Unexpected mode: " + mode)
+
+ # set mesh path request ttl
+ subprocess.check_call(["iw", "dev", dev[0].ifname, "set", "mesh_param",
+ "mesh_element_ttl=" + ("4" if ok else "3")])
+
+ # Check for peer connected
+ for i in range(0, 5):
+ check_mesh_peer_connected(dev[i])
+ for i in range(1, 4):
+ check_mesh_peer_connected(dev[i])
+
+ # Test connectivity 0->4 and 0->4
+ hwsim_utils.test_connectivity(dev[0], dev[4], success_expected=ok)
+
+ # Check mpath table on 0
+ res, data = dev[0].cmd_execute(['iw', dev[0].ifname, 'mpath', 'dump'])
+ if res != 0:
+ raise Exception("iw command failed on dev0")
+ if ok:
+ if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) == -1 or \
+ data.find(dev[4].own_addr() + ' ' + dev[1].own_addr()) == -1:
+ raise Exception("mpath not found on dev0:\n" + data)
+ else:
+ if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) == -1 or \
+ data.find(dev[4].own_addr() + ' 00:00:00:00:00:00') == -1:
+ raise Exception("mpath not found on dev0:\n" + data)
+ if data.find(dev[0].own_addr()) > -1 or \
+ data.find(dev[2].own_addr()) > -1 or \
+ data.find(dev[3].own_addr()) > -1:
+ raise Exception("invalid mpath found on dev0:\n" + data)
+
+ # remove mesh groups
+ for i in range(0, 3):
+ dev[i].mesh_group_remove()
+ check_mesh_group_removed(dev[i])
+ dev[i].dump_monitor()
+
+def test_wmediumd_path_rann(dev, apdev, params):
+ """Mesh path with RANN"""
+ # 0 and 1 is connected
+ # 0 and 2 is connected
+ # 1 and 2 is not connected
+ # 2 is mesh root and RANN enabled
+ # 1 --- 0 --- 2
+ # | |
+ # +-----X-----+
+ # This tests if 1 and 2 can communicate each other via 0.
+ require_wmediumd_version(0, 3, 1)
+ fd, fn = tempfile.mkstemp()
+ try:
+ f = os.fdopen(fd, 'w')
+ f.write(CFG2 % (dev[0].own_addr(), dev[1].own_addr(),
+ dev[2].own_addr()))
+ f.close()
+ p = start_wmediumd(fn, params)
+ try:
+ _test_wmediumd_path_rann(dev, apdev)
+ finally:
+ stop_wmediumd(p, params)
+ finally:
+ os.unlink(fn)
+
+ capfile = os.path.join(params['logdir'], "hwsim0.pcapng")
+
+ # check Root STA address in root announcement element
+ filt = "wlan.fc.type_subtype == 0x000d && " + \
+ "wlan_mgt.fixed.mesh_action == 0x01 && " + \
+ "wlan_mgt.tag.number == 126"
+ out = run_tshark(capfile, filt, ["wlan.rann.root_sta"])
+ if out is None:
+ raise Exception("No captured data found\n")
+ if out.find(dev[2].own_addr()) == -1 or \
+ out.find(dev[0].own_addr()) > -1 or \
+ out.find(dev[1].own_addr()) > -1:
+ raise Exception("RANN should be sent by dev2 only:\n" + out)
+
+ # check RANN interval is in range
+ filt = "wlan.sa == 02:00:00:00:02:00 && " + \
+ "wlan.fc.type_subtype == 0x000d && " + \
+ "wlan_mgt.fixed.mesh_action == 0x01 && " + \
+ "wlan_mgt.tag.number == 126"
+ out = run_tshark(capfile, filt, ["frame.time_relative"])
+ if out is None:
+ raise Exception("No captured data found\n")
+ lines = out.splitlines()
+ prev = float(lines[len(lines) - 1])
+ for i in reversed(list(range(1, len(lines) - 1))):
+ now = float(lines[i])
+ if prev - now < 1.0 or 3.0 < prev - now:
+ raise Exception("RANN interval " + str(prev - now) +
+ "(sec) should be close to 2.0(sec)\n")
+ prev = now
+
+ # check no one uses broadcast path request
+ filt = "wlan.da == ff:ff:ff:ff:ff:ff && " + \
+ "wlan.fc.type_subtype == 0x000d && " + \
+ "wlan_mgt.fixed.mesh_action == 0x01 && " + \
+ "wlan_mgt.tag.number == 130"
+ out = run_tshark(capfile, filt, ["wlan.sa", "wlan.da"])
+ if out is None:
+ raise Exception("No captured data found\n")
+ if len(out) > 0:
+ raise Exception("invalid broadcast path requests\n" + out)
+
+def _test_wmediumd_path_rann(dev, apdev):
+ for i in range(0, 3):
+ check_mesh_support(dev[i])
+ add_open_mesh_network(dev[i], freq="2462", basic_rates="60 120 240")
+
+ # Check for mesh joined
+ for i in range(0, 3):
+ check_mesh_group_added(dev[i])
+
+ state = dev[i].get_status_field("wpa_state")
+ if state != "COMPLETED":
+ raise Exception("Unexpected wpa_state on dev" + str(i) + ": " + state)
+
+ mode = dev[i].get_status_field("mode")
+ if mode != "mesh":
+ raise Exception("Unexpected mode: " + mode)
+
+ # set node 2 as RANN supported root
+ subprocess.check_call(["iw", "dev", dev[0].ifname, "set", "mesh_param",
+ "mesh_hwmp_rootmode=0"])
+ subprocess.check_call(["iw", "dev", dev[1].ifname, "set", "mesh_param",
+ "mesh_hwmp_rootmode=0"])
+ subprocess.check_call(["iw", "dev", dev[2].ifname, "set", "mesh_param",
+ "mesh_hwmp_rootmode=4"])
+ subprocess.check_call(["iw", "dev", dev[2].ifname, "set", "mesh_param",
+ "mesh_hwmp_rann_interval=2000"])
+
+ # Check for peer connected
+ check_mesh_peer_connected(dev[0])
+ check_mesh_peer_connected(dev[0])
+ check_mesh_peer_connected(dev[1])
+ check_mesh_peer_connected(dev[2])
+
+ # Wait for RANN frame
+ time.sleep(10)
+
+ # Test connectivity 1->2 and 2->1
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+
+ # Check mpath table on 0
+ res, data = dev[0].cmd_execute(['iw', dev[0].ifname, 'mpath', 'dump'])
+ if res != 0:
+ raise Exception("iw command failed on dev0")
+ if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) == -1 or \
+ data.find(dev[2].own_addr() + ' ' + dev[2].own_addr()) == -1:
+ raise Exception("mpath not found on dev0:\n" + data)
+ if data.find(dev[0].own_addr()) > -1:
+ raise Exception("invalid mpath found on dev0:\n" + data)
+
+ # Check mpath table on 1
+ res, data = dev[1].cmd_execute(['iw', dev[1].ifname, 'mpath', 'dump'])
+ if res != 0:
+ raise Exception("iw command failed on dev1")
+ if data.find(dev[0].own_addr() + ' ' + dev[0].own_addr()) == -1 or \
+ data.find(dev[2].own_addr() + ' ' + dev[0].own_addr()) == -1:
+ raise Exception("mpath not found on dev1:\n" + data)
+ if data.find(dev[2].own_addr() + ' ' + dev[2].own_addr()) > -1 or \
+ data.find(dev[1].own_addr()) > -1:
+ raise Exception("invalid mpath found on dev1:\n" + data)
+
+ # Check mpath table on 2
+ res, data = dev[2].cmd_execute(['iw', dev[2].ifname, 'mpath', 'dump'])
+ if res != 0:
+ raise Exception("iw command failed on dev2")
+ if data.find(dev[0].own_addr() + ' ' + dev[0].own_addr()) == -1 or \
+ data.find(dev[1].own_addr() + ' ' + dev[0].own_addr()) == -1:
+ raise Exception("mpath not found on dev2:\n" + data)
+ if data.find(dev[1].own_addr() + ' ' + dev[1].own_addr()) > -1 or \
+ data.find(dev[2].own_addr()) > -1:
+ raise Exception("invalid mpath found on dev2:\n" + data)
+
+ # remove mesh groups
+ for i in range(0, 3):
+ dev[i].mesh_group_remove()
+ check_mesh_group_removed(dev[i])
+ dev[i].dump_monitor()
+
+def test_wmediumd_scan_only_one(dev, apdev, params):
+ """Test that scanning with a single active AP only returns that one (wmediund)"""
+ fd, fn = tempfile.mkstemp()
+ try:
+ f = os.fdopen(fd, 'w')
+ f.write(CFG % (apdev[0]['bssid'], dev[0].own_addr()))
+ f.close()
+ p = start_wmediumd(fn, params)
+ try:
+ _test_scan_only_one(dev, apdev)
+ finally:
+ stop_wmediumd(p, params)
+ finally:
+ os.unlink(fn)
diff --git a/contrib/wpa/tests/hwsim/test_wnm.py b/contrib/wpa/tests/hwsim/test_wnm.py
new file mode 100644
index 000000000000..354822327210
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_wnm.py
@@ -0,0 +1,1984 @@
+# WNM tests
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import binascii
+import struct
+import time
+import logging
+logger = logging.getLogger()
+import subprocess
+
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+from wlantest import Wlantest
+from datetime import datetime
+
+def clear_regdom_state(dev, hapd, hapd2):
+ for i in range(0, 3):
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ if ev is None or "init=COUNTRY_IE" in ev:
+ break
+ if hapd:
+ hapd.request("DISABLE")
+ if hapd2:
+ hapd2.request("DISABLE")
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].disconnect_and_stop_scan()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def start_wnm_ap(apdev, bss_transition=True, time_adv=False, ssid=None,
+ wnm_sleep_mode=False, wnm_sleep_mode_no_keys=False, rsn=False,
+ ocv=False, ap_max_inactivity=0, coloc_intf_reporting=False,
+ hw_mode=None, channel=None, country_code=None, country3=None,
+ pmf=True, passphrase=None, ht=True, vht=False, mbo=False,
+ beacon_prot=False):
+ if rsn:
+ if not ssid:
+ ssid = "test-wnm-rsn"
+ if not passphrase:
+ passphrase = "12345678"
+ params = hostapd.wpa2_params(ssid, passphrase)
+ if pmf:
+ params["wpa_key_mgmt"] = "WPA-PSK-SHA256"
+ params["ieee80211w"] = "2"
+ if beacon_prot:
+ params["beacon_prot"] = "1"
+ else:
+ params = {"ssid": "test-wnm"}
+ if bss_transition:
+ params["bss_transition"] = "1"
+ if time_adv:
+ params["time_advertisement"] = "2"
+ params["time_zone"] = "EST5"
+ if wnm_sleep_mode:
+ params["wnm_sleep_mode"] = "1"
+ if wnm_sleep_mode_no_keys:
+ params["wnm_sleep_mode_no_keys"] = "1"
+ if ocv:
+ params["ocv"] = "1"
+ if ap_max_inactivity:
+ params["ap_max_inactivity"] = str(ap_max_inactivity)
+ if coloc_intf_reporting:
+ params["coloc_intf_reporting"] = "1"
+ if hw_mode:
+ params["hw_mode"] = hw_mode
+ if channel:
+ params["channel"] = channel
+ if country_code:
+ params["country_code"] = country_code
+ params["ieee80211d"] = "1"
+ if country3:
+ params["country3"] = country3
+ if not ht:
+ params['ieee80211n'] = '0'
+ if vht:
+ params['ieee80211ac'] = "1"
+ params["vht_oper_chwidth"] = "0"
+ params["vht_oper_centr_freq_seg0_idx"] = "0"
+ if mbo:
+ params["mbo"] = "1"
+ try:
+ hapd = hostapd.add_ap(apdev, params)
+ except Exception as e:
+ if "Failed to set hostapd parameter ocv" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+ if rsn:
+ Wlantest.setup(hapd)
+ wt = Wlantest()
+ wt.flush()
+ wt.add_passphrase("12345678")
+ return hapd
+
+@remote_compatible
+def test_wnm_bss_transition_mgmt(dev, apdev):
+ """WNM BSS Transition Management"""
+ start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True)
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("WNM_BSS_QUERY 0")
+
+def test_wnm_bss_transition_mgmt_oom(dev, apdev):
+ """WNM BSS Transition Management OOM"""
+ hapd = start_wnm_ap(apdev[0])
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ with alloc_fail(hapd, 1, "ieee802_11_send_bss_trans_mgmt_request"):
+ dev[0].request("WNM_BSS_QUERY 0")
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+
+@remote_compatible
+def test_wnm_disassoc_imminent(dev, apdev):
+ """WNM Disassociation Imminent"""
+ hapd = start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True)
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].p2p_interface_addr()
+ hapd.request("DISASSOC_IMMINENT " + addr + " 10")
+ ev = dev[0].wait_event(["WNM: Disassociation Imminent"])
+ if ev is None:
+ raise Exception("Timeout while waiting for disassociation imminent")
+ if "Disassociation Timer 10" not in ev:
+ raise Exception("Unexpected disassociation imminent contents")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Timeout while waiting for re-connection scan")
+
+def test_wnm_disassoc_imminent_fail(dev, apdev):
+ """WNM Disassociation Imminent failure"""
+ hapd = start_wnm_ap(apdev[0])
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+ with fail_test(hapd, 1, "wnm_send_disassoc_imminent"):
+ if "FAIL" not in hapd.request("DISASSOC_IMMINENT " + addr + " 10"):
+ raise Exception("DISASSOC_IMMINENT succeeded during failure testing")
+
+@remote_compatible
+def test_wnm_ess_disassoc_imminent(dev, apdev):
+ """WNM ESS Disassociation Imminent"""
+ hapd = start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True)
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].p2p_interface_addr()
+ hapd.request("ESS_DISASSOC " + addr + " 10 http://example.com/session-info")
+ ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"])
+ if ev is None:
+ raise Exception("Timeout while waiting for ESS disassociation imminent")
+ if "0 1024 http://example.com/session-info" not in ev:
+ raise Exception("Unexpected ESS disassociation imminent message contents")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Timeout while waiting for re-connection scan")
+
+def test_wnm_ess_disassoc_imminent_fail(dev, apdev):
+ """WNM ESS Disassociation Imminent failure"""
+ hapd = start_wnm_ap(apdev[0])
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+ if "FAIL" not in hapd.request("ESS_DISASSOC " + addr + " 10 http://" + 256*'a'):
+ raise Exception("Invalid ESS_DISASSOC URL accepted")
+ with fail_test(hapd, 1, "wnm_send_ess_disassoc_imminent"):
+ if "FAIL" not in hapd.request("ESS_DISASSOC " + addr + " 10 http://example.com/session-info"):
+ raise Exception("ESS_DISASSOC succeeded during failure testing")
+
+def test_wnm_ess_disassoc_imminent_reject(dev, apdev):
+ """WNM ESS Disassociation Imminent getting rejected"""
+ hapd = start_wnm_ap(apdev[0])
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+ if "OK" not in dev[0].request("SET reject_btm_req_reason 123"):
+ raise Exception("Failed to set reject_btm_req_reason")
+
+ hapd.request("ESS_DISASSOC " + addr + " 1 http://example.com/session-info")
+ ev = hapd.wait_event(["BSS-TM-RESP"], timeout=10)
+ if ev is None:
+ raise Exception("BSS-TM-RESP not seen")
+ if "status_code=123" not in ev:
+ raise Exception("Unexpected response status: " + ev)
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+
+@remote_compatible
+def test_wnm_ess_disassoc_imminent_pmf(dev, apdev):
+ """WNM ESS Disassociation Imminent"""
+ hapd = start_wnm_ap(apdev[0], rsn=True)
+ dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+ addr = dev[0].p2p_interface_addr()
+ hapd.request("ESS_DISASSOC " + addr + " 10 http://example.com/session-info")
+ ev = dev[0].wait_event(["ESS-DISASSOC-IMMINENT"])
+ if ev is None:
+ raise Exception("Timeout while waiting for ESS disassociation imminent")
+ if "1 1024 http://example.com/session-info" not in ev:
+ raise Exception("Unexpected ESS disassociation imminent message contents")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Timeout while waiting for re-connection scan")
+
+def check_wnm_sleep_mode_enter_exit(hapd, dev, interval=None, tfs_req=None,
+ rekey=False):
+ addr = dev.p2p_interface_addr()
+ sta = hapd.get_sta(addr)
+ if "[WNM_SLEEP_MODE]" in sta['flags']:
+ raise Exception("Station unexpectedly in WNM-Sleep Mode")
+
+ logger.info("Going to WNM Sleep Mode")
+ extra = ""
+ if interval is not None:
+ extra += " interval=" + str(interval)
+ if tfs_req:
+ extra += " tfs_req=" + tfs_req
+ if "OK" not in dev.request("WNM_SLEEP enter" + extra):
+ raise Exception("WNM_SLEEP failed")
+ ok = False
+ for i in range(20):
+ time.sleep(0.1)
+ sta = hapd.get_sta(addr)
+ if "[WNM_SLEEP_MODE]" in sta['flags']:
+ ok = True
+ break
+ if not ok:
+ raise Exception("Station failed to enter WNM-Sleep Mode")
+
+ if rekey:
+ time.sleep(0.1)
+ if "OK" not in hapd.request("REKEY_GTK"):
+ raise Exception("REKEY_GTK failed")
+ ev = dev.wait_event(["WPA: Group rekeying completed"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected report of GTK rekey during WNM-Sleep Mode")
+
+ logger.info("Waking up from WNM Sleep Mode")
+ ok = False
+ dev.request("WNM_SLEEP exit")
+ for i in range(20):
+ time.sleep(0.1)
+ sta = hapd.get_sta(addr)
+ if "[WNM_SLEEP_MODE]" not in sta['flags']:
+ ok = True
+ break
+ if not ok:
+ raise Exception("Station failed to exit WNM-Sleep Mode")
+
+ if rekey:
+ time.sleep(0.1)
+ if "OK" not in hapd.request("REKEY_GTK"):
+ raise Exception("REKEY_GTK failed")
+ ev = dev.wait_event(["WPA: Group rekeying completed"], timeout=2)
+ if ev is None:
+ raise Exception("GTK rekey timed out")
+
+@remote_compatible
+def test_wnm_sleep_mode_open(dev, apdev):
+ """WNM Sleep Mode - open"""
+ hapd = start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True)
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0], interval=100)
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0], tfs_req="5b17010001130e110000071122334455661122334455661234")
+
+ cmds = ["foo",
+ "exit tfs_req=123 interval=10",
+ "enter tfs_req=qq interval=10"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("WNM_SLEEP " + cmd):
+ raise Exception("Invalid WNM_SLEEP accepted")
+
+def test_wnm_sleep_mode_open_fail(dev, apdev):
+ """WNM Sleep Mode - open (fail)"""
+ hapd = start_wnm_ap(apdev[0], wnm_sleep_mode=True)
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ with fail_test(hapd, 1, "nl80211_send_frame_cmd;ieee802_11_send_wnmsleep_resp"):
+ dev[0].request("WNM_SLEEP enter")
+ wait_fail_trigger(hapd, "GET_FAIL")
+
+def test_wnm_sleep_mode_disabled_on_ap(dev, apdev):
+ """WNM Sleep Mode disabled on AP"""
+ hapd = start_wnm_ap(apdev[0], wnm_sleep_mode=False)
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ # Ignore WNM-Sleep Mode Request from 02:00:00:00:00:00 since WNM-Sleep Mode is disabled
+ dev[0].request("WNM_SLEEP enter")
+ time.sleep(0.1)
+
+@remote_compatible
+def test_wnm_sleep_mode_rsn(dev, apdev):
+ """WNM Sleep Mode - RSN"""
+ hapd = start_wnm_ap(apdev[0], time_adv=True, wnm_sleep_mode=True, rsn=True,
+ pmf=False)
+ dev[0].connect("test-wnm-rsn", psk="12345678", scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+
+@remote_compatible
+def test_wnm_sleep_mode_ap_oom(dev, apdev):
+ """WNM Sleep Mode - AP side OOM"""
+ hapd = start_wnm_ap(apdev[0], bss_transition=False, wnm_sleep_mode=True)
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ with alloc_fail(hapd, 1, "ieee802_11_send_wnmsleep_resp"):
+ dev[0].request("WNM_SLEEP enter")
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+ with alloc_fail(hapd, 2, "ieee802_11_send_wnmsleep_resp"):
+ dev[0].request("WNM_SLEEP exit")
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+
+@remote_compatible
+def test_wnm_sleep_mode_rsn_pmf(dev, apdev):
+ """WNM Sleep Mode - RSN with PMF"""
+ hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True, time_adv=True)
+ dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+
+def test_wnm_sleep_mode_rsn_beacon_prot(dev, apdev):
+ """WNM Sleep Mode - RSN with PMF and beacon protection"""
+ hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True, time_adv=True,
+ beacon_prot=True)
+ dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
+ beacon_prot="1",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0], rekey=True)
+
+@remote_compatible
+def test_wnm_sleep_mode_rsn_ocv(dev, apdev):
+ """WNM Sleep Mode - RSN with OCV"""
+ hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True,
+ time_adv=True, ocv=True)
+
+ dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2", ocv="1",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+
+ # Check if OCV succeeded or failed
+ ev = dev[0].wait_event(["OCV failed"], timeout=1)
+ if ev is not None:
+ raise Exception("OCI verification failed: " + ev)
+
+@remote_compatible
+def test_wnm_sleep_mode_rsn_badocv(dev, apdev):
+ """WNM Sleep Mode - RSN with OCV and bad OCI elements"""
+ ssid = "test-wnm-rsn"
+ hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True, ocv=True)
+ bssid = apdev[0]['bssid']
+ dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK-SHA256", ocv="1",
+ proto="WPA2", ieee80211w="2", scan_freq="2412")
+ dev[0].request("WNM_SLEEP enter")
+ time.sleep(0.1)
+
+ msg = {'fc': MGMT_SUBTYPE_ACTION << 4,
+ 'da': bssid,
+ 'sa': dev[0].own_addr(),
+ 'bssid': bssid}
+
+ logger.debug("WNM Sleep Mode Request - Missing OCI element")
+ msg['payload'] = struct.pack("<BBBBBBBHBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_REQ, 0,
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT, 0, 0,
+ WLAN_EID_TFS_REQ, 0)
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq=2412 wait_time=200 no_cck=1 action={}".format(
+ msg['da'], msg['bssid'], binascii.hexlify(msg['payload']).decode()))
+ ev = hapd.wait_event(["OCV failed"], timeout=5)
+ if ev is None:
+ raise Exception("AP did not report missing OCI element")
+
+ logger.debug("WNM Sleep Mode Request - Bad OCI element")
+ msg['payload'] = struct.pack("<BBBBBBBHBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_REQ, 0,
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT, 0,
+ 0,
+ WLAN_EID_TFS_REQ, 0)
+ oci_ie = struct.pack("<BBB", 81, 2, 0)
+ msg['payload'] += struct.pack("<BBB", WLAN_EID_EXTENSION, 1 + len(oci_ie),
+ WLAN_EID_EXT_OCV_OCI) + oci_ie
+ mgmt_tx(dev[0], "MGMT_TX {} {} freq=2412 wait_time=200 no_cck=1 action={}".format(
+ msg['da'], msg['bssid'], binascii.hexlify(msg['payload']).decode()))
+ ev = hapd.wait_event(["OCV failed"], timeout=5)
+ if ev is None:
+ raise Exception("AP did not report bad OCI element")
+
+ msg = {'fc': MGMT_SUBTYPE_ACTION << 4,
+ 'da': dev[0].own_addr(),
+ 'sa': bssid,
+ 'bssid': bssid}
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ logger.debug("WNM Sleep Mode Response - Missing OCI element")
+ msg['payload'] = struct.pack("<BBBHBBBBHBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0,
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
+ WNM_STATUS_SLEEP_ACCEPT, 0,
+ WLAN_EID_TFS_RESP, 0)
+ dev[0].request("WNM_SLEEP exit")
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+ ev = dev[0].wait_event(["OCV failed"], timeout=5)
+ if ev is None:
+ raise Exception("STA did not report missing OCI element")
+
+ logger.debug("WNM Sleep Mode Response - Bad OCI element")
+ msg['payload'] = struct.pack("<BBBHBBBBHBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0,
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
+ WNM_STATUS_SLEEP_ACCEPT, 0,
+ WLAN_EID_TFS_RESP, 0)
+ oci_ie = struct.pack("<BBB", 81, 2, 0)
+ msg['payload'] += struct.pack("<BBB", WLAN_EID_EXTENSION, 1 + len(oci_ie),
+ WLAN_EID_EXT_OCV_OCI) + oci_ie
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+ ev = dev[0].wait_event(["OCV failed"], timeout=5)
+ if ev is None:
+ raise Exception("STA did not report bad OCI element")
+
+def test_wnm_sleep_mode_rsn_ocv_failure(dev, apdev):
+ """WNM Sleep Mode - RSN with OCV - local failure"""
+ hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True,
+ time_adv=True, ocv=True)
+
+ dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2", ocv="1",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+ # Failed to allocate buffer for OCI element in WNM-Sleep Mode frame
+ with alloc_fail(hapd, 2, "ieee802_11_send_wnmsleep_resp"):
+ if "OK" not in dev[0].request("WNM_SLEEP enter"):
+ raise Exception("WNM_SLEEP failed")
+ wait_fail_trigger(hapd, "GET_ALLOC_FAIL")
+
+def test_wnm_sleep_mode_rsn_pmf_key_workaround(dev, apdev):
+ """WNM Sleep Mode - RSN with PMF and GTK/IGTK workaround"""
+ hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True,
+ wnm_sleep_mode_no_keys=True,
+ time_adv=True, ocv=True)
+ dev[0].connect("test-wnm-rsn", psk="12345678", ieee80211w="2",
+ key_mgmt="WPA-PSK-SHA256", proto="WPA2", scan_freq="2412")
+ ev = hapd.wait_event(["AP-STA-CONNECTED"], timeout=5)
+ if ev is None:
+ raise Exception("No connection event received from hostapd")
+ check_wnm_sleep_mode_enter_exit(hapd, dev[0])
+
+def test_wnm_sleep_mode_proto(dev, apdev):
+ """WNM Sleep Mode - protocol testing"""
+ hapd = start_wnm_ap(apdev[0], wnm_sleep_mode=True, bss_transition=False)
+ bssid = hapd.own_addr()
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
+ hapd.set("ext_mgmt_frame_handling", "1")
+ tests = ["0a10",
+ "0a1001",
+ "0a10015d00",
+ "0a10015d01",
+ "0a10015d0400000000",
+ "0a1001" + 7*("5bff" + 255*"00") + "5d00",
+ "0a1001ff00"]
+ for t in tests:
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+MGMT_SUBTYPE_ACTION = 13
+ACTION_CATEG_WNM = 10
+WNM_ACT_BSS_TM_REQ = 7
+WNM_ACT_BSS_TM_RESP = 8
+WNM_ACT_SLEEP_MODE_REQ = 16
+WNM_ACT_SLEEP_MODE_RESP = 17
+WNM_ACT_NOTIFICATION_REQ = 26
+WNM_ACT_NOTIFICATION_RESP = 27
+WNM_NOTIF_TYPE_FW_UPGRADE = 0
+WNM_NOTIF_TYPE_WFA = 1
+WLAN_EID_TFS_REQ = 91
+WLAN_EID_TFS_RESP = 92
+WLAN_EID_WNMSLEEP = 93
+WLAN_EID_EXTENSION = 255
+WLAN_EID_EXT_OCV_OCI = 54
+WNM_SLEEP_MODE_ENTER = 0
+WNM_SLEEP_MODE_EXIT = 1
+WNM_STATUS_SLEEP_ACCEPT = 0
+WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE = 1
+WNM_STATUS_DENIED_ACTION = 2
+WNM_STATUS_DENIED_TMP = 3
+WNM_STATUS_DENIED_KEY = 4
+WNM_STATUS_DENIED_OTHER_WNM_SERVICE = 5
+WNM_SLEEP_SUBELEM_GTK = 0
+WNM_SLEEP_SUBELEM_IGTK = 1
+
+def bss_tm_req(dst, src, dialog_token=1, req_mode=0, disassoc_timer=0,
+ validity_interval=1):
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = dst
+ msg['sa'] = src
+ msg['bssid'] = src
+ msg['payload'] = struct.pack("<BBBBHB",
+ ACTION_CATEG_WNM, WNM_ACT_BSS_TM_REQ,
+ dialog_token, req_mode, disassoc_timer,
+ validity_interval)
+ return msg
+
+def rx_bss_tm_resp(hapd, expect_dialog=None, expect_status=None):
+ for i in range(0, 100):
+ resp = hapd.mgmt_rx()
+ if resp is None:
+ raise Exception("No BSS TM Response received")
+ if resp['subtype'] == MGMT_SUBTYPE_ACTION:
+ break
+ if i == 99:
+ raise Exception("Not an Action frame")
+ payload = resp['payload']
+ if len(payload) < 2 + 3:
+ raise Exception("Too short payload")
+ (category, action) = struct.unpack('BB', payload[0:2])
+ if category != ACTION_CATEG_WNM or action != WNM_ACT_BSS_TM_RESP:
+ raise Exception("Not a BSS TM Response")
+ pos = payload[2:]
+ (dialog, status, bss_term_delay) = struct.unpack('BBB', pos[0:3])
+ resp['dialog'] = dialog
+ resp['status'] = status
+ resp['bss_term_delay'] = bss_term_delay
+ pos = pos[3:]
+ if len(pos) >= 6 and status == 0:
+ resp['target_bssid'] = binascii.hexlify(pos[0:6])
+ pos = pos[6:]
+ resp['candidates'] = pos
+ if expect_dialog is not None and dialog != expect_dialog:
+ raise Exception("Unexpected dialog token")
+ if expect_status is not None and status != expect_status:
+ raise Exception("Unexpected status code %d" % status)
+ return resp
+
+def expect_ack(hapd):
+ ev = hapd.wait_event(["MGMT-TX-STATUS"], timeout=5)
+ if ev is None:
+ raise Exception("Missing TX status")
+ if "ok=1" not in ev:
+ raise Exception("Action frame not acknowledged")
+
+def mgmt_tx(dev, msg):
+ if "FAIL" in dev.request(msg):
+ raise Exception("Failed to send Action frame")
+ ev = dev.wait_event(["MGMT-TX-STATUS"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on MGMT-TX-STATUS")
+ if "result=SUCCESS" not in ev:
+ raise Exception("Peer did not ack Action frame")
+
+@remote_compatible
+def test_wnm_bss_tm_req(dev, apdev):
+ """BSS Transition Management Request"""
+ hapd = start_wnm_ap(apdev[0])
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ # truncated BSS TM Request
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x08)
+ req['payload'] = struct.pack("<BBBBH",
+ ACTION_CATEG_WNM, WNM_ACT_BSS_TM_REQ,
+ 1, 0, 0)
+ hapd.mgmt_tx(req)
+ expect_ack(hapd)
+ dev[0].dump_monitor()
+
+ # no disassociation and no candidate list
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ dialog_token=2)
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=2, expect_status=1)
+ dev[0].dump_monitor()
+
+ # truncated BSS Termination Duration
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x08)
+ hapd.mgmt_tx(req)
+ expect_ack(hapd)
+ dev[0].dump_monitor()
+
+ # BSS Termination Duration with TSF=0 and Duration=10
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x08, dialog_token=3)
+ req['payload'] += struct.pack("<BBQH", 4, 10, 0, 10)
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=3, expect_status=1)
+ dev[0].dump_monitor()
+
+ # truncated Session Information URL
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x10)
+ hapd.mgmt_tx(req)
+ expect_ack(hapd)
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x10)
+ req['payload'] += struct.pack("<BBB", 3, 65, 66)
+ hapd.mgmt_tx(req)
+ expect_ack(hapd)
+ dev[0].dump_monitor()
+
+ # Session Information URL
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x10, dialog_token=4)
+ req['payload'] += struct.pack("<BBB", 2, 65, 66)
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=4, expect_status=0)
+ dev[0].dump_monitor()
+
+ # Preferred Candidate List without any entries
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x01, dialog_token=5)
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=5, expect_status=7)
+ dev[0].dump_monitor()
+
+ # Preferred Candidate List with a truncated entry
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x01)
+ req['payload'] += struct.pack("<BB", 52, 1)
+ hapd.mgmt_tx(req)
+ expect_ack(hapd)
+ dev[0].dump_monitor()
+
+ # Preferred Candidate List with a too short entry
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x01, dialog_token=6)
+ req['payload'] += struct.pack("<BB", 52, 0)
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=6, expect_status=7)
+ dev[0].dump_monitor()
+
+ # Preferred Candidate List with a non-matching entry
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x01, dialog_token=6)
+ req['payload'] += struct.pack("<BB6BLBBB", 52, 13,
+ 1, 2, 3, 4, 5, 6,
+ 0, 81, 1, 7)
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=6, expect_status=7)
+ dev[0].dump_monitor()
+
+ # Preferred Candidate List with a truncated subelement
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x01, dialog_token=7)
+ req['payload'] += struct.pack("<BB6BLBBBBB", 52, 13 + 2,
+ 1, 2, 3, 4, 5, 6,
+ 0, 81, 1, 7,
+ 1, 1)
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=7, expect_status=7)
+ dev[0].dump_monitor()
+
+ # Preferred Candidate List with lots of invalid optional subelements
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x01, dialog_token=8)
+ subelems = struct.pack("<BBHB", 1, 3, 0, 100)
+ subelems += struct.pack("<BBB", 2, 1, 65)
+ subelems += struct.pack("<BB", 3, 0)
+ subelems += struct.pack("<BBQB", 4, 9, 0, 10)
+ subelems += struct.pack("<BBHLB", 5, 7, 0, 0, 0)
+ subelems += struct.pack("<BB", 66, 0)
+ subelems += struct.pack("<BBBBBB", 70, 4, 0, 0, 0, 0)
+ subelems += struct.pack("<BB", 71, 0)
+ req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems),
+ 1, 2, 3, 4, 5, 6,
+ 0, 81, 1, 7) + subelems
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
+ dev[0].dump_monitor()
+
+ # Preferred Candidate List with lots of valid optional subelements (twice)
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x01, dialog_token=8)
+ # TSF Information
+ subelems = struct.pack("<BBHH", 1, 4, 0, 100)
+ # Condensed Country String
+ subelems += struct.pack("<BBBB", 2, 2, 65, 66)
+ # BSS Transition Candidate Preference
+ subelems += struct.pack("<BBB", 3, 1, 100)
+ # BSS Termination Duration
+ subelems += struct.pack("<BBQH", 4, 10, 0, 10)
+ # Bearing
+ subelems += struct.pack("<BBHLH", 5, 8, 0, 0, 0)
+ # Measurement Pilot Transmission
+ subelems += struct.pack("<BBBBB", 66, 3, 0, 0, 0)
+ # RM Enabled Capabilities
+ subelems += struct.pack("<BBBBBBB", 70, 5, 0, 0, 0, 0, 0)
+ # Multiple BSSID
+ subelems += struct.pack("<BBBB", 71, 2, 0, 0)
+ req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems) * 2,
+ 1, 2, 3, 4, 5, 6,
+ 0, 81, 1, 7) + subelems + subelems
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
+ dev[0].dump_monitor()
+
+ # Preferred Candidate List with truncated BSS Termination Duration
+ # WNM: Too short BSS termination duration
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x01, dialog_token=8)
+ # BSS Termination Duration (truncated)
+ subelems = struct.pack("<BBQB", 4, 9, 0, 10)
+ req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems),
+ 1, 2, 3, 4, 5, 6,
+ 0, 81, 1, 7) + subelems
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
+ dev[0].dump_monitor()
+
+ # Preferred Candidate List followed by vendor element
+ req = bss_tm_req(addr, apdev[0]['bssid'],
+ req_mode=0x01, dialog_token=8)
+ subelems = b''
+ req['payload'] += struct.pack("<BB6BLBBB", 52, 13 + len(subelems),
+ 1, 2, 3, 4, 5, 6,
+ 0, 81, 1, 7) + subelems
+ req['payload'] += binascii.unhexlify("DD0411223344")
+ hapd.mgmt_tx(req)
+ resp = rx_bss_tm_resp(hapd, expect_dialog=8, expect_status=7)
+ dev[0].dump_monitor()
+
+@remote_compatible
+def test_wnm_bss_keep_alive(dev, apdev):
+ """WNM keep-alive"""
+ hapd = start_wnm_ap(apdev[0], bss_transition=False, ap_max_inactivity=1)
+ addr = dev[0].p2p_interface_addr()
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ start = hapd.get_sta(addr)
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=2)
+ if ev is not None:
+ raise Exception("Unexpected disconnection")
+ end = hapd.get_sta(addr)
+ if int(end['rx_packets']) <= int(start['rx_packets']):
+ raise Exception("No keep-alive packets received")
+ try:
+ # Disable client keep-alive so that hostapd will verify connection
+ # with client poll
+ dev[0].request("SET no_keep_alive 1")
+ for i in range(60):
+ sta = hapd.get_sta(addr)
+ logger.info("timeout_next=%s rx_packets=%s tx_packets=%s" % (sta['timeout_next'], sta['rx_packets'], sta['tx_packets']))
+ if i > 1 and sta['timeout_next'] != "NULLFUNC POLL" and int(sta['tx_packets']) > int(end['tx_packets']):
+ break
+ ev = dev[0].wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected disconnection (client poll expected)")
+ finally:
+ dev[0].request("SET no_keep_alive 0")
+ if int(sta['tx_packets']) <= int(end['tx_packets']):
+ raise Exception("No client poll packet seen")
+
+def test_wnm_bss_tm(dev, apdev):
+ """WNM BSS Transition Management"""
+ try:
+ hapd = None
+ hapd2 = None
+ hapd = start_wnm_ap(apdev[0], country_code="FI")
+ id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ dev[0].set_network(id, "scan_freq", "")
+
+ hapd2 = start_wnm_ap(apdev[1], country_code="FI", hw_mode="a",
+ channel="36")
+
+ addr = dev[0].p2p_interface_addr()
+ dev[0].dump_monitor()
+
+ logger.info("No neighbor list entries")
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if addr not in ev:
+ raise Exception("Unexpected BSS Transition Management Response address")
+ if "status_code=0" in ev:
+ raise Exception("BSS transition accepted unexpectedly")
+ dev[0].dump_monitor()
+
+ logger.info("Neighbor list entry, but not claimed as Preferred Candidate List")
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " neighbor=11:22:33:44:55:66,0x0000,81,3,7"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" in ev:
+ raise Exception("BSS transition accepted unexpectedly")
+ dev[0].dump_monitor()
+
+ logger.info("Preferred Candidate List (no matching neighbor) without Disassociation Imminent")
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 neighbor=11:22:33:44:55:66,0x0000,81,3,7,0301ff neighbor=22:33:44:55:66:77,0x0000,1,44,7 neighbor=00:11:22:33:44:55,0x0000,81,4,7,03010a"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" in ev:
+ raise Exception("BSS transition accepted unexpectedly")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("No scan started")
+ dev[0].dump_monitor()
+
+ logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ if "target_bssid=" + apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected target BSS: " + ev)
+ dev[0].wait_connected(timeout=15, error="No reassociation seen")
+ if apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected reassociation target: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected scan started")
+ dev[0].dump_monitor()
+
+ logger.info("Preferred Candidate List with two matches, no roam needed")
+ if "OK" not in hapd2.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[0]['bssid'] + ",0x0000,81,1,7,030101 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd2.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ if "target_bssid=" + apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected target BSS: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected scan started")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected reassociation")
+
+ logger.info("Preferred Candidate List with two matches and extra frequency (160 MHz), no roam needed")
+ if "OK" not in hapd2.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[0]['bssid'] + ",0x0000,81,1,7,030101 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff" + ' neighbor=00:11:22:33:44:55,0x0000,129,36,7'):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd2.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ if "target_bssid=" + apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected target BSS: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected scan started")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected reassociation")
+ finally:
+ clear_regdom_state(dev, hapd, hapd2)
+
+def test_wnm_bss_tm_steering_timeout(dev, apdev):
+ """WNM BSS Transition Management and steering timeout"""
+ hapd = start_wnm_ap(apdev[0])
+ id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ hapd2 = start_wnm_ap(apdev[1])
+ dev[0].scan_for_bss(apdev[1]['bssid'], 2412)
+ hapd2.disable()
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,81,1,7,0301ff"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=5)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ # Wait for the ap_sta_reset_steer_flag_timer timeout to occur
+ # "Reset steering flag for STA 02:00:00:00:00:00"
+ time.sleep(2.1)
+
+ ev = dev[0].wait_event(["Trying to authenticate"], timeout=5)
+ if ev is None:
+ raise Exception("No authentication attempt seen")
+ if hapd2.own_addr() not in ev:
+ raise Exception("Unexpected authentication target: " + ev)
+ # Wait for return back to the previous AP
+ dev[0].wait_connected()
+
+def test_wnm_bss_tm_errors(dev, apdev):
+ """WNM BSS Transition Management errors"""
+ hapd = start_wnm_ap(apdev[0])
+ id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ tests = ["BSS_TM_REQ q",
+ "BSS_TM_REQ 22:22:22:22:22:22",
+ "BSS_TM_REQ %s disassoc_timer=-1" % addr,
+ "BSS_TM_REQ %s disassoc_timer=65536" % addr,
+ "BSS_TM_REQ %s bss_term=foo" % addr,
+ "BSS_TM_REQ %s neighbor=q" % addr,
+ "BSS_TM_REQ %s neighbor=02:11:22:33:44:55" % addr,
+ "BSS_TM_REQ %s neighbor=02:11:22:33:44:55,0" % addr,
+ "BSS_TM_REQ %s neighbor=02:11:22:33:44:55,0,0" % addr,
+ "BSS_TM_REQ %s neighbor=02:11:22:33:44:55,0,0,0" % addr,
+ "BSS_TM_REQ %s neighbor=02:11:22:33:44:55,0,0,0,0,q" % addr,
+ "BSS_TM_REQ %s neighbor=02:11:22:33:44:55,0,0,0,0,0q" % addr,
+ "BSS_TM_REQ " + addr + " url=" + 256*'a',
+ "BSS_TM_REQ %s url=foo mbo=1:2" % addr,
+ "BSS_TM_REQ %s url=foo mbo=100000:0:0" % addr,
+ "BSS_TM_REQ %s url=foo mbo=0:0:254" % addr,
+ "BSS_TM_REQ %s url=foo mbo=0:100000:0" % addr]
+ for t in tests:
+ if "FAIL" not in hapd.request(t):
+ raise Exception("Invalid command accepted: %s" % t)
+
+ with alloc_fail(hapd, 1, "=hostapd_ctrl_iface_bss_tm_req"):
+ if "FAIL" not in hapd.request("BSS_TM_REQ %s url=http://foo" % addr):
+ raise Exception("BSS_TM_REQ accepted during OOM")
+
+ with alloc_fail(hapd, 1, "=wnm_send_bss_tm_req"):
+ if "FAIL" not in hapd.request("BSS_TM_REQ %s url=http://foo" % addr):
+ raise Exception("BSS_TM_REQ accepted during OOM")
+
+ with fail_test(hapd, 1, "wnm_send_bss_tm_req"):
+ if "FAIL" not in hapd.request("BSS_TM_REQ %s url=http://foo" % addr):
+ raise Exception("BSS_TM_REQ accepted during failure testing")
+
+def test_wnm_bss_tm_termination(dev, apdev):
+ """WNM BSS Transition Management and BSS termination"""
+ hapd = start_wnm_ap(apdev[0])
+ id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ if "OK" not in hapd.request("BSS_TM_REQ %s bss_term=0,1" % addr):
+ raise Exception("BSS_TM_REQ failed")
+ ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
+ if ev is None:
+ raise Exception("No BSS-TM-RESP event seen")
+
+ if "OK" not in hapd.request("BSS_TM_REQ %s url=http://example.com/" % addr):
+ raise Exception("BSS_TM_REQ failed")
+ ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
+ if ev is None:
+ raise Exception("No BSS-TM-RESP event seen")
+
+def test_wnm_bss_tm_scan_not_needed(dev, apdev):
+ """WNM BSS Transition Management and scan not needed"""
+ run_wnm_bss_tm_scan_not_needed(dev, apdev)
+
+def test_wnm_bss_tm_nei_vht(dev, apdev):
+ """WNM BSS Transition Management and VHT neighbor"""
+ run_wnm_bss_tm_scan_not_needed(dev, apdev, vht=True, nei_info="115,36,9")
+
+def test_wnm_bss_tm_nei_11a(dev, apdev):
+ """WNM BSS Transition Management and 11a neighbor"""
+ run_wnm_bss_tm_scan_not_needed(dev, apdev, ht=False, nei_info="115,36,4")
+
+def test_wnm_bss_tm_nei_11g(dev, apdev):
+ """WNM BSS Transition Management and 11g neighbor"""
+ run_wnm_bss_tm_scan_not_needed(dev, apdev, ht=False, hwmode='g',
+ channel='2', freq=2417, nei_info="81,2,6")
+
+def test_wnm_bss_tm_nei_11b(dev, apdev):
+ """WNM BSS Transition Management and 11g neighbor"""
+ run_wnm_bss_tm_scan_not_needed(dev, apdev, ht=False, hwmode='b',
+ channel='3', freq=2422, nei_info="81,2,5")
+
+def run_wnm_bss_tm_scan_not_needed(dev, apdev, ht=True, vht=False, hwmode='a',
+ channel='36', freq=5180,
+ nei_info="115,36,7,0301ff"):
+ try:
+ hapd = None
+ hapd2 = None
+ hapd = start_wnm_ap(apdev[0], country_code="FI", hw_mode="g",
+ channel="1")
+ hapd2 = start_wnm_ap(apdev[1], country_code="FI", hw_mode=hwmode,
+ channel=channel, ht=ht, vht=vht)
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq)
+
+ id = dev[0].connect("test-wnm", key_mgmt="NONE",
+ bssid=apdev[0]['bssid'], scan_freq="2412")
+ dev[0].set_network(id, "scan_freq", "")
+ dev[0].set_network(id, "bssid", "")
+
+ addr = dev[0].own_addr()
+ dev[0].dump_monitor()
+
+ logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000," + nei_info):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ if "target_bssid=" + apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected target BSS: " + ev)
+ dev[0].wait_connected(timeout=15, error="No reassociation seen")
+ if apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected reassociation target: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected scan started")
+ dev[0].dump_monitor()
+ finally:
+ clear_regdom_state(dev, hapd, hapd2)
+
+def test_wnm_bss_tm_scan_needed(dev, apdev):
+ """WNM BSS Transition Management and scan needed"""
+ try:
+ hapd = None
+ hapd2 = None
+ hapd = start_wnm_ap(apdev[0], country_code="FI", hw_mode="g",
+ channel="1")
+ hapd2 = start_wnm_ap(apdev[1], country_code="FI", hw_mode="a",
+ channel="36")
+
+ dev[0].scan_for_bss(apdev[1]['bssid'], 5180)
+
+ id = dev[0].connect("test-wnm", key_mgmt="NONE",
+ bssid=apdev[0]['bssid'], scan_freq="2412")
+ dev[0].set_network(id, "scan_freq", "")
+ dev[0].set_network(id, "bssid", "")
+
+ addr = dev[0].own_addr()
+ dev[0].dump_monitor()
+
+ logger.info("Wait 11 seconds for the last scan result to be too old, but still present in BSS table")
+ time.sleep(11)
+ logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ if "target_bssid=" + apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected target BSS: " + ev)
+ dev[0].wait_connected(timeout=15, error="No reassociation seen")
+ if apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected reassociation target: " + ev)
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected scan started")
+ dev[0].dump_monitor()
+ finally:
+ clear_regdom_state(dev, hapd, hapd2)
+
+def test_wnm_bss_tm_scan_needed_e4(dev, apdev):
+ """WNM BSS Transition Management and scan needed (Table E-4)"""
+ try:
+ hapd = None
+ hapd2 = None
+ hapd = start_wnm_ap(apdev[0], country_code="FI", country3="0x04",
+ hw_mode="g", channel="1")
+ hapd2 = start_wnm_ap(apdev[1], country_code="FI", country3="0x04",
+ hw_mode="a", channel="36")
+ id = dev[0].connect("test-wnm", key_mgmt="NONE",
+ bssid=apdev[0]['bssid'], scan_freq="2412")
+ dev[0].set_network(id, "scan_freq", "")
+ dev[0].set_network(id, "bssid", "")
+
+ addr = dev[0].own_addr()
+ dev[0].dump_monitor()
+
+ logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=4)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response seen quickly enough - did scan optimization fail?")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ dev[0].wait_connected(timeout=15, error="No reassociation seen")
+ # Wait for regdom change due to country IE to avoid issues with that
+ # processing happening only after the disconnection and cfg80211 ending
+ # up intersecting regdoms when we try to clear state back to world (00)
+ # regdom below.
+ while True:
+ ev = dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ if not ev or "COUNTRY_IE" in ev:
+ break
+ dev[0].dump_monitor()
+ finally:
+ clear_regdom_state(dev, hapd, hapd2)
+
+def start_wnm_tm(ap, country, dev, country3=None):
+ hapd = start_wnm_ap(ap, country_code=country, country3=country3)
+ id = dev.connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ wait_regdom_changes(dev)
+ dev.dump_monitor()
+ dev.set_network(id, "scan_freq", "")
+ return hapd, id
+
+def stop_wnm_tm(hapd, dev):
+ if hapd:
+ hapd.request("DISABLE")
+ time.sleep(0.1)
+ dev[0].disconnect_and_stop_scan()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ wait_regdom_changes(dev[0])
+ country = dev[0].get_driver_status_field("country")
+ logger.info("Country code at the end: " + country)
+ if country != "00":
+ clear_country(dev)
+
+ dev[0].flush_scan_cache()
+
+def wnm_bss_tm_check(hapd, dev, data):
+ addr = dev.p2p_interface_addr()
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " " + data):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("No scan started")
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
+ if ev is None:
+ raise Exception("Scan did not complete")
+
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=7" not in ev:
+ raise Exception("Unexpected response: " + ev)
+
+def test_wnm_bss_tm_country_us(dev, apdev):
+ """WNM BSS Transition Management (US)"""
+ try:
+ hapd = None
+ hapd, id = start_wnm_tm(apdev[0], "US", dev[0])
+
+ logger.info("Preferred Candidate List (no matching neighbor, known channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,12,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,2,52,7,03010a neighbor=00:11:22:33:44:57,0x0000,4,100,7 neighbor=00:11:22:33:44:59,0x0000,3,149,7 neighbor=00:11:22:33:44:5b,0x0000,34,1,7 neighbor=00:11:22:33:44:5d,0x0000,5,149,7")
+
+ # Make the test take less time by limiting full scans
+ dev[0].set_network(id, "scan_freq", "2412")
+ logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,12,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,12,12,7 neighbor=00:11:22:33:44:55,0x0000,2,35,7,03010a neighbor=00:11:22:33:44:56,0x0000,2,65,7 neighbor=00:11:22:33:44:57,0x0000,4,99,7 neighbor=00:11:22:33:44:58,0x0000,4,145,7")
+
+ logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:59,0x0000,3,148,7 neighbor=00:11:22:33:44:5a,0x0000,3,162,7 neighbor=00:11:22:33:44:5b,0x0000,34,0,7 neighbor=00:11:22:33:44:5c,0x0000,34,4,7 neighbor=00:11:22:33:44:5d,0x0000,5,148,7 neighbor=00:11:22:33:44:5e,0x0000,5,166,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
+ finally:
+ stop_wnm_tm(hapd, dev)
+
+def test_wnm_bss_tm_country_fi(dev, apdev):
+ """WNM BSS Transition Management (FI)"""
+ addr = dev[0].p2p_interface_addr()
+ try:
+ hapd = None
+ hapd, id = start_wnm_tm(apdev[0], "FI", dev[0])
+
+ logger.info("Preferred Candidate List (no matching neighbor, known channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,4,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,1,36,7,03010a neighbor=00:11:22:33:44:57,0x0000,3,100,7 neighbor=00:11:22:33:44:59,0x0000,17,149,7 neighbor=00:11:22:33:44:5c,0x0000,18,1,7")
+
+ # Make the test take less time by limiting full scans
+ dev[0].set_network(id, "scan_freq", "2412")
+ logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,4,0,7 neighbor=00:11:22:33:44:01,0x0000,4,14,7 neighbor=00:11:22:33:44:02,0x0000,1,35,7 neighbor=00:11:22:33:44:03,0x0000,1,65,7 neighbor=00:11:22:33:44:04,0x0000,3,99,7 neighbor=00:11:22:33:44:05,0x0000,3,141,7 neighbor=00:11:22:33:44:06,0x0000,17,148,7 neighbor=00:11:22:33:44:07,0x0000,17,170,7 neighbor=00:11:22:33:44:08,0x0000,18,0,7 neighbor=00:11:22:33:44:09,0x0000,18,5,7")
+
+ logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,0,0,7")
+ finally:
+ stop_wnm_tm(hapd, dev)
+
+def test_wnm_bss_tm_country_jp(dev, apdev):
+ """WNM BSS Transition Management (JP)"""
+ addr = dev[0].p2p_interface_addr()
+ try:
+ hapd = None
+ hapd, id = start_wnm_tm(apdev[0], "JP", dev[0])
+
+ logger.info("Preferred Candidate List (no matching neighbor, known channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,30,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,31,14,7,03010a neighbor=00:11:22:33:44:57,0x0000,1,36,7 neighbor=00:11:22:33:44:59,0x0000,34,100,7 neighbor=00:11:22:33:44:5c,0x0000,59,1,7")
+
+ # Make the test take less time by limiting full scans
+ dev[0].set_network(id, "scan_freq", "2412")
+ logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,30,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,30,14,7 neighbor=00:11:22:33:44:56,0x0000,31,13,7 neighbor=00:11:22:33:44:57,0x0000,1,33,7 neighbor=00:11:22:33:44:58,0x0000,1,65,7 neighbor=00:11:22:33:44:5a,0x0000,34,99,7 neighbor=00:11:22:33:44:5b,0x0000,34,141,7 neighbor=00:11:22:33:44:5d,0x0000,59,0,7 neighbor=00:11:22:33:44:5e,0x0000,59,4,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
+ finally:
+ stop_wnm_tm(hapd, dev)
+
+def test_wnm_bss_tm_country_cn(dev, apdev):
+ """WNM BSS Transition Management (CN)"""
+ addr = dev[0].p2p_interface_addr()
+ try:
+ hapd = None
+ hapd, id = start_wnm_tm(apdev[0], "CN", dev[0])
+
+ logger.info("Preferred Candidate List (no matching neighbor, known channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,7,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,1,36,7,03010a neighbor=00:11:22:33:44:57,0x0000,3,149,7 neighbor=00:11:22:33:44:59,0x0000,6,149,7")
+
+ # Make the test take less time by limiting full scans
+ dev[0].set_network(id, "scan_freq", "2412")
+ logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,7,0,7,0301ff neighbor=22:33:44:55:66:77,0x0000,7,14,7 neighbor=00:11:22:33:44:56,0x0000,1,35,7 neighbor=00:11:22:33:44:57,0x0000,1,65,7 neighbor=00:11:22:33:44:58,0x0000,3,148,7 neighbor=00:11:22:33:44:5a,0x0000,3,166,7 neighbor=00:11:22:33:44:5f,0x0000,0,0,7")
+ finally:
+ stop_wnm_tm(hapd, dev)
+
+def test_wnm_bss_tm_global(dev, apdev):
+ """WNM BSS Transition Management (global)"""
+ run_wnm_bss_tm_global(dev, apdev, "XX", None)
+
+def test_wnm_bss_tm_global4(dev, apdev):
+ """WNM BSS Transition Management (global; indicate table E-4)"""
+ run_wnm_bss_tm_global(dev, apdev, "FI", "0x04")
+
+def run_wnm_bss_tm_global(dev, apdev, country, country3):
+ addr = dev[0].p2p_interface_addr()
+ try:
+ hapd = None
+ hapd, id = start_wnm_tm(apdev[0], country, dev[0], country3=country3)
+
+ logger.info("Preferred Candidate List (no matching neighbor, known channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=11:22:33:44:55:66,0x0000,81,3,7,0301ff neighbor=00:11:22:33:44:55,0x0000,82,14,7,03010a neighbor=00:11:22:33:44:57,0x0000,83,1,7 neighbor=00:11:22:33:44:59,0x0000,115,36,7 neighbor=00:11:22:33:44:5a,0x0000,121,100,7 neighbor=00:11:22:33:44:5c,0x0000,124,149,7 neighbor=00:11:22:33:44:5d,0x0000,125,149,7 neighbor=00:11:22:33:44:5e,0x0000,128,42,7 neighbor=00:11:22:33:44:5f,0x0000,129,50,7 neighbor=00:11:22:33:44:60,0x0000,180,1,7")
+
+ # Make the test take less time by limiting full scans
+ dev[0].set_network(id, "scan_freq", "2412")
+ logger.info("Preferred Candidate List (no matching neighbor, unknown channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,81,0,7 neighbor=00:11:22:33:44:01,0x0000,81,14,7 neighbor=00:11:22:33:44:02,0x0000,82,13,7 neighbor=00:11:22:33:44:03,0x0000,83,0,7 neighbor=00:11:22:33:44:04,0x0000,83,14,7 neighbor=00:11:22:33:44:05,0x0000,115,35,7 neighbor=00:11:22:33:44:06,0x0000,115,65,7 neighbor=00:11:22:33:44:07,0x0000,121,99,7 neighbor=00:11:22:33:44:08,0x0000,121,141,7 neighbor=00:11:22:33:44:09,0x0000,124,148,7")
+
+ logger.info("Preferred Candidate List (no matching neighbor, unknown channels 2)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:00,0x0000,124,162,7 neighbor=00:11:22:33:44:01,0x0000,125,148,7 neighbor=00:11:22:33:44:02,0x0000,125,170,7 neighbor=00:11:22:33:44:03,0x0000,128,35,7 neighbor=00:11:22:33:44:04,0x0000,128,162,7 neighbor=00:11:22:33:44:05,0x0000,129,49,7 neighbor=00:11:22:33:44:06,0x0000,129,115,7 neighbor=00:11:22:33:44:07,0x0000,180,0,7 neighbor=00:11:22:33:44:08,0x0000,180,5,7 neighbor=00:11:22:33:44:09,0x0000,0,0,7")
+ finally:
+ stop_wnm_tm(hapd, dev)
+
+def test_wnm_bss_tm_op_class_0(dev, apdev):
+ """WNM BSS Transition Management with invalid operating class"""
+ try:
+ hapd = None
+ hapd, id = start_wnm_tm(apdev[0], "US", dev[0])
+
+ logger.info("Preferred Candidate List (no matching neighbor, invalid op class specified for channels)")
+ wnm_bss_tm_check(hapd, dev[0], "pref=1 neighbor=00:11:22:33:44:59,0x0000,0,149,7 neighbor=00:11:22:33:44:5b,0x0000,0,1,7")
+ finally:
+ stop_wnm_tm(hapd, dev)
+
+def test_wnm_bss_tm_rsn(dev, apdev):
+ """WNM BSS Transition Management with RSN"""
+ passphrase = "zxcvbnm,.-"
+ try:
+ hapd = None
+ hapd2 = None
+ hapd = start_wnm_ap(apdev[0], country_code="FI", hw_mode="g",
+ channel="1",
+ rsn=True, pmf=False, passphrase=passphrase)
+ hapd2 = start_wnm_ap(apdev[1], country_code="FI", hw_mode="a",
+ channel="36",
+ rsn=True, pmf=False, passphrase=passphrase)
+ dev[0].scan_for_bss(apdev[1]['bssid'], 5180)
+
+ id = dev[0].connect("test-wnm-rsn", psk=passphrase,
+ bssid=apdev[0]['bssid'], scan_freq="2412")
+ dev[0].set_network(id, "scan_freq", "")
+ dev[0].set_network(id, "bssid", "")
+
+ addr = dev[0].own_addr()
+ dev[0].dump_monitor()
+
+ time.sleep(0.5)
+ logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000," + "115,36,7,0301ff"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ if "target_bssid=" + apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected target BSS: " + ev)
+ dev[0].wait_connected(timeout=15, error="No reassociation seen")
+ if apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected reassociation target: " + ev)
+ finally:
+ clear_regdom_state(dev, hapd, hapd2)
+
+def test_wnm_action_proto(dev, apdev):
+ """WNM Action protocol testing"""
+ hapd = start_wnm_ap(apdev[0], bss_transition=False, wnm_sleep_mode=True)
+ bssid = apdev[0]['bssid']
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("WNM_SLEEP enter")
+ time.sleep(0.1)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = dev[0].own_addr()
+ msg['sa'] = bssid
+ msg['bssid'] = bssid
+
+ dialog_token = 1
+
+ logger.debug("Unexpected WNM-Notification Response")
+ # Note: This is actually not registered for user space processing in
+ # driver_nl80211.c nl80211_mgmt_subscribe_non_ap() and as such, won't make
+ # it to wpa_supplicant.
+ msg['payload'] = struct.pack("<BBBB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_RESP,
+ dialog_token, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("Truncated WNM-Notification Request (no Type field)")
+ msg['payload'] = struct.pack("<BBB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WFA WNM-Notification Request with truncated IE (min)")
+ msg['payload'] = struct.pack("<BBBBBB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token, WNM_NOTIF_TYPE_WFA, 0, 1)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WFA WNM-Notification Request with truncated IE (max)")
+ msg['payload'] = struct.pack("<BBBBBB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token, WNM_NOTIF_TYPE_WFA, 0, 255)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WFA WNM-Notification Request with too short IE")
+ msg['payload'] = struct.pack("<BBBBBB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token, WNM_NOTIF_TYPE_WFA, 0, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WFA WNM-Notification Request with truncated Sub Rem URL")
+ msg['payload'] = struct.pack(">BBBBBBLB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 5,
+ 0x506f9a00, 1)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WFA WNM-Notification Request with truncated Sub Rem URL(2)")
+ msg['payload'] = struct.pack(">BBBBBBLBB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 6,
+ 0x506f9a00, 1, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WFA WNM-Notification Request with truncated Sub Rem URL(3)")
+ msg['payload'] = struct.pack(">BBBBBBLB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 5,
+ 0x506f9a00, 0xff)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WFA WNM-Notification Request with truncated Deauth Imminent URL(min)")
+ msg['payload'] = struct.pack(">BBBBBBLBHB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 8,
+ 0x506f9a01, 0, 0, 1)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WFA WNM-Notification Request with truncated Deauth Imminent URL(max)")
+ msg['payload'] = struct.pack(">BBBBBBLBHB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 8,
+ 0x506f9a01, 0, 0, 0xff)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WFA WNM-Notification Request with unsupported IE")
+ msg['payload'] = struct.pack("<BBBBBBL",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token, WNM_NOTIF_TYPE_WFA, 0xdd, 4, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM-Notification Request with unknown WNM-Notification type 0")
+ msg['payload'] = struct.pack("<BBBB",
+ ACTION_CATEG_WNM, WNM_ACT_NOTIFICATION_REQ,
+ dialog_token, WNM_NOTIF_TYPE_FW_UPGRADE)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("Truncated WNM Sleep Mode Response - no Dialog Token")
+ msg['payload'] = struct.pack("<BB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("Truncated WNM Sleep Mode Response - no Key Data Length")
+ msg['payload'] = struct.pack("<BBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("Truncated WNM Sleep Mode Response - truncated Key Data (min)")
+ msg['payload'] = struct.pack("<BBBH",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 1)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("Truncated WNM Sleep Mode Response - truncated Key Data (max)")
+ msg['payload'] = struct.pack("<BBBH",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0xffff)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - truncated IE header")
+ msg['payload'] = struct.pack("<BBBHB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - truncated IE")
+ msg['payload'] = struct.pack("<BBBHBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0, 0, 1)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - Empty TFS Response")
+ msg['payload'] = struct.pack("<BBBHBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0, WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - EID 0 not recognized")
+ msg['payload'] = struct.pack("<BBBHBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0, 0, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - Empty WNM Sleep Mode element and TFS Response element")
+ msg['payload'] = struct.pack("<BBBHBBBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0, WLAN_EID_WNMSLEEP, 0, WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - WNM Sleep Mode element and empty TFS Response element")
+ msg['payload'] = struct.pack("<BBBHBBBBHBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0, WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_ENTER,
+ WNM_STATUS_SLEEP_ACCEPT, 0,
+ WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - WNM Sleep Mode element(exit, deny key) and empty TFS Response element")
+ msg['payload'] = struct.pack("<BBBHBBBBHBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0, WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
+ WNM_STATUS_DENIED_KEY, 0,
+ WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - WNM Sleep Mode element(enter, deny key) and empty TFS Response element")
+ msg['payload'] = struct.pack("<BBBHBBBBHBB",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ 0, WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_ENTER,
+ WNM_STATUS_DENIED_KEY, 0,
+ WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+@remote_compatible
+def test_wnm_action_proto_pmf(dev, apdev):
+ """WNM Action protocol testing (PMF enabled)"""
+ ssid = "test-wnm-pmf"
+ hapd = start_wnm_ap(apdev[0], rsn=True, wnm_sleep_mode=True, ssid=ssid)
+ bssid = apdev[0]['bssid']
+ dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK-SHA256",
+ proto="WPA2", ieee80211w="2", scan_freq="2412")
+ dev[0].request("WNM_SLEEP enter")
+ time.sleep(0.1)
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = dev[0].own_addr()
+ msg['sa'] = bssid
+ msg['bssid'] = bssid
+
+ logger.debug("WNM Sleep Mode Response - Invalid Key Data element length")
+ keydata = struct.pack("<BB", 0, 1)
+ msg['payload'] = struct.pack("<BBBH",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ len(keydata))
+ msg['payload'] += keydata
+ msg['payload'] += struct.pack("<BBBBHBB",
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
+ WNM_STATUS_SLEEP_ACCEPT, 0,
+ WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - Too short GTK subelem")
+ keydata = struct.pack("<BB", WNM_SLEEP_SUBELEM_GTK, 0)
+ msg['payload'] = struct.pack("<BBBH",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ len(keydata))
+ msg['payload'] += keydata
+ msg['payload'] += struct.pack("<BBBBHBB",
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
+ WNM_STATUS_SLEEP_ACCEPT, 0,
+ WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - Invalid GTK subelem")
+ keydata = struct.pack("<BBHB2L4L", WNM_SLEEP_SUBELEM_GTK, 11 + 16,
+ 0, 17, 0, 0, 0, 0, 0, 0)
+ msg['payload'] = struct.pack("<BBBH",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ len(keydata))
+ msg['payload'] += keydata
+ msg['payload'] += struct.pack("<BBBBHBB",
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
+ WNM_STATUS_SLEEP_ACCEPT, 0,
+ WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - Invalid GTK subelem (2)")
+ keydata = struct.pack("<BBHB2L4L", WNM_SLEEP_SUBELEM_GTK, 11 + 16,
+ 0, 0, 0, 0, 0, 0, 0, 0)
+ msg['payload'] = struct.pack("<BBBH",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ len(keydata))
+ msg['payload'] += keydata
+ msg['payload'] += struct.pack("<BBBBHBB",
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
+ WNM_STATUS_SLEEP_ACCEPT, 0,
+ WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - GTK subelem and too short IGTK subelem")
+ keydata = struct.pack("<BBHB", WNM_SLEEP_SUBELEM_GTK, 11 + 16, 0, 16)
+ keydata += struct.pack(">2L4L", 0x01020304, 0x05060708,
+ 0x11223344, 0x55667788, 0x9900aabb, 0xccddeeff)
+ keydata += struct.pack("<BB", WNM_SLEEP_SUBELEM_IGTK, 0)
+ msg['payload'] = struct.pack("<BBBH",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ len(keydata))
+ msg['payload'] += keydata
+ msg['payload'] += struct.pack("<BBBBHBB",
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
+ WNM_STATUS_SLEEP_ACCEPT, 0,
+ WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ logger.debug("WNM Sleep Mode Response - Unknown subelem")
+ keydata = struct.pack("<BB", 255, 0)
+ msg['payload'] = struct.pack("<BBBH",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ len(keydata))
+ msg['payload'] += keydata
+ msg['payload'] += struct.pack("<BBBBHBB",
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
+ WNM_STATUS_SLEEP_ACCEPT, 0,
+ WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+@remote_compatible
+def test_wnm_action_proto_no_pmf(dev, apdev):
+ """WNM Action protocol testing (PMF disabled)"""
+ ssid = "test-wnm-no-pmf"
+ hapd = start_wnm_ap(apdev[0], rsn=True, pmf=False, bss_transition=False,
+ wnm_sleep_mode=True, ssid=ssid)
+ bssid = apdev[0]['bssid']
+ dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
+ proto="WPA2", ieee80211w="0", scan_freq="2412")
+ dev[0].request("WNM_SLEEP enter")
+ time.sleep(0.1)
+ hapd.set("ext_mgmt_frame_handling", "1")
+ hapd.dump_monitor()
+ dev[0].request("WNM_SLEEP exit")
+ ev = hapd.wait_event(['MGMT-RX'], timeout=5)
+ if ev is None:
+ raise Exception("WNM-Sleep Mode Request not seen")
+
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = dev[0].own_addr()
+ msg['sa'] = bssid
+ msg['bssid'] = bssid
+
+ logger.debug("WNM Sleep Mode Response - GTK subelem and IGTK subelem")
+ keydata = struct.pack("<BBHB", WNM_SLEEP_SUBELEM_GTK, 11 + 16, 0, 16)
+ keydata += struct.pack(">2L4L", 0x01020304, 0x05060708,
+ 0x11223344, 0x55667788, 0x9900aabb, 0xccddeeff)
+ keydata += struct.pack("<BBHLH4L", WNM_SLEEP_SUBELEM_IGTK, 2 + 6 + 16, 0,
+ 0x10203040, 0x5060,
+ 0xf1f2f3f4, 0xf5f6f7f8, 0xf9f0fafb, 0xfcfdfeff)
+ msg['payload'] = struct.pack("<BBBH",
+ ACTION_CATEG_WNM, WNM_ACT_SLEEP_MODE_RESP, 0,
+ len(keydata))
+ msg['payload'] += keydata
+ msg['payload'] += struct.pack("<BBBBHBB",
+ WLAN_EID_WNMSLEEP, 4, WNM_SLEEP_MODE_EXIT,
+ WNM_STATUS_SLEEP_ACCEPT, 0,
+ WLAN_EID_TFS_RESP, 0)
+ hapd.mgmt_tx(msg)
+ expect_ack(hapd)
+
+ ev = dev[0].wait_event(["WNM: Ignore Key Data"], timeout=5)
+ if ev is None:
+ raise Exception("Key Data not ignored")
+
+def test_wnm_bss_tm_req_with_mbo_ie(dev, apdev):
+ """WNM BSS transition request with MBO IE and reassociation delay attribute"""
+ ssid = "test-wnm-mbo"
+ hapd = start_wnm_ap(apdev[0], rsn=True, pmf=False, ssid=ssid)
+ bssid = apdev[0]['bssid']
+ if "OK" not in dev[0].request("SET mbo_cell_capa 1"):
+ raise Exception("Failed to set STA as cellular data capable")
+
+ dev[0].connect(ssid, psk="12345678", key_mgmt="WPA-PSK",
+ proto="WPA2", ieee80211w="0", scan_freq="2412")
+
+ logger.debug("BTM request with MBO reassociation delay when disassoc imminent is not set")
+ if 'FAIL' not in hapd.request("BSS_TM_REQ " + dev[0].own_addr() + " mbo=3:2:1"):
+ raise Exception("BSS transition management succeeded unexpectedly")
+
+ logger.debug("BTM request with invalid MBO transition reason code")
+ if 'FAIL' not in hapd.request("BSS_TM_REQ " + dev[0].own_addr() + " mbo=10:2:1"):
+ raise Exception("BSS transition management succeeded unexpectedly")
+
+ logger.debug("BTM request with MBO reassociation retry delay of 5 seconds")
+ if 'OK' not in hapd.request("BSS_TM_REQ " + dev[0].own_addr() + " disassoc_imminent=1 disassoc_timer=3 mbo=3:5:1"):
+ raise Exception("BSS transition management command failed")
+
+ ev = dev[0].wait_event(['MBO-CELL-PREFERENCE'], 1)
+ if ev is None or "preference=1" not in ev:
+ raise Exception("Timeout waiting for MBO-CELL-PREFERENCE event")
+
+ ev = dev[0].wait_event(['MBO-TRANSITION-REASON'], 1)
+ if ev is None or "reason=3" not in ev:
+ raise Exception("Timeout waiting for MBO-TRANSITION-REASON event")
+
+ t0 = datetime.now()
+
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if dev[0].own_addr() not in ev:
+ raise Exception("Unexpected BSS Transition Management Response address")
+
+ ev = dev[0].wait_event(['CTRL-EVENT-DISCONNECTED'], 5)
+ if ev is None:
+ raise Exception("Station did not disconnect although disassoc imminent was set")
+
+ # Set the scan interval to make dev[0] look for connections
+ if 'OK' not in dev[0].request("SCAN_INTERVAL 1"):
+ raise Exception("Failed to set scan interval")
+
+ # Wait until connected
+ ev = dev[0].wait_event(['CTRL-EVENT-CONNECTED'], 10)
+ if ev is None:
+ raise Exception("Station did not connect")
+
+ # Make sure no connection is made during the retry delay
+ time_diff = datetime.now() - t0
+ if time_diff.total_seconds() < 5:
+ raise Exception("Station connected before assoc retry delay was over")
+
+ if "OK" not in dev[0].request("SET mbo_cell_capa 3"):
+ raise Exception("Failed to set STA as cellular data not-capable")
+
+@remote_compatible
+def test_wnm_bss_transition_mgmt_query(dev, apdev):
+ """WNM BSS Transition Management query"""
+ hapd = start_wnm_ap(apdev[0])
+ params = {"ssid": "another"}
+ hapd2 = hostapd.add_ap(apdev[1], params)
+
+ dev[0].scan_for_bss(apdev[1]['bssid'], 2412)
+ dev[0].scan_for_bss(apdev[0]['bssid'], 2412)
+
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("WNM_BSS_QUERY 0 list")
+
+ ev = dev[0].wait_event(["WNM: BSS Transition Management Request"],
+ timeout=5)
+ if ev is None:
+ raise Exception("No BSS Transition Management Request frame seen")
+
+ ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response frame seen")
+
+def test_wnm_bss_transition_mgmt_query_disabled_on_ap(dev, apdev):
+ """WNM BSS Transition Management query - TM disabled on AP"""
+ hapd = start_wnm_ap(apdev[0], bss_transition=False)
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ # Ignore BSS Transition Management Query from 02:00:00:00:00:00 since BSS Transition Management is disabled
+ dev[0].request("WNM_BSS_QUERY 0 list")
+ ev = hapd.wait_event(["BSS-TM-RESP"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected BSS TM Response reported")
+
+def test_wnm_bss_transition_mgmt_query_mbo(dev, apdev):
+ """WNM BSS Transition Management query - TM only due to MBO on AP"""
+ hapd = start_wnm_ap(apdev[0], bss_transition=False, mbo=True)
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("WNM_BSS_QUERY 0 list")
+ ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
+ if ev is None:
+ raise Exception("No BSS TM Response reported")
+
+@remote_compatible
+def test_wnm_bss_tm_security_mismatch(dev, apdev):
+ """WNM BSS Transition Management and security mismatch"""
+ hapd = start_wnm_ap(apdev[0], hw_mode="g", channel="1", ssid="test-wnm",
+ rsn=True, pmf=False)
+ hapd2 = start_wnm_ap(apdev[1], hw_mode="g", channel="11")
+ dev[0].scan_for_bss(apdev[1]['bssid'], 2462)
+
+ id = dev[0].connect("test-wnm", psk="12345678",
+ bssid=apdev[0]['bssid'], scan_freq="2412")
+ dev[0].set_network(id, "scan_freq", "")
+ dev[0].set_network(id, "bssid", "")
+
+ addr = dev[0].own_addr()
+ dev[0].dump_monitor()
+
+ logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=7" not in ev:
+ raise Exception("Unexpected BSS transition request response: " + ev)
+
+def test_wnm_bss_tm_connect_cmd(dev, apdev):
+ """WNM BSS Transition Management and cfg80211 connect command"""
+ hapd = start_wnm_ap(apdev[0], hw_mode="g", channel="1")
+ hapd2 = start_wnm_ap(apdev[1], hw_mode="g", channel="11")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", drv_params="force_connect_cmd=1")
+
+ wpas.scan_for_bss(apdev[1]['bssid'], 2462)
+
+ id = wpas.connect("test-wnm", key_mgmt="NONE",
+ bssid=apdev[0]['bssid'], scan_freq="2412")
+ wpas.set_network(id, "scan_freq", "")
+ wpas.set_network(id, "bssid", "")
+
+ addr = wpas.own_addr()
+ wpas.dump_monitor()
+
+ logger.info("Preferred Candidate List (matching neighbor for another BSS) without Disassociation Imminent")
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " pref=1 abridged=1 valid_int=255 neighbor=" + apdev[1]['bssid'] + ",0x0000,115,36,7,0301ff"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if "status_code=0" not in ev:
+ raise Exception("BSS transition request was not accepted: " + ev)
+ if "target_bssid=" + apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected target BSS: " + ev)
+ ev = wpas.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ if ev is None:
+ raise Exception("No reassociation seen")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ raise Exception("Unexpected disconnection reported")
+ if apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected reassociation target: " + ev)
+
+def test_wnm_bss_tm_reject(dev, apdev):
+ """WNM BSS Transition Management request getting rejected"""
+ try:
+ hapd = None
+ hapd = start_wnm_ap(apdev[0], country_code="FI", hw_mode="g",
+ channel="1")
+ id = dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+ dev[0].dump_monitor()
+
+ if "OK" not in dev[0].request("SET reject_btm_req_reason 123"):
+ raise Exception("Failed to set reject_btm_req_reason")
+
+ if "OK" not in hapd.request("BSS_TM_REQ " + addr + " disassoc_timer=1"):
+ raise Exception("BSS_TM_REQ command failed")
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=10)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response")
+ if addr not in ev:
+ raise Exception("Unexpected BSS Transition Management Response address")
+ if "status_code=123" not in ev:
+ raise Exception("Unexpected BSS Transition Management Response status: " + ev)
+ dev[0].wait_disconnected()
+ dev[0].wait_connected()
+ finally:
+ if hapd:
+ hapd.request("DISABLE")
+ dev[0].disconnect_and_stop_scan()
+ subprocess.call(['iw', 'reg', 'set', '00'])
+ dev[0].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.5)
+ dev[0].flush_scan_cache()
+
+def test_wnm_bss_tm_ap_proto(dev, apdev):
+ """WNM BSS TM - protocol testing for AP message parsing"""
+ hapd = start_wnm_ap(apdev[0])
+ bssid = hapd.own_addr()
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
+ hapd.set("ext_mgmt_frame_handling", "1")
+ tests = ["0a",
+ "0a06",
+ "0a0601",
+ "0a060100",
+ "0a080000",
+ "0a08000000",
+ "0a080000001122334455",
+ "0a08000000112233445566",
+ "0a08000000112233445566112233445566778899",
+ "0a08ffffff",
+ "0a08ffffff112233445566778899",
+ "0a1a",
+ "0a1a00",
+ "0a1a0000",
+ "0a0c016015007f0f000000000000000000000000000000000000",
+ "0a0700",
+ "0aff00",
+ "0aff"]
+ for t in tests:
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+def test_wnm_bss_transition_mgmt_query_with_unknown_candidates(dev, apdev):
+ """WNM BSS Transition Management query with unknown candidates"""
+ hapd = start_wnm_ap(apdev[0])
+ dev[0].scan_for_bss(apdev[0]['bssid'], 2412)
+
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ dev[0].request("WNM_BSS_QUERY 0 neighbor=00:11:22:33:44:55,0,81,1,4")
+
+ ev = dev[0].wait_event(["WNM: BSS Transition Management Request"],
+ timeout=5)
+ if ev is None:
+ raise Exception("No BSS Transition Management Request frame seen")
+
+ ev = hapd.wait_event(["BSS-TM-RESP"], timeout=5)
+ if ev is None:
+ raise Exception("No BSS Transition Management Response frame seen")
+
+def test_wnm_time_adv_without_time_zone(dev, apdev):
+ """WNM Time Advertisement without time zone configuration"""
+ params = {"ssid": "test-wnm",
+ "time_advertisement": "2"}
+ hostapd.add_ap(apdev[0], params)
+
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+
+def test_wnm_coloc_intf_reporting(dev, apdev):
+ """WNM Collocated Interference Reporting"""
+ hapd = start_wnm_ap(apdev[0], bss_transition=False,
+ coloc_intf_reporting=True)
+
+ no_intf = struct.pack("<BBBBBLLLLH", 96, 21, 0, 127, 0x0f, 0, 0, 0, 0, 0)
+
+ try:
+ dev[0].set("coloc_intf_reporting", "1")
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+ if "OK" not in hapd.request("COLOC_INTF_REQ %s 1 5" % addr):
+ raise Exception("Could not send Collocated Interference Request")
+ ev = dev[0].wait_event(["COLOC-INTF-REQ"], timeout=2)
+ if ev is None:
+ raise Exception("No Collocated Interference Request frame seen")
+ vals = ev.split(' ')
+ if vals[2] != '1' or vals[3] != '5':
+ raise Exception("Unexpected request values: " + ev)
+ dev[0].set("coloc_intf_elems", binascii.hexlify(no_intf).decode())
+ ev = hapd.wait_event(["COLOC-INTF-REPORT"], timeout=1)
+ if ev is None:
+ raise Exception("No Collocated Interference Report frame seen")
+ if addr + " 1 " + binascii.hexlify(no_intf).decode() not in ev:
+ raise Exception("Unexpected report values: " + ev)
+
+ if "OK" not in hapd.request("COLOC_INTF_REQ %s 0 0" % addr):
+ raise Exception("Could not send Collocated Interference Request")
+ ev = dev[0].wait_event(["COLOC-INTF-REQ"], timeout=2)
+ if ev is None:
+ raise Exception("No Collocated Interference Request frame seen")
+ vals = ev.split(' ')
+ if vals[2] != '0' or vals[3] != '0':
+ raise Exception("Unexpected request values: " + ev)
+
+ res = dev[0].request("COLOC_INTF_REPORT " + binascii.hexlify(no_intf).decode())
+ if "OK" not in res:
+ raise Exception("Could not send unsolicited report")
+ ev = hapd.wait_event(["COLOC-INTF-REPORT"], timeout=1)
+ if ev is None:
+ raise Exception("No Collocated Interference Report frame seen")
+ if addr + " 0 " + binascii.hexlify(no_intf).decode() not in ev:
+ raise Exception("Unexpected report values: " + ev)
+
+ if "FAIL" not in hapd.request("COLOC_INTF_REQ foo 1 5"):
+ raise Exception("Invalid COLOC_INTF_REQ accepted")
+ if "FAIL" not in hapd.request("COLOC_INTF_REQ 02:ff:ff:ff:ff:ff 1 5"):
+ raise Exception("COLOC_INTF_REQ for unknown STA accepted")
+ if "FAIL" not in hapd.request("COLOC_INTF_REQ %s 1" % addr):
+ raise Exception("Invalid COLOC_INTF_REQ accepted")
+ if "FAIL" not in hapd.request("COLOC_INTF_REQ %s" % addr):
+ raise Exception("Invalid COLOC_INTF_REQ accepted")
+ finally:
+ dev[0].set("coloc_intf_reporting", "0")
+ dev[0].set("coloc_intf_elems", "")
+
+def test_wnm_coloc_intf_reporting_errors(dev, apdev):
+ """WNM Collocated Interference Reporting errors"""
+ hapd = start_wnm_ap(apdev[0], bss_transition=False,
+ coloc_intf_reporting=True)
+ bssid = hapd.own_addr()
+ dev[0].set("coloc_intf_reporting", "1")
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+ if "FAIL" not in hapd.request("COLOC_INTF_REQ %s 4 5" % addr):
+ raise Exception("Invalid Collocated Interference Request accepted")
+ hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
+ hapd.set("ext_mgmt_frame_handling", "1")
+ tests = ["0a0c016015007f0f000000000000000000000000000000000000",
+ "0a0c"]
+ with alloc_fail(hapd, 1, "ieee802_11_rx_wnm_coloc_intf_report"):
+ for t in tests:
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+def test_wnm_bss_transition_mgmt_disabled(dev, apdev):
+ """WNM BSS Transition Management disabled"""
+ hapd = start_wnm_ap(apdev[0])
+ try:
+ dev[0].set("disable_btm", "1")
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+ hapd.request("BSS_TM_REQ " + addr)
+ ev = hapd.wait_event(['BSS-TM-RESP'], timeout=0.5)
+ if ev is not None:
+ raise Exception("Unexpected BSS Transition Management Response")
+ finally:
+ dev[0].set("disable_btm", "0")
+
+def test_wnm_time_adv_restart(dev, apdev):
+ """WNM time advertisement and interface restart"""
+ hapd = start_wnm_ap(apdev[0], time_adv=True)
+ hapd.disable()
+ hapd.enable()
+ dev[0].connect("test-wnm", key_mgmt="NONE", scan_freq="2412")
diff --git a/contrib/wpa/tests/hwsim/test_wpas_ap.py b/contrib/wpa/tests/hwsim/test_wpas_ap.py
new file mode 100644
index 000000000000..b5b43114a12b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_wpas_ap.py
@@ -0,0 +1,905 @@
+# wpa_supplicant AP mode tests
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import hostapd
+from remotehost import remote_compatible
+import time
+import logging
+logger = logging.getLogger()
+
+import hwsim_utils
+from utils import *
+from wpasupplicant import WpaSupplicant
+from test_p2p_channel import set_country
+
+def wait_ap_ready(dev):
+ ev = dev.wait_event(["CTRL-EVENT-CONNECTED"])
+ if ev is None:
+ raise Exception("AP failed to start")
+
+def test_wpas_ap_open(dev):
+ """wpa_supplicant AP mode - open network"""
+ if "FAIL" not in dev[0].request("DEAUTHENTICATE 00:11:22:33:44:55"):
+ raise Exception("Unexpected DEAUTHENTICATE accepted")
+ if "FAIL" not in dev[0].request("DISASSOCIATE 00:11:22:33:44:55"):
+ raise Exception("Unexpected DISASSOCIATE accepted")
+ if "FAIL" not in dev[0].request("CHAN_SWITCH 0 2432"):
+ raise Exception("Unexpected CHAN_SWITCH accepted")
+
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ if "FAIL" not in dev[0].request("DEAUTHENTICATE foo"):
+ raise Exception("Invalid DEAUTHENTICATE accepted")
+ if "FAIL" not in dev[0].request("DISASSOCIATE foo"):
+ raise Exception("Invalid DISASSOCIATE accepted")
+
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ dev[2].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+
+ addr1 = dev[1].p2p_interface_addr()
+ addr2 = dev[2].p2p_interface_addr()
+ addrs = [addr1, addr2]
+ sta = dev[0].get_sta(None)
+ if sta['addr'] not in addrs:
+ raise Exception("Unexpected STA address")
+ sta1 = dev[0].get_sta(sta['addr'])
+ if sta1['addr'] not in addrs:
+ raise Exception("Unexpected STA address")
+ sta2 = dev[0].get_sta(sta['addr'], next=True)
+ if sta2['addr'] not in addrs:
+ raise Exception("Unexpected STA2 address")
+ sta3 = dev[0].get_sta(sta2['addr'], next=True)
+ if len(sta3) != 0:
+ raise Exception("Unexpected STA iteration result (did not stop)")
+
+ status = dev[0].get_status()
+ if status['mode'] != "AP":
+ raise Exception("Unexpected status mode")
+
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+ dev[0].request("DEAUTHENTICATE " + addr1)
+ dev[0].request("DISASSOCIATE " + addr2)
+ dev[1].wait_disconnected(timeout=10)
+ dev[2].wait_disconnected(timeout=10)
+ dev[1].wait_connected(timeout=10, error="Reconnection timed out")
+ dev[2].wait_connected(timeout=10, error="Reconnection timed out")
+ dev[1].request("DISCONNECT")
+ dev[2].request("DISCONNECT")
+
+def test_wpas_ap_open_isolate(dev):
+ """wpa_supplicant AP mode - open network with client isolation"""
+ try:
+ dev[0].set("ap_isolate", "1")
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ dev[2].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ hwsim_utils.test_connectivity(dev[0], dev[2])
+ hwsim_utils.test_connectivity(dev[1], dev[2], success_expected=False,
+ timeout=1)
+ finally:
+ dev[0].set("ap_isolate", "0")
+
+@remote_compatible
+def test_wpas_ap_wep(dev):
+ """wpa_supplicant AP mode - WEP"""
+ check_wep_capa(dev[0])
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-wep")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network_quoted(id, "wep_key0", "hello")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ dev[1].connect("wpas-ap-wep", key_mgmt="NONE", wep_key0='"hello"',
+ scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ dev[1].request("DISCONNECT")
+
+@remote_compatible
+def test_wpas_ap_no_ssid(dev):
+ """wpa_supplicant AP mode - invalid network configuration"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected AP start")
+
+@remote_compatible
+def test_wpas_ap_default_frequency(dev):
+ """wpa_supplicant AP mode - default frequency"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2462")
+ dev[1].request("DISCONNECT")
+
+@remote_compatible
+def test_wpas_ap_invalid_frequency(dev):
+ """wpa_supplicant AP mode - invalid frequency configuration"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2413")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected AP start")
+
+def test_wpas_ap_wps(dev):
+ """wpa_supplicant AP mode - WPS operations"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-wps")
+ dev[0].set_network_quoted(id, "psk", "1234567890")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+ bssid = dev[0].p2p_interface_addr()
+
+ logger.info("Test PBC mode start/stop")
+ if "FAIL" not in dev[0].request("WPS_CANCEL"):
+ raise Exception("Unexpected WPS_CANCEL success")
+ dev[0].request("WPS_PBC")
+ ev = dev[0].wait_event(["WPS-PBC-ACTIVE"])
+ if ev is None:
+ raise Exception("PBC mode start timeout")
+ if "OK" not in dev[0].request("WPS_CANCEL"):
+ raise Exception("Unexpected WPS_CANCEL failure")
+ ev = dev[0].wait_event(["WPS-TIMEOUT"])
+ if ev is None:
+ raise Exception("PBC mode disabling timeout")
+
+ logger.info("Test PBC protocol run")
+ dev[0].request("WPS_PBC")
+ ev = dev[0].wait_event(["WPS-PBC-ACTIVE"])
+ if ev is None:
+ raise Exception("PBC mode start timeout")
+ dev[1].request("WPS_PBC")
+ dev[1].wait_connected(timeout=30, error="WPS PBC operation timed out")
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+ logger.info("Test AP PIN to learn configuration")
+ pin = dev[0].request("WPS_AP_PIN random")
+ if "FAIL" in pin:
+ raise Exception("Could not generate random AP PIN")
+ if pin not in dev[0].request("WPS_AP_PIN get"):
+ raise Exception("Could not fetch current AP PIN")
+ dev[2].wps_reg(bssid, pin)
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+
+ dev[1].request("REMOVE_NETWORK all")
+ dev[2].request("REMOVE_NETWORK all")
+
+ logger.info("Test AP PIN operations")
+ dev[0].request("WPS_AP_PIN disable")
+ dev[0].request("WPS_AP_PIN set " + pin + " 1")
+ time.sleep(1.1)
+ if "FAIL" not in dev[0].request("WPS_AP_PIN get"):
+ raise Exception("AP PIN unexpectedly still enabled")
+
+ pin = dev[1].wps_read_pin()
+ dev[0].request("WPS_PIN any " + pin)
+ dev[1].request("WPS_PIN any " + pin)
+ dev[1].wait_connected(timeout=30)
+ dev[1].request("REMOVE_NETWORK all")
+ dev[1].dump_monitor()
+
+ dev[0].request("WPS_PIN any " + pin + " 100")
+ dev[1].request("WPS_PIN any " + pin)
+ dev[1].wait_connected(timeout=30)
+ dev[1].request("REMOVE_NETWORK all")
+ dev[1].dump_monitor()
+
+ dev[0].request("WPS_AP_PIN set 12345670")
+ dev[0].dump_monitor()
+
+ runs = ("88887777", "12340000", "00000000", "12345670")
+ for pin in runs:
+ logger.info("Try AP PIN " + pin)
+ dev[2].dump_monitor()
+ dev[2].request("WPS_REG " + bssid + " " + pin)
+ ev = dev[2].wait_event(["WPS-SUCCESS", "WPS-FAIL msg"], timeout=15)
+ if ev is None:
+ raise Exception("WPS operation timed out")
+ if "WPS-SUCCESS" in ev:
+ raise Exception("WPS operation succeeded unexpectedly")
+ dev[2].wait_disconnected(timeout=10)
+ dev[2].request("WPS_CANCEL")
+ dev[2].request("REMOVE_NETWORK all")
+ ev = dev[0].wait_event(["WPS-AP-SETUP-LOCKED"])
+ if ev is None:
+ raise Exception("WPS AP PIN not locked")
+
+ dev[0].dump_monitor()
+ logger.info("Test random AP PIN timeout")
+ pin = dev[0].request("WPS_AP_PIN random 1")
+ if "FAIL" in pin:
+ raise Exception("Could not generate random AP PIN")
+ res = dev[0].request("WPS_AP_PIN get")
+ if pin not in res:
+ raise Exception("Could not fetch current AP PIN")
+ for i in range(10):
+ time.sleep(0.2)
+ res = dev[0].request("WPS_AP_PIN get")
+ if "FAIL" in res:
+ break
+ if "FAIL" not in res:
+ raise Exception("WPS_AP_PIN random timeout did not work")
+
+ if "FAIL" not in dev[0].request("WPS_AP_PIN foo"):
+ raise Exception("Invalid WPS_AP_PIN command not rejected")
+ if "FAIL" not in dev[0].request("WPS_AP_PIN set"):
+ raise Exception("Invalid WPS_AP_PIN command not rejected")
+
+def test_wpas_ap_wps_frag(dev):
+ """wpa_supplicant AP mode - WPS operations with fragmentation"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-wps")
+ dev[0].set_network_quoted(id, "psk", "1234567890")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "fragment_size", "300")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+ bssid = dev[0].own_addr()
+
+ pin = dev[1].wps_read_pin()
+ dev[0].request("WPS_PIN any " + pin)
+ dev[1].scan_for_bss(bssid, freq="2412")
+ dev[1].request("WPS_PIN " + bssid + " " + pin)
+ dev[1].wait_connected(timeout=30)
+
+def test_wpas_ap_wps_pbc_overlap(dev):
+ """wpa_supplicant AP mode - WPS operations with PBC overlap"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-wps")
+ dev[0].set_network_quoted(id, "psk", "1234567890")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+ bssid = dev[0].p2p_interface_addr()
+
+ dev[1].scan_for_bss(bssid, freq="2412")
+ dev[1].dump_monitor()
+ dev[2].scan_for_bss(bssid, freq="2412")
+ dev[2].dump_monitor()
+ dev[0].request("WPS_PBC")
+ dev[1].request("WPS_PBC " + bssid)
+ dev[2].request("WPS_PBC " + bssid)
+
+ ev = dev[1].wait_event(["WPS-M2D"], timeout=15)
+ if ev is None:
+ raise Exception("PBC session overlap not detected (dev1)")
+ if "config_error=12" not in ev:
+ raise Exception("PBC session overlap not correctly reported (dev1)")
+
+ ev = dev[2].wait_event(["WPS-M2D"], timeout=15)
+ if ev is None:
+ raise Exception("PBC session overlap not detected (dev2)")
+ if "config_error=12" not in ev:
+ raise Exception("PBC session overlap not correctly reported (dev2)")
+
+ if "FAIL-PBC-OVERLAP" not in dev[0].request("WPS_PBC"):
+ raise Exception("WPS_PBC(AP) accepted during overlap")
+ if "FAIL-PBC-OVERLAP" not in dev[0].request("WPS_PBC any"):
+ raise Exception("WPS_PBC(AP) accepted during overlap")
+ dev[0].request("WPS_CANCEL")
+ dev[1].request("WPS_CANCEL")
+ dev[2].request("WPS_CANCEL")
+
+@remote_compatible
+def test_wpas_ap_wps_disabled(dev):
+ """wpa_supplicant AP mode - WPS disabled"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-no-wps")
+ dev[0].set_network_quoted(id, "psk", "12345678")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "wps_disabled", "1")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ dev[1].connect("wpas-ap-no-wps", psk="12345678", scan_freq="2412")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+
+def test_wpas_ap_dfs(dev):
+ """wpa_supplicant AP mode - DFS"""
+ if dev[0].get_mcc() > 1:
+ raise HwsimSkip("DFS is not supported with multi channel contexts")
+
+ try:
+ _test_wpas_ap_dfs(dev)
+ finally:
+ set_country("00")
+ dev[0].request("SET country 00")
+ dev[1].flush_scan_cache()
+
+def _test_wpas_ap_dfs(dev):
+ set_country("US")
+ dev[0].request("SET country US")
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-dfs")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "5260")
+ dev[0].set_network(id, "scan_freq", "5260")
+ dev[0].select_network(id)
+
+ ev = dev[0].wait_event(["DFS-CAC-START"])
+ if ev is None:
+ # For now, assume DFS is not supported by all kernel builds.
+ raise HwsimSkip("CAC did not start - assume not supported")
+
+ ev = dev[0].wait_event(["DFS-CAC-COMPLETED"], timeout=70)
+ if ev is None:
+ raise Exception("CAC did not complete")
+ if "success=1" not in ev:
+ raise Exception("CAC failed")
+ if "freq=5260" not in ev:
+ raise Exception("Unexpected DFS freq result")
+
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"])
+ if ev is None:
+ raise Exception("AP failed to start")
+
+ dev[1].connect("wpas-ap-dfs", key_mgmt="NONE")
+ dev[1].wait_regdom(country_ie=True)
+ dev[0].request("DISCONNECT")
+ dev[1].disconnect_and_stop_scan()
+
+@remote_compatible
+def test_wpas_ap_disable(dev):
+ """wpa_supplicant AP mode - DISABLE_NETWORK"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+
+ ev = dev[0].wait_event(["AP-ENABLED"])
+ if ev is None:
+ raise Exception("AP-ENABLED event not seen")
+ wait_ap_ready(dev[0])
+ dev[0].request("DISABLE_NETWORK %d" % id)
+ ev = dev[0].wait_event(["AP-DISABLED"])
+ if ev is None:
+ raise Exception("AP-DISABLED event not seen")
+ dev[0].wait_disconnected()
+
+def test_wpas_ap_acs(dev):
+ """wpa_supplicant AP mode - ACS"""
+ res = dev[0].get_capability("acs")
+ if res is None or "ACS" not in res:
+ raise HwsimSkip("ACS not supported")
+
+ # For now, make sure the last operating channel was on 2.4 GHz band to get
+ # sufficient survey data from mac80211_hwsim.
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2417")
+ dev[0].set_network(id, "scan_freq", "2417")
+ dev[0].set_network(id, "acs", "1")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ # ACS prefers channels 1, 6, 11
+ freq = dev[0].get_status_field('freq')
+ if freq == "2417":
+ raise Exception("Unexpected operating channel selected")
+
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq=freq)
+
+@remote_compatible
+def test_wpas_ap_and_assoc_req_p2p_ie(dev):
+ """wpa_supplicant AP mode - unexpected P2P IE in Association Request"""
+ try:
+ _test_wpas_ap_and_assoc_req_p2p_ie(dev)
+ finally:
+ dev[1].request("VENDOR_ELEM_REMOVE 13 *")
+ dev[0].request("P2P_SET disabled 0")
+
+def _test_wpas_ap_and_assoc_req_p2p_ie(dev):
+ dev[0].request("P2P_SET disabled 1")
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ dev[1].request("VENDOR_ELEM_ADD 13 dd04506f9a09")
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+@remote_compatible
+def test_wpas_ap_open_ht_disabled(dev):
+ """wpa_supplicant AP mode - open network and HT disabled"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "disable_ht", "1")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def test_wpas_ap_failures(dev):
+ """wpa_supplicant AP mode - failures"""
+ # No SSID configured for AP mode
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected connection event")
+ dev[0].request("REMOVE_NETWORK all")
+
+ # Invalid pbss value(2) for AP mode
+ dev[0].dump_monitor()
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "pbss", "2")
+ dev[0].select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=0.1)
+ if ev is not None and "CTRL-EVENT-CONNECTED" in ev:
+ raise Exception("Unexpected connection event(2)")
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_wpas_ap_oom(dev):
+ """wpa_supplicant AP mode - OOM"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap")
+ dev[0].set_network_quoted(id, "psk", "1234567890")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ with alloc_fail(dev[0], 1, "=wpa_supplicant_conf_ap"):
+ dev[0].select_network(id)
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap")
+ dev[0].set_network(id, "psk", "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ with alloc_fail(dev[0], 1, "=wpa_supplicant_conf_ap"):
+ dev[0].select_network(id)
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+
+ if "WEP40" in dev[0].get_capability("group"):
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network_quoted(id, "wep_key0", "hello")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ with alloc_fail(dev[0], 1, "=wpa_supplicant_conf_ap"):
+ dev[0].select_network(id)
+ dev[0].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.request("SET manufacturer test")
+ wpas.request("SET model_name test")
+ wpas.request("SET model_number test")
+ wpas.request("SET serial_number test")
+ wpas.request("SET serial_number test")
+ wpas.request("SET serial_number test")
+ wpas.request("SET ap_vendor_elements dd0411223301")
+ id = wpas.add_network()
+ wpas.set_network(id, "mode", "2")
+ wpas.set_network_quoted(id, "ssid", "wpas-ap")
+ wpas.set_network(id, "key_mgmt", "NONE")
+ wpas.set_network(id, "frequency", "2412")
+ wpas.set_network(id, "scan_freq", "2412")
+
+ for i in range(5):
+ with alloc_fail(wpas, i, "=wpa_supplicant_conf_ap"):
+ wpas.select_network(id)
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=1)
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+
+def test_wpas_ap_params(dev):
+ """wpa_supplicant AP mode - parameters"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.request("SET manufacturer test")
+ wpas.request("SET model_name test")
+ wpas.request("SET model_number test")
+ wpas.request("SET serial_number test")
+ wpas.request("SET serial_number test")
+ wpas.request("SET serial_number test")
+ wpas.request("SET ap_vendor_elements dd0411223301")
+ id = wpas.add_network()
+ wpas.set_network(id, "mode", "2")
+ wpas.set_network_quoted(id, "ssid", "wpas-ap")
+ wpas.set_network(id, "key_mgmt", "NONE")
+ wpas.set_network(id, "frequency", "2412")
+ wpas.set_network(id, "scan_freq", "2412")
+ wpas.select_network(id)
+ wpas.wait_connected()
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+
+ wpas.request("SET beacon_int 200 3")
+ wpas.request("SET dtim_period 3")
+ wpas.select_network(id)
+ wpas.wait_connected()
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+
+ wpas.set_network(id, "beacon_int", "300")
+ wpas.set_network(id, "dtim_period", "2")
+ wpas.select_network(id)
+ wpas.wait_connected()
+ if "---- AP ----" not in wpas.request("PMKSA"):
+ raise Exception("AP section missing from PMKSA output")
+ if "OK" not in wpas.request("PMKSA_FLUSH"):
+ raise Exception("PMKSA_FLUSH failed")
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+
+def test_wpas_ap_global_sta(dev):
+ """wpa_supplicant AP mode - STA commands on global control interface"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+
+ addr1 = dev[1].own_addr()
+ res = dev[0].global_request("STA " + addr1)
+ if "UNKNOWN COMMAND" in res:
+ raise Exception("STA command not known on global control interface")
+ res = dev[0].global_request("STA-FIRST")
+ if "UNKNOWN COMMAND" in res:
+ raise Exception("STA-FIRST command not known on global control interface")
+ res = dev[0].global_request("STA-NEXT " + addr1)
+ if "UNKNOWN COMMAND" in res:
+ raise Exception("STA-NEXT command not known on global control interface")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ dev[0].request("DISCONNECT")
+ dev[0].wait_disconnected()
+
+def test_wpas_ap_5ghz(dev):
+ """wpa_supplicant AP mode - 5 GHz"""
+ try:
+ _test_wpas_ap_5ghz(dev)
+ finally:
+ set_country("00")
+ dev[0].request("SET country 00")
+ dev[1].flush_scan_cache()
+
+def _test_wpas_ap_5ghz(dev):
+ set_country("US")
+ dev[0].request("SET country US")
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-5ghz")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "5180")
+ dev[0].set_network(id, "scan_freq", "5180")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ dev[1].connect("wpas-ap-5ghz", key_mgmt="NONE", scan_freq="5180")
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+
+def test_wpas_ap_open_vht80(dev):
+ """wpa_supplicant AP mode - VHT 80 MHz"""
+ id = dev[0].add_network()
+ dev[0].set("country", "FI")
+ try:
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "5180")
+ dev[0].set_network(id, "scan_freq", "5180")
+ dev[0].set_network(id, "vht", "1")
+ dev[0].set_network(id, "vht_center_freq1", "5210")
+ dev[0].set_network(id, "max_oper_chwidth", "1")
+ dev[0].set_network(id, "ht40", "1")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="5180")
+ sig = dev[1].request("SIGNAL_POLL").splitlines()
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ if "FREQUENCY=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(1): " + str(sig))
+ if "WIDTH=80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ finally:
+ set_country("00")
+ dev[0].set("country", "00")
+ dev[1].flush_scan_cache()
+
+def test_wpas_ap_no_ht(dev):
+ """wpa_supplicant AP mode - HT disabled"""
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "ht", "0")
+ dev[0].set_network(id, "wps_disabled", "1")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ sig = dev[1].request("SIGNAL_POLL").splitlines()
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "wps_disabled", "1")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+ dev[1].flush_scan_cache()
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ sig2 = dev[1].request("SIGNAL_POLL").splitlines()
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ dev[0].request("REMOVE_NETWORK all")
+ dev[0].wait_disconnected()
+
+ if "WIDTH=20 MHz (no HT)" not in sig:
+ raise Exception("HT was not disabled: " + str(sig))
+ if "WIDTH=20 MHz" not in sig2:
+ raise Exception("HT was not enabled: " + str(sig2))
+
+def test_wpas_ap_async_fail(dev):
+ """wpa_supplicant AP mode - Async failure"""
+ id = dev[0].add_network()
+ dev[0].set("country", "FI")
+ try:
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "5180")
+ dev[0].set_network(id, "scan_freq", "5180")
+ dev[0].set_network(id, "vht", "1")
+ dev[0].set_network(id, "vht_center_freq1", "5210")
+ dev[0].set_network(id, "max_oper_chwidth", "1")
+ dev[0].set_network(id, "ht40", "1")
+
+ with alloc_fail(dev[0], 1,
+ "nl80211_get_scan_results;ieee80211n_check_scan"):
+ dev[0].select_network(id)
+ dev[0].wait_disconnected()
+ finally:
+ clear_regdom_dev(dev)
+
+def test_wpas_ap_sae(dev):
+ """wpa_supplicant AP mode - SAE using psk"""
+ run_wpas_ap_sae(dev, False)
+
+def test_wpas_ap_sae_password(dev):
+ """wpa_supplicant AP mode - SAE using sae_password"""
+ run_wpas_ap_sae(dev, True)
+
+def test_wpas_ap_sae_pwe_1(dev):
+ """wpa_supplicant AP mode - SAE using sae_password and sae_pwe=1"""
+ try:
+ dev[0].set("sae_pwe", "1")
+ dev[1].set("sae_pwe", "1")
+ run_wpas_ap_sae(dev, True, sae_password_id=True)
+ finally:
+ dev[0].set("sae_pwe", "0")
+ dev[1].set("sae_pwe", "0")
+
+def run_wpas_ap_sae(dev, sae_password, sae_password_id=False):
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ if "SAE" not in dev[1].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ dev[0].request("SET sae_groups ")
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-sae")
+ dev[0].set_network(id, "proto", "WPA2")
+ dev[0].set_network(id, "key_mgmt", "SAE")
+ dev[0].set_network(id, "pairwise", "CCMP")
+ dev[0].set_network(id, "group", "CCMP")
+ if sae_password:
+ dev[0].set_network_quoted(id, "sae_password", "12345678")
+ else:
+ dev[0].set_network_quoted(id, "psk", "12345678")
+ if sae_password_id:
+ pw_id = "pw id"
+ dev[0].set_network_quoted(id, "sae_password_id", pw_id)
+ else:
+ pw_id = None
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "wps_disabled", "1")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ dev[1].request("SET sae_groups ")
+ dev[1].connect("wpas-ap-sae", key_mgmt="SAE", sae_password="12345678",
+ sae_password_id=pw_id, scan_freq="2412")
+
+def test_wpas_ap_scan(dev, apdev):
+ """wpa_supplicant AP mode and scanning"""
+ dev[0].flush_scan_cache()
+
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ bssid = hapd.own_addr()
+
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-open")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+ dev[0].dump_monitor()
+
+ if "OK" not in dev[0].request("SCAN freq=2412"):
+ raise Exception("SCAN command not accepted")
+ ev = dev[0].wait_event(["CTRL-EVENT-SCAN-RESULTS",
+ "CTRL-EVENT-SCAN-FAILED"], 15)
+ if ev is None:
+ raise Exception("Scan result timed out")
+ if "CTRL-EVENT-SCAN-FAILED ret=-95" in ev:
+ # Scanning in AP mode not supported
+ return
+ if "CTRL-EVENT-SCAN-FAILED" in ev:
+ raise Exception("Unexpected scan failure reason: " + ev)
+ if "CTRL-EVENT-SCAN-RESULTS" in ev:
+ bss = dev[0].get_bss(bssid)
+ if not bss:
+ raise Exception("AP not found in scan")
+
+def test_wpas_ap_sae(dev):
+ """wpa_supplicant AP mode - SAE using psk"""
+ run_wpas_ap_sae(dev, False)
+
+def test_wpas_ap_sae_and_psk_transition_disable(dev):
+ """wpa_supplicant AP mode - SAE+PSK transition disable indication"""
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ if "SAE" not in dev[1].get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+ dev[0].set("sae_groups", "")
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "2")
+ dev[0].set_network_quoted(id, "ssid", "wpas-ap-sae")
+ dev[0].set_network(id, "proto", "WPA2")
+ dev[0].set_network(id, "key_mgmt", "SAE")
+ dev[0].set_network(id, "transition_disable", "1")
+ dev[0].set_network(id, "ieee80211w", "1")
+ dev[0].set_network(id, "pairwise", "CCMP")
+ dev[0].set_network(id, "group", "CCMP")
+ dev[0].set_network_quoted(id, "psk", "12345678")
+ dev[0].set_network(id, "frequency", "2412")
+ dev[0].set_network(id, "scan_freq", "2412")
+ dev[0].set_network(id, "wps_disabled", "1")
+ dev[0].select_network(id)
+ wait_ap_ready(dev[0])
+
+ dev[1].set("sae_groups", "")
+ dev[1].connect("wpas-ap-sae", key_mgmt="SAE WPA-PSK",
+ psk="12345678", ieee80211w="1",
+ scan_freq="2412")
+ ev = dev[1].wait_event(["TRANSITION-DISABLE"], timeout=1)
+ if ev is None:
+ raise Exception("Transition disable not indicated")
+ if ev.split(' ')[1] != "01":
+ raise Exception("Unexpected transition disable bitmap: " + ev)
+
+ val = dev[1].get_network(id, "ieee80211w")
+ if val != "2":
+ raise Exception("Unexpected ieee80211w value: " + val)
+ val = dev[1].get_network(id, "key_mgmt")
+ if val != "SAE":
+ raise Exception("Unexpected key_mgmt value: " + val)
+ val = dev[1].get_network(id, "group")
+ if val != "CCMP":
+ raise Exception("Unexpected group value: " + val)
+ val = dev[1].get_network(id, "proto")
+ if val != "RSN":
+ raise Exception("Unexpected proto value: " + val)
+
+ dev[1].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ dev[1].request("RECONNECT")
+ dev[1].wait_connected()
diff --git a/contrib/wpa/tests/hwsim/test_wpas_config.py b/contrib/wpa/tests/hwsim/test_wpas_config.py
new file mode 100644
index 000000000000..d105fed0dc36
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_wpas_config.py
@@ -0,0 +1,656 @@
+# wpa_supplicant config file
+# Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os
+
+from wpasupplicant import WpaSupplicant
+import hostapd
+from utils import *
+
+config_checks = [("ap_scan", "0"),
+ ("update_config", "1"),
+ ("device_name", "name"),
+ ("eapol_version", "2"),
+ ("wps_priority", "5"),
+ ("ip_addr_go", "192.168.1.1"),
+ ("ip_addr_mask", "255.255.255.0"),
+ ("ip_addr_start", "192.168.1.10"),
+ ("ip_addr_end", "192.168.1.20"),
+ ("disable_scan_offload", "1"),
+ ("fast_reauth", "0"),
+ ("uuid", "6aeae5e3-c1fc-4e76-8293-7346e1d1459d"),
+ ("manufacturer", "MANUF"),
+ ("model_name", "MODEL"),
+ ("model_number", "MODEL NUM"),
+ ("serial_number", "123qwerty"),
+ ("device_type", "1234-0050F204-4321"),
+ ("os_version", "01020304"),
+ ("config_methods", "label push_button"),
+ ("wps_cred_processing", "1"),
+ ("wps_vendor_ext_m1", "000137100100020001"),
+ ("p2p_listen_reg_class", "81"),
+ ("p2p_listen_channel", "6"),
+ ("p2p_oper_reg_class", "82"),
+ ("p2p_oper_channel", "14"),
+ ("p2p_go_intent", "14"),
+ ("p2p_ssid_postfix", "foobar"),
+ ("persistent_reconnect", "1"),
+ ("p2p_intra_bss", "0"),
+ ("p2p_group_idle", "2"),
+ ("p2p_passphrase_len", "63"),
+ ("p2p_pref_chan", "81:1,82:14,81:11"),
+ ("p2p_no_go_freq", "2412-2432,2462,5000-6000"),
+ ("p2p_add_cli_chan", "1"),
+ ("p2p_optimize_listen_chan", "1"),
+ ("p2p_go_ht40", "1"),
+ ("p2p_go_vht", "1"),
+ ("p2p_go_ctwindow", "1"),
+ ("p2p_disabled", "1"),
+ ("p2p_no_group_iface", "1"),
+ ("p2p_ignore_shared_freq", "1"),
+ ("p2p_cli_probe", "1"),
+ ("p2p_go_freq_change_policy", "0"),
+ ("country", "FI"),
+ ("bss_max_count", "123"),
+ ("bss_expiration_age", "45"),
+ ("bss_expiration_scan_count", "17"),
+ ("filter_ssids", "1"),
+ ("filter_rssi", "-10"),
+ ("max_num_sta", "3"),
+ ("disassoc_low_ack", "1"),
+ ("hs20", "1"),
+ ("interworking", "1"),
+ ("hessid", "02:03:04:05:06:07"),
+ ("access_network_type", "7"),
+ ("pbc_in_m1", "1"),
+ ("wps_nfc_dev_pw_id", "12345"),
+ ("wps_nfc_dh_pubkey", "1234567890ABCDEF"),
+ ("wps_nfc_dh_privkey", "FF1234567890ABCDEFFF"),
+ ("ext_password_backend", "test"),
+ ("p2p_go_max_inactivity", "9"),
+ ("auto_interworking", "1"),
+ ("okc", "1"),
+ ("pmf", "1"),
+ ("dtim_period", "3"),
+ ("beacon_int", "102"),
+ ("sae_groups", "5 19"),
+ ("ap_vendor_elements", "dd0411223301"),
+ ("ignore_old_scan_res", "1"),
+ ("freq_list", "2412 2437"),
+ ("scan_cur_freq", "1"),
+ ("sched_scan_interval", "13"),
+ ("external_sim", "1"),
+ ("tdls_external_control", "1"),
+ ("wowlan_triggers", "any"),
+ ("bgscan", '"simple:30:-45:300"'),
+ ("p2p_search_delay", "123"),
+ ("mac_addr", "2"),
+ ("rand_addr_lifetime", "123456789"),
+ ("preassoc_mac_addr", "1"),
+ ("gas_rand_addr_lifetime", "567"),
+ ("gas_rand_mac_addr", "2"),
+ ("key_mgmt_offload", "0"),
+ ("user_mpm", "0"),
+ ("max_peer_links", "17"),
+ ("cert_in_cb", "0"),
+ ("mesh_max_inactivity", "31"),
+ ("dot11RSNASAERetransPeriod", "19"),
+ ("passive_scan", "1"),
+ ("reassoc_same_bss_optim", "1"),
+ ("wpa_rsc_relaxation", "0"),
+ ("sched_scan_plans", "10:100 20:200 30"),
+ ("non_pref_chan", "81:5:10:2 81:1:0:2 81:9:0:2"),
+ ("mbo_cell_capa", "1"),
+ ("gas_address3", "1"),
+ ("ftm_responder", "1"),
+ ("ftm_initiator", "1"),
+ ("pcsc_reader", "foo"),
+ ("pcsc_pin", "1234"),
+ ("driver_param", "testing"),
+ ("dot11RSNAConfigPMKLifetime", "43201"),
+ ("dot11RSNAConfigPMKReauthThreshold", "71"),
+ ("dot11RSNAConfigSATimeout", "61"),
+ ("sec_device_type", "12345-0050F204-54321"),
+ ("autoscan", "exponential:3:300"),
+ ("osu_dir", "/tmp/osu"),
+ ("fst_group_id", "bond0"),
+ ("fst_priority", "5"),
+ ("fst_llt", "7"),
+ ("go_interworking", "1"),
+ ("go_access_network_type", "2"),
+ ("go_internet", "1"),
+ ("go_venue_group", "3"),
+ ("go_venue_type", "4"),
+ ("p2p_device_random_mac_addr", "1"),
+ ("p2p_device_persistent_mac_addr", "02:12:34:56:78:9a"),
+ ("p2p_interface_random_mac_addr", "1"),
+ ("openssl_ciphers", "DEFAULT")]
+
+def supported_param(capa, field):
+ mesh_params = ["user_mpm", "max_peer_links", "mesh_max_inactivity"]
+ if field in mesh_params and not capa['mesh']:
+ return False
+
+ sae_params = ["dot11RSNASAERetransPeriod"]
+ if field in sae_params and not capa['sae']:
+ return False
+
+ return True
+
+def check_config(capa, config):
+ with open(config, "r") as f:
+ data = f.read()
+ if "ctrl_interface=DIR=/var/run/wpa_supplicant GROUP=" not in data:
+ raise Exception("Missing ctrl_interface")
+ if "blob-base64-foo={" not in data:
+ raise Exception("Missing blob")
+ if "cred={" not in data:
+ raise Exception("Missing cred")
+ if "network={" not in data:
+ raise Exception("Missing network")
+ for field, value in config_checks:
+ if supported_param(capa, field):
+ if "\n" + field + "=" + value + "\n" not in data:
+ raise Exception("Missing value: " + field)
+ return data
+
+def test_wpas_config_file(dev, apdev, params):
+ """wpa_supplicant config file parsing/writing"""
+ config = os.path.join(params['logdir'], 'wpas_config_file.conf')
+ if os.path.exists(config):
+ try:
+ os.remove(config)
+ except:
+ pass
+ try:
+ os.rmdir(config)
+ except:
+ pass
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ try:
+ wpas.interface_add("wlan5", config=config)
+ initialized = True
+ except:
+ initialized = False
+ if initialized:
+ raise Exception("Missing config file did not result in an error")
+
+ try:
+ with open(config, "w") as f:
+ f.write("update_config=1 \t\r\n")
+ f.write("# foo\n")
+ f.write("\n")
+ f.write(" \t\reapol_version=2")
+ for i in range(0, 100):
+ f.write(" ")
+ f.write("foo\n")
+ f.write("device_name=name#foo\n")
+
+ wpas.interface_add("wlan5", config=config)
+ capa = {}
+ capa['mesh'] = "MESH" in wpas.get_capability("modes")
+ capa['sae'] = "SAE" in wpas.get_capability("auth_alg")
+
+ id = wpas.add_network()
+ wpas.set_network_quoted(id, "ssid", "foo")
+ wpas.set_network_quoted(id, "psk", "12345678")
+ wpas.set_network(id, "bssid", "00:11:22:33:44:55")
+ wpas.set_network(id, "proto", "RSN")
+ wpas.set_network(id, "key_mgmt", "WPA-PSK-SHA256")
+ wpas.set_network(id, "pairwise", "CCMP")
+ wpas.set_network(id, "group", "CCMP")
+ wpas.set_network(id, "auth_alg", "OPEN")
+
+ id = wpas.add_cred()
+ wpas.set_cred(id, "priority", "3")
+ wpas.set_cred(id, "sp_priority", "6")
+ wpas.set_cred(id, "update_identifier", "4")
+ wpas.set_cred(id, "ocsp", "1")
+ wpas.set_cred(id, "eap", "TTLS")
+ wpas.set_cred(id, "req_conn_capab", "6:1234")
+ wpas.set_cred_quoted(id, "realm", "example.com")
+ wpas.set_cred_quoted(id, "provisioning_sp", "example.com")
+ wpas.set_cred_quoted(id, "domain", "example.com")
+ wpas.set_cred_quoted(id, "domain_suffix_match", "example.com")
+ wpas.set_cred(id, "roaming_consortium", "112233")
+ wpas.set_cred(id, "required_roaming_consortium", "112233")
+ wpas.set_cred_quoted(id, "roaming_consortiums",
+ "112233,aabbccddee,445566")
+ wpas.set_cred_quoted(id, "roaming_partner",
+ "roaming.example.net,1,127,*")
+ wpas.set_cred_quoted(id, "ca_cert", "/tmp/ca.pem")
+ wpas.set_cred_quoted(id, "username", "user")
+ wpas.set_cred_quoted(id, "password", "secret")
+ ev = wpas.wait_event(["CRED-MODIFIED 0 password"])
+
+ wpas.request("SET blob foo 12345678")
+
+ for field, value in config_checks:
+ if supported_param(capa, field):
+ wpas.set(field, value)
+
+ if "OK" not in wpas.request("SAVE_CONFIG"):
+ raise Exception("Failed to save configuration file")
+ if "OK" not in wpas.global_request("SAVE_CONFIG"):
+ raise Exception("Failed to save configuration file")
+
+ wpas.interface_remove("wlan5")
+ data1 = check_config(capa, config)
+
+ wpas.interface_add("wlan5", config=config)
+ if len(wpas.list_networks()) != 1:
+ raise Exception("Unexpected number of networks")
+ if len(wpas.request("LIST_CREDS").splitlines()) != 2:
+ raise Exception("Unexpected number of credentials")
+
+ val = wpas.get_cred(0, "roaming_consortiums")
+ if val != "112233,aabbccddee,445566":
+ raise Exception("Unexpected roaming_consortiums value: " + val)
+
+ if "OK" not in wpas.request("SAVE_CONFIG"):
+ raise Exception("Failed to save configuration file")
+ data2 = check_config(capa, config)
+
+ if data1 != data2:
+ logger.debug(data1)
+ logger.debug(data2)
+ raise Exception("Unexpected configuration change")
+
+ wpas.request("SET update_config 0")
+ wpas.global_request("SET update_config 0")
+ if "OK" in wpas.request("SAVE_CONFIG"):
+ raise Exception("SAVE_CONFIG succeeded unexpectedly")
+ if "OK" in wpas.global_request("SAVE_CONFIG"):
+ raise Exception("SAVE_CONFIG (global) succeeded unexpectedly")
+
+ # replace the config file with a directory to break writing/renaming
+ os.remove(config)
+ os.mkdir(config)
+ wpas.request("SET update_config 1")
+ wpas.global_request("SET update_config 1")
+ if "OK" in wpas.request("SAVE_CONFIG"):
+ raise Exception("SAVE_CONFIG succeeded unexpectedly")
+ if "OK" in wpas.global_request("SAVE_CONFIG"):
+ raise Exception("SAVE_CONFIG (global) succeeded unexpectedly")
+
+ finally:
+ try:
+ os.rmdir(config)
+ except:
+ pass
+ if not wpas.ifname:
+ wpas.interface_add("wlan5")
+ wpas.dump_monitor()
+ wpas.request("SET country 00")
+ wpas.wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+
+def test_wpas_config_file_wps(dev, apdev):
+ """wpa_supplicant config file parsing/writing with WPS"""
+ config = "/tmp/test_wpas_config_file.conf"
+ if os.path.exists(config):
+ os.remove(config)
+
+ params = {"ssid": "test-wps", "eap_server": "1", "wps_state": "2",
+ "skip_cred_build": "1", "extra_cred": "wps-ctrl-cred"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+
+ try:
+ with open(config, "w") as f:
+ f.write("update_config=1\n")
+
+ wpas.interface_add("wlan5", config=config)
+
+ hapd.request("WPS_PIN any 12345670")
+ wpas.scan_for_bss(apdev[0]['bssid'], freq="2412")
+ wpas.request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ ev = wpas.wait_event(["WPS-FAIL"], timeout=10)
+ if ev is None:
+ raise Exception("WPS-FAIL event timed out")
+
+ with open(config, "r") as f:
+ data = f.read()
+ logger.info("Configuration file contents: " + data)
+ if "network=" in data:
+ raise Exception("Unexpected network block in configuration data")
+
+ finally:
+ try:
+ os.remove(config)
+ except:
+ pass
+ try:
+ os.remove(config + ".tmp")
+ except:
+ pass
+ try:
+ os.rmdir(config)
+ except:
+ pass
+
+def test_wpas_config_file_wps2(dev, apdev):
+ """wpa_supplicant config file parsing/writing with WPS (2)"""
+ config = "/tmp/test_wpas_config_file.conf"
+ if os.path.exists(config):
+ os.remove(config)
+
+ params = {"ssid": "test-wps", "eap_server": "1", "wps_state": "2",
+ "skip_cred_build": "1", "extra_cred": "wps-ctrl-cred2"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+
+ try:
+ with open(config, "w") as f:
+ f.write("update_config=1\n")
+
+ wpas.interface_add("wlan5", config=config)
+
+ hapd.request("WPS_PIN any 12345670")
+ wpas.scan_for_bss(apdev[0]['bssid'], freq="2412")
+ wpas.request("WPS_PIN " + apdev[0]['bssid'] + " 12345670")
+ ev = wpas.wait_event(["WPS-SUCCESS"], timeout=10)
+ if ev is None:
+ raise Exception("WPS-SUCCESS event timed out")
+
+ with open(config, "r") as f:
+ data = f.read()
+ logger.info("Configuration file contents: " + data)
+
+ with open(config, "r") as f:
+ data = f.read()
+ if "network=" not in data:
+ raise Exception("Missing network block in configuration data")
+ if "ssid=410a420d430044" not in data:
+ raise Exception("Unexpected ssid parameter value")
+
+ finally:
+ try:
+ os.remove(config)
+ except:
+ pass
+ try:
+ os.remove(config + ".tmp")
+ except:
+ pass
+ try:
+ os.rmdir(config)
+ except:
+ pass
+
+def test_wpas_config_file_set_psk(dev):
+ """wpa_supplicant config file parsing/writing with arbitrary PSK value"""
+ config = "/tmp/test_wpas_config_file.conf"
+ if os.path.exists(config):
+ os.remove(config)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+
+ try:
+ with open(config, "w") as f:
+ f.write("update_config=1\n")
+
+ wpas.interface_add("wlan5", config=config)
+
+ id = wpas.add_network()
+ wpas.set_network_quoted(id, "ssid", "foo")
+ if "OK" in wpas.request('SET_NETWORK %d psk "12345678"\n}\nmodel_name=foobar\nnetwork={\n#\"' % id):
+ raise Exception("Invalid psk value accepted")
+
+ if "OK" not in wpas.request("SAVE_CONFIG"):
+ raise Exception("Failed to save configuration file")
+
+ with open(config, "r") as f:
+ data = f.read()
+ logger.info("Configuration file contents: " + data)
+ if "model_name" in data:
+ raise Exception("Unexpected parameter added to configuration")
+
+ wpas.interface_remove("wlan5")
+ wpas.interface_add("wlan5", config=config)
+
+ finally:
+ try:
+ os.remove(config)
+ except:
+ pass
+ try:
+ os.remove(config + ".tmp")
+ except:
+ pass
+ try:
+ os.rmdir(config)
+ except:
+ pass
+
+def test_wpas_config_file_set_cred(dev):
+ """wpa_supplicant config file parsing/writing with arbitrary cred values"""
+ config = "/tmp/test_wpas_config_file.conf"
+ if os.path.exists(config):
+ os.remove(config)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+
+ try:
+ with open(config, "w") as f:
+ f.write("update_config=1\n")
+
+ wpas.interface_add("wlan5", config=config)
+
+ id = wpas.add_cred()
+ wpas.set_cred_quoted(id, "username", "hello")
+ fields = ["username", "milenage", "imsi", "password", "realm",
+ "phase1", "phase2", "provisioning_sp"]
+ for field in fields:
+ if "FAIL" not in wpas.request('SET_CRED %d %s "hello"\n}\nmodel_name=foobar\ncred={\n#\"' % (id, field)):
+ raise Exception("Invalid %s value accepted" % field)
+
+ if "OK" not in wpas.request("SAVE_CONFIG"):
+ raise Exception("Failed to save configuration file")
+
+ with open(config, "r") as f:
+ data = f.read()
+ logger.info("Configuration file contents: " + data)
+ if "model_name" in data:
+ raise Exception("Unexpected parameter added to configuration")
+
+ wpas.interface_remove("wlan5")
+ wpas.interface_add("wlan5", config=config)
+
+ finally:
+ try:
+ os.remove(config)
+ except:
+ pass
+ try:
+ os.remove(config + ".tmp")
+ except:
+ pass
+ try:
+ os.rmdir(config)
+ except:
+ pass
+
+def test_wpas_config_file_set_global(dev):
+ """wpa_supplicant config file parsing/writing with arbitrary global values"""
+ config = "/tmp/test_wpas_config_file.conf"
+ if os.path.exists(config):
+ os.remove(config)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+
+ try:
+ with open(config, "w") as f:
+ f.write("update_config=1\n")
+
+ wpas.interface_add("wlan5", config=config)
+
+ fields = ["model_name", "device_name", "ctrl_interface_group",
+ "opensc_engine_path", "pkcs11_engine_path",
+ "pkcs11_module_path", "openssl_ciphers", "pcsc_reader",
+ "pcsc_pin", "driver_param", "manufacturer", "model_name",
+ "model_number", "serial_number", "config_methods",
+ "p2p_ssid_postfix", "autoscan", "ext_password_backend",
+ "osu_dir", "wowlan_triggers", "fst_group_id",
+ "sched_scan_plans", "non_pref_chan"]
+ for field in fields:
+ if "FAIL" not in wpas.request('SET %s hello\nmodel_name=foobar' % field):
+ raise Exception("Invalid %s value accepted" % field)
+
+ if "OK" not in wpas.request("SAVE_CONFIG"):
+ raise Exception("Failed to save configuration file")
+
+ with open(config, "r") as f:
+ data = f.read()
+ logger.info("Configuration file contents: " + data)
+ if "model_name" in data:
+ raise Exception("Unexpected parameter added to configuration")
+
+ wpas.interface_remove("wlan5")
+ wpas.interface_add("wlan5", config=config)
+
+ finally:
+ try:
+ os.remove(config)
+ except:
+ pass
+ try:
+ os.remove(config + ".tmp")
+ except:
+ pass
+ try:
+ os.rmdir(config)
+ except:
+ pass
+
+def test_wpas_config_file_key_mgmt(dev, apdev, params):
+ """wpa_supplicant config file writing and key_mgmt values"""
+ config = os.path.join(params['logdir'],
+ 'wpas_config_file_key_mgmt.conf')
+ if os.path.exists(config):
+ os.remove(config)
+
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+
+ with open(config, "w") as f:
+ f.write("update_config=1\n")
+
+ wpas.interface_add("wlan5", config=config)
+
+ from test_dpp import params1_csign, params1_sta_connector, params1_sta_netaccesskey, check_dpp_capab
+
+ check_dpp_capab(wpas)
+
+ id = wpas.add_network()
+ wpas.set_network_quoted(id, "ssid", "foo")
+ wpas.set_network(id, "key_mgmt", "DPP")
+ wpas.set_network(id, "ieee80211w", "2")
+ wpas.set_network_quoted(id, "dpp_csign", params1_csign)
+ wpas.set_network_quoted(id, "dpp_connector", params1_sta_connector)
+ wpas.set_network_quoted(id, "dpp_netaccesskey", params1_sta_netaccesskey)
+ if "OK" not in wpas.request("SAVE_CONFIG"):
+ raise Exception("Failed to save configuration file")
+
+ with open(config, "r") as f:
+ data = f.read()
+ logger.info("Configuration file contents: " + data)
+ if "key_mgmt=DPP" not in data:
+ raise Exception("Missing key_mgmt")
+ if 'dpp_connector="' + params1_sta_connector + '"' not in data:
+ raise Exception("Missing dpp_connector")
+ if 'dpp_netaccesskey="' + params1_sta_netaccesskey + '"' not in data:
+ raise Exception("Missing dpp_netaccesskey")
+ if 'dpp_csign="' + params1_csign + '"' not in data:
+ raise Exception("Missing dpp_csign")
+
+ wpas.set_network(id, "dpp_csign", "NULL")
+ wpas.set_network(id, "dpp_connector", "NULL")
+ wpas.set_network(id, "dpp_netaccesskey", "NULL")
+ wpas.set_network_quoted(id, "psk", "12345678")
+ wpas.set_network(id, "ieee80211w", "0")
+
+ tests = ["WPA-PSK", "WPA-EAP", "IEEE8021X", "NONE", "WPA-NONE", "FT-PSK",
+ "FT-EAP", "FT-EAP-SHA384", "WPA-PSK-SHA256", "WPA-EAP-SHA256",
+ "SAE", "FT-SAE", "OSEN", "WPA-EAP-SUITE-B",
+ "WPA-EAP-SUITE-B-192", "FILS-SHA256", "FILS-SHA384",
+ "FT-FILS-SHA256", "FT-FILS-SHA384", "OWE", "DPP"]
+ supported_key_mgmts = dev[0].get_capability("key_mgmt")
+ for key_mgmt in tests:
+ if key_mgmt == "WPA-EAP-SUITE-B-192" and key_mgmt not in supported_key_mgmts:
+ logger.info("Skip unsupported " + key_mgmt)
+ continue
+ wpas.set_network(id, "key_mgmt", key_mgmt)
+ if "OK" not in wpas.request("SAVE_CONFIG"):
+ raise Exception("Failed to save configuration file")
+ with open(config, "r") as f:
+ data = f.read()
+ logger.info("Configuration file contents: " + data)
+ if "key_mgmt=" + key_mgmt not in data:
+ raise Exception("Missing key_mgmt " + key_mgmt)
+
+ wpas.interface_remove("wlan5")
+ wpas.interface_add("wlan5", config=config)
+
+def check_network_config(config, network_expected, check=None):
+ with open(config, "r") as f:
+ data = f.read()
+ logger.info("Configuration file contents:\n" + data.rstrip())
+ if network_expected and "network=" not in data:
+ raise Exception("Missing network block in configuration data")
+ if not network_expected and "network=" in data:
+ raise Exception("Unexpected network block in configuration data")
+ if check and check not in data:
+ raise Exception("Missing " + check)
+
+def test_wpas_config_file_sae(dev, apdev, params):
+ """wpa_supplicant config file writing with SAE"""
+ config = os.path.join(params['logdir'], 'wpas_config_file_sae.conf')
+ with open(config, "w") as f:
+ f.write("update_config=1\n")
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5", config=config)
+ check_sae_capab(wpas)
+
+ # Valid SAE configuration with sae_password
+ wpas.connect("test-sae", sae_password="sae-password", key_mgmt="SAE",
+ only_add_network=True)
+ wpas.save_config()
+ check_network_config(config, True, check="key_mgmt=SAE")
+
+ wpas.request("REMOVE_NETWORK all")
+ wpas.save_config()
+ check_network_config(config, False)
+
+ # Valid SAE configuration with psk
+ wpas.connect("test-sae", psk="sae-password", key_mgmt="SAE",
+ only_add_network=True)
+ wpas.save_config()
+ check_network_config(config, True, check="key_mgmt=SAE")
+ wpas.request("REMOVE_NETWORK all")
+
+ # Invalid PSK configuration with sae_password
+ wpas.connect("test-psk", sae_password="sae-password", key_mgmt="WPA-PSK",
+ only_add_network=True)
+ wpas.save_config()
+ check_network_config(config, False)
+
+ # Invalid SAE configuration with raw_psk
+ wpas.connect("test-sae", raw_psk=32*"00", key_mgmt="SAE",
+ only_add_network=True)
+ wpas.save_config()
+ check_network_config(config, False)
+
+def test_wpas_config_update_without_file(dev, apdev):
+ """wpa_supplicant SAVE_CONFIG without config file"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ wpas.set("update_config", "1")
+ if "FAIL" not in wpas.request("SAVE_CONFIG"):
+ raise Exception("SAVE_CONFIG accepted unexpectedly")
diff --git a/contrib/wpa/tests/hwsim/test_wpas_ctrl.py b/contrib/wpa/tests/hwsim/test_wpas_ctrl.py
new file mode 100644
index 000000000000..210c11907ee7
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_wpas_ctrl.py
@@ -0,0 +1,2159 @@
+# wpa_supplicant control interface
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import os
+import socket
+import subprocess
+import time
+import binascii
+
+import hostapd
+import hwsim_utils
+from hwsim import HWSimRadio
+from wpasupplicant import WpaSupplicant
+from utils import *
+from test_wpas_ap import wait_ap_ready
+
+@remote_compatible
+def test_wpas_ctrl_network(dev):
+ """wpa_supplicant ctrl_iface network set/get"""
+ skip_without_tkip(dev[0])
+ id = dev[0].add_network()
+
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id)):
+ raise Exception("Unexpected success for invalid SET_NETWORK")
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " name"):
+ raise Exception("Unexpected success for invalid SET_NETWORK")
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id + 1) + " proto OPEN"):
+ raise Exception("Unexpected success for invalid network id")
+ if "FAIL" not in dev[0].request("GET_NETWORK " + str(id)):
+ raise Exception("Unexpected success for invalid GET_NETWORK")
+ if "FAIL" not in dev[0].request("GET_NETWORK " + str(id + 1) + " proto"):
+ raise Exception("Unexpected success for invalid network id")
+
+ if "OK" not in dev[0].request("SET_NETWORK " + str(id) + " proto \t WPA2 "):
+ raise Exception("Unexpected failure for SET_NETWORK proto")
+ res = dev[0].request("GET_NETWORK " + str(id) + " proto")
+ if res != "RSN":
+ raise Exception("Unexpected SET_NETWORK/GET_NETWORK conversion for proto: " + res)
+
+ if "OK" not in dev[0].request("SET_NETWORK " + str(id) + " key_mgmt \t WPA-PSK "):
+ raise Exception("Unexpected success for SET_NETWORK key_mgmt")
+ res = dev[0].request("GET_NETWORK " + str(id) + " key_mgmt")
+ if res != "WPA-PSK":
+ raise Exception("Unexpected SET_NETWORK/GET_NETWORK conversion for key_mgmt: " + res)
+
+ if "OK" not in dev[0].request("SET_NETWORK " + str(id) + " auth_alg \t OPEN "):
+ raise Exception("Unexpected failure for SET_NETWORK auth_alg")
+ res = dev[0].request("GET_NETWORK " + str(id) + " auth_alg")
+ if res != "OPEN":
+ raise Exception("Unexpected SET_NETWORK/GET_NETWORK conversion for auth_alg: " + res)
+
+ if "OK" not in dev[0].request("SET_NETWORK " + str(id) + " eap \t TLS "):
+ raise Exception("Unexpected failure for SET_NETWORK eap")
+ res = dev[0].request("GET_NETWORK " + str(id) + " eap")
+ if res != "TLS":
+ raise Exception("Unexpected SET_NETWORK/GET_NETWORK conversion for eap: " + res)
+
+ tests = ("bssid foo", "key_mgmt foo", "key_mgmt ", "group NONE")
+ for t in tests:
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " " + t):
+ raise Exception("Unexpected success for invalid SET_NETWORK: " + t)
+
+ tests = [("key_mgmt", "WPA-PSK WPA-EAP IEEE8021X NONE WPA-NONE FT-PSK FT-EAP WPA-PSK-SHA256 WPA-EAP-SHA256"),
+ ("pairwise", "CCMP-256 GCMP-256 CCMP GCMP TKIP"),
+ ("group", "CCMP-256 GCMP-256 CCMP GCMP TKIP"),
+ ("auth_alg", "OPEN SHARED LEAP"),
+ ("scan_freq", "1 2 3 4 5 6 7 8 9 10 11 12 13 14 15"),
+ ("freq_list", "2412 2417"),
+ ("scan_ssid", "1"),
+ ("bssid", "00:11:22:33:44:55"),
+ ("proto", "WPA RSN OSEN"),
+ ("eap", "TLS"),
+ ("go_p2p_dev_addr", "22:33:44:55:66:aa"),
+ ("p2p_client_list", "22:33:44:55:66:bb 02:11:22:33:44:55")]
+ if "SAE" not in dev[0].get_capability("auth_alg"):
+ tests.append(("key_mgmt", "WPS OSEN"))
+ else:
+ tests.append(("key_mgmt", "WPS SAE FT-SAE OSEN"))
+
+ dev[0].set_network_quoted(id, "ssid", "test")
+ for field, value in tests:
+ dev[0].set_network(id, field, value)
+ res = dev[0].get_network(id, field)
+ if res != value:
+ raise Exception("Unexpected response for '" + field + "': '" + res + "'")
+
+ try:
+ value = "WPA-EAP-SUITE-B WPA-EAP-SUITE-B-192"
+ dev[0].set_network(id, "key_mgmt", value)
+ res = dev[0].get_network(id, "key_mgmt")
+ if res != value:
+ raise Exception("Unexpected response for key_mgmt")
+ except Exception as e:
+ if str(e).startswith("Unexpected"):
+ raise
+ else:
+ pass
+
+ q_tests = (("identity", "hello"),
+ ("anonymous_identity", "foo@nowhere.com"))
+ for field, value in q_tests:
+ dev[0].set_network_quoted(id, field, value)
+ res = dev[0].get_network(id, field)
+ if res != '"' + value + '"':
+ raise Exception("Unexpected quoted response for '" + field + "': '" + res + "'")
+
+ get_tests = (("foo", None), ("ssid", '"test"'))
+ for field, value in get_tests:
+ res = dev[0].get_network(id, field)
+ if res != value:
+ raise Exception("Unexpected response for '" + field + "': '" + res + "'")
+
+ if dev[0].get_network(id, "password"):
+ raise Exception("Unexpected response for 'password'")
+ dev[0].set_network_quoted(id, "password", "foo")
+ if dev[0].get_network(id, "password") != '*':
+ raise Exception("Unexpected response for 'password' (expected *)")
+ dev[0].set_network(id, "password", "hash:12345678901234567890123456789012")
+ if dev[0].get_network(id, "password") != '*':
+ raise Exception("Unexpected response for 'password' (expected *)")
+ dev[0].set_network(id, "password", "NULL")
+ if dev[0].get_network(id, "password"):
+ raise Exception("Unexpected response for 'password'")
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " password hash:12"):
+ raise Exception("Unexpected success for invalid password hash")
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + " password hash:123456789012345678x0123456789012"):
+ raise Exception("Unexpected success for invalid password hash")
+
+ dev[0].set_network(id, "identity", "414243")
+ if dev[0].get_network(id, "identity") != '"ABC"':
+ raise Exception("Unexpected identity hex->text response")
+
+ dev[0].set_network(id, "identity", 'P"abc\ndef"')
+ if dev[0].get_network(id, "identity") != "6162630a646566":
+ raise Exception("Unexpected identity printf->hex response")
+
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' identity P"foo'):
+ raise Exception("Unexpected success for invalid identity string")
+
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' identity 12x3'):
+ raise Exception("Unexpected success for invalid identity string")
+
+ if "WEP40" in dev[0].get_capability("group"):
+ for i in range(0, 4):
+ if "FAIL" in dev[0].request("SET_NETWORK " + str(id) + ' wep_key' + str(i) + ' aabbccddee'):
+ raise Exception("Unexpected wep_key set failure")
+ if dev[0].get_network(id, "wep_key" + str(i)) != '*':
+ raise Exception("Unexpected wep_key get failure")
+
+ if "FAIL" in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
+ raise Exception("Unexpected failure for psk_list string")
+
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list 00:11:x2:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
+ raise Exception("Unexpected success for invalid psk_list string")
+
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:x2:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
+ raise Exception("Unexpected success for invalid psk_list string")
+
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55+0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef'):
+ raise Exception("Unexpected success for invalid psk_list string")
+
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcde'):
+ raise Exception("Unexpected success for invalid psk_list string")
+
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' psk_list P2P-00:11:22:33:44:55-0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdex'):
+ raise Exception("Unexpected success for invalid psk_list string")
+
+ if dev[0].get_network(id, "psk_list"):
+ raise Exception("Unexpected psk_list get response")
+
+ if dev[0].list_networks()[0]['ssid'] != "test":
+ raise Exception("Unexpected ssid in LIST_NETWORKS")
+ dev[0].set_network(id, "ssid", "NULL")
+ if dev[0].list_networks()[0]['ssid'] != "":
+ raise Exception("Unexpected ssid in LIST_NETWORKS after clearing it")
+
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' ssid "0123456789abcdef0123456789abcdef0"'):
+ raise Exception("Too long SSID accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' scan_ssid qwerty'):
+ raise Exception("Invalid integer accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' scan_ssid 2'):
+ raise Exception("Too large integer accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' psk 12345678'):
+ raise Exception("Invalid PSK accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' psk "1234567"'):
+ raise Exception("Too short PSK accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' psk "1234567890123456789012345678901234567890123456789012345678901234"'):
+ raise Exception("Too long PSK accepted")
+ dev[0].set_network_quoted(id, "psk", "123456768")
+ dev[0].set_network_quoted(id, "psk", "123456789012345678901234567890123456789012345678901234567890123")
+ if dev[0].get_network(id, "psk") != '*':
+ raise Exception("Unexpected psk read result")
+
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' eap UNKNOWN'):
+ raise Exception("Unknown EAP method accepted")
+
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' password "foo'):
+ raise Exception("Invalid password accepted")
+
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' wep_key0 "foo'):
+ raise Exception("Invalid WEP key accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' wep_key0 "12345678901234567"'):
+ raise Exception("Too long WEP key accepted")
+ if "WEP40" in dev[0].get_capability("group"):
+ # too short WEP key is ignored
+ dev[0].set_network_quoted(id, "wep_key0", "1234")
+ dev[0].set_network_quoted(id, "wep_key1", "12345")
+ dev[0].set_network_quoted(id, "wep_key2", "1234567890123")
+ dev[0].set_network_quoted(id, "wep_key3", "1234567890123456")
+
+ dev[0].set_network(id, "go_p2p_dev_addr", "any")
+ if dev[0].get_network(id, "go_p2p_dev_addr") is not None:
+ raise Exception("Unexpected go_p2p_dev_addr value")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' go_p2p_dev_addr 00:11:22:33:44'):
+ raise Exception("Invalid go_p2p_dev_addr accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' p2p_client_list 00:11:22:33:44'):
+ raise Exception("Invalid p2p_client_list accepted")
+ if "FAIL" in dev[0].request('SET_NETWORK ' + str(id) + ' p2p_client_list 00:11:22:33:44:55 00:1'):
+ raise Exception("p2p_client_list truncation workaround failed")
+ if dev[0].get_network(id, "p2p_client_list") != "00:11:22:33:44:55":
+ raise Exception("p2p_client_list truncation workaround did not work")
+
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' auth_alg '):
+ raise Exception("Empty auth_alg accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' auth_alg FOO'):
+ raise Exception("Invalid auth_alg accepted")
+
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' proto '):
+ raise Exception("Empty proto accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' proto FOO'):
+ raise Exception("Invalid proto accepted")
+
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' pairwise '):
+ raise Exception("Empty pairwise accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' pairwise FOO'):
+ raise Exception("Invalid pairwise accepted")
+ if "FAIL" not in dev[0].request('SET_NETWORK ' + str(id) + ' pairwise WEP40'):
+ raise Exception("Invalid pairwise accepted")
+
+ if "OK" not in dev[0].request('BSSID ' + str(id) + ' 00:11:22:33:44:55'):
+ raise Exception("Unexpected BSSID failure")
+ if dev[0].request("GET_NETWORK 0 bssid") != '00:11:22:33:44:55':
+ raise Exception("BSSID command did not set network bssid")
+ if "OK" not in dev[0].request('BSSID ' + str(id) + ' 00:00:00:00:00:00'):
+ raise Exception("Unexpected BSSID failure")
+ if "FAIL" not in dev[0].request("GET_NETWORK 0 bssid"):
+ raise Exception("bssid claimed configured after clearing")
+ if "FAIL" not in dev[0].request('BSSID 123 00:11:22:33:44:55'):
+ raise Exception("Unexpected BSSID success")
+ if "FAIL" not in dev[0].request('BSSID ' + str(id) + ' 00:11:22:33:44'):
+ raise Exception("Unexpected BSSID success")
+ if "FAIL" not in dev[0].request('BSSID ' + str(id)):
+ raise Exception("Unexpected BSSID success")
+
+ tests = ["02:11:22:33:44:55",
+ "02:11:22:33:44:55 02:ae:be:ce:53:77",
+ "02:11:22:33:44:55/ff:00:ff:00:ff:00",
+ "02:11:22:33:44:55/ff:00:ff:00:ff:00 f2:99:88:77:66:55",
+ "f2:99:88:77:66:55 02:11:22:33:44:55/ff:00:ff:00:ff:00",
+ "f2:99:88:77:66:55 02:11:22:33:44:55/ff:00:ff:00:ff:00 12:34:56:78:90:ab",
+ "02:11:22:33:44:55/ff:ff:ff:00:00:00 02:ae:be:ce:53:77/00:00:00:00:00:ff"]
+ for val in tests:
+ dev[0].set_network(id, "bssid_ignore", val)
+ res = dev[0].get_network(id, "bssid_ignore")
+ if res != val:
+ raise Exception("Unexpected bssid_ignore value: %s != %s" % (res, val))
+ dev[0].set_network(id, "bssid_accept", val)
+ res = dev[0].get_network(id, "bssid_accept")
+ if res != val:
+ raise Exception("Unexpected bssid_accept value: %s != %s" % (res, val))
+
+ tests = ["foo",
+ "00:11:22:33:44:5",
+ "00:11:22:33:44:55q",
+ "00:11:22:33:44:55/",
+ "00:11:22:33:44:55/66:77:88:99:aa:b"]
+ for val in tests:
+ if "FAIL" not in dev[0].request("SET_NETWORK %d bssid_ignore %s" % (id, val)):
+ raise Exception("Invalid bssid_ignore value accepted")
+
+@remote_compatible
+def test_wpas_ctrl_network_oom(dev):
+ """wpa_supplicant ctrl_iface network OOM in string parsing"""
+ id = dev[0].add_network()
+
+ tests = [('"foo"', 1, 'dup_binstr;wpa_config_set'),
+ ('P"foo"', 1, 'dup_binstr;wpa_config_set'),
+ ('P"foo"', 2, 'wpa_config_set'),
+ ('112233', 1, 'wpa_config_set')]
+ for val, count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ if "FAIL" not in dev[0].request("SET_NETWORK " + str(id) + ' ssid ' + val):
+ raise Exception("Unexpected success for SET_NETWORK during OOM")
+
+@remote_compatible
+def test_wpas_ctrl_many_networks(dev, apdev):
+ """wpa_supplicant ctrl_iface LIST_NETWORKS with huge number of networks"""
+ for i in range(1000):
+ id = dev[0].add_network()
+ res = dev[0].request("LIST_NETWORKS")
+ if str(id) in res:
+ raise Exception("Last added network was unexpectedly included")
+ res = dev[0].request("LIST_NETWORKS LAST_ID=%d" % (id - 2))
+ if str(id) not in res:
+ raise Exception("Last added network was not present when using LAST_ID")
+ # This command can take a very long time under valgrind testing on a low
+ # power CPU, so increase the command timeout significantly to avoid issues
+ # with the test case failing and following reset operation timing out.
+ dev[0].request("REMOVE_NETWORK all", timeout=60)
+
+@remote_compatible
+def test_wpas_ctrl_dup_network(dev, apdev):
+ """wpa_supplicant ctrl_iface DUP_NETWORK"""
+ ssid = "target"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hostapd.add_ap(apdev[0], params)
+
+ src = dev[0].connect("another", psk=passphrase, scan_freq="2412",
+ only_add_network=True)
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", ssid)
+ for f in ["key_mgmt", "psk", "scan_freq"]:
+ res = dev[0].request("DUP_NETWORK {} {} {}".format(src, id, f))
+ if "OK" not in res:
+ raise Exception("DUP_NETWORK failed")
+ dev[0].connect_network(id)
+
+ if "FAIL" not in dev[0].request("DUP_NETWORK "):
+ raise Exception("Unexpected DUP_NETWORK success")
+ if "FAIL" not in dev[0].request("DUP_NETWORK %d " % id):
+ raise Exception("Unexpected DUP_NETWORK success")
+ if "FAIL" not in dev[0].request("DUP_NETWORK %d %d" % (id, id)):
+ raise Exception("Unexpected DUP_NETWORK success")
+ if "FAIL" not in dev[0].request("DUP_NETWORK 123456 1234567 "):
+ raise Exception("Unexpected DUP_NETWORK success")
+ if "FAIL" not in dev[0].request("DUP_NETWORK %d 123456 " % id):
+ raise Exception("Unexpected DUP_NETWORK success")
+ if "FAIL" not in dev[0].request("DUP_NETWORK %d %d foo" % (id, id)):
+ raise Exception("Unexpected DUP_NETWORK success")
+ dev[0].request("DISCONNECT")
+ if "OK" not in dev[0].request("DUP_NETWORK %d %d ssid" % (id, id)):
+ raise Exception("Unexpected DUP_NETWORK failure")
+
+@remote_compatible
+def test_wpas_ctrl_dup_network_global(dev, apdev):
+ """wpa_supplicant ctrl_iface DUP_NETWORK (global)"""
+ ssid = "target"
+ passphrase = 'qwertyuiop'
+ params = hostapd.wpa2_params(ssid=ssid, passphrase=passphrase)
+ hostapd.add_ap(apdev[0], params)
+
+ src = dev[0].connect("another", psk=passphrase, scan_freq="2412",
+ only_add_network=True)
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", ssid)
+ for f in ["key_mgmt", "psk", "scan_freq"]:
+ res = dev[0].global_request("DUP_NETWORK {} {} {} {} {}".format(dev[0].ifname, dev[0].ifname, src, id, f))
+ if "OK" not in res:
+ raise Exception("DUP_NETWORK failed")
+ dev[0].connect_network(id)
+
+ if "FAIL" not in dev[0].global_request("DUP_NETWORK "):
+ raise Exception("Unexpected DUP_NETWORK success")
+ if "FAIL" not in dev[0].global_request("DUP_NETWORK %s" % dev[0].ifname):
+ raise Exception("Unexpected DUP_NETWORK success")
+ if "FAIL" not in dev[0].global_request("DUP_NETWORK %s %s" % (dev[0].ifname, dev[0].ifname)):
+ raise Exception("Unexpected DUP_NETWORK success")
+ if "FAIL" not in dev[0].global_request("DUP_NETWORK %s %s %d" % (dev[0].ifname, dev[0].ifname, id)):
+ raise Exception("Unexpected DUP_NETWORK success")
+ if "FAIL" not in dev[0].global_request("DUP_NETWORK %s %s %d %d" % (dev[0].ifname, dev[0].ifname, id, id)):
+ raise Exception("Unexpected DUP_NETWORK success")
+ dev[0].request("DISCONNECT")
+ if "OK" not in dev[0].global_request("DUP_NETWORK %s %s %d %d ssid" % (dev[0].ifname, dev[0].ifname, id, id)):
+ raise Exception("Unexpected DUP_NETWORK failure")
+
+def add_cred(dev):
+ id = dev.add_cred()
+ ev = dev.wait_event(["CRED-ADDED"])
+ if ev is None:
+ raise Exception("Missing CRED-ADDED event")
+ if " " + str(id) not in ev:
+ raise Exception("CRED-ADDED event without matching id")
+ return id
+
+def set_cred(dev, id, field, value):
+ dev.set_cred(id, field, value)
+ ev = dev.wait_event(["CRED-MODIFIED"])
+ if ev is None:
+ raise Exception("Missing CRED-MODIFIED event")
+ if " " + str(id) + " " not in ev:
+ raise Exception("CRED-MODIFIED event without matching id")
+ if field not in ev:
+ raise Exception("CRED-MODIFIED event without matching field")
+
+def set_cred_quoted(dev, id, field, value):
+ dev.set_cred_quoted(id, field, value)
+ ev = dev.wait_event(["CRED-MODIFIED"])
+ if ev is None:
+ raise Exception("Missing CRED-MODIFIED event")
+ if " " + str(id) + " " not in ev:
+ raise Exception("CRED-MODIFIED event without matching id")
+ if field not in ev:
+ raise Exception("CRED-MODIFIED event without matching field")
+
+def remove_cred(dev, id):
+ dev.remove_cred(id)
+ ev = dev.wait_event(["CRED-REMOVED"])
+ if ev is None:
+ raise Exception("Missing CRED-REMOVED event")
+ if " " + str(id) not in ev:
+ raise Exception("CRED-REMOVED event without matching id")
+
+@remote_compatible
+def test_wpas_ctrl_cred(dev):
+ """wpa_supplicant ctrl_iface cred set"""
+ id1 = add_cred(dev[0])
+ if "FAIL" not in dev[0].request("SET_CRED " + str(id1 + 1) + " temporary 1"):
+ raise Exception("SET_CRED succeeded unexpectedly on unknown cred id")
+ if "FAIL" not in dev[0].request("SET_CRED " + str(id1)):
+ raise Exception("Invalid SET_CRED succeeded unexpectedly")
+ if "FAIL" not in dev[0].request("SET_CRED " + str(id1) + " temporary"):
+ raise Exception("Invalid SET_CRED succeeded unexpectedly")
+ if "FAIL" not in dev[0].request("GET_CRED " + str(id1 + 1) + " temporary"):
+ raise Exception("GET_CRED succeeded unexpectedly on unknown cred id")
+ if "FAIL" not in dev[0].request("GET_CRED " + str(id1)):
+ raise Exception("Invalid GET_CRED succeeded unexpectedly")
+ if "FAIL" not in dev[0].request("GET_CRED " + str(id1) + " foo"):
+ raise Exception("Invalid GET_CRED succeeded unexpectedly")
+ id = add_cred(dev[0])
+ id2 = add_cred(dev[0])
+ set_cred(dev[0], id, "temporary", "1")
+ set_cred(dev[0], id, "priority", "1")
+ set_cred(dev[0], id, "pcsc", "1")
+ set_cred(dev[0], id, "sim_num", "0")
+ set_cred_quoted(dev[0], id, "private_key_passwd", "test")
+ set_cred_quoted(dev[0], id, "domain_suffix_match", "test")
+ set_cred_quoted(dev[0], id, "phase1", "test")
+ set_cred_quoted(dev[0], id, "phase2", "test")
+
+ if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " eap FOO"):
+ raise Exception("Unexpected success on unknown EAP method")
+
+ if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " username 12xa"):
+ raise Exception("Unexpected success on invalid string")
+
+ for i in ("11", "1122", "112233445566778899aabbccddeeff00"):
+ if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " roaming_consortium " + i):
+ raise Exception("Unexpected success on invalid roaming_consortium")
+
+ dev[0].set_cred(id, "excluded_ssid", "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff")
+ if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " excluded_ssid 00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00"):
+ raise Exception("Unexpected success on invalid excluded_ssid")
+
+ if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " foo 4142"):
+ raise Exception("Unexpected success on unknown field")
+
+ tests = ["sp_priority 256",
+ 'roaming_partner "example.org"',
+ 'roaming_partner "' + 200*'a' + '.example.org,"',
+ 'roaming_partner "example.org,1"',
+ 'roaming_partner "example.org,1,2"',
+ 'roaming_partner "example.org,1,2,ABC"']
+ for t in tests:
+ if "FAIL" not in dev[0].request("SET_CRED " + str(id) + " " + t):
+ raise Exception("Unexpected success on invalid SET_CRED value: " + t)
+
+ id3 = add_cred(dev[0])
+ id4 = add_cred(dev[0])
+ if len(dev[0].request("LIST_CREDS").splitlines()) != 6:
+ raise Exception("Unexpected LIST_CREDS result(1)")
+
+ remove_cred(dev[0], id1)
+ remove_cred(dev[0], id3)
+ remove_cred(dev[0], id4)
+ remove_cred(dev[0], id2)
+ remove_cred(dev[0], id)
+ if "FAIL" not in dev[0].request("REMOVE_CRED 1"):
+ raise Exception("Unexpected success on invalid remove cred")
+ if len(dev[0].request("LIST_CREDS").splitlines()) != 1:
+ raise Exception("Unexpected LIST_CREDS result(2)")
+
+ id = add_cred(dev[0])
+ values = [("temporary", "1", False),
+ ("temporary", "0", False),
+ ("pcsc", "1", False),
+ ("realm", "example.com", True),
+ ("username", "user@example.com", True),
+ ("password", "foo", True, "*"),
+ ("ca_cert", "ca.pem", True),
+ ("client_cert", "user.pem", True),
+ ("private_key", "key.pem", True),
+ ("private_key_passwd", "foo", True, "*"),
+ ("imsi", "310026-000000000", True),
+ ("milenage", "foo", True, "*"),
+ ("domain_suffix_match", "example.com", True),
+ ("domain", "example.com", True),
+ ("domain", "example.org", True, "example.com\nexample.org"),
+ ("roaming_consortium", "0123456789", False),
+ ("required_roaming_consortium", "456789", False),
+ ("eap", "TTLS", False),
+ ("phase1", "foo=bar1", True),
+ ("phase2", "foo=bar2", True),
+ ("excluded_ssid", "test", True),
+ ("excluded_ssid", "foo", True, "test\nfoo"),
+ ("roaming_partner", "example.com,0,4,*", True),
+ ("roaming_partner", "example.org,1,2,US", True,
+ "example.com,0,4,*\nexample.org,1,2,US"),
+ ("update_identifier", "4", False),
+ ("provisioning_sp", "sp.example.com", True),
+ ("sp_priority", "7", False),
+ ("min_dl_bandwidth_home", "100", False),
+ ("min_ul_bandwidth_home", "101", False),
+ ("min_dl_bandwidth_roaming", "102", False),
+ ("min_ul_bandwidth_roaming", "103", False),
+ ("max_bss_load", "57", False),
+ ("req_conn_capab", "6:22,80,443", False),
+ ("req_conn_capab", "17:500", False, "6:22,80,443\n17:500"),
+ ("req_conn_capab", "50", False, "6:22,80,443\n17:500\n50"),
+ ("ocsp", "1", False)]
+ for v in values:
+ if v[2]:
+ set_cred_quoted(dev[0], id, v[0], v[1])
+ else:
+ set_cred(dev[0], id, v[0], v[1])
+ val = dev[0].get_cred(id, v[0])
+ if len(v) == 4:
+ expect = v[3]
+ else:
+ expect = v[1]
+ if val != expect:
+ raise Exception("Unexpected GET_CRED value for {}: {} != {}".format(v[0], val, expect))
+ creds = dev[0].request("LIST_CREDS").splitlines()
+ if len(creds) != 2:
+ raise Exception("Unexpected LIST_CREDS result(3)")
+ if creds[1] != "0\texample.com\tuser@example.com\texample.com\t310026-000000000":
+ raise Exception("Unexpected LIST_CREDS value")
+ remove_cred(dev[0], id)
+ if len(dev[0].request("LIST_CREDS").splitlines()) != 1:
+ raise Exception("Unexpected LIST_CREDS result(4)")
+
+ id = add_cred(dev[0])
+ set_cred_quoted(dev[0], id, "domain", "foo.example.com")
+ id = add_cred(dev[0])
+ set_cred_quoted(dev[0], id, "domain", "bar.example.com")
+ id = add_cred(dev[0])
+ set_cred_quoted(dev[0], id, "domain", "foo.example.com")
+ if "OK" not in dev[0].request("REMOVE_CRED sp_fqdn=foo.example.com"):
+ raise Exception("REMOVE_CRED failed")
+ creds = dev[0].request("LIST_CREDS")
+ if "foo.example.com" in creds:
+ raise Exception("REMOVE_CRED sp_fqdn did not remove cred")
+ if "bar.example.com" not in creds:
+ raise Exception("REMOVE_CRED sp_fqdn removed incorrect cred")
+ dev[0].request("REMOVE_CRED all")
+
+ id = add_cred(dev[0])
+ set_cred_quoted(dev[0], id, "domain", "foo.example.com")
+ set_cred_quoted(dev[0], id, "provisioning_sp", "sp.foo.example.com")
+ id = add_cred(dev[0])
+ set_cred_quoted(dev[0], id, "domain", "bar.example.com")
+ set_cred_quoted(dev[0], id, "provisioning_sp", "sp.bar.example.com")
+ id = add_cred(dev[0])
+ set_cred_quoted(dev[0], id, "domain", "foo.example.com")
+ set_cred_quoted(dev[0], id, "provisioning_sp", "sp.foo.example.com")
+ if "OK" not in dev[0].request("REMOVE_CRED provisioning_sp=sp.foo.example.com"):
+ raise Exception("REMOVE_CRED failed")
+ creds = dev[0].request("LIST_CREDS")
+ if "foo.example.com" in creds:
+ raise Exception("REMOVE_CRED provisioning_sp did not remove cred")
+ if "bar.example.com" not in creds:
+ raise Exception("REMOVE_CRED provisioning_sp removed incorrect cred")
+ dev[0].request("REMOVE_CRED all")
+
+ # Test large number of creds and LIST_CREDS truncation
+ dev[0].dump_monitor()
+ for i in range(0, 100):
+ id = add_cred(dev[0])
+ set_cred_quoted(dev[0], id, "realm", "relatively.long.realm.test%d.example.com" % i)
+ dev[0].dump_monitor()
+ creds = dev[0].request("LIST_CREDS")
+ for i in range(0, 100):
+ dev[0].remove_cred(i)
+ dev[0].dump_monitor()
+ if len(creds) < 3900 or len(creds) > 4100:
+ raise Exception("Unexpected LIST_CREDS length: %d" % len(creds))
+ if "test10.example.com" not in creds:
+ raise Exception("Missing credential")
+ if len(creds.splitlines()) > 95:
+ raise Exception("Too many LIST_CREDS entries in the buffer")
+
+def test_wpas_ctrl_pno(dev):
+ """wpa_supplicant ctrl_iface pno"""
+ if "FAIL" not in dev[0].request("SET pno 1"):
+ raise Exception("Unexpected success in enabling PNO without enabled network blocks")
+ id = dev[0].add_network()
+ dev[0].set_network_quoted(id, "ssid", "test")
+ dev[0].set_network(id, "key_mgmt", "NONE")
+ dev[0].request("ENABLE_NETWORK " + str(id) + " no-connect")
+ #mac80211_hwsim does not yet support PNO, so this fails
+ if "FAIL" not in dev[0].request("SET pno 1"):
+ raise Exception("Unexpected success in enabling PNO")
+ if "FAIL" not in dev[0].request("SET pno 1 freq=2000-3000,5180"):
+ raise Exception("Unexpected success in enabling PNO")
+ if "FAIL" not in dev[0].request("SET pno 1 freq=0-6000"):
+ raise Exception("Unexpected success in enabling PNO")
+ if "FAIL" in dev[0].request("SET pno 0"):
+ raise Exception("Unexpected failure in disabling PNO")
+
+@remote_compatible
+def test_wpas_ctrl_get(dev):
+ """wpa_supplicant ctrl_iface get"""
+ if "FAIL" in dev[0].request("GET version"):
+ raise Exception("Unexpected get failure for version")
+ if "FAIL" in dev[0].request("GET wifi_display"):
+ raise Exception("Unexpected get failure for wifi_display")
+ if "FAIL" not in dev[0].request("GET foo"):
+ raise Exception("Unexpected success on get command")
+
+ dev[0].set("wifi_display", "0")
+ if dev[0].request("GET wifi_display") != '0':
+ raise Exception("Unexpected wifi_display value")
+ dev[0].set("wifi_display", "1")
+ if dev[0].request("GET wifi_display") != '1':
+ raise Exception("Unexpected wifi_display value")
+ dev[0].request("P2P_SET disabled 1")
+ if dev[0].request("GET wifi_display") != '0':
+ raise Exception("Unexpected wifi_display value (P2P disabled)")
+ dev[0].request("P2P_SET disabled 0")
+ if dev[0].request("GET wifi_display") != '1':
+ raise Exception("Unexpected wifi_display value (P2P re-enabled)")
+ dev[0].set("wifi_display", "0")
+ if dev[0].request("GET wifi_display") != '0':
+ raise Exception("Unexpected wifi_display value")
+
+@remote_compatible
+def test_wpas_ctrl_preauth(dev):
+ """wpa_supplicant ctrl_iface preauth"""
+ if "FAIL" not in dev[0].request("PREAUTH "):
+ raise Exception("Unexpected success on invalid PREAUTH")
+ if "FAIL" in dev[0].request("PREAUTH 00:11:22:33:44:55"):
+ raise Exception("Unexpected failure on PREAUTH")
+
+@remote_compatible
+def test_wpas_ctrl_tdls_discover(dev):
+ """wpa_supplicant ctrl_iface tdls_discover"""
+ if "FAIL" not in dev[0].request("TDLS_DISCOVER "):
+ raise Exception("Unexpected success on invalid TDLS_DISCOVER")
+ if "FAIL" not in dev[0].request("TDLS_DISCOVER 00:11:22:33:44:55"):
+ raise Exception("Unexpected success on TDLS_DISCOVER")
+
+@remote_compatible
+def test_wpas_ctrl_tdls_chan_switch(dev):
+ """wpa_supplicant ctrl_iface tdls_chan_switch error cases"""
+ for args in ['', '00:11:22:33:44:55']:
+ if "FAIL" not in dev[0].request("TDLS_CANCEL_CHAN_SWITCH " + args):
+ raise Exception("Unexpected success on invalid TDLS_CANCEL_CHAN_SWITCH: " + args)
+
+ for args in ['', 'foo ', '00:11:22:33:44:55 ', '00:11:22:33:44:55 q',
+ '00:11:22:33:44:55 81', '00:11:22:33:44:55 81 1234',
+ '00:11:22:33:44:55 81 1234 center_freq1=234 center_freq2=345 bandwidth=456 sec_channel_offset=567 ht vht']:
+ if "FAIL" not in dev[0].request("TDLS_CHAN_SWITCH " + args):
+ raise Exception("Unexpected success on invalid TDLS_CHAN_SWITCH: " + args)
+
+@remote_compatible
+def test_wpas_ctrl_addr(dev):
+ """wpa_supplicant ctrl_iface invalid address"""
+ if "FAIL" not in dev[0].request("TDLS_SETUP "):
+ raise Exception("Unexpected success on invalid TDLS_SETUP")
+ if "FAIL" not in dev[0].request("TDLS_TEARDOWN "):
+ raise Exception("Unexpected success on invalid TDLS_TEARDOWN")
+ if "FAIL" not in dev[0].request("FT_DS "):
+ raise Exception("Unexpected success on invalid FT_DS")
+ if "FAIL" not in dev[0].request("WPS_PBC 00:11:22:33:44"):
+ raise Exception("Unexpected success on invalid WPS_PBC")
+ if "FAIL" not in dev[0].request("WPS_PIN 00:11:22:33:44"):
+ raise Exception("Unexpected success on invalid WPS_PIN")
+ if "FAIL" not in dev[0].request("WPS_NFC 00:11:22:33:44"):
+ raise Exception("Unexpected success on invalid WPS_NFC")
+ if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44 12345670"):
+ raise Exception("Unexpected success on invalid WPS_REG")
+ if "FAIL" not in dev[0].request("IBSS_RSN 00:11:22:33:44"):
+ raise Exception("Unexpected success on invalid IBSS_RSN")
+ if "FAIL" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44"):
+ raise Exception("Unexpected success on invalid BSSID_IGNORE")
+
+@remote_compatible
+def test_wpas_ctrl_wps_errors(dev):
+ """wpa_supplicant ctrl_iface WPS error cases"""
+ if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55"):
+ raise Exception("Unexpected success on invalid WPS_REG")
+ if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55 12345670 2233"):
+ raise Exception("Unexpected success on invalid WPS_REG")
+ if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55 12345670 2233 OPEN"):
+ raise Exception("Unexpected success on invalid WPS_REG")
+ if "FAIL" not in dev[0].request("WPS_REG 00:11:22:33:44:55 12345670 2233 OPEN NONE"):
+ raise Exception("Unexpected success on invalid WPS_REG")
+
+ if "FAIL" not in dev[0].request("WPS_AP_PIN random"):
+ raise Exception("Unexpected success on WPS_AP_PIN in non-AP mode")
+
+ if "FAIL" not in dev[0].request("WPS_ER_PIN any"):
+ raise Exception("Unexpected success on invalid WPS_ER_PIN")
+
+ if "FAIL" not in dev[0].request("WPS_ER_LEARN 00:11:22:33:44:55"):
+ raise Exception("Unexpected success on invalid WPS_ER_LEARN")
+
+ if "FAIL" not in dev[0].request("WPS_ER_SET_CONFIG 00:11:22:33:44:55"):
+ raise Exception("Unexpected success on invalid WPS_ER_SET_CONFIG")
+
+ if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55"):
+ raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
+ if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670"):
+ raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
+ if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670 2233"):
+ raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
+ if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670 2233 OPEN"):
+ raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
+ if "FAIL" not in dev[0].request("WPS_ER_CONFIG 00:11:22:33:44:55 12345670 2233 OPEN NONE"):
+ raise Exception("Unexpected success on invalid WPS_ER_CONFIG")
+
+ if "FAIL" not in dev[0].request("WPS_ER_NFC_CONFIG_TOKEN WPS"):
+ raise Exception("Unexpected success on invalid WPS_ER_NFC_CONFIG_TOKEN")
+ if "FAIL" not in dev[0].request("WPS_ER_NFC_CONFIG_TOKEN FOO 00:11:22:33:44:55"):
+ raise Exception("Unexpected success on invalid WPS_ER_NFC_CONFIG_TOKEN")
+ if "FAIL" not in dev[0].request("WPS_ER_NFC_CONFIG_TOKEN NDEF 00:11:22:33:44:55"):
+ raise Exception("Unexpected success on invalid WPS_ER_NFC_CONFIG_TOKEN")
+
+ if "FAIL" not in dev[0].request("WPS_NFC_CONFIG_TOKEN FOO"):
+ raise Exception("Unexpected success on invalid WPS_NFC_CONFIG_TOKEN")
+ if "FAIL" not in dev[0].request("WPS_NFC_CONFIG_TOKEN WPS FOO"):
+ raise Exception("Unexpected success on invalid WPS_NFC_CONFIG_TOKEN")
+ if "FAIL" not in dev[0].request("WPS_NFC_TOKEN FOO"):
+ raise Exception("Unexpected success on invalid WPS_NFC_TOKEN")
+
+@remote_compatible
+def test_wpas_ctrl_config_parser(dev):
+ """wpa_supplicant ctrl_iface SET config parser"""
+ if "FAIL" not in dev[0].request("SET pbc_in_m1 qwerty"):
+ raise Exception("Non-number accepted as integer")
+ if "FAIL" not in dev[0].request("SET eapol_version 0"):
+ raise Exception("Out-of-range value accepted")
+ if "FAIL" not in dev[0].request("SET eapol_version 10"):
+ raise Exception("Out-of-range value accepted")
+
+ if "FAIL" not in dev[0].request("SET serial_number 0123456789abcdef0123456789abcdef0"):
+ raise Exception("Too long string accepted")
+
+@remote_compatible
+def test_wpas_ctrl_mib(dev):
+ """wpa_supplicant ctrl_iface MIB"""
+ mib = dev[0].get_mib()
+ if "dot11RSNAOptionImplemented" not in mib:
+ raise Exception("Missing MIB entry")
+ if mib["dot11RSNAOptionImplemented"] != "TRUE":
+ raise Exception("Unexpected dot11RSNAOptionImplemented value")
+
+def test_wpas_ctrl_set_wps_params(dev):
+ """wpa_supplicant ctrl_iface SET config_methods"""
+ try:
+ _test_wpas_ctrl_set_wps_params(dev)
+ finally:
+ dev[2].request("SET config_methods ")
+
+def _test_wpas_ctrl_set_wps_params(dev):
+ ts = ["config_methods label virtual_display virtual_push_button keypad",
+ "device_type 1-0050F204-1",
+ "os_version 01020300",
+ "uuid 12345678-9abc-def0-1234-56789abcdef0"]
+ for t in ts:
+ if "OK" not in dev[2].request("SET " + t):
+ raise Exception("SET failed for: " + t)
+
+ ts = ["uuid 12345678+9abc-def0-1234-56789abcdef0",
+ "uuid 12345678-qabc-def0-1234-56789abcdef0",
+ "uuid 12345678-9abc+def0-1234-56789abcdef0",
+ "uuid 12345678-9abc-qef0-1234-56789abcdef0",
+ "uuid 12345678-9abc-def0+1234-56789abcdef0",
+ "uuid 12345678-9abc-def0-q234-56789abcdef0",
+ "uuid 12345678-9abc-def0-1234+56789abcdef0",
+ "uuid 12345678-9abc-def0-1234-q6789abcdef0",
+ "uuid qwerty"]
+ for t in ts:
+ if "FAIL" not in dev[2].request("SET " + t):
+ raise Exception("SET succeeded for: " + t)
+
+def test_wpas_ctrl_level(dev):
+ """wpa_supplicant ctrl_iface LEVEL"""
+ try:
+ if "FAIL" not in dev[2].request("LEVEL 3"):
+ raise Exception("Unexpected LEVEL success")
+ if "OK" not in dev[2].mon.request("LEVEL 2"):
+ raise Exception("Unexpected LEVEL failure")
+ dev[2].request("SCAN freq=2412")
+ ev = dev[2].wait_event(["State:"], timeout=5)
+ if ev is None:
+ raise Exception("No debug message received")
+ dev[2].wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=5)
+ finally:
+ dev[2].mon.request("LEVEL 3")
+
+@remote_compatible
+def test_wpas_ctrl_bssid_filter(dev, apdev):
+ """wpa_supplicant bssid_filter"""
+ try:
+ if "OK" not in dev[2].request("SET bssid_filter " + apdev[0]['bssid']):
+ raise Exception("Failed to set bssid_filter")
+ params = {"ssid": "test"}
+ hostapd.add_ap(apdev[0], params)
+ hostapd.add_ap(apdev[1], params)
+ dev[2].scan_for_bss(apdev[0]['bssid'], freq="2412")
+ dev[2].scan(freq="2412")
+ bss = dev[2].get_bss(apdev[0]['bssid'])
+ if bss is None or len(bss) == 0:
+ raise Exception("Missing BSS data")
+ bss = dev[2].get_bss(apdev[1]['bssid'])
+ if bss and len(bss) != 0:
+ raise Exception("Unexpected BSS data")
+ dev[2].request("SET bssid_filter " + apdev[0]['bssid'] + " " + \
+ apdev[1]['bssid'])
+ dev[2].scan(freq="2412")
+ bss = dev[2].get_bss(apdev[0]['bssid'])
+ if bss is None or len(bss) == 0:
+ raise Exception("Missing BSS data")
+ bss = dev[2].get_bss(apdev[1]['bssid'])
+ if bss is None or len(bss) == 0:
+ raise Exception("Missing BSS data(2)")
+ res = dev[2].request("SCAN_RESULTS").splitlines()
+ if "test" not in res[1] or "test" not in res[2]:
+ raise Exception("SSID missing from SCAN_RESULTS")
+ if apdev[0]['bssid'] not in res[1] and apdev[1]['bssid'] not in res[1]:
+ raise Exception("BSS1 missing from SCAN_RESULTS")
+ if apdev[0]['bssid'] not in res[2] and apdev[1]['bssid'] not in res[2]:
+ raise Exception("BSS1 missing from SCAN_RESULTS")
+
+ if "FAIL" not in dev[2].request("SET bssid_filter 00:11:22:33:44:55 00:11:22:33:44"):
+ raise Exception("Unexpected success for invalid SET bssid_filter")
+ finally:
+ dev[2].request("SET bssid_filter ")
+
+@remote_compatible
+def test_wpas_ctrl_disallow_aps(dev, apdev):
+ """wpa_supplicant ctrl_iface disallow_aps"""
+ params = {"ssid": "test"}
+ hostapd.add_ap(apdev[0], params)
+
+ if "FAIL" not in dev[0].request("SET disallow_aps bssid "):
+ raise Exception("Unexpected success on invalid disallow_aps")
+ if "FAIL" not in dev[0].request("SET disallow_aps bssid 00:11:22:33:44"):
+ raise Exception("Unexpected success on invalid disallow_aps")
+ if "FAIL" not in dev[0].request("SET disallow_aps ssid 0"):
+ raise Exception("Unexpected success on invalid disallow_aps")
+ if "FAIL" not in dev[0].request("SET disallow_aps ssid 4q"):
+ raise Exception("Unexpected success on invalid disallow_aps")
+ if "FAIL" not in dev[0].request("SET disallow_aps bssid 00:11:22:33:44:55 ssid 112233 ssid 123"):
+ raise Exception("Unexpected success on invalid disallow_aps")
+ if "FAIL" not in dev[0].request("SET disallow_aps ssid 000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f00"):
+ raise Exception("Unexpected success on invalid disallow_aps")
+ if "FAIL" not in dev[0].request("SET disallow_aps foo 112233445566"):
+ raise Exception("Unexpected success on invalid disallow_aps")
+
+ dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+ hostapd.add_ap(apdev[1], params)
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq="2412")
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("SET disallow_aps bssid 00:11:22:33:44:55 bssid 00:22:33:44:55:66"):
+ raise Exception("Failed to set disallow_aps")
+ if "OK" not in dev[0].request("SET disallow_aps bssid " + apdev[0]['bssid']):
+ raise Exception("Failed to set disallow_aps")
+ ev = dev[0].wait_connected(timeout=30, error="Reassociation timed out")
+ if apdev[1]['bssid'] not in ev:
+ raise Exception("Unexpected BSSID")
+
+ dev[0].dump_monitor()
+ if "OK" not in dev[0].request("SET disallow_aps ssid " + binascii.hexlify(b"test").decode()):
+ raise Exception("Failed to set disallow_aps")
+ dev[0].wait_disconnected(timeout=5, error="Disconnection not seen")
+ ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected reassociation")
+
+ dev[0].request("DISCONNECT")
+ dev[0].p2p_start_go(freq=2412)
+ if "OK" not in dev[0].request("SET disallow_aps "):
+ raise Exception("Failed to set disallow_aps")
+
+@remote_compatible
+def test_wpas_ctrl_blob(dev):
+ """wpa_supplicant ctrl_iface SET blob"""
+ if "FAIL" not in dev[0].request("SET blob foo"):
+ raise Exception("Unexpected SET success")
+ if "FAIL" not in dev[0].request("SET blob foo 0"):
+ raise Exception("Unexpected SET success")
+ if "FAIL" not in dev[0].request("SET blob foo 0q"):
+ raise Exception("Unexpected SET success")
+ if "OK" not in dev[0].request("SET blob foo 00"):
+ raise Exception("Unexpected SET failure")
+ if "OK" not in dev[0].request("SET blob foo 0011"):
+ raise Exception("Unexpected SET failure")
+
+@remote_compatible
+def test_wpas_ctrl_set_uapsd(dev):
+ """wpa_supplicant ctrl_iface SET uapsd"""
+ if "FAIL" not in dev[0].request("SET uapsd foo"):
+ raise Exception("Unexpected SET success")
+ if "FAIL" not in dev[0].request("SET uapsd 0,0,0"):
+ raise Exception("Unexpected SET success")
+ if "FAIL" not in dev[0].request("SET uapsd 0,0"):
+ raise Exception("Unexpected SET success")
+ if "FAIL" not in dev[0].request("SET uapsd 0"):
+ raise Exception("Unexpected SET success")
+ if "OK" not in dev[0].request("SET uapsd 1,1,1,1;1"):
+ raise Exception("Unexpected SET failure")
+ if "OK" not in dev[0].request("SET uapsd 0,0,0,0;0"):
+ raise Exception("Unexpected SET failure")
+ if "OK" not in dev[0].request("SET uapsd disable"):
+ raise Exception("Unexpected SET failure")
+
+def test_wpas_ctrl_set(dev):
+ """wpa_supplicant ctrl_iface SET"""
+ vals = ["foo",
+ "ampdu 0",
+ "radio_disable 0",
+ "ps 10",
+ "dot11RSNAConfigPMKLifetime 0",
+ "dot11RSNAConfigPMKReauthThreshold 101",
+ "dot11RSNAConfigSATimeout 0",
+ "wps_version_number -1",
+ "wps_version_number 256",
+ "fst_group_id ",
+ "fst_llt 0"]
+ for val in vals:
+ if "FAIL" not in dev[0].request("SET " + val):
+ raise Exception("Unexpected SET success for " + val)
+
+ vals = ["ps 1"]
+ for val in vals:
+ dev[0].request("SET " + val)
+
+ vals = ["EAPOL::heldPeriod 60",
+ "EAPOL::authPeriod 30",
+ "EAPOL::startPeriod 30",
+ "EAPOL::maxStart 3",
+ "dot11RSNAConfigSATimeout 60",
+ "ps -1",
+ "ps 0",
+ "no_keep_alive 0",
+ "tdls_disabled 1",
+ "tdls_disabled 0"]
+ for val in vals:
+ if "OK" not in dev[0].request("SET " + val):
+ raise Exception("Unexpected SET failure for " + val)
+
+ # This fails if wpa_supplicant is built with loadable EAP peer method
+ # support due to missing file and succeeds if no support for loadable
+ # methods is included, so don't check the return value for now.
+ dev[0].request("SET load_dynamic_eap /tmp/hwsim-eap-not-found.so")
+
+@remote_compatible
+def test_wpas_ctrl_get_capability(dev):
+ """wpa_supplicant ctrl_iface GET_CAPABILITY"""
+ if "FAIL" not in dev[0].request("GET_CAPABILITY 1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"):
+ raise Exception("Unexpected success on invalid GET_CAPABILITY")
+ if "FAIL" not in dev[0].request("GET_CAPABILITY eap foo"):
+ raise Exception("Unexpected success on invalid GET_CAPABILITY")
+ if "AP" not in dev[0].request("GET_CAPABILITY modes strict"):
+ raise Exception("Unexpected GET_CAPABILITY response")
+ res = dev[0].get_capability("eap")
+ if "TTLS" not in res:
+ raise Exception("Unexpected GET_CAPABILITY eap response: " + str(res))
+
+ res = dev[0].get_capability("pairwise")
+ if "CCMP" not in res:
+ raise Exception("Unexpected GET_CAPABILITY pairwise response: " + str(res))
+
+ res = dev[0].get_capability("group")
+ if "CCMP" not in res:
+ raise Exception("Unexpected GET_CAPABILITY group response: " + str(res))
+
+ res = dev[0].get_capability("key_mgmt")
+ if "WPA-PSK" not in res or "WPA-EAP" not in res:
+ raise Exception("Unexpected GET_CAPABILITY key_mgmt response: " + str(res))
+
+ res = dev[0].get_capability("key_mgmt iftype=STATION")
+ if "WPA-PSK" not in res or "WPA-EAP" not in res:
+ raise Exception("Unexpected GET_CAPABILITY key_mgmt iftype=STATION response: " + str(res))
+
+ iftypes = [ "STATION", "AP_VLAN", "AP", "P2P_GO", "P2P_CLIENT",
+ "P2P_DEVICE", "MESH", "IBSS", "NAN", "UNKNOWN" ]
+ for i in iftypes:
+ res = dev[0].get_capability("key_mgmt iftype=" + i)
+ logger.info("GET_CAPABILITY key_mgmt iftype=%s: %s" % (i, res))
+
+ res = dev[0].get_capability("proto")
+ if "WPA" not in res or "RSN" not in res:
+ raise Exception("Unexpected GET_CAPABILITY proto response: " + str(res))
+
+ res = dev[0].get_capability("auth_alg")
+ if "OPEN" not in res or "SHARED" not in res:
+ raise Exception("Unexpected GET_CAPABILITY auth_alg response: " + str(res))
+
+ res = dev[0].get_capability("modes")
+ if "IBSS" not in res or "AP" not in res:
+ raise Exception("Unexpected GET_CAPABILITY modes response: " + str(res))
+
+ res = dev[0].get_capability("channels")
+ if "8" not in res or "36" not in res:
+ raise Exception("Unexpected GET_CAPABILITY channels response: " + str(res))
+
+ res = dev[0].get_capability("freq")
+ if "2457" not in res or "5180" not in res:
+ raise Exception("Unexpected GET_CAPABILITY freq response: " + str(res))
+
+ res = dev[0].get_capability("tdls")
+ if "EXTERNAL" not in res[0]:
+ raise Exception("Unexpected GET_CAPABILITY tdls response: " + str(res))
+
+ res = dev[0].get_capability("erp")
+ if res is None or "ERP" not in res[0]:
+ raise Exception("Unexpected GET_CAPABILITY erp response: " + str(res))
+
+ if dev[0].get_capability("foo") is not None:
+ raise Exception("Unexpected GET_CAPABILITY foo response: " + str(res))
+
+@remote_compatible
+def test_wpas_ctrl_nfc_report_handover(dev):
+ """wpa_supplicant ctrl_iface NFC_REPORT_HANDOVER"""
+ vals = ["FOO",
+ "ROLE freq=12345",
+ "ROLE TYPE",
+ "ROLE TYPE REQ",
+ "ROLE TYPE REQ SEL",
+ "ROLE TYPE 0Q SEL",
+ "ROLE TYPE 00 SEL",
+ "ROLE TYPE 00 0Q",
+ "ROLE TYPE 00 00"]
+ for v in vals:
+ if "FAIL" not in dev[0].request("NFC_REPORT_HANDOVER " + v):
+ raise Exception("Unexpected NFC_REPORT_HANDOVER success for " + v)
+
+@remote_compatible
+def test_wpas_ctrl_nfc_tag_read(dev):
+ """wpa_supplicant ctrl_iface WPS_NFC_TAG_READ"""
+ vals = ["FOO", "0Q", "00", "000000", "10000001", "10000000", "00000000",
+ "100e0000", "100e0001ff", "100e000411110000", "100e0004100e0001"]
+ for v in vals:
+ if "FAIL" not in dev[0].request("WPS_NFC_TAG_READ " + v):
+ raise Exception("Unexpected WPS_NFC_TAG_READ success for " + v)
+
+@remote_compatible
+def test_wpas_ctrl_nfc_get_handover(dev):
+ """wpa_supplicant ctrl_iface NFC_GET_HANDOVER"""
+ vals = ["FOO", "FOO BAR", "WPS WPS", "WPS WPS-CR", "WPS FOO", "NDEF P2P"]
+ for v in vals:
+ if "FAIL" not in dev[0].request("NFC_GET_HANDOVER_REQ " + v):
+ raise Exception("Unexpected NFC_GET_HANDOVER_REQ success for " + v)
+
+ vals = ["NDEF WPS", "NDEF P2P-CR", "WPS P2P-CR"]
+ for v in vals:
+ if "FAIL" in dev[0].request("NFC_GET_HANDOVER_REQ " + v):
+ raise Exception("Unexpected NFC_GET_HANDOVER_REQ failure for " + v)
+
+ vals = ["FOO", "FOO BAR", "WPS WPS", "WPS WPS-CR", "WPS FOO", "NDEF P2P",
+ "NDEF WPS", "NDEF WPS uuid"]
+ for v in vals:
+ if "FAIL" not in dev[0].request("NFC_GET_HANDOVER_SEL " + v):
+ raise Exception("Unexpected NFC_GET_HANDOVER_SEL success for " + v)
+
+ vals = ["NDEF P2P-CR", "WPS P2P-CR", "NDEF P2P-CR-TAG",
+ "WPS P2P-CR-TAG"]
+ for v in vals:
+ if "FAIL" in dev[0].request("NFC_GET_HANDOVER_SEL " + v):
+ raise Exception("Unexpected NFC_GET_HANDOVER_SEL failure for " + v)
+
+def get_bssid_ignore_list(dev):
+ return dev.request("BSSID_IGNORE").splitlines()
+
+@remote_compatible
+def test_wpas_ctrl_bssid_ignore(dev):
+ """wpa_supplicant ctrl_iface BSSID_IGNORE"""
+ if "OK" not in dev[0].request("BSSID_IGNORE clear"):
+ raise Exception("BSSID_IGNORE clear failed")
+ b = get_bssid_ignore_list(dev[0])
+ if len(b) != 0:
+ raise Exception("Unexpected BSSID ignore list contents: " + str(b))
+ if "OK" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44:55"):
+ raise Exception("BSSID_IGNORE add failed")
+ b = get_bssid_ignore_list(dev[0])
+ if "00:11:22:33:44:55" not in b:
+ raise Exception("Unexpected BSSID ignore list contents: " + str(b))
+ if "OK" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44:56"):
+ raise Exception("BSSID_IGNORE add failed")
+ b = get_bssid_ignore_list(dev[0])
+ if "00:11:22:33:44:55" not in b or "00:11:22:33:44:56" not in b:
+ raise Exception("Unexpected BSSID ignore list contents: " + str(b))
+ if "OK" not in dev[0].request("BSSID_IGNORE 00:11:22:33:44:56"):
+ raise Exception("BSSID_IGNORE add failed")
+ b = get_bssid_ignore_list(dev[0])
+ if "00:11:22:33:44:55" not in b or "00:11:22:33:44:56" not in b or len(b) != 2:
+ raise Exception("Unexpected BSSID ignore list contents: " + str(b))
+
+ if "OK" not in dev[0].request("BSSID_IGNORE clear"):
+ raise Exception("BSSID_IGNORE clear failed")
+ if dev[0].request("BSSID_IGNORE") != "":
+ raise Exception("Unexpected BSSID ignore list contents")
+
+@remote_compatible
+def test_wpas_ctrl_bssid_ignore_oom(dev):
+ """wpa_supplicant ctrl_iface BSSID_IGNORE and out-of-memory"""
+ with alloc_fail(dev[0], 1, "wpa_bssid_ignore_add"):
+ if "FAIL" not in dev[0].request("BSSID_IGNORE aa:bb:cc:dd:ee:ff"):
+ raise Exception("Unexpected success with allocation failure")
+
+def test_wpas_ctrl_log_level(dev):
+ """wpa_supplicant ctrl_iface LOG_LEVEL"""
+ level = dev[2].request("LOG_LEVEL")
+ if "Current level: MSGDUMP" not in level:
+ raise Exception("Unexpected debug level(1): " + level)
+ if "Timestamp: 1" not in level:
+ raise Exception("Unexpected timestamp(1): " + level)
+
+ if "OK" not in dev[2].request("LOG_LEVEL MSGDUMP 0"):
+ raise Exception("LOG_LEVEL failed")
+ level = dev[2].request("LOG_LEVEL")
+ if "Current level: MSGDUMP" not in level:
+ raise Exception("Unexpected debug level(2): " + level)
+ if "Timestamp: 0" not in level:
+ raise Exception("Unexpected timestamp(2): " + level)
+
+ if "OK" not in dev[2].request("LOG_LEVEL MSGDUMP 1"):
+ raise Exception("LOG_LEVEL failed")
+ level = dev[2].request("LOG_LEVEL")
+ if "Current level: MSGDUMP" not in level:
+ raise Exception("Unexpected debug level(3): " + level)
+ if "Timestamp: 1" not in level:
+ raise Exception("Unexpected timestamp(3): " + level)
+
+ if "FAIL" not in dev[2].request("LOG_LEVEL FOO"):
+ raise Exception("Invalid LOG_LEVEL accepted")
+
+ for lev in ["EXCESSIVE", "MSGDUMP", "DEBUG", "INFO", "WARNING", "ERROR"]:
+ if "OK" not in dev[2].request("LOG_LEVEL " + lev):
+ raise Exception("LOG_LEVEL failed for " + lev)
+ level = dev[2].request("LOG_LEVEL")
+ if "Current level: " + lev not in level:
+ raise Exception("Unexpected debug level: " + level)
+
+ if "OK" not in dev[2].request("LOG_LEVEL MSGDUMP 1"):
+ raise Exception("LOG_LEVEL failed")
+ level = dev[2].request("LOG_LEVEL")
+ if "Current level: MSGDUMP" not in level:
+ raise Exception("Unexpected debug level(3): " + level)
+ if "Timestamp: 1" not in level:
+ raise Exception("Unexpected timestamp(3): " + level)
+
+@remote_compatible
+def test_wpas_ctrl_enable_disable_network(dev, apdev):
+ """wpa_supplicant ctrl_iface ENABLE/DISABLE_NETWORK"""
+ params = {"ssid": "test"}
+ hostapd.add_ap(apdev[0], params)
+
+ id = dev[0].connect("test", key_mgmt="NONE", scan_freq="2412",
+ only_add_network=True)
+ if "OK" not in dev[0].request("DISABLE_NETWORK " + str(id)):
+ raise Exception("Failed to disable network")
+ if "OK" not in dev[0].request("ENABLE_NETWORK " + str(id) + " no-connect"):
+ raise Exception("Failed to enable network")
+ if "OK" not in dev[0].request("DISABLE_NETWORK all"):
+ raise Exception("Failed to disable networks")
+ if "OK" not in dev[0].request("ENABLE_NETWORK " + str(id)):
+ raise Exception("Failed to enable network")
+ dev[0].wait_connected(timeout=10)
+ if "OK" not in dev[0].request("DISABLE_NETWORK " + str(id)):
+ raise Exception("Failed to disable network")
+ dev[0].wait_disconnected(timeout=10)
+ time.sleep(0.1)
+
+ if "OK" not in dev[0].request("ENABLE_NETWORK all"):
+ raise Exception("Failed to enable network")
+ dev[0].wait_connected(timeout=10)
+ if "OK" not in dev[0].request("DISABLE_NETWORK all"):
+ raise Exception("Failed to disable network")
+ dev[0].wait_disconnected(timeout=10)
+
+def test_wpas_ctrl_country(dev, apdev):
+ """wpa_supplicant SET/GET country code"""
+ try:
+ # work around issues with possible pending regdom event from the end of
+ # the previous test case
+ time.sleep(0.2)
+ dev[0].dump_monitor()
+
+ if "OK" not in dev[0].request("SET country FI"):
+ raise Exception("Failed to set country code")
+ if dev[0].request("GET country") != "FI":
+ raise Exception("Country code set failed")
+ ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], 10)
+ if ev is None:
+ raise Exception("regdom change event not seen")
+ if "init=USER type=COUNTRY alpha2=FI" not in ev:
+ raise Exception("Unexpected event contents: " + ev)
+ dev[0].request("SET country 00")
+ if dev[0].request("GET country") != "00":
+ raise Exception("Country code set failed")
+ ev = dev[0].wait_global_event(["CTRL-EVENT-REGDOM-CHANGE"], 10)
+ if ev is None:
+ raise Exception("regdom change event not seen")
+ # init=CORE was previously used due to invalid db.txt data for 00. For
+ # now, allow both it and the new init=USER after fixed db.txt.
+ if "init=CORE type=WORLD" not in ev and "init=USER type=WORLD" not in ev:
+ raise Exception("Unexpected event contents: " + ev)
+ finally:
+ subprocess.call(['iw', 'reg', 'set', '00'])
+
+def test_wpas_ctrl_suspend_resume(dev):
+ """wpa_supplicant SUSPEND/RESUME"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ if "OK" not in wpas.global_request("SUSPEND"):
+ raise Exception("SUSPEND failed")
+ time.sleep(1)
+ if "OK" not in wpas.global_request("RESUME"):
+ raise Exception("RESUME failed")
+ if "OK" not in wpas.request("SUSPEND"):
+ raise Exception("Per-interface SUSPEND failed")
+ if "OK" not in wpas.request("RESUME"):
+ raise Exception("Per-interface RESUME failed")
+ ev = wpas.wait_event(["CTRL-EVENT-SCAN-RESULTS"], timeout=10)
+ if ev is None:
+ raise Exception("Scan not completed")
+
+def test_wpas_ctrl_global(dev):
+ """wpa_supplicant global control interface"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+
+ if "PONG" not in wpas.global_request("PING"):
+ raise Exception("PING failed")
+ if "wlan5" not in wpas.global_request("INTERFACES"):
+ raise Exception("Interface not found")
+ if "UNKNOWN COMMAND" not in wpas.global_request("FOO"):
+ raise Exception("Unexpected response to unknown command")
+ if "PONG" not in wpas.global_request("IFNAME=wlan5 PING"):
+ raise Exception("Per-interface PING failed")
+ if "FAIL-NO-IFNAME-MATCH" not in wpas.global_request("IFNAME=notfound PING"):
+ raise Exception("Unknown interface not reported correctly")
+ if "FAIL" not in wpas.global_request("SAVE_CONFIG"):
+ raise Exception("SAVE_CONFIG succeeded unexpectedly")
+ if "OK" not in wpas.global_request("SET wifi_display 0"):
+ raise Exception("SET failed")
+ if "wifi_display=0" not in wpas.global_request("STATUS"):
+ raise Exception("wifi_display not disabled")
+ if "OK" not in wpas.global_request("SET wifi_display 1"):
+ raise Exception("SET failed")
+ if "wifi_display=1" not in wpas.global_request("STATUS"):
+ raise Exception("wifi_display not enabled")
+ if "FAIL" not in wpas.global_request("SET foo 1"):
+ raise Exception("SET succeeded unexpectedly")
+
+ if "p2p_state=IDLE" not in wpas.global_request("STATUS"):
+ raise Exception("P2P was disabled")
+ wpas.global_request("P2P_SET disabled 1")
+ if "p2p_state=DISABLED" not in wpas.global_request("STATUS"):
+ raise Exception("P2P was not disabled")
+ wpas.global_request("P2P_SET disabled 0")
+ if "p2p_state=IDLE" not in wpas.global_request("STATUS"):
+ raise Exception("P2P was not enabled")
+
+ # driver_nl80211.c does not support interface list, so do not fail because
+ # of that
+ logger.debug(wpas.global_request("INTERFACE_LIST"))
+
+ if "FAIL" not in wpas.global_request("INTERFACE_ADD "):
+ raise Exception("INTERFACE_ADD succeeded unexpectedly")
+ if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO"):
+ raise Exception("INTERFACE_ADD succeeded unexpectedly")
+ if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf"):
+ raise Exception("INTERFACE_ADD succeeded unexpectedly")
+ if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver"):
+ raise Exception("INTERFACE_ADD succeeded unexpectedly")
+ if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver ctrliface"):
+ raise Exception("INTERFACE_ADD succeeded unexpectedly")
+ if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver ctrliface driverparam"):
+ raise Exception("INTERFACE_ADD succeeded unexpectedly")
+ if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver ctrliface driverparam bridge"):
+ raise Exception("INTERFACE_ADD succeeded unexpectedly")
+ if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver ctrliface driverparam bridge foo"):
+ raise Exception("INTERFACE_ADD succeeded unexpectedly")
+ if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO "):
+ raise Exception("INTERFACE_ADD succeeded unexpectedly")
+ if "FAIL" not in wpas.global_request("INTERFACE_ADD FOO conf driver ctrliface driverparam bridge create abcd"):
+ raise Exception("INTERFACE_ADD succeeded unexpectedly")
+
+@remote_compatible
+def test_wpas_ctrl_roam(dev, apdev):
+ """wpa_supplicant ctrl_iface ROAM error cases"""
+ if "FAIL" not in dev[0].request("ROAM 00:11:22:33:44"):
+ raise Exception("Unexpected success")
+ if "FAIL" not in dev[0].request("ROAM 00:11:22:33:44:55"):
+ raise Exception("Unexpected success")
+ params = {"ssid": "test"}
+ hostapd.add_ap(apdev[0], params)
+ id = dev[0].connect("test", key_mgmt="NONE", scan_freq="2412")
+ if "FAIL" not in dev[0].request("ROAM 00:11:22:33:44:55"):
+ raise Exception("Unexpected success")
+
+@remote_compatible
+def test_wpas_ctrl_ipaddr(dev, apdev):
+ """wpa_supplicant IP address in STATUS"""
+ try:
+ dev[0].cmd_execute(['ip', 'addr', 'add', '10.174.65.207/32', 'dev',
+ dev[0].ifname])
+ ipaddr = dev[0].get_status_field('ip_address')
+ if ipaddr != '10.174.65.207':
+ raise Exception("IP address not in STATUS output")
+ finally:
+ dev[0].cmd_execute(['ip', 'addr', 'del', '10.174.65.207/32', 'dev',
+ dev[0].ifname])
+
+@remote_compatible
+def test_wpas_ctrl_rsp(dev, apdev):
+ """wpa_supplicant ctrl_iface CTRL-RSP-"""
+ if "FAIL" not in dev[0].request("CTRL-RSP-"):
+ raise Exception("Request succeeded unexpectedly")
+ if "FAIL" not in dev[0].request("CTRL-RSP-foo-"):
+ raise Exception("Request succeeded unexpectedly")
+ if "FAIL" not in dev[0].request("CTRL-RSP-foo-1234567"):
+ raise Exception("Request succeeded unexpectedly")
+ if "FAIL" not in dev[0].request("CTRL-RSP-foo-1234567:"):
+ raise Exception("Request succeeded unexpectedly")
+ id = dev[0].add_network()
+ if "FAIL" not in dev[0].request("CTRL-RSP-foo-%d:" % id):
+ raise Exception("Request succeeded unexpectedly")
+ for req in ["IDENTITY", "PASSWORD", "NEW_PASSWORD", "PIN", "OTP",
+ "PASSPHRASE", "SIM"]:
+ if "OK" not in dev[0].request("CTRL-RSP-%s-%d:" % (req, id)):
+ raise Exception("Request failed unexpectedly")
+ if "OK" not in dev[0].request("CTRL-RSP-%s-%d:" % (req, id)):
+ raise Exception("Request failed unexpectedly")
+
+def test_wpas_ctrl_vendor_test(dev, apdev):
+ """wpas_supplicant and VENDOR test command"""
+ OUI_QCA = 0x001374
+ QCA_NL80211_VENDOR_SUBCMD_TEST = 1
+ QCA_WLAN_VENDOR_ATTR_TEST = 8
+ attr = struct.pack("@HHI", 4 + 4, QCA_WLAN_VENDOR_ATTR_TEST, 123)
+ cmd = "VENDOR %x %d %s" % (OUI_QCA, QCA_NL80211_VENDOR_SUBCMD_TEST, binascii.hexlify(attr).decode())
+
+ res = dev[0].request(cmd)
+ if "FAIL" in res:
+ raise Exception("VENDOR command failed")
+ val, = struct.unpack("@I", binascii.unhexlify(res))
+ if val != 125:
+ raise Exception("Incorrect response value")
+
+ res = dev[0].request(cmd + " nested=1")
+ if "FAIL" in res:
+ raise Exception("VENDOR command failed")
+ val, = struct.unpack("@I", binascii.unhexlify(res))
+ if val != 125:
+ raise Exception("Incorrect response value")
+
+ res = dev[0].request(cmd + " nested=0")
+ if "FAIL" not in res:
+ raise Exception("VENDOR command with invalid (not nested) data accepted")
+
+@remote_compatible
+def test_wpas_ctrl_vendor(dev, apdev):
+ """wpa_supplicant ctrl_iface VENDOR"""
+ cmds = ["foo",
+ "1",
+ "1 foo",
+ "1 2foo",
+ "1 2 qq"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("VENDOR " + cmd):
+ raise Exception("Invalid VENDOR command accepted: " + cmd)
+
+@remote_compatible
+def test_wpas_ctrl_mgmt_tx(dev, apdev):
+ """wpa_supplicant ctrl_iface MGMT_TX"""
+ cmds = ["foo",
+ "00:11:22:33:44:55 foo",
+ "00:11:22:33:44:55 11:22:33:44:55:66",
+ "00:11:22:33:44:55 11:22:33:44:55:66 freq=0 no_cck=0 wait_time=0 action=123",
+ "00:11:22:33:44:55 11:22:33:44:55:66 action=12qq"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("MGMT_TX " + cmd):
+ raise Exception("Invalid MGMT_TX command accepted: " + cmd)
+
+ if "OK" not in dev[0].request("MGMT_TX_DONE"):
+ raise Exception("MGMT_TX_DONE failed")
+
+@remote_compatible
+def test_wpas_ctrl_driver_event(dev, apdev):
+ """wpa_supplicant ctrl_iface DRIVER_EVENT"""
+ if "FAIL" not in dev[0].request("DRIVER_EVENT foo"):
+ raise Exception("Invalid DRIVER_EVENT accepted")
+ if "OK" not in dev[0].request("DRIVER_EVENT ASSOC reassoc=1 req_ies=0000 resp_ies=0000 resp_frame=0000 beacon_ies=0000 freq=2412 wmm::info_bitmap=0 wmm::uapsd_queues=0 addr=02:02:02:02:02:02 authorized=0 key_replay_ctr=00 ptk_kck=00 ptk_kek=00 subnet_status=0 fils_erp_next_seq_num=0 fils_pmk=00 fils_pmkid=" + 16*"00"):
+ raise Exception("DRIVER_EVENT ASSOC did not succeed")
+
+@remote_compatible
+def test_wpas_ctrl_eapol_rx(dev, apdev):
+ """wpa_supplicant ctrl_iface EAPOL_RX"""
+ cmds = ["foo",
+ "00:11:22:33:44:55 123",
+ "00:11:22:33:44:55 12qq"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("EAPOL_RX " + cmd):
+ raise Exception("Invalid EAPOL_RX command accepted: " + cmd)
+
+@remote_compatible
+def test_wpas_ctrl_data_test(dev, apdev):
+ """wpa_supplicant ctrl_iface DATA_TEST"""
+ dev[0].request("DATA_TEST_CONFIG 0")
+ if "FAIL" not in dev[0].request("DATA_TEST_TX 00:11:22:33:44:55 00:11:22:33:44:55 0"):
+ raise Exception("DATA_TEST_TX accepted when not in test mode")
+
+ try:
+ if "OK" not in dev[0].request("DATA_TEST_CONFIG 1"):
+ raise Exception("DATA_TEST_CONFIG failed")
+ if "OK" not in dev[0].request("DATA_TEST_CONFIG 1"):
+ raise Exception("DATA_TEST_CONFIG failed")
+ cmds = ["foo",
+ "00:11:22:33:44:55 foo",
+ "00:11:22:33:44:55 00:11:22:33:44:55 -1",
+ "00:11:22:33:44:55 00:11:22:33:44:55 256"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("DATA_TEST_TX " + cmd):
+ raise Exception("Invalid DATA_TEST_TX command accepted: " + cmd)
+ if "OK" not in dev[0].request("DATA_TEST_TX 00:11:22:33:44:55 00:11:22:33:44:55 0"):
+ raise Exception("DATA_TEST_TX failed")
+ finally:
+ dev[0].request("DATA_TEST_CONFIG 0")
+
+ cmds = ["",
+ "00",
+ "00112233445566778899aabbccdde",
+ "00112233445566778899aabbccdq"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("DATA_TEST_FRAME " + cmd):
+ raise Exception("Invalid DATA_TEST_FRAME command accepted: " + cmd)
+
+ if "OK" not in dev[0].request("DATA_TEST_FRAME 00112233445566778899aabbccddee"):
+ raise Exception("DATA_TEST_FRAME failed")
+
+@remote_compatible
+def test_wpas_ctrl_vendor_elem(dev, apdev):
+ """wpa_supplicant ctrl_iface VENDOR_ELEM"""
+ if "OK" not in dev[0].request("VENDOR_ELEM_ADD 1 "):
+ raise Exception("VENDOR_ELEM_ADD failed")
+ cmds = ["-1 ",
+ "255 ",
+ "1",
+ "1 123",
+ "1 12qq34"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("VENDOR_ELEM_ADD " + cmd):
+ raise Exception("Invalid VENDOR_ELEM_ADD command accepted: " + cmd)
+
+ cmds = ["-1 ",
+ "255 "]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("VENDOR_ELEM_GET " + cmd):
+ raise Exception("Invalid VENDOR_ELEM_GET command accepted: " + cmd)
+
+ dev[0].request("VENDOR_ELEM_REMOVE 1 *")
+ cmds = ["-1 ",
+ "255 ",
+ "1",
+ "1",
+ "1 123",
+ "1 12qq34",
+ "1 12",
+ "1 0000"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("VENDOR_ELEM_REMOVE " + cmd):
+ raise Exception("Invalid VENDOR_ELEM_REMOVE command accepted: " + cmd)
+
+ dev[0].request("VENDOR_ELEM_ADD 1 000100")
+ if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 "):
+ raise Exception("VENDOR_ELEM_REMOVE failed")
+ cmds = ["-1 ",
+ "255 ",
+ "1",
+ "1 123",
+ "1 12qq34",
+ "1 12",
+ "1 0000"]
+ for cmd in cmds:
+ if "FAIL" not in dev[0].request("VENDOR_ELEM_REMOVE " + cmd):
+ raise Exception("Invalid VENDOR_ELEM_REMOVE command accepted: " + cmd)
+ if "OK" not in dev[0].request("VENDOR_ELEM_REMOVE 1 000100"):
+ raise Exception("VENDOR_ELEM_REMOVE failed")
+
+def test_wpas_ctrl_misc(dev, apdev):
+ """wpa_supplicant ctrl_iface and miscellaneous commands"""
+ if "OK" not in dev[0].request("RELOG"):
+ raise Exception("RELOG failed")
+ if dev[0].request("IFNAME") != dev[0].ifname:
+ raise Exception("IFNAME returned unexpected response")
+ if "FAIL" not in dev[0].request("REATTACH"):
+ raise Exception("REATTACH accepted while disabled")
+ if "OK" not in dev[2].request("RECONFIGURE"):
+ raise Exception("RECONFIGURE failed")
+ if "FAIL" in dev[0].request("INTERFACE_LIST"):
+ raise Exception("INTERFACE_LIST failed")
+ if "UNKNOWN COMMAND" not in dev[0].request("FOO"):
+ raise Exception("Unknown command accepted")
+
+ if "FAIL" not in dev[0].global_request("INTERFACE_REMOVE foo"):
+ raise Exception("Invalid INTERFACE_REMOVE accepted")
+ if "FAIL" not in dev[0].global_request("SET foo"):
+ raise Exception("Invalid global SET accepted")
+
+@remote_compatible
+def test_wpas_ctrl_dump(dev, apdev):
+ """wpa_supplicant ctrl_iface and DUMP/GET global parameters"""
+ vals = dev[0].get_config()
+ logger.info("Config values from DUMP: " + str(vals))
+ for field in vals:
+ res = dev[0].request("GET " + field)
+ if res == 'FAIL\n':
+ res = "null"
+ if res != vals[field]:
+ print("'{}' != '{}'".format(res, vals[field]))
+ raise Exception("Mismatch in config field " + field)
+ if "beacon_int" not in vals:
+ raise Exception("Missing config field")
+
+def test_wpas_ctrl_interface_add(dev, apdev):
+ """wpa_supplicant INTERFACE_ADD/REMOVE with vif creation/removal"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ ifname = "test-" + dev[0].ifname
+ dev[0].interface_add(ifname, create=True)
+ wpas = WpaSupplicant(ifname=ifname)
+ wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[0].global_request("INTERFACE_REMOVE " + ifname)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_wpas_ctrl_interface_add_sta(dev, apdev):
+ """wpa_supplicant INTERFACE_ADD/REMOVE with STA vif creation/removal"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ ifname = "test-" + dev[0].ifname
+ dev[0].interface_add(ifname, create=True, if_type='sta')
+ wpas = WpaSupplicant(ifname=ifname)
+ wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+ wpas.request("DISCONNECT")
+ wpas.wait_disconnected()
+ dev[0].global_request("INTERFACE_REMOVE " + ifname)
+
+def test_wpas_ctrl_interface_add_ap(dev, apdev):
+ """wpa_supplicant INTERFACE_ADD/REMOVE AP interface"""
+ with HWSimRadio() as (radio, iface):
+ wpas0 = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas0.interface_add(iface)
+
+ ifname = "test-wpas-ap"
+ wpas0.interface_add(ifname, create=True, if_type='ap')
+ wpas = WpaSupplicant(ifname=ifname)
+
+ id = wpas.add_network()
+ wpas.set_network(id, "mode", "2")
+ wpas.set_network_quoted(id, "ssid", "wpas-ap-open")
+ wpas.set_network(id, "key_mgmt", "NONE")
+ wpas.set_network(id, "frequency", "2412")
+ wpas.set_network(id, "scan_freq", "2412")
+ wpas.select_network(id)
+ wait_ap_ready(wpas)
+
+ dev[1].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+ dev[2].connect("wpas-ap-open", key_mgmt="NONE", scan_freq="2412")
+
+ hwsim_utils.test_connectivity(wpas, dev[1])
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+
+ dev[1].request("DISCONNECT")
+ dev[2].request("DISCONNECT")
+ dev[1].wait_disconnected()
+ dev[2].wait_disconnected()
+ wpas0.global_request("INTERFACE_REMOVE " + ifname)
+
+def test_wpas_ctrl_interface_add_many(dev, apdev):
+ """wpa_supplicant INTERFACE_ADD/REMOVE with vif creation/removal (many)"""
+ try:
+ _test_wpas_ctrl_interface_add_many(dev, apdev)
+ finally:
+ for i in range(10):
+ ifname = "test%d-" % i + dev[0].ifname
+ dev[0].global_request("INTERFACE_REMOVE " + ifname)
+
+def _test_wpas_ctrl_interface_add_many(dev, apdev):
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ dev[0].dump_monitor()
+
+ l = []
+ for i in range(10):
+ ifname = "test%d-" % i + dev[0].ifname
+ dev[0].interface_add(ifname, create=True)
+ wpas = WpaSupplicant(ifname=ifname)
+ wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+ wpas.dump_monitor()
+ l.append(wpas)
+ dev[0].dump_monitor()
+ for wpas in l:
+ wpas.dump_monitor()
+ hwsim_utils.test_connectivity(wpas, hapd)
+ wpas.dump_monitor()
+ dev[0].dump_monitor()
+
+def test_wpas_ctrl_interface_add2(dev, apdev):
+ """wpa_supplicant INTERFACE_ADD/REMOVE with vif without creation/removal"""
+ ifname = "test-ext-" + dev[0].ifname
+ try:
+ _test_wpas_ctrl_interface_add2(dev, apdev, ifname)
+ finally:
+ subprocess.call(['iw', 'dev', ifname, 'del'])
+
+def _test_wpas_ctrl_interface_add2(dev, apdev, ifname):
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+ subprocess.call(['iw', 'dev', dev[0].ifname, 'interface', 'add', ifname,
+ 'type', 'station'])
+ subprocess.call(['ip', 'link', 'set', 'dev', ifname, 'address',
+ '02:01:00:00:02:01'])
+ dev[0].interface_add(ifname, set_ifname=False, all_params=True)
+ wpas = WpaSupplicant(ifname=ifname)
+ wpas.connect("open", key_mgmt="NONE", scan_freq="2412")
+ hwsim_utils.test_connectivity(wpas, hapd)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ del wpas
+ dev[0].global_request("INTERFACE_REMOVE " + ifname)
+ hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_wpas_ctrl_wait(dev, apdev, test_params):
+ """wpa_supplicant control interface wait for client"""
+ logfile = os.path.join(test_params['logdir'], 'wpas_ctrl_wait.log-wpas')
+ pidfile = os.path.join(test_params['logdir'], 'wpas_ctrl_wait.pid-wpas')
+ conffile = os.path.join(test_params['logdir'], 'wpas_ctrl_wait.conf')
+ with open(conffile, 'w') as f:
+ f.write("ctrl_interface=DIR=/var/run/wpa_supplicant\n")
+
+ prg = os.path.join(test_params['logdir'],
+ 'alt-wpa_supplicant/wpa_supplicant/wpa_supplicant')
+ if not os.path.exists(prg):
+ prg = '../../wpa_supplicant/wpa_supplicant'
+ arg = [prg]
+ cmd = subprocess.Popen(arg, stdout=subprocess.PIPE)
+ out = cmd.communicate()[0].decode()
+ cmd.wait()
+ tracing = "Linux tracing" in out
+
+ with HWSimRadio() as (radio, iface):
+ arg = [prg, '-BdddW', '-P', pidfile, '-f', logfile,
+ '-Dnl80211', '-c', conffile, '-i', iface]
+ if tracing:
+ arg += ['-T']
+ logger.info("Start wpa_supplicant: " + str(arg))
+ subprocess.call(arg)
+ wpas = WpaSupplicant(ifname=iface)
+ if "PONG" not in wpas.request("PING"):
+ raise Exception("Could not PING wpa_supplicant")
+ if not os.path.exists(pidfile):
+ raise Exception("PID file not created")
+ if "OK" not in wpas.request("TERMINATE"):
+ raise Exception("Could not TERMINATE")
+ ev = wpas.wait_event(["CTRL-EVENT-TERMINATING"], timeout=2)
+ if ev is None:
+ raise Exception("No termination event received")
+ for i in range(20):
+ if not os.path.exists(pidfile):
+ break
+ time.sleep(0.1)
+ if os.path.exists(pidfile):
+ raise Exception("PID file not removed")
+
+@remote_compatible
+def test_wpas_ctrl_oom(dev):
+ """Various wpa_supplicant ctrl_iface OOM cases"""
+ try:
+ _test_wpas_ctrl_oom(dev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 1 *")
+ dev[0].request("VENDOR_ELEM_REMOVE 2 *")
+ dev[0].request("SET bssid_filter ")
+
+def _test_wpas_ctrl_oom(dev):
+ dev[0].request('VENDOR_ELEM_ADD 2 000100')
+ tests = [('DRIVER_EVENT AVOID_FREQUENCIES 2412', 'FAIL',
+ 1, 'freq_range_list_parse'),
+ ('P2P_SET disallow_freq 2412', 'FAIL',
+ 1, 'freq_range_list_parse'),
+ ('SCAN freq=2412', 'FAIL',
+ 1, 'freq_range_list_parse'),
+ ('INTERWORKING_SELECT freq=2412', 'FAIL',
+ 1, 'freq_range_list_parse'),
+ ('SCAN ssid 112233', 'FAIL',
+ 1, 'wpas_ctrl_scan'),
+ ('MGMT_TX 00:00:00:00:00:00 00:00:00:00:00:00 action=00', 'FAIL',
+ 1, 'wpas_ctrl_iface_mgmt_tx'),
+ ('EAPOL_RX 00:00:00:00:00:00 00', 'FAIL',
+ 1, 'wpas_ctrl_iface_eapol_rx'),
+ ('DATA_TEST_FRAME 00112233445566778899aabbccddee', 'FAIL',
+ 1, 'wpas_ctrl_iface_data_test_frame'),
+ ('DATA_TEST_FRAME 00112233445566778899aabbccddee', 'FAIL',
+ 1, 'l2_packet_init;wpas_ctrl_iface_data_test_frame'),
+ ('VENDOR_ELEM_ADD 1 000100', 'FAIL',
+ 1, 'wpas_ctrl_vendor_elem_add'),
+ ('VENDOR_ELEM_ADD 2 000100', 'FAIL',
+ 2, 'wpas_ctrl_vendor_elem_add'),
+ ('VENDOR_ELEM_REMOVE 2 000100', 'FAIL',
+ 1, 'wpas_ctrl_vendor_elem_remove'),
+ ('SET bssid_filter 00:11:22:33:44:55', 'FAIL',
+ 1, 'set_bssid_filter'),
+ ('SET disallow_aps bssid 00:11:22:33:44:55', 'FAIL',
+ 1, 'set_disallow_aps'),
+ ('SET disallow_aps ssid 11', 'FAIL',
+ 1, 'set_disallow_aps'),
+ ('SET blob foo 0011', 'FAIL',
+ 1, 'wpas_ctrl_set_blob'),
+ ('SET blob foo 0011', 'FAIL',
+ 2, 'wpas_ctrl_set_blob'),
+ ('SET blob foo 0011', 'FAIL',
+ 3, 'wpas_ctrl_set_blob'),
+ ('WPS_NFC_TAG_READ 00', 'FAIL',
+ 1, 'wpa_supplicant_ctrl_iface_wps_nfc_tag_read'),
+ ('WPS_NFC_TOKEN NDEF', 'FAIL',
+ 1, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+ ('WPS_NFC_TOKEN NDEF', 'FAIL',
+ 2, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+ ('WPS_NFC_TOKEN NDEF', 'FAIL',
+ 3, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+ ('WPS_NFC_TOKEN NDEF', 'FAIL',
+ 4, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+ ('NFC_REPORT_HANDOVER ROLE TYPE 00 00', 'FAIL',
+ 1, 'wpas_ctrl_nfc_report_handover'),
+ ('NFC_REPORT_HANDOVER ROLE TYPE 00 00', 'FAIL',
+ 2, 'wpas_ctrl_nfc_report_handover'),
+ ('NFC_GET_HANDOVER_REQ NDEF WPS-CR', 'FAIL',
+ 1, 'wps_build_nfc_handover_req'),
+ ('NFC_GET_HANDOVER_REQ NDEF WPS-CR', 'FAIL',
+ 1, 'ndef_build_record'),
+ ('NFC_GET_HANDOVER_REQ NDEF P2P-CR', None,
+ 1, 'wpas_p2p_nfc_handover'),
+ ('NFC_GET_HANDOVER_REQ NDEF P2P-CR', None,
+ 1, 'wps_build_nfc_handover_req_p2p'),
+ ('NFC_GET_HANDOVER_REQ NDEF P2P-CR', 'FAIL',
+ 1, 'ndef_build_record'),
+ ('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', None,
+ 1, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
+ ('NFC_GET_HANDOVER_SEL NDEF P2P-CR', None,
+ 1, 'wpas_ctrl_nfc_get_handover_sel_p2p'),
+ ('P2P_ASP_PROVISION_RESP 00:11:22:33:44:55 id=1', 'FAIL',
+ 1, 'p2p_parse_asp_provision_cmd'),
+ ('P2P_SERV_DISC_REQ 00:11:22:33:44:55 02000001', 'FAIL',
+ 1, 'p2p_ctrl_serv_disc_req'),
+ ('P2P_SERV_DISC_RESP 2412 00:11:22:33:44:55 1 00', 'FAIL',
+ 1, 'p2p_ctrl_serv_disc_resp'),
+ ('P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027',
+ 'FAIL',
+ 1, 'p2p_ctrl_service_add_bonjour'),
+ ('P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027',
+ 'FAIL',
+ 2, 'p2p_ctrl_service_add_bonjour'),
+ ('P2P_SERVICE_ADD bonjour 0b5f6166706f766572746370c00c000c01 074578616d706c65c027',
+ 'FAIL',
+ 3, 'p2p_ctrl_service_add_bonjour'),
+ ('P2P_SERVICE_DEL bonjour 0b5f6166706f766572746370c00c000c01',
+ 'FAIL',
+ 1, 'p2p_ctrl_service_del_bonjour'),
+ ('GAS_REQUEST 00:11:22:33:44:55 00', 'FAIL',
+ 1, 'gas_request'),
+ ('GAS_REQUEST 00:11:22:33:44:55 00 11', 'FAIL',
+ 2, 'gas_request'),
+ ('HS20_GET_NAI_HOME_REALM_LIST 00:11:22:33:44:55 realm=example.com',
+ 'FAIL',
+ 1, 'hs20_nai_home_realm_list'),
+ ('HS20_GET_NAI_HOME_REALM_LIST 00:11:22:33:44:55 00',
+ 'FAIL',
+ 1, 'hs20_get_nai_home_realm_list'),
+ ('WNM_SLEEP enter tfs_req=11', 'FAIL',
+ 1, 'wpas_ctrl_iface_wnm_sleep'),
+ ('WNM_SLEEP enter tfs_req=11', 'FAIL',
+ 2, 'wpas_ctrl_iface_wnm_sleep'),
+ ('WNM_SLEEP enter tfs_req=11', 'FAIL',
+ 3, 'wpas_ctrl_iface_wnm_sleep'),
+ ('WNM_SLEEP enter tfs_req=11', 'FAIL',
+ 4, 'wpas_ctrl_iface_wnm_sleep'),
+ ('WNM_SLEEP enter tfs_req=11', 'FAIL',
+ 5, 'wpas_ctrl_iface_wnm_sleep'),
+ ('WNM_SLEEP enter', 'FAIL',
+ 3, 'wpas_ctrl_iface_wnm_sleep'),
+ ('VENDOR 1 1 00', 'FAIL',
+ 1, 'wpa_supplicant_vendor_cmd'),
+ ('VENDOR 1 1 00', 'FAIL',
+ 2, 'wpa_supplicant_vendor_cmd'),
+ ('RADIO_WORK add test', 'FAIL',
+ 1, 'wpas_ctrl_radio_work_add'),
+ ('RADIO_WORK add test', 'FAIL',
+ 2, 'wpas_ctrl_radio_work_add'),
+ ('AUTOSCAN periodic:1', 'FAIL',
+ 1, 'wpa_supplicant_ctrl_iface_autoscan'),
+ ('PING', None,
+ 1, 'wpa_supplicant_ctrl_iface_process')]
+ tls = dev[0].request("GET tls_library")
+ if not tls.startswith("internal"):
+ tests.append(('NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG', 'FAIL',
+ 4, 'wpas_ctrl_nfc_get_handover_sel_p2p'))
+ for cmd, exp, count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ res = dev[0].request(cmd)
+ if exp and exp not in res:
+ raise Exception("Unexpected success for '%s' during OOM (%d:%s)" % (cmd, count, func))
+
+ tests = [('FOO', None,
+ 1, 'wpa_supplicant_global_ctrl_iface_process'),
+ ('IFNAME=notfound PING', 'FAIL\n',
+ 1, 'wpas_global_ctrl_iface_ifname')]
+ for cmd, exp, count, func in tests:
+ with alloc_fail(dev[0], count, func):
+ res = dev[0].global_request(cmd)
+ if exp and exp not in res:
+ raise Exception("Unexpected success for '%s' during OOM" % cmd)
+
+@remote_compatible
+def test_wpas_ctrl_error(dev):
+ """Various wpa_supplicant ctrl_iface error cases"""
+ tests = [('WPS_NFC_TOKEN NDEF', 'FAIL',
+ 1, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+ ('WPS_NFC_TOKEN NDEF', 'FAIL',
+ 2, 'wpa_supplicant_ctrl_iface_wps_nfc_token'),
+ ('NFC_GET_HANDOVER_REQ NDEF P2P-CR', None,
+ 1, 'wpas_p2p_nfc_handover'),
+ ('NFC_GET_HANDOVER_REQ NDEF P2P-CR', None,
+ 1, 'wps_build_nfc_handover_req_p2p')]
+ for cmd, exp, count, func in tests:
+ with fail_test(dev[0], count, func):
+ res = dev[0].request(cmd)
+ if exp and exp not in res:
+ raise Exception("Unexpected success for '%s' during failure testing (%d:%s)" % (cmd, count, func))
+
+def test_wpas_ctrl_socket_full(dev, apdev, test_params):
+ """wpa_supplicant control socket and full send buffer"""
+ if not dev[0].ping():
+ raise Exception("Could not ping wpa_supplicant at the beginning of the test")
+ dev[0].get_status()
+
+ counter = 0
+
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ local = "/tmp/wpa_ctrl_test_%d-%d" % (os.getpid(), counter)
+ counter += 1
+ s.bind(local)
+ s.connect("/var/run/wpa_supplicant/wlan0")
+ for i in range(20):
+ logger.debug("Command %d" % i)
+ try:
+ s.send(b"MIB")
+ except Exception as e:
+ logger.info("Could not send command %d: %s" % (i, str(e)))
+ break
+ # Close without receiving response
+ time.sleep(0.01)
+
+ if not dev[0].ping():
+ raise Exception("Could not ping wpa_supplicant in the middle of the test")
+ dev[0].get_status()
+
+ s2 = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ local2 = "/tmp/wpa_ctrl_test_%d-%d" % (os.getpid(), counter)
+ counter += 1
+ s2.bind(local2)
+ s2.connect("/var/run/wpa_supplicant/wlan0")
+ for i in range(10):
+ logger.debug("Command %d [2]" % i)
+ try:
+ s2.send(b"MIB")
+ except Exception as e:
+ logger.info("Could not send command %d [2]: %s" % (i, str(e)))
+ break
+ # Close without receiving response
+ time.sleep(0.01)
+
+ s.close()
+ os.unlink(local)
+
+ for i in range(10):
+ logger.debug("Command %d [3]" % i)
+ try:
+ s2.send(b"MIB")
+ except Exception as e:
+ logger.info("Could not send command %d [3]: %s" % (i, str(e)))
+ break
+ # Close without receiving response
+ time.sleep(0.01)
+
+ s2.close()
+ os.unlink(local2)
+
+ if not dev[0].ping():
+ raise Exception("Could not ping wpa_supplicant in the middle of the test [2]")
+ dev[0].get_status()
+
+ s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ local = "/tmp/wpa_ctrl_test_%d-%d" % (os.getpid(), counter)
+ counter += 1
+ s.bind(local)
+ s.connect("/var/run/wpa_supplicant/wlan0")
+ s.send(b"ATTACH")
+ res = s.recv(100).decode()
+ if "OK" not in res:
+ raise Exception("Could not attach a test socket")
+
+ for i in range(5):
+ dev[0].scan(freq=2412)
+
+ s.close()
+ os.unlink(local)
+
+ for i in range(5):
+ dev[0].scan(freq=2412)
+
+ if not dev[0].ping():
+ raise Exception("Could not ping wpa_supplicant at the end of the test")
+ dev[0].get_status()
+
+def test_wpas_ctrl_event_burst(dev, apdev):
+ """wpa_supplicant control socket and event burst"""
+ if "OK" not in dev[0].request("EVENT_TEST 1000"):
+ raise Exception("Could not request event messages")
+
+ total_i = 0
+ total_g = 0
+ for i in range(100):
+ (i, g) = dev[0].dump_monitor()
+ total_i += i
+ total_g += g
+ logger.info("Received i=%d g=%d" % (i, g))
+ if total_i >= 1000 and total_g >= 1000:
+ break
+ time.sleep(0.05)
+
+ if total_i < 1000:
+ raise Exception("Some per-interface events not seen: %d" % total_i)
+ if total_g < 1000:
+ raise Exception("Some global events not seen: %d" % total_g)
+
+ if not dev[0].ping():
+ raise Exception("Could not ping wpa_supplicant at the end of the test")
+
+@remote_compatible
+def test_wpas_ctrl_sched_scan_plans(dev, apdev):
+ """wpa_supplicant sched_scan_plans parsing"""
+ dev[0].request("SET sched_scan_plans foo")
+ dev[0].request("SET sched_scan_plans 10:100 20:200 30")
+ dev[0].request("SET sched_scan_plans 4294967295:0")
+ dev[0].request("SET sched_scan_plans 1 1")
+ dev[0].request("SET sched_scan_plans ")
+ try:
+ with alloc_fail(dev[0], 1, "wpas_sched_scan_plans_set"):
+ dev[0].request("SET sched_scan_plans 10:100")
+ finally:
+ dev[0].request("SET sched_scan_plans ")
+
+def test_wpas_ctrl_signal_monitor(dev, apdev):
+ """wpa_supplicant SIGNAL_MONITOR command"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ dev[1].connect("open", key_mgmt="NONE", scan_freq="2412",
+ bgscan="simple:1:-45:2")
+ dev[2].connect("open", key_mgmt="NONE", scan_freq="2412")
+
+ tests = [" THRESHOLD=-45", " THRESHOLD=-44 HYSTERESIS=5", ""]
+ try:
+ if "FAIL" in dev[2].request("SIGNAL_MONITOR THRESHOLD=-1 HYSTERESIS=5"):
+ raise Exception("SIGNAL_MONITOR command failed")
+ for t in tests:
+ if "OK" not in dev[0].request("SIGNAL_MONITOR" + t):
+ raise Exception("SIGNAL_MONITOR command failed: " + t)
+ if "FAIL" not in dev[1].request("SIGNAL_MONITOR THRESHOLD=-44 HYSTERESIS=5"):
+ raise Exception("SIGNAL_MONITOR command accepted while using bgscan")
+ ev = dev[2].wait_event(["CTRL-EVENT-SIGNAL-CHANGE"], timeout=10)
+ if ev is None:
+ raise Exception("No signal change event seen")
+ if "above=0" not in ev:
+ raise Exception("Unexpected signal change event contents: " + ev)
+ finally:
+ dev[0].request("SIGNAL_MONITOR")
+ dev[1].request("SIGNAL_MONITOR")
+ dev[2].request("SIGNAL_MONITOR")
+
+ dev[0].request("REMOVE_NETWORK all")
+ dev[1].request("REMOVE_NETWORK all")
+ dev[1].wait_disconnected()
+
+def test_wpas_ctrl_p2p_listen_offload(dev, apdev):
+ """wpa_supplicant P2P_LO_START and P2P_LO_STOP commands"""
+ dev[0].request("P2P_LO_STOP")
+ dev[0].request("P2P_LO_START ")
+ dev[0].request("P2P_LO_START 2412")
+ dev[0].request("P2P_LO_START 2412 100 200 3")
+ dev[0].request("P2P_LO_STOP")
+
+def test_wpas_ctrl_driver_flags(dev, apdev):
+ """DRIVER_FLAGS command"""
+ params = hostapd.wpa2_params(ssid="test", passphrase="12345678")
+ hapd = hostapd.add_ap(apdev[0], params)
+ hapd_flags = hapd.request("DRIVER_FLAGS")
+ wpas_flags = dev[0].request("DRIVER_FLAGS")
+ if "FAIL" in hapd_flags:
+ raise Exception("DRIVER_FLAGS failed")
+ if hapd_flags != wpas_flags:
+ raise Exception("Unexpected difference in hostapd vs. wpa_supplicant DRIVER_FLAGS output")
+ logger.info("DRIVER_FLAGS: " + hapd_flags)
+ flags = hapd_flags.split('\n')
+ if 'AP' not in flags:
+ raise Exception("AP flag missing from DRIVER_FLAGS")
+
+def test_wpas_ctrl_bss_current(dev, apdev):
+ """wpa_supplicant BSS CURRENT command"""
+ hapd = hostapd.add_ap(apdev[0], {"ssid": "open"})
+ bssid = hapd.own_addr()
+ res = dev[0].request("BSS CURRENT")
+ if res != '':
+ raise Exception("Unexpected BSS CURRENT response in disconnected state")
+ dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+ res = dev[0].request("BSS CURRENT")
+ if bssid not in res:
+ raise Exception("Unexpected BSS CURRENT response in connected state")
+
+def test_wpas_ctrl_set_lci_errors(dev):
+ """wpa_supplicant SET lci error cases"""
+ if "FAIL" not in dev[0].request("SET lci q"):
+ raise Exception("Invalid LCI value accepted")
+
+ with fail_test(dev[0], 1, "os_get_reltime;wpas_ctrl_iface_set_lci"):
+ if "FAIL" not in dev[0].request("SET lci 00"):
+ raise Exception("SET lci accepted with failing os_get_reltime")
+
+def test_wpas_ctrl_set_radio_disabled(dev):
+ """wpa_supplicant SET radio_disabled"""
+ # This is not currently supported with nl80211, but execute the commands
+ # without checking the result for some additional code coverage.
+ dev[0].request("SET radio_disabled 1")
+ dev[0].request("SET radio_disabled 0")
+
+def test_wpas_ctrl_set_tdls_trigger_control(dev):
+ """wpa_supplicant SET tdls_trigger_control"""
+ # This is not supported with upstream nl80211, but execute the commands
+ # without checking the result for some additional code coverage.
+ dev[0].request("SET tdls_trigger_control 1")
+ dev[0].request("SET tdls_trigger_control 0")
+
+def test_wpas_ctrl_set_sched_scan_relative_rssi(dev):
+ """wpa_supplicant SET relative RSSI"""
+ tests = ["relative_rssi -1",
+ "relative_rssi 101",
+ "relative_band_adjust 2G",
+ "relative_band_adjust 2G:-101",
+ "relative_band_adjust 2G:101",
+ "relative_band_adjust 3G:1"]
+ for t in tests:
+ if "FAIL" not in dev[0].request("SET " + t):
+ raise Exception("No failure reported for SET " + t)
+
+ tests = ["relative_rssi 0",
+ "relative_rssi 10",
+ "relative_rssi disable",
+ "relative_band_adjust 2G:-1",
+ "relative_band_adjust 2G:0",
+ "relative_band_adjust 2G:1",
+ "relative_band_adjust 5G:-1",
+ "relative_band_adjust 5G:1",
+ "relative_band_adjust 5G:0"]
+ for t in tests:
+ if "OK" not in dev[0].request("SET " + t):
+ raise Exception("Failed to SET " + t)
+
+def test_wpas_ctrl_get_pref_freq_list_override(dev):
+ """wpa_supplicant get_pref_freq_list_override"""
+ if dev[0].request("GET_PREF_FREQ_LIST ").strip() != "FAIL":
+ raise Exception("Invalid GET_PREF_FREQ_LIST accepted")
+
+ dev[0].set("get_pref_freq_list_override", "foo")
+ res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
+ if res != "FAIL":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+
+ dev[0].set("get_pref_freq_list_override", "1234:1,2,3 0")
+ res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
+ if res != "FAIL":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+
+ dev[0].set("get_pref_freq_list_override", "1234:1,2,3 0:")
+ res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
+ if res != "0":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+
+ dev[0].set("get_pref_freq_list_override", "0:1,2")
+ res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
+ if res != "1,2":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+
+ dev[0].set("get_pref_freq_list_override", "1:3,4 0:1,2 2:5,6")
+ res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
+ if res != "1,2":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+
+ dev[0].set("get_pref_freq_list_override", "1:3,4 0:1 2:5,6")
+ res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
+ if res != "1":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+
+ dev[0].set("get_pref_freq_list_override", "0:1000,1001 2:1002,1003 3:1004,1005 4:1006,1007 8:1010,1011 9:1008,1009")
+ res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
+ if res != "1000,1001":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+ res = dev[0].request("GET_PREF_FREQ_LIST AP").strip()
+ if res != "1002,1003":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+ res = dev[0].request("GET_PREF_FREQ_LIST P2P_GO").strip()
+ if res != "1004,1005":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+ res = dev[0].request("GET_PREF_FREQ_LIST P2P_CLIENT").strip()
+ if res != "1006,1007":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+ res = dev[0].request("GET_PREF_FREQ_LIST IBSS").strip()
+ if res != "1008,1009":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+ res = dev[0].request("GET_PREF_FREQ_LIST TDLS").strip()
+ if res != "1010,1011":
+ raise Exception("Unexpected GET_PREF_FREQ_LIST response: " + res)
+
+ dev[0].set("get_pref_freq_list_override", "")
+ res = dev[0].request("GET_PREF_FREQ_LIST STATION").strip()
+ logger.info("STATION (without override): " + res)
+
+def test_wpas_ctrl_interface_add_driver_init_failure(dev, apdev):
+ """wpa_supplicant INTERFACE_ADD and driver init failing"""
+ for i in range(1000):
+ res = dev[0].global_request("INTERFACE_ADD FOO")
+ if "FAIL" not in res:
+ raise Exception("Unexpected result: " + res)
+ dev[0].dump_monitor()
diff --git a/contrib/wpa/tests/hwsim/test_wpas_mesh.py b/contrib/wpa/tests/hwsim/test_wpas_mesh.py
new file mode 100644
index 000000000000..75bc02146eb8
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_wpas_mesh.py
@@ -0,0 +1,2534 @@
+# wpa_supplicant mesh mode tests
+# Copyright (c) 2014, cozybit Inc.
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import logging
+logger = logging.getLogger()
+import os
+import struct
+import subprocess
+import time
+import json
+import binascii
+
+import hwsim_utils
+import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import *
+from tshark import run_tshark, run_tshark_json
+from test_sae import build_sae_commit, sae_rx_commit_token_req
+from hwsim_utils import set_group_map
+
+def check_mesh_support(dev, secure=False):
+ if "MESH" not in dev.get_capability("modes"):
+ raise HwsimSkip("Driver does not support mesh")
+ if secure and "SAE" not in dev.get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+def check_mesh_scan(dev, params, other_started=False, beacon_int=0):
+ if not other_started:
+ dev.dump_monitor()
+ id = dev.request("SCAN " + params)
+ if "FAIL" in id:
+ raise Exception("Failed to start scan")
+ id = int(id)
+
+ if other_started:
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ if ev is None:
+ raise Exception("Other scan did not start")
+ if "id=" + str(id) in ev:
+ raise Exception("Own scan id unexpectedly included in start event")
+
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Other scan did not complete")
+ if "id=" + str(id) in ev:
+ raise Exception(
+ "Own scan id unexpectedly included in completed event")
+
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-STARTED"])
+ if ev is None:
+ raise Exception("Scan did not start")
+ if "id=" + str(id) not in ev:
+ raise Exception("Scan id not included in start event")
+
+ ev = dev.wait_event(["CTRL-EVENT-SCAN-RESULTS"])
+ if ev is None:
+ raise Exception("Scan did not complete")
+ if "id=" + str(id) not in ev:
+ raise Exception("Scan id not included in completed event")
+
+ res = dev.request("SCAN_RESULTS")
+
+ if res.find("[MESH]") < 0:
+ raise Exception("Scan did not contain a MESH network")
+
+ bssid = res.splitlines()[1].split(' ')[0]
+ bss = dev.get_bss(bssid)
+ if bss is None:
+ raise Exception("Could not get BSS entry for mesh")
+ if 'mesh_capability' not in bss:
+ raise Exception("mesh_capability missing from BSS entry")
+ if beacon_int:
+ if 'beacon_int' not in bss:
+ raise Exception("beacon_int missing from BSS entry")
+ if str(beacon_int) != bss['beacon_int']:
+ raise Exception("Unexpected beacon_int in BSS entry: " + bss['beacon_int'])
+ if '[MESH]' not in bss['flags']:
+ raise Exception("BSS output did not include MESH flag")
+
+def check_dfs_started(dev, timeout=10):
+ ev = dev.wait_event(["DFS-CAC-START"], timeout=timeout)
+ if ev is None:
+ raise Exception("Test exception: CAC did not start")
+
+def check_dfs_finished(dev, timeout=70):
+ ev = dev.wait_event(["DFS-CAC-COMPLETED"], timeout=timeout)
+ if ev is None:
+ raise Exception("Test exception: CAC did not finish")
+
+def check_mesh_radar_handling_finished(dev, timeout=75):
+ ev = dev.wait_event(["CTRL-EVENT-CHANNEL-SWITCH", "MESH-GROUP-STARTED"],
+ timeout=timeout)
+ if ev is None:
+ raise Exception("Test exception: Couldn't join mesh")
+
+def check_mesh_group_added(dev, timeout=10):
+ ev = dev.wait_event(["MESH-GROUP-STARTED"], timeout=timeout)
+ if ev is None:
+ raise Exception("Test exception: Couldn't join mesh")
+
+
+def check_mesh_group_removed(dev):
+ ev = dev.wait_event(["MESH-GROUP-REMOVED"])
+ if ev is None:
+ raise Exception("Test exception: Couldn't leave mesh")
+
+def check_regdom_change(dev, timeout=10):
+ ev = dev.wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=timeout)
+ if ev is None:
+ raise Exception("Test exception: No regdom change happened.")
+
+def check_mesh_peer_connected(dev, timeout=10):
+ ev = dev.wait_event(["MESH-PEER-CONNECTED"], timeout=timeout)
+ if ev is None:
+ raise Exception("Test exception: Remote peer did not connect.")
+
+
+def check_mesh_peer_disconnected(dev):
+ ev = dev.wait_event(["MESH-PEER-DISCONNECTED"])
+ if ev is None:
+ raise Exception("Test exception: Peer disconnect event not detected.")
+
+def check_mesh_joined2(dev):
+ check_mesh_group_added(dev[0])
+ check_mesh_group_added(dev[1])
+
+def check_mesh_connected2(dev, timeout0=10, connectivity=False):
+ check_mesh_peer_connected(dev[0], timeout=timeout0)
+ check_mesh_peer_connected(dev[1])
+ if connectivity:
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def check_mesh_joined_connected(dev, connectivity=False, timeout0=10):
+ check_mesh_joined2(dev)
+ check_mesh_connected2(dev, timeout0=timeout0, connectivity=connectivity)
+
+def test_wpas_add_set_remove_support(dev):
+ """wpa_supplicant MESH add/set/remove network support"""
+ check_mesh_support(dev[0])
+ id = dev[0].add_network()
+ dev[0].set_network(id, "mode", "5")
+ dev[0].remove_network(id)
+
+def add_open_mesh_network(dev, freq="2412", start=True, beacon_int=0,
+ basic_rates=None, chwidth=-1, disable_vht=False,
+ disable_ht40=False):
+ id = dev.add_network()
+ dev.set_network(id, "mode", "5")
+ dev.set_network_quoted(id, "ssid", "wpas-mesh-open")
+ dev.set_network(id, "key_mgmt", "NONE")
+ if freq:
+ dev.set_network(id, "frequency", freq)
+ if chwidth > -1:
+ dev.set_network(id, "max_oper_chwidth", str(chwidth))
+ if beacon_int:
+ dev.set_network(id, "beacon_int", str(beacon_int))
+ if basic_rates:
+ dev.set_network(id, "mesh_basic_rates", basic_rates)
+ if disable_vht:
+ dev.set_network(id, "disable_vht", "1")
+ if disable_ht40:
+ dev.set_network(id, "disable_ht40", "1")
+ if start:
+ dev.mesh_group_add(id)
+ return id
+
+def test_wpas_mesh_group_added(dev):
+ """wpa_supplicant MESH group add"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0])
+
+ # Check for MESH-GROUP-STARTED event
+ check_mesh_group_added(dev[0])
+
+
+def test_wpas_mesh_group_remove(dev):
+ """wpa_supplicant MESH group remove"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0])
+ # Check for MESH-GROUP-STARTED event
+ check_mesh_group_added(dev[0])
+ dev[0].mesh_group_remove()
+ # Check for MESH-GROUP-REMOVED event
+ check_mesh_group_removed(dev[0])
+ dev[0].mesh_group_remove()
+
+def dfs_simulate_radar(dev):
+ logger.info("Trigger a simulated radar event")
+ phyname = dev.get_driver_status_field("phyname")
+ radar_file = '/sys/kernel/debug/ieee80211/' + phyname + '/hwsim/dfs_simulate_radar'
+ with open(radar_file, 'w') as f:
+ f.write('1')
+
+@long_duration_test
+def test_mesh_peer_connected_dfs(dev):
+ """Mesh peer connected (DFS)"""
+ dev[0].set("country", "DE")
+ dev[1].set("country", "DE")
+
+ check_regdom_change(dev[0])
+ check_regdom_change(dev[1])
+
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0], freq="5500", beacon_int=160)
+ add_open_mesh_network(dev[1], freq="5500", beacon_int=160)
+ check_dfs_started(dev[0])
+ check_dfs_finished(dev[0])
+ check_mesh_joined_connected(dev, timeout0=10)
+
+ dfs_simulate_radar(dev[0])
+
+ check_mesh_radar_handling_finished(dev[0], timeout=75)
+
+ dev[0].set("country", "00")
+ dev[1].set("country", "00")
+
+ check_regdom_change(dev[0])
+ check_regdom_change(dev[1])
+
+def test_wpas_mesh_peer_connected(dev):
+ """wpa_supplicant MESH peer connected"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0], beacon_int=160)
+ add_open_mesh_network(dev[1], beacon_int=160)
+ check_mesh_joined_connected(dev)
+
+def test_wpas_mesh_peer_disconnected(dev):
+ """wpa_supplicant MESH peer disconnected"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0])
+ add_open_mesh_network(dev[1])
+ check_mesh_joined_connected(dev)
+
+ # Remove group on dev 1
+ dev[1].mesh_group_remove()
+ # Device 0 should get a disconnection event
+ check_mesh_peer_disconnected(dev[0])
+
+
+def test_wpas_mesh_mode_scan(dev):
+ """wpa_supplicant MESH scan support"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0])
+ add_open_mesh_network(dev[1], beacon_int=175)
+
+ check_mesh_joined2(dev)
+
+ # Check for Mesh scan
+ check_mesh_scan(dev[0], "use_id=1 freq=2412", beacon_int=175)
+
+def test_wpas_mesh_open(dev, apdev):
+ """wpa_supplicant open MESH network connectivity"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0], freq="2462", basic_rates="60 120 240")
+ add_open_mesh_network(dev[1], freq="2462", basic_rates="60 120 240")
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+ state = dev[0].get_status_field("wpa_state")
+ if state != "COMPLETED":
+ raise Exception("Unexpected wpa_state on dev0: " + state)
+ state = dev[1].get_status_field("wpa_state")
+ if state != "COMPLETED":
+ raise Exception("Unexpected wpa_state on dev1: " + state)
+
+ mode = dev[0].get_status_field("mode")
+ if mode != "mesh":
+ raise Exception("Unexpected mode: " + mode)
+
+ dev[0].scan(freq="2462")
+ bss = dev[0].get_bss(dev[1].own_addr())
+ if bss and 'ie' in bss and "ff0724" in bss['ie']:
+ sta = dev[0].request("STA " + dev[1].own_addr())
+ logger.info("STA info:\n" + sta.rstrip())
+ if "[HE]" not in sta:
+ raise Exception("Missing STA HE flag")
+ if "[VHT]" in sta:
+ raise Exception("Unexpected STA VHT flag")
+
+def test_wpas_mesh_open_no_auto(dev, apdev):
+ """wpa_supplicant open MESH network connectivity"""
+ check_mesh_support(dev[0])
+ id = add_open_mesh_network(dev[0], start=False)
+ dev[0].set_network(id, "dot11MeshMaxRetries", "16")
+ dev[0].set_network(id, "dot11MeshRetryTimeout", "255")
+ dev[0].mesh_group_add(id)
+
+ id = add_open_mesh_network(dev[1], start=False)
+ dev[1].set_network(id, "no_auto_peer", "1")
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev, connectivity=True, timeout0=30)
+
+def test_mesh_open_no_auto2(dev, apdev):
+ """Open mesh network connectivity, no_auto on both peers"""
+ check_mesh_support(dev[0])
+ id = add_open_mesh_network(dev[0], start=False)
+ dev[0].set_network(id, "no_auto_peer", "1")
+ dev[0].mesh_group_add(id)
+
+ id = add_open_mesh_network(dev[1], start=False)
+ dev[1].set_network(id, "no_auto_peer", "1")
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined2(dev)
+
+ ev = dev[0].wait_event(["will not initiate new peer link"], timeout=10)
+ if ev is None:
+ raise Exception("Missing no-initiate message")
+ addr1 = dev[1].own_addr()
+ if "OK" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD failed")
+ if "FAIL" not in dev[0].request("MESH_PEER_ADD ff:ff:ff:ff:ff:ff"):
+ raise Exception("MESH_PEER_ADD with unknown STA succeeded")
+ check_mesh_connected2(dev, timeout0=30)
+ if "FAIL" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD succeeded for connected STA")
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def test_mesh_open_rssi_threshold(dev, apdev):
+ """Open mesh network with RSSI threshold"""
+ check_mesh_support(dev[0])
+
+ _test_mesh_open_rssi_threshold(dev, apdev, -255, -255)
+ _test_mesh_open_rssi_threshold(dev, apdev, 0, 0)
+ _test_mesh_open_rssi_threshold(dev, apdev, 1, 0)
+
+def _test_mesh_open_rssi_threshold(dev, apdev, value, expected):
+ id = add_open_mesh_network(dev[0], start=False)
+ dev[0].set_network(id, "mesh_rssi_threshold", str(value))
+ dev[0].mesh_group_add(id)
+ check_mesh_group_added(dev[0])
+
+ cmd = subprocess.Popen(["iw", "dev", dev[0].ifname, "get", "mesh_param",
+ "mesh_rssi_threshold"], stdout=subprocess.PIPE)
+ mesh_rssi_threshold = int(cmd.stdout.read().decode().split(" ")[0])
+
+ dev[0].mesh_group_remove()
+ check_mesh_group_removed(dev[0])
+
+ if mesh_rssi_threshold != expected:
+ raise Exception("mesh_rssi_threshold should be " + str(expected) +
+ ": " + str(mesh_rssi_threshold))
+
+def add_mesh_secure_net(dev, psk=True, pmf=False, pairwise=None, group=None,
+ group_mgmt=None,
+ sae_password=False, sae_password_id=None, ocv=False):
+ id = dev.add_network()
+ dev.set_network(id, "mode", "5")
+ dev.set_network_quoted(id, "ssid", "wpas-mesh-sec")
+ dev.set_network(id, "key_mgmt", "SAE")
+ dev.set_network(id, "frequency", "2412")
+ if sae_password:
+ dev.set_network_quoted(id, "sae_password", "thisismypassphrase!")
+ if sae_password_id:
+ dev.set_network_quoted(id, "sae_password_id", sae_password_id)
+ if psk:
+ dev.set_network_quoted(id, "psk", "thisismypassphrase!")
+ if pmf:
+ dev.set_network(id, "ieee80211w", "2")
+ if pairwise:
+ dev.set_network(id, "pairwise", pairwise)
+ if group:
+ dev.set_network(id, "group", group)
+ if group_mgmt:
+ dev.set_network(id, "group_mgmt", group_mgmt)
+ if ocv:
+ try:
+ dev.set_network(id, "ocv", "1")
+ except Exception as e:
+ if "SET_NETWORK failed" in str(e):
+ raise HwsimSkip("OCV not supported")
+ raise
+ return id
+
+def test_wpas_mesh_secure(dev, apdev):
+ """wpa_supplicant secure MESH network connectivity"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+ state = dev[0].get_status_field("wpa_state")
+ if state != "COMPLETED":
+ raise Exception("Unexpected wpa_state on dev0: " + state)
+ state = dev[1].get_status_field("wpa_state")
+ if state != "COMPLETED":
+ raise Exception("Unexpected wpa_state on dev1: " + state)
+
+def test_wpas_mesh_secure_sae_password(dev, apdev):
+ """wpa_supplicant secure mesh using sae_password"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0], psk=False, sae_password=True)
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+def test_mesh_secure_pmf(dev, apdev):
+ """Secure mesh network connectivity with PMF enabled"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0], pmf=True)
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1], pmf=True)
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+def test_mesh_secure_ocv(dev, apdev):
+ """Secure mesh network connectivity with OCV enabled"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0], pmf=True, ocv=True)
+ dev[0].mesh_group_add(id)
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1], pmf=True, ocv=True)
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+def test_mesh_secure_ocv_compat(dev, apdev):
+ """Secure mesh network where only one peer has OCV enabled"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0], pmf=True, ocv=True)
+ dev[0].mesh_group_add(id)
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1], pmf=True, ocv=False)
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+def set_reg(dev, country):
+ subprocess.call(['iw', 'reg', 'set', country])
+ for i in range(2):
+ for j in range(5):
+ ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=" + country in ev:
+ break
+
+def clear_reg_setting(dev):
+ dev[0].request("MESH_GROUP_REMOVE " + dev[0].ifname)
+ dev[1].request("MESH_GROUP_REMOVE " + dev[1].ifname)
+ clear_regdom_dev(dev)
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+
+def test_mesh_secure_ocv_mix_legacy(dev, apdev):
+ """Mesh network with a VHT STA and a legacy STA under OCV"""
+ try:
+ run_mesh_secure_ocv_mix_legacy(dev, apdev)
+ finally:
+ clear_reg_setting(dev)
+
+def run_mesh_secure_ocv_mix_legacy(dev, apdev):
+ check_mesh_support(dev[0], secure=True)
+ set_reg(dev, 'AZ')
+
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0], pmf=True, ocv=True)
+ dev[0].set_network(id, "frequency", "5200")
+ dev[0].set_network(id, "max_oper_chwidth", "2")
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1], pmf=True, ocv=True)
+ dev[1].set_network(id, "frequency", "5200")
+ dev[1].set_network(id, "disable_vht", "1")
+ dev[1].set_network(id, "disable_ht40", "1")
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+def test_mesh_secure_ocv_mix_ht(dev, apdev):
+ """Mesh network with a VHT STA and a HT STA under OCV"""
+ try:
+ run_mesh_secure_ocv_mix_ht(dev, apdev)
+ finally:
+ clear_reg_setting(dev)
+
+def run_mesh_secure_ocv_mix_ht(dev, apdev):
+ check_mesh_support(dev[0], secure=True)
+ set_reg(dev, 'AZ')
+
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0], pmf=True, ocv=True)
+ dev[0].set_network(id, "frequency", "5200")
+ dev[0].set_network(id, "max_oper_chwidth", "2")
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1], pmf=True, ocv=True)
+ dev[1].set_network(id, "frequency", "5200")
+ dev[1].set_network(id, "disable_vht", "1")
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+def run_mesh_secure(dev, cipher, pmf=False, group_mgmt=None):
+ if cipher not in dev[0].get_capability("pairwise"):
+ raise HwsimSkip("Cipher %s not supported" % cipher)
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0], pairwise=cipher, group=cipher, pmf=pmf,
+ group_mgmt=group_mgmt)
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1], pairwise=cipher, group=cipher, pmf=pmf,
+ group_mgmt=group_mgmt)
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+def test_mesh_secure_ccmp(dev, apdev):
+ """Secure mesh with CCMP"""
+ run_mesh_secure(dev, "CCMP")
+
+def test_mesh_secure_gcmp(dev, apdev):
+ """Secure mesh with GCMP"""
+ run_mesh_secure(dev, "GCMP")
+
+def test_mesh_secure_gcmp_256(dev, apdev):
+ """Secure mesh with GCMP-256"""
+ run_mesh_secure(dev, "GCMP-256")
+
+def test_mesh_secure_ccmp_256(dev, apdev):
+ """Secure mesh with CCMP-256"""
+ run_mesh_secure(dev, "CCMP-256")
+
+def test_mesh_secure_ccmp_cmac(dev, apdev):
+ """Secure mesh with CCMP-128 and BIP-CMAC-128"""
+ run_mesh_secure(dev, "CCMP", pmf=True, group_mgmt="AES-128-CMAC")
+
+def test_mesh_secure_gcmp_gmac(dev, apdev):
+ """Secure mesh with GCMP-128 and BIP-GMAC-128"""
+ run_mesh_secure(dev, "GCMP", pmf=True, group_mgmt="BIP-GMAC-128")
+
+def test_mesh_secure_ccmp_256_cmac_256(dev, apdev):
+ """Secure mesh with CCMP-256 and BIP-CMAC-256"""
+ run_mesh_secure(dev, "CCMP-256", pmf=True, group_mgmt="BIP-CMAC-256")
+
+def test_mesh_secure_gcmp_256_gmac_256(dev, apdev):
+ """Secure mesh with GCMP-256 and BIP-GMAC-256"""
+ run_mesh_secure(dev, "GCMP-256", pmf=True, group_mgmt="BIP-GMAC-256")
+
+def test_mesh_secure_invalid_pairwise_cipher(dev, apdev):
+ """Secure mesh and invalid group cipher"""
+ check_mesh_support(dev[0], secure=True)
+ skip_without_tkip(dev[0])
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0], pairwise="TKIP", group="CCMP")
+ if dev[0].mesh_group_add(id) != None:
+ raise Exception("Unexpected group add success")
+ ev = dev[0].wait_event(["mesh: Invalid pairwise cipher"], timeout=1)
+ if ev is None:
+ raise Exception("Invalid pairwise cipher not reported")
+
+def test_mesh_secure_invalid_group_cipher(dev, apdev):
+ """Secure mesh and invalid group cipher"""
+ skip_without_tkip(dev[0])
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0], pairwise="CCMP", group="TKIP")
+ if dev[0].mesh_group_add(id) != None:
+ raise Exception("Unexpected group add success")
+ ev = dev[0].wait_event(["mesh: Invalid group cipher"], timeout=1)
+ if ev is None:
+ raise Exception("Invalid group cipher not reported")
+
+def test_wpas_mesh_secure_sae_group_mismatch(dev, apdev):
+ """wpa_supplicant secure MESH and SAE group mismatch"""
+ check_mesh_support(dev[0], secure=True)
+ addr0 = dev[0].p2p_interface_addr()
+ addr1 = dev[1].p2p_interface_addr()
+ addr2 = dev[2].p2p_interface_addr()
+
+ dev[0].request("SET sae_groups 19 25")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups 19")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+
+ dev[2].request("SET sae_groups 26")
+ id = add_mesh_secure_net(dev[2])
+ dev[2].mesh_group_add(id)
+
+ check_mesh_group_added(dev[0])
+ check_mesh_group_added(dev[1])
+ check_mesh_group_added(dev[2])
+
+ ev = dev[0].wait_event(["MESH-PEER-CONNECTED"])
+ if ev is None:
+ raise Exception("Remote peer did not connect")
+ if addr1 not in ev:
+ raise Exception("Unexpected peer connected: " + ev)
+
+ ev = dev[1].wait_event(["MESH-PEER-CONNECTED"])
+ if ev is None:
+ raise Exception("Remote peer did not connect")
+ if addr0 not in ev:
+ raise Exception("Unexpected peer connected: " + ev)
+
+ ev = dev[2].wait_event(["MESH-PEER-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected peer connection at dev[2]: " + ev)
+
+ ev = dev[0].wait_event(["MESH-PEER-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected peer connection: " + ev)
+
+ ev = dev[1].wait_event(["MESH-PEER-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected peer connection: " + ev)
+
+ dev[0].request("SET sae_groups ")
+ dev[1].request("SET sae_groups ")
+ dev[2].request("SET sae_groups ")
+
+def test_wpas_mesh_secure_sae_group_negotiation(dev, apdev):
+ """wpa_supplicant secure MESH and SAE group negotiation"""
+ check_mesh_support(dev[0], secure=True)
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+
+ #dev[0].request("SET sae_groups 21 20 25 26")
+ dev[0].request("SET sae_groups 26")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups 19 26")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev)
+
+ dev[0].request("SET sae_groups ")
+ dev[1].request("SET sae_groups ")
+
+def test_wpas_mesh_secure_sae_missing_password(dev, apdev):
+ """wpa_supplicant secure MESH and missing SAE password"""
+ check_mesh_support(dev[0], secure=True)
+ id = add_mesh_secure_net(dev[0], psk=False)
+ dev[0].set_network(id, "psk", "8f20b381f9b84371d61b5080ad85cac3c61ab3ca9525be5b2d0f4da3d979187a")
+ dev[0].mesh_group_add(id)
+ ev = dev[0].wait_event(["MESH-GROUP-STARTED", "Could not join mesh"],
+ timeout=5)
+ if ev is None:
+ raise Exception("Timeout on mesh start event")
+ if "MESH-GROUP-STARTED" in ev:
+ raise Exception("Unexpected mesh group start")
+ ev = dev[0].wait_event(["MESH-GROUP-STARTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected mesh group start")
+
+def test_wpas_mesh_secure_no_auto(dev, apdev):
+ """wpa_supplicant secure MESH network connectivity"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups 19")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups 19")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].set_network(id, "no_auto_peer", "1")
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+ dev[0].request("SET sae_groups ")
+ dev[1].request("SET sae_groups ")
+
+def test_wpas_mesh_secure_dropped_frame(dev, apdev):
+ """Secure mesh network connectivity when the first plink Open is dropped"""
+ check_mesh_support(dev[0], secure=True)
+
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined2(dev)
+
+ # Drop the first Action frame (plink Open) to test unexpected order of
+ # Confirm/Open messages.
+ count = 0
+ while True:
+ count += 1
+ if count > 10:
+ raise Exception("Did not see Action frames")
+ rx_msg = dev[0].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ if rx_msg['subtype'] == 13:
+ logger.info("Drop the first Action frame")
+ break
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
+ rx_msg['freq'], rx_msg['datarate'], rx_msg['ssi_signal'], binascii.hexlify(rx_msg['frame']).decode())):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ dev[0].request("SET ext_mgmt_frame_handling 0")
+
+ check_mesh_connected2(dev, connectivity=True)
+
+def test_mesh_secure_fail(dev, apdev):
+ """Secure mesh network connectivity failure"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0], pmf=True)
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1], pmf=True)
+
+ with fail_test(dev[0], 1, "wpa_driver_nl80211_sta_add;mesh_mpm_auth_peer"):
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev)
+
+def test_wpas_mesh_ctrl(dev):
+ """wpa_supplicant ctrl_iface mesh command error cases"""
+ check_mesh_support(dev[0])
+ if "FAIL" not in dev[0].request("MESH_GROUP_ADD 123"):
+ raise Exception("Unexpected MESH_GROUP_ADD success")
+ id = dev[0].add_network()
+ if "FAIL" not in dev[0].request("MESH_GROUP_ADD %d" % id):
+ raise Exception("Unexpected MESH_GROUP_ADD success")
+ dev[0].set_network(id, "mode", "5")
+ dev[0].set_network(id, "key_mgmt", "WPA-PSK")
+ if "FAIL" not in dev[0].request("MESH_GROUP_ADD %d" % id):
+ raise Exception("Unexpected MESH_GROUP_ADD success")
+
+ if "FAIL" not in dev[0].request("MESH_GROUP_REMOVE foo"):
+ raise Exception("Unexpected MESH_GROUP_REMOVE success")
+
+def test_wpas_mesh_dynamic_interface(dev):
+ """wpa_supplicant mesh with dynamic interface"""
+ check_mesh_support(dev[0])
+ mesh0 = None
+ mesh1 = None
+ try:
+ mesh0 = dev[0].request("MESH_INTERFACE_ADD ifname=mesh0")
+ if "FAIL" in mesh0:
+ raise Exception("MESH_INTERFACE_ADD failed")
+ mesh1 = dev[1].request("MESH_INTERFACE_ADD")
+ if "FAIL" in mesh1:
+ raise Exception("MESH_INTERFACE_ADD failed")
+
+ wpas0 = WpaSupplicant(ifname=mesh0)
+ wpas1 = WpaSupplicant(ifname=mesh1)
+ logger.info(mesh0 + " address " + wpas0.get_status_field("address"))
+ logger.info(mesh1 + " address " + wpas1.get_status_field("address"))
+
+ add_open_mesh_network(wpas0)
+ add_open_mesh_network(wpas1)
+ check_mesh_joined_connected([wpas0, wpas1], connectivity=True)
+
+ # Must not allow MESH_GROUP_REMOVE on dynamic interface
+ if "FAIL" not in wpas0.request("MESH_GROUP_REMOVE " + mesh0):
+ raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+ if "FAIL" not in wpas1.request("MESH_GROUP_REMOVE " + mesh1):
+ raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+
+ # Must not allow MESH_GROUP_REMOVE on another radio interface
+ if "FAIL" not in wpas0.request("MESH_GROUP_REMOVE " + mesh1):
+ raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+ if "FAIL" not in wpas1.request("MESH_GROUP_REMOVE " + mesh0):
+ raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+
+ wpas0.remove_ifname()
+ wpas1.remove_ifname()
+
+ if "OK" not in dev[0].request("MESH_GROUP_REMOVE " + mesh0):
+ raise Exception("MESH_GROUP_REMOVE failed")
+ if "OK" not in dev[1].request("MESH_GROUP_REMOVE " + mesh1):
+ raise Exception("MESH_GROUP_REMOVE failed")
+
+ if "FAIL" not in dev[0].request("MESH_GROUP_REMOVE " + mesh0):
+ raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+ if "FAIL" not in dev[1].request("MESH_GROUP_REMOVE " + mesh1):
+ raise Exception("Invalid MESH_GROUP_REMOVE accepted")
+
+ logger.info("Make sure another dynamic group can be added")
+ mesh0 = dev[0].request("MESH_INTERFACE_ADD ifname=mesh0")
+ if "FAIL" in mesh0:
+ raise Exception("MESH_INTERFACE_ADD failed")
+ mesh1 = dev[1].request("MESH_INTERFACE_ADD")
+ if "FAIL" in mesh1:
+ raise Exception("MESH_INTERFACE_ADD failed")
+
+ wpas0 = WpaSupplicant(ifname=mesh0)
+ wpas1 = WpaSupplicant(ifname=mesh1)
+ logger.info(mesh0 + " address " + wpas0.get_status_field("address"))
+ logger.info(mesh1 + " address " + wpas1.get_status_field("address"))
+
+ add_open_mesh_network(wpas0)
+ add_open_mesh_network(wpas1)
+ check_mesh_joined_connected([wpas0, wpas1], connectivity=True)
+ finally:
+ if mesh0:
+ dev[0].request("MESH_GROUP_REMOVE " + mesh0)
+ if mesh1:
+ dev[1].request("MESH_GROUP_REMOVE " + mesh1)
+
+def test_wpas_mesh_dynamic_interface_remove(dev):
+ """wpa_supplicant mesh with dynamic interface and removal"""
+ wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+ wpas.interface_add("wlan5")
+ check_mesh_support(wpas)
+ mesh5 = wpas.request("MESH_INTERFACE_ADD ifname=mesh5")
+ if "FAIL" in mesh5:
+ raise Exception("MESH_INTERFACE_ADD failed")
+
+ wpas5 = WpaSupplicant(ifname=mesh5)
+ logger.info(mesh5 + " address " + wpas5.get_status_field("address"))
+ add_open_mesh_network(wpas5)
+ add_open_mesh_network(dev[0])
+ check_mesh_joined_connected([wpas5, dev[0]], connectivity=True)
+
+ # Remove the main interface while mesh interface is in use
+ wpas.interface_remove("wlan5")
+
+def test_wpas_mesh_max_peering(dev, apdev, params):
+ """Mesh max peering limit"""
+ check_mesh_support(dev[0])
+ try:
+ dev[0].request("SET max_peer_links 1")
+
+ # first, connect dev[0] and dev[1]
+ add_open_mesh_network(dev[0])
+ add_open_mesh_network(dev[1])
+ for i in range(2):
+ ev = dev[i].wait_event(["MESH-PEER-CONNECTED"])
+ if ev is None:
+ raise Exception("dev%d did not connect with any peer" % i)
+
+ # add dev[2] which will try to connect with both dev[0] and dev[1],
+ # but can complete connection only with dev[1]
+ add_open_mesh_network(dev[2])
+ for i in range(1, 3):
+ ev = dev[i].wait_event(["MESH-PEER-CONNECTED"])
+ if ev is None:
+ raise Exception("dev%d did not connect the second peer" % i)
+
+ ev = dev[0].wait_event(["MESH-PEER-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("dev0 connection beyond max peering limit")
+
+ ev = dev[2].wait_event(["MESH-PEER-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("dev2 reported unexpected peering: " + ev)
+
+ for i in range(3):
+ dev[i].mesh_group_remove()
+ check_mesh_group_removed(dev[i])
+ finally:
+ dev[0].request("SET max_peer_links 99")
+
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ addr2 = dev[2].own_addr()
+
+ capfile = os.path.join(params['logdir'], "hwsim0.pcapng")
+ filt = "wlan.fc.type_subtype == 8"
+ out = run_tshark(capfile, filt, ["wlan.sa", "wlan.mesh.config.cap"])
+ pkts = out.splitlines()
+ one = [0, 0, 0]
+ zero = [0, 0, 0]
+ all_cap_one = True
+ for pkt in pkts:
+ addr, cap = pkt.split('\t')
+ cap = int(cap, 16)
+ if cap != 1:
+ all_cap_one = False
+ if addr == addr0:
+ idx = 0
+ elif addr == addr1:
+ idx = 1
+ elif addr == addr2:
+ idx = 2
+ else:
+ continue
+ if cap & 0x01:
+ one[idx] += 1
+ else:
+ zero[idx] += 1
+ logger.info("one: " + str(one))
+ logger.info("zero: " + str(zero))
+ if all_cap_one:
+ # It looks like tshark parser was broken at some point for
+ # wlan.mesh.config.cap which is now (tshark 2.6.3) pointing to incorrect
+ # field (same as wlan.mesh.config.ps_protocol). This used to work with
+ # tshark 2.2.6.
+ #
+ # For now, assume the capability field ends up being the last octet of
+ # the frame.
+ one = [0, 0, 0]
+ zero = [0, 0, 0]
+ addrs = [addr0, addr1, addr2]
+ for idx in range(3):
+ addr = addrs[idx]
+ out = run_tshark_json(capfile, filt + " && wlan.sa == " + addr)
+ pkts = json.loads(out)
+ for pkt in pkts:
+ wlan = pkt["_source"]["layers"]["wlan"]
+ if "wlan.tagged.all" not in wlan:
+ continue
+
+ tagged = wlan["wlan.tagged.all"]
+ if "wlan.tag" not in tagged:
+ continue
+
+ wlan_tag = tagged["wlan.tag"]
+ if "wlan.mesh.config.ps_protocol_raw" not in wlan_tag:
+ continue
+
+ frame = pkt["_source"]["layers"]["frame_raw"][0]
+ cap_offset = wlan_tag["wlan.mesh.config.ps_protocol_raw"][1] + 6
+ cap = int(frame[(cap_offset * 2):(cap_offset * 2 + 2)], 16)
+ if cap & 0x01:
+ one[idx] += 1
+ else:
+ zero[idx] += 1
+ logger.info("one: " + str(one))
+ logger.info("zero: " + str(zero))
+ if zero[0] == 0:
+ raise Exception("Accepting Additional Mesh Peerings not cleared")
+ if one[0] == 0:
+ raise Exception("Accepting Additional Mesh Peerings was not set in the first Beacon frame")
+ if zero[1] > 0 or zero[2] > 0 or one[1] == 0 or one[2] == 0:
+ raise Exception("Unexpected value in Accepting Additional Mesh Peerings from other STAs")
+
+def test_wpas_mesh_open_5ghz(dev, apdev):
+ """wpa_supplicant open MESH network on 5 GHz band"""
+ try:
+ _test_wpas_mesh_open_5ghz(dev, apdev)
+ finally:
+ clear_reg_setting(dev)
+
+def _test_wpas_mesh_open_5ghz(dev, apdev):
+ check_mesh_support(dev[0])
+ subprocess.call(['iw', 'reg', 'set', 'US'])
+ for i in range(2):
+ for j in range(5):
+ ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=US" in ev:
+ break
+ add_open_mesh_network(dev[i], freq="5180")
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+ dev[0].mesh_group_remove()
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[0])
+ check_mesh_group_removed(dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+
+def test_wpas_mesh_open_ht40(dev, apdev):
+ """Mesh and HT40 support difference"""
+ try:
+ _test_wpas_mesh_open_ht40(dev, apdev)
+ finally:
+ dev[0].request("MESH_GROUP_REMOVE " + dev[0].ifname)
+ dev[1].request("MESH_GROUP_REMOVE " + dev[1].ifname)
+ dev[2].request("MESH_GROUP_REMOVE " + dev[2].ifname)
+ clear_regdom_dev(dev)
+ dev[0].flush_scan_cache()
+ dev[1].flush_scan_cache()
+ dev[2].flush_scan_cache()
+
+def _test_wpas_mesh_open_ht40(dev, apdev):
+ check_mesh_support(dev[0])
+ subprocess.call(['iw', 'reg', 'set', 'US'])
+ for i in range(3):
+ for j in range(5):
+ ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=US" in ev:
+ break
+ add_open_mesh_network(dev[i], freq="5180", disable_vht=True,
+ disable_ht40=(i == 2))
+
+ check_mesh_group_added(dev[0])
+ check_mesh_group_added(dev[1])
+ check_mesh_group_added(dev[2])
+
+ check_mesh_peer_connected(dev[0])
+ check_mesh_peer_connected(dev[1])
+ check_mesh_peer_connected(dev[2])
+
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ hwsim_utils.test_connectivity(dev[0], dev[2])
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+
+ dev[0].mesh_group_remove()
+ dev[1].mesh_group_remove()
+ dev[2].mesh_group_remove()
+ check_mesh_group_removed(dev[0])
+ check_mesh_group_removed(dev[1])
+ check_mesh_group_removed(dev[2])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+
+def test_wpas_mesh_open_vht40(dev, apdev):
+ """wpa_supplicant open MESH network on VHT 40 MHz channel"""
+ try:
+ _test_wpas_mesh_open_vht40(dev, apdev)
+ finally:
+ clear_reg_setting(dev)
+
+def _test_wpas_mesh_open_vht40(dev, apdev):
+ check_mesh_support(dev[0])
+ subprocess.call(['iw', 'reg', 'set', 'US'])
+ for i in range(2):
+ for j in range(5):
+ ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=US" in ev:
+ break
+ add_open_mesh_network(dev[i], freq="5180", chwidth=0)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=40 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "CENTER_FRQ1=5190" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+
+ sig = dev[1].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=40 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2b): " + str(sig))
+ if "CENTER_FRQ1=5190" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3b): " + str(sig))
+
+ dev[0].scan(freq="5180")
+ bss = dev[0].get_bss(dev[1].own_addr())
+ if bss and 'ie' in bss and "ff0724" in bss['ie']:
+ sta = dev[0].request("STA " + dev[1].own_addr())
+ logger.info("STA info:\n" + sta.rstrip())
+ if "[HT][VHT][HE]" not in sta:
+ raise Exception("Missing STA flags")
+
+ dev[0].mesh_group_remove()
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[0])
+ check_mesh_group_removed(dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_wpas_mesh_open_vht20(dev, apdev):
+ """wpa_supplicant open MESH network on VHT 20 MHz channel"""
+ try:
+ _test_wpas_mesh_open_vht20(dev, apdev)
+ finally:
+ clear_reg_setting(dev)
+
+def _test_wpas_mesh_open_vht20(dev, apdev):
+ check_mesh_support(dev[0])
+ subprocess.call(['iw', 'reg', 'set', 'US'])
+ for i in range(2):
+ for j in range(5):
+ ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=US" in ev:
+ break
+ add_open_mesh_network(dev[i], freq="5180", chwidth=0, disable_ht40=True)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=20 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "CENTER_FRQ1=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+
+ sig = dev[1].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=20 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2b): " + str(sig))
+ if "CENTER_FRQ1=5180" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3b): " + str(sig))
+
+ dev[0].mesh_group_remove()
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[0])
+ check_mesh_group_removed(dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_wpas_mesh_open_vht_80p80(dev, apdev):
+ """wpa_supplicant open MESH network on VHT 80+80 MHz channel"""
+ try:
+ _test_wpas_mesh_open_vht_80p80(dev, apdev)
+ finally:
+ clear_reg_setting(dev)
+
+def _test_wpas_mesh_open_vht_80p80(dev, apdev):
+ check_mesh_support(dev[0])
+ subprocess.call(['iw', 'reg', 'set', 'US'])
+ for i in range(2):
+ for j in range(5):
+ ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=US" in ev:
+ break
+ add_open_mesh_network(dev[i], freq="5180", chwidth=3)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=80+80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "CENTER_FRQ1=5210" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+ if "CENTER_FRQ2=5775" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(4): " + str(sig))
+
+ sig = dev[1].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=80+80 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2b): " + str(sig))
+ if "CENTER_FRQ1=5210" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3b): " + str(sig))
+ if "CENTER_FRQ2=5775" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(4b): " + str(sig))
+
+ dev[0].mesh_group_remove()
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[0])
+ check_mesh_group_removed(dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_mesh_open_vht_160(dev, apdev):
+ """Open mesh network on VHT 160 MHz channel"""
+ try:
+ _test_mesh_open_vht_160(dev, apdev)
+ finally:
+ clear_reg_setting(dev)
+
+def _test_mesh_open_vht_160(dev, apdev):
+ check_mesh_support(dev[0])
+ subprocess.call(['iw', 'reg', 'set', 'ZA'])
+ for i in range(2):
+ for j in range(5):
+ ev = dev[i].wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=5)
+ if ev is None:
+ raise Exception("No regdom change event")
+ if "alpha2=ZA" in ev:
+ break
+
+ cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
+ reg = cmd.stdout.read()
+ found = False
+ for entry in reg.splitlines():
+ entry = entry.decode()
+ if "@ 160)" in entry and "DFS" not in entry:
+ found = True
+ break
+ if not found:
+ raise HwsimSkip("160 MHz channel without DFS not supported in regulatory information")
+
+ add_open_mesh_network(dev[i], freq="5520", chwidth=2)
+
+ check_mesh_joined_connected(dev, connectivity=True)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ sig = dev[0].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=160 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2): " + str(sig))
+ if "FREQUENCY=5520" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3): " + str(sig))
+
+ sig = dev[1].request("SIGNAL_POLL").splitlines()
+ if "WIDTH=160 MHz" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(2b): " + str(sig))
+ if "FREQUENCY=5520" not in sig:
+ raise Exception("Unexpected SIGNAL_POLL value(3b): " + str(sig))
+
+ dev[0].mesh_group_remove()
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[0])
+ check_mesh_group_removed(dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_wpas_mesh_password_mismatch(dev, apdev):
+ """Mesh network and one device with mismatching password"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+
+ dev[2].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[2])
+ dev[2].set_network_quoted(id, "psk", "wrong password")
+ dev[2].mesh_group_add(id)
+
+ # The two peers with matching password need to be able to connect
+ check_mesh_joined_connected(dev)
+
+ ev = dev[2].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=20)
+ if ev is None:
+ raise Exception("dev2 did not report auth failure (1)")
+ ev = dev[2].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=20)
+ if ev is None:
+ raise Exception("dev2 did not report auth failure (2)")
+ dev[2].dump_monitor()
+
+ count = 0
+ ev = dev[0].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=5)
+ if ev is None:
+ logger.info("dev0 did not report auth failure")
+ else:
+ if "addr=" + dev[2].own_addr() not in ev:
+ raise Exception("Unexpected peer address in dev0 event: " + ev)
+ count += 1
+ dev[0].dump_monitor()
+
+ ev = dev[1].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=5)
+ if ev is None:
+ logger.info("dev1 did not report auth failure")
+ else:
+ if "addr=" + dev[2].own_addr() not in ev:
+ raise Exception("Unexpected peer address in dev1 event: " + ev)
+ count += 1
+ dev[1].dump_monitor()
+
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+ for i in range(2):
+ try:
+ hwsim_utils.test_connectivity(dev[i], dev[2], timeout=1)
+ raise Exception("Data connectivity test passed unexpectedly")
+ except Exception as e:
+ if "data delivery failed" not in str(e):
+ raise
+
+ if count == 0:
+ raise Exception("Neither dev0 nor dev1 reported auth failure")
+
+@long_duration_test
+def test_wpas_mesh_password_mismatch_retry(dev, apdev):
+ """Mesh password mismatch and retry"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].set_network_quoted(id, "psk", "wrong password")
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined2(dev)
+
+ for i in range(4):
+ ev = dev[0].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=20)
+ if ev is None:
+ raise Exception("dev0 did not report auth failure (%d)" % i)
+ ev = dev[1].wait_event(["MESH-SAE-AUTH-FAILURE"], timeout=20)
+ if ev is None:
+ raise Exception("dev1 did not report auth failure (%d)" % i)
+
+ ev = dev[0].wait_event(["MESH-SAE-AUTH-BLOCKED"], timeout=10)
+ if ev is None:
+ raise Exception("dev0 did not report auth blocked")
+ ev = dev[1].wait_event(["MESH-SAE-AUTH-BLOCKED"], timeout=10)
+ if ev is None:
+ raise Exception("dev1 did not report auth blocked")
+
+def test_mesh_wpa_auth_init_oom(dev, apdev):
+ """Secure mesh network setup failing due to wpa_init() OOM"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ with alloc_fail(dev[0], 1, "wpa_init"):
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+ ev = dev[0].wait_event(["MESH-GROUP-STARTED"], timeout=0.2)
+ if ev is not None:
+ raise Exception("Unexpected mesh group start during OOM")
+
+def test_mesh_wpa_init_fail(dev, apdev):
+ """Secure mesh network setup local failure"""
+ check_mesh_support(dev[0], secure=True)
+ check_mesh_support(dev[1], secure=True)
+ check_mesh_support(dev[2], secure=True)
+ dev[0].request("SET sae_groups ")
+
+ with fail_test(dev[0], 1, "os_get_random;=__mesh_rsn_auth_init"):
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+
+ dev[0].dump_monitor()
+ with alloc_fail(dev[0], 1, "mesh_rsn_auth_init"):
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+ dev[0].dump_monitor()
+ with fail_test(dev[0], 1, "os_get_random;mesh_rsn_init_ampe_sta"):
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+
+ with fail_test(dev[0], 2, "=omac1_aes_vector;aes_siv_encrypt"):
+ id = add_mesh_secure_net(dev[2])
+ dev[0].mesh_group_add(id)
+ dev[2].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[2])
+ dev[2].mesh_group_add(id)
+ wait_fail_trigger(dev[0], "GET_FAIL")
+
+def test_wpas_mesh_reconnect(dev, apdev):
+ """Secure mesh network plink counting during reconnection"""
+ check_mesh_support(dev[0])
+ try:
+ _test_wpas_mesh_reconnect(dev)
+ finally:
+ dev[0].request("SET max_peer_links 99")
+
+def _test_wpas_mesh_reconnect(dev):
+ dev[0].request("SET max_peer_links 2")
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].set_network(id, "beacon_int", "100")
+ dev[0].mesh_group_add(id)
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+ check_mesh_joined_connected(dev)
+
+ for i in range(3):
+ # Drop incoming management frames to avoid handling link close
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[1])
+ dev[1].request("FLUSH")
+ dev[0].request("SET ext_mgmt_frame_handling 0")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+ check_mesh_group_added(dev[1])
+ check_mesh_peer_connected(dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def test_wpas_mesh_gate_forwarding(dev, apdev, p):
+ """Mesh forwards traffic to unknown sta to mesh gates"""
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ addr2 = dev[2].own_addr()
+ external_sta = '02:11:22:33:44:55'
+
+ # start 3 node connected mesh
+ check_mesh_support(dev[0])
+ for i in range(3):
+ add_open_mesh_network(dev[i])
+ check_mesh_group_added(dev[i])
+ for i in range(3):
+ check_mesh_peer_connected(dev[i])
+
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+ hwsim_utils.test_connectivity(dev[0], dev[2])
+
+ # dev0 and dev1 are mesh gates
+ subprocess.call(['iw', 'dev', dev[0].ifname, 'set', 'mesh_param',
+ 'mesh_gate_announcements=1'])
+ subprocess.call(['iw', 'dev', dev[1].ifname, 'set', 'mesh_param',
+ 'mesh_gate_announcements=1'])
+
+ # wait for gate announcement frames
+ time.sleep(1)
+
+ # data frame from dev2 -> external sta should be sent to both gates
+ dev[2].request("DATA_TEST_CONFIG 1")
+ dev[2].request("DATA_TEST_TX {} {} 0".format(external_sta, addr2))
+ dev[2].request("DATA_TEST_CONFIG 0")
+
+ capfile = os.path.join(p['logdir'], "hwsim0.pcapng")
+ filt = "wlan.sa==%s && wlan_mgt.fixed.mesh_addr5==%s" % (addr2,
+ external_sta)
+ time.sleep(4)
+ for i in range(5):
+ da = run_tshark(capfile, filt, ["wlan.da"])
+ if addr0 in da and addr1 in da:
+ logger.debug("Frames seen in tshark iteration %d" % i)
+ break
+ time.sleep(0.5)
+
+ if addr0 not in da and addr1 not in da:
+ filt = "wlan.sa==%s" % addr2
+ mesh = run_tshark(capfile, filt, ["wlan.mesh.control_field"])
+ if "1" not in mesh:
+ # Wireshark regression in mesh control field parsing:
+ # https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=15521
+ raise HwsimSkip("tshark bug 15521")
+ if addr0 not in da:
+ raise Exception("Frame to gate %s not observed" % addr0)
+ if addr1 not in da:
+ raise Exception("Frame to gate %s not observed" % addr1)
+
+def test_wpas_mesh_pmksa_caching(dev, apdev):
+ """Secure mesh network and PMKSA caching"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev)
+
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ pmksa0 = dev[0].get_pmksa(addr1)
+ pmksa1 = dev[1].get_pmksa(addr0)
+ if pmksa0 is None or pmksa1 is None:
+ raise Exception("No PMKSA cache entry created")
+ if pmksa0['pmkid'] != pmksa1['pmkid']:
+ raise Exception("PMKID mismatch in PMKSA cache entries")
+
+ if "OK" not in dev[0].request("MESH_PEER_REMOVE " + addr1):
+ raise Exception("Failed to remove peer")
+ pmksa0b = dev[0].get_pmksa(addr1)
+ if pmksa0b is None:
+ raise Exception("PMKSA cache entry not maintained")
+ time.sleep(0.1)
+
+ if "FAIL" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD unexpectedly succeeded in no_auto_peer=0 case")
+
+def test_wpas_mesh_pmksa_caching2(dev, apdev):
+ """Secure mesh network and PMKSA caching with no_auto_peer=1"""
+ check_mesh_support(dev[0], secure=True)
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].set_network(id, "no_auto_peer", "1")
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].set_network(id, "no_auto_peer", "1")
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined2(dev)
+
+ # Check for peer connected
+ ev = dev[0].wait_event(["will not initiate new peer link"], timeout=10)
+ if ev is None:
+ raise Exception("Missing no-initiate message")
+ if "OK" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD failed")
+ check_mesh_connected2(dev)
+
+ pmksa0 = dev[0].get_pmksa(addr1)
+ pmksa1 = dev[1].get_pmksa(addr0)
+ if pmksa0 is None or pmksa1 is None:
+ raise Exception("No PMKSA cache entry created")
+ if pmksa0['pmkid'] != pmksa1['pmkid']:
+ raise Exception("PMKID mismatch in PMKSA cache entries")
+
+ if "OK" not in dev[0].request("MESH_PEER_REMOVE " + addr1):
+ raise Exception("Failed to remove peer")
+ pmksa0b = dev[0].get_pmksa(addr1)
+ if pmksa0b is None:
+ raise Exception("PMKSA cache entry not maintained")
+
+ ev = dev[0].wait_event(["will not initiate new peer link"], timeout=10)
+ if ev is None:
+ raise Exception("Missing no-initiate message (2)")
+ if "OK" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD failed (2)")
+ check_mesh_connected2(dev)
+
+ pmksa0c = dev[0].get_pmksa(addr1)
+ pmksa1c = dev[1].get_pmksa(addr0)
+ if pmksa0c is None or pmksa1c is None:
+ raise Exception("No PMKSA cache entry created (2)")
+ if pmksa0c['pmkid'] != pmksa1c['pmkid']:
+ raise Exception("PMKID mismatch in PMKSA cache entries")
+ if pmksa0['pmkid'] != pmksa0c['pmkid']:
+ raise Exception("PMKID changed")
+
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def test_wpas_mesh_pmksa_caching_no_match(dev, apdev):
+ """Secure mesh network and PMKSA caching with no PMKID match"""
+ check_mesh_support(dev[0], secure=True)
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].set_network(id, "no_auto_peer", "1")
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].set_network(id, "no_auto_peer", "1")
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined2(dev)
+
+ # Check for peer connected
+ ev = dev[0].wait_event(["will not initiate new peer link"], timeout=10)
+ if ev is None:
+ raise Exception("Missing no-initiate message")
+ if "OK" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD failed")
+ check_mesh_connected2(dev)
+
+ pmksa0 = dev[0].get_pmksa(addr1)
+ pmksa1 = dev[1].get_pmksa(addr0)
+ if pmksa0 is None or pmksa1 is None:
+ raise Exception("No PMKSA cache entry created")
+ if pmksa0['pmkid'] != pmksa1['pmkid']:
+ raise Exception("PMKID mismatch in PMKSA cache entries")
+
+ if "OK" not in dev[0].request("MESH_PEER_REMOVE " + addr1):
+ raise Exception("Failed to remove peer")
+
+ if "OK" not in dev[1].request("PMKSA_FLUSH"):
+ raise Exception("Failed to flush PMKSA cache")
+
+ ev = dev[0].wait_event(["will not initiate new peer link"], timeout=10)
+ if ev is None:
+ raise Exception("Missing no-initiate message (2)")
+ if "OK" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD failed (2)")
+ check_mesh_connected2(dev)
+
+ pmksa0c = dev[0].get_pmksa(addr1)
+ pmksa1c = dev[1].get_pmksa(addr0)
+ if pmksa0c is None or pmksa1c is None:
+ raise Exception("No PMKSA cache entry created (2)")
+ if pmksa0c['pmkid'] != pmksa1c['pmkid']:
+ raise Exception("PMKID mismatch in PMKSA cache entries")
+ if pmksa0['pmkid'] == pmksa0c['pmkid']:
+ raise Exception("PMKID did not change")
+
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+def test_mesh_pmksa_caching_oom(dev, apdev):
+ """Secure mesh network and PMKSA caching failing due to OOM"""
+ check_mesh_support(dev[0], secure=True)
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].set_network(id, "no_auto_peer", "1")
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].set_network(id, "no_auto_peer", "1")
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined2(dev)
+
+ # Check for peer connected
+ ev = dev[0].wait_event(["will not initiate new peer link"], timeout=10)
+ if ev is None:
+ raise Exception("Missing no-initiate message")
+ if "OK" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD failed")
+ check_mesh_connected2(dev)
+
+ if "OK" not in dev[0].request("MESH_PEER_REMOVE " + addr1):
+ raise Exception("Failed to remove peer")
+ pmksa0b = dev[0].get_pmksa(addr1)
+ if pmksa0b is None:
+ raise Exception("PMKSA cache entry not maintained")
+
+ ev = dev[0].wait_event(["will not initiate new peer link"], timeout=10)
+ if ev is None:
+ raise Exception("Missing no-initiate message (2)")
+
+ with alloc_fail(dev[0], 1, "wpa_auth_sta_init;mesh_rsn_auth_sae_sta"):
+ if "OK" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD failed (2)")
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+
+def test_wpas_mesh_pmksa_caching_ext(dev, apdev):
+ """Secure mesh network and PMKSA caching and external storage"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined_connected(dev)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ pmksa0 = dev[0].get_pmksa(addr1)
+ pmksa1 = dev[1].get_pmksa(addr0)
+ if pmksa0 is None or pmksa1 is None:
+ raise Exception("No PMKSA cache entry created")
+ if pmksa0['pmkid'] != pmksa1['pmkid']:
+ raise Exception("PMKID mismatch in PMKSA cache entries")
+
+ res1 = dev[1].request("MESH_PMKSA_GET any")
+ res2 = dev[1].request("MESH_PMKSA_GET " + addr0)
+ logger.info("MESH_PMKSA_GET: " + res1)
+ if "UNKNOWN COMMAND" in res1:
+ raise HwsimSkip("MESH_PMKSA_GET not supported in the build")
+ logger.info("MESH_PMKSA_GET: " + res2)
+ if pmksa0['pmkid'] not in res1:
+ raise Exception("PMKID not included in PMKSA entry")
+ if res1 != res2:
+ raise Exception("Unexpected difference in MESH_PMKSA_GET output")
+
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[1])
+ check_mesh_peer_disconnected(dev[0])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ res = dev[1].get_pmksa(addr0)
+ if res is not None:
+ raise Exception("Unexpected PMKSA cache entry remaining")
+
+ time.sleep(0.1)
+ if "OK" not in dev[1].request("MESH_PMKSA_ADD " + res2):
+ raise Exception("MESH_PMKSA_ADD failed")
+ dev[1].mesh_group_add(id)
+ check_mesh_group_added(dev[1])
+ check_mesh_peer_connected(dev[1])
+ check_mesh_peer_connected(dev[0])
+ time.sleep(0.1)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ pmksa1b = dev[1].get_pmksa(addr0)
+ if pmksa1b is None:
+ raise Exception("No PMKSA cache entry created after external storage restore")
+ if pmksa1['pmkid'] != pmksa1b['pmkid']:
+ raise Exception("PMKID mismatch in PMKSA cache entries after external storage restore")
+
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+
+ res = dev[1].request("MESH_PMKSA_GET foo")
+ if "FAIL" not in res:
+ raise Exception("Invalid MESH_PMKSA_GET accepted")
+
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[1])
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[1].request("REMOVE_NETWORK all")
+ res = dev[1].request("MESH_PMKSA_GET any")
+ if "FAIL" not in res:
+ raise Exception("MESH_PMKSA_GET accepted when not in mesh")
+
+ tests = ["foo",
+ "02:02:02:02:02:02",
+ "02:02:02:02:02:02 q",
+ "02:02:02:02:02:02 c3d51a7ccfca0c6d5287291a7169d79b",
+ "02:02:02:02:02:02 c3d51a7ccfca0c6d5287291a7169d79b q",
+ "02:02:02:02:02:02 c3d51a7ccfca0c6d5287291a7169d79b 1bed4fa22ece7997ca1bdc8b829019fe63acac91cba3405522c24c91f7cfb49f",
+ "02:02:02:02:02:02 c3d51a7ccfca0c6d5287291a7169d79b 1bed4fa22ece7997ca1bdc8b829019fe63acac91cba3405522c24c91f7cfb49f q"]
+ for t in tests:
+ if "FAIL" not in dev[1].request("MESH_PMKSA_ADD " + t):
+ raise Exception("Invalid MESH_PMKSA_ADD accepted")
+
+def test_mesh_oom(dev, apdev):
+ """Mesh network setup failing due to OOM"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+
+ with alloc_fail(dev[0], 1, "mesh_config_create"):
+ add_open_mesh_network(dev[0])
+ ev = dev[0].wait_event(["Failed to init mesh"])
+ if ev is None:
+ raise Exception("Init failure not reported")
+
+ with alloc_fail(dev[0], 2, "=wpa_supplicant_mesh_init"):
+ add_open_mesh_network(dev[0], basic_rates="60 120 240")
+ ev = dev[0].wait_event(["Failed to init mesh"])
+ if ev is None:
+ raise Exception("Init failure not reported")
+
+ for i in range(1, 66):
+ dev[0].dump_monitor()
+ logger.info("Test instance %d" % i)
+ try:
+ with alloc_fail(dev[0], i, "wpa_supplicant_mesh_init"):
+ add_open_mesh_network(dev[0])
+ wait_fail_trigger(dev[0], "GET_ALLOC_FAIL")
+ ev = dev[0].wait_event(["Failed to init mesh",
+ "MESH-GROUP-STARTED"])
+ if ev is None:
+ raise Exception("Init failure not reported")
+ except Exception as e:
+ if i < 15:
+ raise
+ logger.info("Ignore no-oom for i=%d" % i)
+
+ with alloc_fail(dev[0], 2, "=wpa_supplicant_mesh_init"):
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+ ev = dev[0].wait_event(["Failed to init mesh"])
+ if ev is None:
+ raise Exception("Init failure not reported")
+
+def test_mesh_add_interface_oom(dev):
+ """wpa_supplicant mesh with dynamic interface addition failing"""
+ check_mesh_support(dev[0])
+ for i in range(1, 3):
+ mesh = None
+ try:
+ with alloc_fail(dev[0], i, "wpas_mesh_add_interface"):
+ mesh = dev[0].request("MESH_INTERFACE_ADD").strip()
+ finally:
+ if mesh and mesh != "FAIL":
+ dev[0].request("MESH_GROUP_REMOVE " + mesh)
+
+def test_mesh_scan_oom(dev):
+ """wpa_supplicant mesh scan results and OOM"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0])
+ check_mesh_group_added(dev[0])
+ for i in range(5):
+ dev[1].scan(freq="2412")
+ res = dev[1].request("SCAN_RESULTS")
+ if "[MESH]" in res:
+ break
+ for r in res.splitlines():
+ if "[MESH]" in r:
+ break
+ bssid = r.split('\t')[0]
+
+ bss = dev[1].get_bss(bssid)
+ if bss is None:
+ raise Exception("Could not get BSS entry for mesh")
+
+ for i in range(1, 3):
+ with alloc_fail(dev[1], i, "mesh_attr_text"):
+ bss = dev[1].get_bss(bssid)
+ if bss and "mesh_id" in bss:
+ raise Exception("Unexpected BSS result during OOM")
+
+def test_mesh_drv_fail(dev, apdev):
+ """Mesh network setup failing due to driver command failure"""
+ check_mesh_support(dev[0], secure=True)
+ dev[0].request("SET sae_groups ")
+
+ with fail_test(dev[0], 1, "nl80211_join_mesh"):
+ add_open_mesh_network(dev[0])
+ ev = dev[0].wait_event(["mesh join error"])
+ if ev is None:
+ raise Exception("Join failure not reported")
+
+ dev[0].dump_monitor()
+ with fail_test(dev[0], 1, "wpa_driver_nl80211_if_add"):
+ if "FAIL" not in dev[0].request("MESH_INTERFACE_ADD").strip():
+ raise Exception("Interface added unexpectedly")
+
+ dev[0].dump_monitor()
+ with fail_test(dev[0], 1, "wpa_driver_nl80211_init_mesh"):
+ add_open_mesh_network(dev[0])
+ ev = dev[0].wait_event(["Could not join mesh"])
+ if ev is None:
+ raise Exception("Join failure not reported")
+
+def test_mesh_sae_groups_invalid(dev, apdev):
+ """Mesh with invalid SAE group configuration"""
+ check_mesh_support(dev[0], secure=True)
+
+ dev[0].request("SET sae_groups 26")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups 123 122 121")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined2(dev)
+
+ ev = dev[0].wait_event(["new peer notification"], timeout=10)
+ if ev is None:
+ raise Exception("dev[0] did not see peer")
+ ev = dev[1].wait_event(["new peer notification"], timeout=10)
+ if ev is None:
+ raise Exception("dev[1] did not see peer")
+
+ ev = dev[0].wait_event(["MESH-PEER-CONNECTED"], timeout=0.1)
+ if ev is not None:
+ raise Exception("Unexpected connection(0)")
+
+ ev = dev[1].wait_event(["MESH-PEER-CONNECTED"], timeout=0.01)
+ if ev is not None:
+ raise Exception("Unexpected connection(1)")
+
+ # Additional coverage in mesh_rsn_sae_group() with non-zero
+ # wpa_s->mesh_rsn->sae_group_index.
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[2].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[2])
+ dev[2].mesh_group_add(id)
+ check_mesh_group_added(dev[2])
+ check_mesh_peer_connected(dev[0])
+ check_mesh_peer_connected(dev[2])
+ ev = dev[1].wait_event(["new peer notification"], timeout=10)
+ if ev is None:
+ raise Exception("dev[1] did not see peer(2)")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[2].dump_monitor()
+
+ dev[0].request("SET sae_groups ")
+ dev[1].request("SET sae_groups ")
+ dev[2].request("SET sae_groups ")
+
+def test_mesh_sae_failure(dev, apdev):
+ """Mesh and local SAE failures"""
+ check_mesh_support(dev[0], secure=True)
+
+ dev[0].request("SET sae_groups ")
+ dev[1].request("SET sae_groups ")
+
+ funcs = [(1, "=mesh_rsn_auth_sae_sta", True),
+ (1, "mesh_rsn_build_sae_commit;mesh_rsn_auth_sae_sta", False),
+ (1, "auth_sae_init_committed;mesh_rsn_auth_sae_sta", True),
+ (1, "=mesh_rsn_protect_frame", True),
+ (2, "=mesh_rsn_protect_frame", True),
+ (1, "aes_siv_encrypt;mesh_rsn_protect_frame", True),
+ (1, "=mesh_rsn_process_ampe", True),
+ (1, "aes_siv_decrypt;mesh_rsn_process_ampe", True)]
+ for count, func, success in funcs:
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ with alloc_fail(dev[1], count, func):
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+ check_mesh_joined2(dev)
+ if success:
+ # retry is expected to work
+ check_mesh_connected2(dev)
+ else:
+ wait_fail_trigger(dev[1], "GET_ALLOC_FAIL")
+ dev[0].mesh_group_remove()
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[0])
+ check_mesh_group_removed(dev[1])
+
+def test_mesh_failure(dev, apdev):
+ """Mesh and local failures"""
+ check_mesh_support(dev[0])
+
+ funcs = [(1, "ap_sta_add;mesh_mpm_add_peer", True),
+ (1, "wpabuf_alloc;mesh_mpm_send_plink_action", True)]
+ for count, func, success in funcs:
+ add_open_mesh_network(dev[0])
+
+ with alloc_fail(dev[1], count, func):
+ add_open_mesh_network(dev[1])
+ check_mesh_joined2(dev)
+ if success:
+ # retry is expected to work
+ check_mesh_connected2(dev)
+ else:
+ wait_fail_trigger(dev[1], "GET_ALLOC_FAIL")
+ dev[0].mesh_group_remove()
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[0])
+ check_mesh_group_removed(dev[1])
+
+ funcs = [(1, "mesh_mpm_init_link", True)]
+ for count, func, success in funcs:
+ add_open_mesh_network(dev[0])
+
+ with fail_test(dev[1], count, func):
+ add_open_mesh_network(dev[1])
+ check_mesh_joined2(dev)
+ if success:
+ # retry is expected to work
+ check_mesh_connected2(dev)
+ else:
+ wait_fail_trigger(dev[1], "GET_FAIL")
+ dev[0].mesh_group_remove()
+ dev[1].mesh_group_remove()
+ check_mesh_group_removed(dev[0])
+ check_mesh_group_removed(dev[1])
+
+def test_mesh_invalid_frequency(dev, apdev):
+ """Mesh and invalid frequency configuration"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0], freq=None)
+ ev = dev[0].wait_event(["MESH-GROUP-STARTED",
+ "Could not join mesh"])
+ if ev is None or "Could not join mesh" not in ev:
+ raise Exception("Mesh join failure not reported")
+ dev[0].request("REMOVE_NETWORK all")
+
+ add_open_mesh_network(dev[0], freq="2413")
+ ev = dev[0].wait_event(["MESH-GROUP-STARTED",
+ "Could not join mesh"])
+ if ev is None or "Could not join mesh" not in ev:
+ raise Exception("Mesh join failure not reported")
+
+def test_mesh_default_beacon_int(dev, apdev):
+ """Mesh and default beacon interval"""
+ check_mesh_support(dev[0])
+ try:
+ dev[0].request("SET beacon_int 200")
+ add_open_mesh_network(dev[0])
+ check_mesh_group_added(dev[0])
+ finally:
+ dev[0].request("SET beacon_int 0")
+
+def test_mesh_scan_parse_error(dev, apdev):
+ """Mesh scan element parse error"""
+ check_mesh_support(dev[0])
+ params = {"ssid": "open",
+ "beacon_int": "2000"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ bssid = apdev[0]['bssid']
+ hapd.set('vendor_elements', 'dd0201')
+ for i in range(10):
+ dev[0].scan(freq=2412)
+ if bssid in dev[0].request("SCAN_RESULTS"):
+ break
+ # This will fail in IE parsing due to the truncated IE in the Probe
+ # Response frame.
+ bss = dev[0].request("BSS " + bssid)
+
+def test_mesh_missing_mic(dev, apdev):
+ """Secure mesh network and missing MIC"""
+ check_mesh_support(dev[0], secure=True)
+
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined2(dev)
+
+ count = 0
+ remove_mic = True
+ while True:
+ count += 1
+ if count > 15:
+ raise Exception("Did not see Action frames")
+ rx_msg = dev[0].mgmt_rx()
+ if rx_msg is None:
+ ev = dev[1].wait_event(["MESH-PEER-CONNECTED"], timeout=0.01)
+ if ev:
+ break
+ raise Exception("MGMT-RX timeout")
+ if rx_msg['subtype'] == 13:
+ payload = rx_msg['payload']
+ frame = rx_msg['frame']
+ (categ, action) = struct.unpack('BB', payload[0:2])
+ if categ == 15 and action == 1 and remove_mic:
+ # Mesh Peering Open
+ pos = frame.find(b'\x8c\x10')
+ if not pos:
+ raise Exception("Could not find MIC element")
+ logger.info("Found MIC at %d" % pos)
+ # Remove MIC
+ rx_msg['frame'] = frame[0:pos]
+ remove_mic = False
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
+ rx_msg['freq'], rx_msg['datarate'], rx_msg['ssi_signal'], binascii.hexlify(rx_msg['frame']).decode())):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = dev[1].wait_event(["MESH-PEER-CONNECTED"], timeout=0.01)
+ if ev:
+ break
+
+def test_mesh_pmkid_mismatch(dev, apdev):
+ """Secure mesh network and PMKID mismatch"""
+ check_mesh_support(dev[0], secure=True)
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ dev[0].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].set_network(id, "no_auto_peer", "1")
+ dev[0].mesh_group_add(id)
+
+ dev[1].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].set_network(id, "no_auto_peer", "1")
+ dev[1].mesh_group_add(id)
+
+ check_mesh_joined2(dev)
+
+ # Check for peer connected
+ ev = dev[0].wait_event(["will not initiate new peer link"], timeout=10)
+ if ev is None:
+ raise Exception("Missing no-initiate message")
+ if "OK" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD failed")
+ check_mesh_connected2(dev)
+
+ if "OK" not in dev[0].request("MESH_PEER_REMOVE " + addr1):
+ raise Exception("Failed to remove peer")
+
+ ev = dev[0].wait_event(["will not initiate new peer link"], timeout=10)
+ if ev is None:
+ raise Exception("Missing no-initiate message (2)")
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ if "OK" not in dev[0].request("MESH_PEER_ADD " + addr1):
+ raise Exception("MESH_PEER_ADD failed (2)")
+
+ count = 0
+ break_pmkid = True
+ while True:
+ count += 1
+ if count > 50:
+ raise Exception("Did not see Action frames")
+ rx_msg = dev[0].mgmt_rx()
+ if rx_msg is None:
+ ev = dev[1].wait_event(["MESH-PEER-CONNECTED"], timeout=0.1)
+ if ev:
+ break
+ raise Exception("MGMT-RX timeout")
+ if rx_msg['subtype'] == 13:
+ payload = rx_msg['payload']
+ frame = rx_msg['frame']
+ (categ, action) = struct.unpack('BB', payload[0:2])
+ if categ == 15 and action == 1 and break_pmkid:
+ # Mesh Peering Open
+ pos = frame.find(b'\x75\x14')
+ if not pos:
+ raise Exception("Could not find Mesh Peering Management element")
+ logger.info("Found Mesh Peering Management element at %d" % pos)
+ # Break PMKID to hit "Mesh RSN: Invalid PMKID (Chosen PMK did
+ # not match calculated PMKID)"
+ rx_msg['frame'] = frame[0:pos + 6] + b'\x00\x00\x00\x00' + frame[pos + 10:]
+ break_pmkid = False
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
+ rx_msg['freq'], rx_msg['datarate'], rx_msg['ssi_signal'], binascii.hexlify(rx_msg['frame']).decode())):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = dev[1].wait_event(["MESH-PEER-CONNECTED"], timeout=0.01)
+ if ev:
+ break
+
+def test_mesh_peering_proto(dev, apdev):
+ """Mesh peering management protocol testing"""
+ check_mesh_support(dev[0])
+
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ add_open_mesh_network(dev[0], beacon_int=160)
+ add_open_mesh_network(dev[1], beacon_int=160)
+
+ count = 0
+ test = 1
+ while True:
+ count += 1
+ if count > 50:
+ raise Exception("Did not see Action frames")
+ rx_msg = dev[0].mgmt_rx()
+ if rx_msg is None:
+ ev = dev[1].wait_event(["MESH-PEER-CONNECTED"], timeout=0.01)
+ if ev:
+ break
+ raise Exception("MGMT-RX timeout")
+ if rx_msg['subtype'] == 13:
+ payload = rx_msg['payload']
+ frame = rx_msg['frame']
+ (categ, action) = struct.unpack('BB', payload[0:2])
+ if categ == 15 and action == 1 and test == 1:
+ # Mesh Peering Open
+ pos = frame.find(b'\x75\x04')
+ if not pos:
+ raise Exception("Could not find Mesh Peering Management element")
+ logger.info("Found Mesh Peering Management element at %d" % pos)
+ # Remove the element to hit
+ # "MPM: No Mesh Peering Management element"
+ rx_msg['frame'] = frame[0:pos]
+ test += 1
+ elif categ == 15 and action == 1 and test == 2:
+ # Mesh Peering Open
+ pos = frame.find(b'\x72\x0e')
+ if not pos:
+ raise Exception("Could not find Mesh ID element")
+ logger.info("Found Mesh ID element at %d" % pos)
+ # Remove the element to hit
+ # "MPM: No Mesh ID or Mesh Configuration element"
+ rx_msg['frame'] = frame[0:pos] + frame[pos + 16:]
+ test += 1
+ elif categ == 15 and action == 1 and test == 3:
+ # Mesh Peering Open
+ pos = frame.find(b'\x72\x0e')
+ if not pos:
+ raise Exception("Could not find Mesh ID element")
+ logger.info("Found Mesh ID element at %d" % pos)
+ # Replace Mesh ID to hit "MPM: Mesh ID or Mesh Configuration
+ # element do not match local MBSS"
+ rx_msg['frame'] = frame[0:pos] + b'\x72\x0etest-test-test' + frame[pos + 16:]
+ test += 1
+ elif categ == 15 and action == 1 and test == 4:
+ # Mesh Peering Open
+ # Remove IEs to hit
+ # "MPM: Ignore too short action frame 1 ie_len 0"
+ rx_msg['frame'] = frame[0:26]
+ test += 1
+ elif categ == 15 and action == 1 and test == 5:
+ # Mesh Peering Open
+ # Truncate IEs to hit
+ # "MPM: Failed to parse PLINK IEs"
+ rx_msg['frame'] = frame[0:30]
+ test += 1
+ elif categ == 15 and action == 1 and test == 6:
+ # Mesh Peering Open
+ pos = frame.find(b'\x75\x04')
+ if not pos:
+ raise Exception("Could not find Mesh Peering Management element")
+ logger.info("Found Mesh Peering Management element at %d" % pos)
+ # Truncate the element to hit
+ # "MPM: Invalid peer mgmt ie" and
+ # "MPM: Mesh parsing rejected frame"
+ rx_msg['frame'] = frame[0:pos] + b'\x75\x00\x00\x00' + frame[pos + 6:]
+ test += 1
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
+ rx_msg['freq'], rx_msg['datarate'], rx_msg['ssi_signal'], binascii.hexlify(rx_msg['frame']).decode())):
+ raise Exception("MGMT_RX_PROCESS failed")
+ ev = dev[1].wait_event(["MESH-PEER-CONNECTED"], timeout=0.01)
+ if ev:
+ break
+
+ if test != 7:
+ raise Exception("Not all test frames completed")
+
+def test_mesh_mpm_init_proto(dev, apdev):
+ """Mesh peering management protocol testing for peer addition"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0])
+ check_mesh_group_added(dev[0])
+ dev[0].dump_monitor()
+
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+
+ addr = "020000000100"
+ hdr = "d000ac00020000000000" + addr + addr + "1000"
+ fixed = "0f010000"
+ supp_rates = "010802040b168c129824"
+ ext_supp_rates = "3204b048606c"
+ mesh_id = "720e777061732d6d6573682d6f70656e"
+ mesh_conf = "710701010001000009"
+ mpm = "75040000079d"
+ ht_capab = "2d1a7c001bffff000000000000000000000100000000000000000000"
+ ht_oper = "3d160b000000000000000000000000000000000000000000"
+
+ dev[0].request("NOTE no supported rates")
+ frame = hdr + fixed + ext_supp_rates + mesh_id + mesh_conf + mpm + ht_capab + ht_oper
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ dev[0].request("NOTE Invalid supported rates element length 33+0")
+ long_supp_rates = "012100112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00"
+ frame = hdr + fixed + long_supp_rates + mesh_id + mesh_conf + mpm + ht_capab + ht_oper
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ dev[0].request("NOTE Too short mesh config")
+ short_mesh_conf = "710401010001"
+ frame = hdr + fixed + supp_rates + mesh_id + short_mesh_conf + mpm + ht_capab + ht_oper
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ dev[0].request("NOTE Add STA failure")
+ frame = hdr + fixed + supp_rates + ext_supp_rates + mesh_id + mesh_conf + mpm + ht_capab + ht_oper
+ with fail_test(dev[0], 1, "wpa_driver_nl80211_sta_add"):
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ dev[0].request("NOTE Send Action failure")
+ with fail_test(dev[0], 1, "driver_nl80211_send_action"):
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ dev[0].request("NOTE Set STA failure")
+ addr = "020000000101"
+ hdr = "d000ac00020000000000" + addr + addr + "1000"
+ frame = hdr + fixed + supp_rates + ext_supp_rates + mesh_id + mesh_conf + mpm + ht_capab + ht_oper
+ with fail_test(dev[0], 2, "wpa_driver_nl80211_sta_add"):
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ dev[0].request("NOTE ap_sta_add OOM")
+ addr = "020000000102"
+ hdr = "d000ac00020000000000" + addr + addr + "1000"
+ frame = hdr + fixed + supp_rates + ext_supp_rates + mesh_id + mesh_conf + mpm + ht_capab + ht_oper
+ with alloc_fail(dev[0], 1, "ap_sta_add"):
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ dev[0].request("NOTE hostapd_get_aid() failure")
+ addr = "020000000103"
+ hdr = "d000ac00020000000000" + addr + addr + "1000"
+ frame = hdr + fixed + supp_rates + ext_supp_rates + mesh_id + mesh_conf + mpm + ht_capab + ht_oper
+ with fail_test(dev[0], 1, "hostapd_get_aid"):
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ if "OK" not in dev[0].request("MESH_PEER_REMOVE 02:00:00:00:01:00"):
+ raise Exception("Failed to remove peer")
+ if "FAIL" not in dev[0].request("MESH_PEER_REMOVE 02:00:00:00:01:02"):
+ raise Exception("Unexpected MESH_PEER_REMOVE success")
+ if "FAIL" not in dev[1].request("MESH_PEER_REMOVE 02:00:00:00:01:02"):
+ raise Exception("Unexpected MESH_PEER_REMOVE success(2)")
+ if "FAIL" not in dev[1].request("MESH_PEER_ADD 02:00:00:00:01:02"):
+ raise Exception("Unexpected MESH_PEER_ADD success")
+
+def test_mesh_holding(dev, apdev):
+ """Mesh MPM FSM and HOLDING state event OPN_ACPT"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0])
+ add_open_mesh_network(dev[1])
+ check_mesh_joined_connected(dev)
+
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ if "OK" not in dev[0].request("MESH_PEER_REMOVE " + addr1):
+ raise Exception("Failed to remove peer")
+
+ rx_msg = dev[0].mgmt_rx()
+ if rx_msg is None:
+ raise Exception("MGMT-RX timeout")
+ if rx_msg['subtype'] != 13:
+ raise Exception("Unexpected management frame")
+ payload = rx_msg['payload']
+ (categ, action) = struct.unpack('BB', payload[0:2])
+ if categ != 0x0f or action != 0x03:
+ raise Exception("Did not see Mesh Peering Close")
+
+ peer_lid = binascii.hexlify(payload[-6:-4]).decode()
+ my_lid = binascii.hexlify(payload[-4:-2]).decode()
+
+ # Drop Mesh Peering Close and instead, process an unexpected Mesh Peering
+ # Open to trigger transmission of another Mesh Peering Close in the HOLDING
+ # state based on an OPN_ACPT event.
+
+ dst = addr0.replace(':', '')
+ src = addr1.replace(':', '')
+ hdr = "d000ac00" + dst + src + src + "1000"
+ fixed = "0f010000"
+ supp_rates = "010802040b168c129824"
+ ext_supp_rates = "3204b048606c"
+ mesh_id = "720e777061732d6d6573682d6f70656e"
+ mesh_conf = "710701010001000009"
+ mpm = "7504" + my_lid + peer_lid
+ ht_capab = "2d1a7c001bffff000000000000000000000100000000000000000000"
+ ht_oper = "3d160b000000000000000000000000000000000000000000"
+
+ frame = hdr + fixed + supp_rates + ext_supp_rates + mesh_id + mesh_conf + mpm + ht_capab + ht_oper
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=%s" % frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+ time.sleep(0.1)
+
+def test_mesh_cnf_rcvd_event_cls_acpt(dev, apdev):
+ """Mesh peering management protocol testing - CLS_ACPT event in CNF_RCVD"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0])
+ check_mesh_group_added(dev[0])
+ dev[0].dump_monitor()
+
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ add_open_mesh_network(dev[1])
+ check_mesh_group_added(dev[1])
+
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+
+ rx_msg = dev[0].mgmt_rx()
+ # Drop Mesh Peering Open
+
+ rx_msg = dev[0].mgmt_rx()
+ # Allow Mesh Peering Confirm to go through
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq={} datarate={} ssi_signal={} frame={}".format(
+ rx_msg['freq'], rx_msg['datarate'], rx_msg['ssi_signal'], binascii.hexlify(rx_msg['frame']).decode())):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ payload = rx_msg['payload']
+ peer_lid = binascii.hexlify(payload[51:53]).decode()
+ my_lid = binascii.hexlify(payload[53:55]).decode()
+
+ dst = addr0.replace(':', '')
+ src = addr1.replace(':', '')
+ hdr = "d000ac00" + dst + src + src + "1000"
+ fixed = "0f03"
+ mesh_id = "720e777061732d6d6573682d6f70656e"
+ mpm = "75080000" + peer_lid + my_lid + "3700"
+ frame = hdr + fixed + mesh_id + mpm
+
+ # Inject Mesh Peering Close to hit "state CNF_RCVD event CLS_ACPT" to
+ # HOLDING transition.
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+def test_mesh_opn_snt_event_cls_acpt(dev, apdev):
+ """Mesh peering management protocol testing - CLS_ACPT event in OPN_SNT"""
+ check_mesh_support(dev[0])
+ add_open_mesh_network(dev[0])
+ check_mesh_group_added(dev[0])
+ dev[0].dump_monitor()
+
+ dev[0].request("SET ext_mgmt_frame_handling 1")
+ add_open_mesh_network(dev[1])
+ check_mesh_group_added(dev[1])
+
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+
+ rx_msg = dev[0].mgmt_rx()
+ # Drop Mesh Peering Open
+
+ rx_msg = dev[0].mgmt_rx()
+ # Drop Mesh Peering Confirm
+
+ payload = rx_msg['payload']
+ peer_lid = "0000"
+ my_lid = binascii.hexlify(payload[53:55]).decode()
+
+ dst = addr0.replace(':', '')
+ src = addr1.replace(':', '')
+ hdr = "d000ac00" + dst + src + src + "1000"
+ fixed = "0f03"
+ mesh_id = "720e777061732d6d6573682d6f70656e"
+ mpm = "75080000" + peer_lid + my_lid + "3700"
+ frame = hdr + fixed + mesh_id + mpm
+
+ # Inject Mesh Peering Close to hit "state OPN_SNTevent CLS_ACPT" to
+ # HOLDING transition.
+ if "OK" not in dev[0].request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + frame):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+def test_mesh_select_network(dev):
+ """Mesh network and SELECT_NETWORK"""
+ check_mesh_support(dev[0])
+ id0 = add_open_mesh_network(dev[0], start=False)
+ id1 = add_open_mesh_network(dev[1], start=False)
+ dev[0].select_network(id0)
+ dev[1].select_network(id1)
+ check_mesh_joined_connected(dev, connectivity=True)
+
+def test_mesh_forwarding(dev):
+ """Mesh with two stations that can't reach each other directly"""
+ try:
+ set_group_map(dev[0], 1)
+ set_group_map(dev[1], 3)
+ set_group_map(dev[2], 2)
+ check_mesh_support(dev[0])
+ for i in range(3):
+ add_open_mesh_network(dev[i])
+ check_mesh_group_added(dev[i])
+ for i in range(3):
+ check_mesh_peer_connected(dev[i])
+
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+ hwsim_utils.test_connectivity(dev[0], dev[2])
+ finally:
+ # reset groups
+ set_group_map(dev[0], 1)
+ set_group_map(dev[1], 1)
+ set_group_map(dev[2], 1)
+
+def test_mesh_forwarding_secure(dev):
+ """Mesh with two stations that can't reach each other directly (RSN)"""
+ check_mesh_support(dev[0], secure=True)
+ try:
+ set_group_map(dev[0], 1)
+ set_group_map(dev[1], 3)
+ set_group_map(dev[2], 2)
+ for i in range(3):
+ dev[i].request("SET sae_groups ")
+ id = add_mesh_secure_net(dev[i])
+ dev[i].mesh_group_add(id)
+ check_mesh_group_added(dev[i])
+ for i in range(3):
+ check_mesh_peer_connected(dev[i])
+
+ hwsim_utils.test_connectivity(dev[0], dev[1])
+ hwsim_utils.test_connectivity(dev[1], dev[2])
+ hwsim_utils.test_connectivity(dev[0], dev[2])
+ finally:
+ # reset groups
+ set_group_map(dev[0], 1)
+ set_group_map(dev[1], 1)
+ set_group_map(dev[2], 1)
+
+def test_mesh_sae_anti_clogging(dev, apdev):
+ """Mesh using SAE and anti-clogging"""
+ try:
+ run_mesh_sae_anti_clogging(dev, apdev)
+ finally:
+ stop_monitor(apdev[1]["ifname"])
+
+def run_mesh_sae_anti_clogging(dev, apdev):
+ check_mesh_support(dev[0], secure=True)
+ check_mesh_support(dev[1], secure=True)
+ check_mesh_support(dev[2], secure=True)
+
+ sock = start_monitor(apdev[1]["ifname"])
+ radiotap = radiotap_build()
+
+ dev[0].request("SET sae_groups 21")
+ id = add_mesh_secure_net(dev[0])
+ dev[0].mesh_group_add(id)
+ check_mesh_group_added(dev[0])
+
+ # This flood of SAE authentication frames is from not yet known mesh STAs,
+ # so the messages get dropped.
+ addr0 = binascii.unhexlify(dev[0].own_addr().replace(':', ''))
+ for i in range(16):
+ addr = binascii.unhexlify("f2%010x" % i)
+ frame = build_sae_commit(addr0, addr)
+ sock.send(radiotap + frame)
+
+ dev[1].request("SET sae_groups 21")
+ id = add_mesh_secure_net(dev[1])
+ dev[1].mesh_group_add(id)
+ check_mesh_group_added(dev[1])
+ check_mesh_connected2(dev)
+
+ # Inject Beacon frames to make the sources of the second flood known to the
+ # target.
+ bcn1 = binascii.unhexlify("80000000" + "ffffffffffff")
+ bcn2 = binascii.unhexlify("0000dd20c44015840500e80310000000010882848b968c1298240301010504000200003204b048606c30140100000fac040100000fac040100000fac0800002d1afe131bffff0000000000000000000001000000000000000000003d16010000000000ffff0000000000000000000000000000720d777061732d6d6573682d736563710701010001010009")
+ for i in range(16):
+ addr = binascii.unhexlify("f4%010x" % i)
+ frame = bcn1 + addr + addr + bcn2
+ sock.send(radiotap + frame)
+
+ # This flood of SAE authentication frames is from known mesh STAs, so the
+ # target will need to process these.
+ for i in range(16):
+ addr = binascii.unhexlify("f4%010x" % i)
+ frame = build_sae_commit(addr0, addr)
+ sock.send(radiotap + frame)
+
+ dev[2].request("SET sae_groups 21")
+ id = add_mesh_secure_net(dev[2])
+ dev[2].mesh_group_add(id)
+ check_mesh_group_added(dev[2])
+ check_mesh_peer_connected(dev[2])
+ check_mesh_peer_connected(dev[0])
+
+def test_mesh_link_probe(dev, apdev, params):
+ """Mesh link probing"""
+ addr0 = dev[0].own_addr()
+ addr1 = dev[1].own_addr()
+ addr2 = dev[2].own_addr()
+
+ check_mesh_support(dev[0])
+ for i in range(3):
+ add_open_mesh_network(dev[i])
+ check_mesh_group_added(dev[i])
+ for i in range(3):
+ check_mesh_peer_connected(dev[i])
+
+ res = dev[0].request("MESH_LINK_PROBE " + addr1)
+ if "FAIL" in res:
+ raise HwsimSkip("MESH_LINK_PROBE kernel side support missing")
+ dev[0].request("MESH_LINK_PROBE " + addr2 + " payload=aabbccdd")
+ dev[1].request("MESH_LINK_PROBE " + addr0 + " payload=bbccddee")
+ dev[1].request("MESH_LINK_PROBE " + addr2 + " payload=ccddeeff")
+ dev[2].request("MESH_LINK_PROBE " + addr0 + " payload=aaaa")
+ dev[2].request("MESH_LINK_PROBE " + addr1 + " payload=000102030405060708090a0b0c0d0e0f")
+
+ capfile = os.path.join(params['logdir'], "hwsim0.pcapng")
+ filt = "wlan.fc == 0x8803"
+ for i in range(10):
+ out = run_tshark(capfile, filt, ["wlan.sa", "wlan.da"])
+ if len(out.splitlines()) >= 6:
+ break
+ time.sleep(0.5)
+ for i in [addr0, addr1, addr2]:
+ for j in [addr0, addr1, addr2]:
+ if i == j:
+ continue
+ if i + "\t" + j not in out:
+ raise Exception("Did not see probe %s --> %s" % (i, j))
diff --git a/contrib/wpa/tests/hwsim/test_wpas_wmm_ac.py b/contrib/wpa/tests/hwsim/test_wpas_wmm_ac.py
new file mode 100644
index 000000000000..f9c40f33b2af
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/test_wpas_wmm_ac.py
@@ -0,0 +1,400 @@
+# Test cases for wpa_supplicant WMM-AC operations
+# Copyright (c) 2014, Intel Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from remotehost import remote_compatible
+import logging
+logger = logging.getLogger()
+import struct
+import sys
+
+import hwsim_utils
+import hostapd
+from utils import fail_test
+
+def add_wmm_ap(apdev, acm_list):
+ params = {"ssid": "wmm_ac",
+ "hw_mode": "g",
+ "channel": "11",
+ "wmm_enabled": "1"}
+
+ for ac in acm_list:
+ params["wmm_ac_%s_acm" % (ac.lower())] = "1"
+
+ return hostapd.add_ap(apdev, params)
+
+def test_tspec(dev, apdev):
+ """Basic addts/delts tests"""
+ # configure ap with VO and VI requiring admission-control
+ hapd = add_wmm_ap(apdev[0], ["VO", "VI"])
+ dev[0].connect("wmm_ac", key_mgmt="NONE", scan_freq="2462")
+ hwsim_utils.test_connectivity(dev[0], hapd)
+ status = dev[0].request("WMM_AC_STATUS")
+ if "WMM AC is Enabled" not in status:
+ raise Exception("WMM-AC not enabled")
+ if "TSID" in status:
+ raise Exception("Unexpected TSID info")
+ if "BK: acm=0 uapsd=0" not in status:
+ raise Exception("Unexpected BK info" + status)
+ if "BE: acm=0 uapsd=0" not in status:
+ raise Exception("Unexpected BE info" + status)
+ if "VI: acm=1 uapsd=0" not in status:
+ raise Exception("Unexpected VI info" + status)
+ if "VO: acm=1 uapsd=0" not in status:
+ raise Exception("Unexpected VO info" + status)
+
+ # no tsid --> tsid out of range
+ if "FAIL" not in dev[0].request("WMM_AC_ADDTS downlink"):
+ raise Exception("Invalid WMM_AC_ADDTS accepted")
+ # no direction
+ if "FAIL" not in dev[0].request("WMM_AC_ADDTS tsid=5"):
+ raise Exception("Invalid WMM_AC_ADDTS accepted")
+ # param out of range
+ if "FAIL" not in dev[0].request("WMM_AC_ADDTS tsid=5 downlink"):
+ raise Exception("Invalid WMM_AC_ADDTS accepted")
+
+ tsid = 5
+
+ # make sure we fail when the ac is not configured for acm
+ try:
+ dev[0].add_ts(tsid, 3)
+ raise Exception("ADDTS succeeded although it should have failed")
+ except Exception as e:
+ if not str(e).startswith("ADDTS failed"):
+ raise
+ status = dev[0].request("WMM_AC_STATUS")
+ if "TSID" in status:
+ raise Exception("Unexpected TSID info")
+
+ # add tspec for UP=6
+ dev[0].add_ts(tsid, 6)
+ status = dev[0].request("WMM_AC_STATUS")
+ if "TSID" not in status:
+ raise Exception("Missing TSID info")
+
+ # using the same tsid for a different ac is invalid
+ try:
+ dev[0].add_ts(tsid, 5)
+ raise Exception("ADDTS succeeded although it should have failed")
+ except Exception as e:
+ if not str(e).startswith("ADDTS failed"):
+ raise
+
+ # update the tspec for a different UP of the same ac
+ dev[0].add_ts(tsid, 7, extra="fixed_nominal_msdu")
+ dev[0].del_ts(tsid)
+ status = dev[0].request("WMM_AC_STATUS")
+ if "TSID" in status:
+ raise Exception("Unexpected TSID info")
+
+ # verify failure on uplink/bidi without driver support
+ tsid = 6
+ try:
+ dev[0].add_ts(tsid, 7, direction="uplink")
+ raise Exception("ADDTS succeeded although it should have failed")
+ except Exception as e:
+ if not str(e).startswith("ADDTS failed"):
+ raise
+ try:
+ dev[0].add_ts(tsid, 7, direction="bidi")
+ raise Exception("ADDTS succeeded although it should have failed")
+ except Exception as e:
+ if not str(e).startswith("ADDTS failed"):
+ raise
+
+ # attempt to delete non-existing tsid
+ try:
+ dev[0].del_ts(tsid)
+ raise Exception("DELTS succeeded although it should have failed")
+ except Exception as e:
+ if not str(e).startswith("DELTS failed"):
+ raise
+
+ # "CTRL: Invalid WMM_AC_ADDTS parameter: 'foo'
+ if "FAIL" not in dev[0].request("WMM_AC_ADDTS foo"):
+ raise Exception("Invalid WMM_AC_ADDTS command accepted")
+
+def test_tspec_protocol(dev, apdev):
+ """Protocol tests for addts/delts"""
+ # configure ap with VO and VI requiring admission-control
+ hapd = add_wmm_ap(apdev[0], ["VO", "VI"])
+ dev[0].connect("wmm_ac", key_mgmt="NONE", scan_freq="2462")
+
+ dev[0].dump_monitor()
+ hapd.set("ext_mgmt_frame_handling", "1")
+
+ tsid = 6
+
+ # timeout on ADDTS response
+ dev[0].add_ts(tsid, 7, expect_failure=True)
+
+ hapd.dump_monitor()
+ req = "WMM_AC_ADDTS downlink tsid=6 up=7 nominal_msdu_size=1500 sba=9000 mean_data_rate=1500 min_phy_rate=6000000"
+ if "OK" not in dev[0].request(req):
+ raise Exception("WMM_AC_ADDTS failed")
+ # a new request while previous is still pending
+ if "FAIL" not in dev[0].request(req):
+ raise Exception("WMM_AC_ADDTS accepted while oen was still pending")
+ msg = hapd.mgmt_rx()
+ payload = msg['payload']
+ (categ, action, dialog, status) = struct.unpack('BBBB', payload[0:4])
+ if action != 0:
+ raise Exception("Unexpected Action code: %d" % action)
+
+ msg['da'] = msg['sa']
+ msg['sa'] = apdev[0]['bssid']
+
+ # unexpected dialog token
+ msg['payload'] = struct.pack('BBBB', 17, 1, (dialog + 1) & 0xff, 0) + payload[4:]
+ hapd.mgmt_tx(msg)
+
+ # valid response
+ msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:]
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_event(["TSPEC-ADDED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on TSPEC-ADDED")
+ if "tsid=%d" % tsid not in ev:
+ raise Exception("Unexpected TSPEC-ADDED contents: " + ev)
+
+ # duplicated response
+ msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:]
+ hapd.mgmt_tx(msg)
+
+ # too short ADDTS
+ msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0)
+ hapd.mgmt_tx(msg)
+
+ # invalid IE
+ msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:] + struct.pack('BB', 0xdd, 100)
+ hapd.mgmt_tx(msg)
+
+ # too short WMM element
+ msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 0) + payload[4:] + b'\xdd\x06\x00\x50\xf2\x02\x02\x01'
+ hapd.mgmt_tx(msg)
+
+ # DELTS
+ dev[0].dump_monitor()
+ msg['payload'] = struct.pack('BBBB', 17, 2, 0, 0) + payload[4:]
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_event(['TSPEC-REMOVED'], timeout=6)
+ if ev is None:
+ raise Exception("Timeout on TSPEC-REMOVED event")
+ if "tsid=%d" % tsid not in ev:
+ raise Exception("Unexpected TSPEC-REMOVED contents: " + ev)
+ # DELTS duplicated
+ msg['payload'] = struct.pack('BBBB', 17, 2, 0, 0) + payload[4:]
+ hapd.mgmt_tx(msg)
+
+ # start a new request
+ hapd.dump_monitor()
+ if "OK" not in dev[0].request(req):
+ raise Exception("WMM_AC_ADDTS failed")
+ msg = hapd.mgmt_rx()
+ payload = msg['payload']
+ (categ, action, dialog, status) = struct.unpack('BBBB', payload[0:4])
+ if action != 0:
+ raise Exception("Unexpected Action code: %d" % action)
+
+ msg['da'] = msg['sa']
+ msg['sa'] = apdev[0]['bssid']
+
+ # modified parameters
+ p12int = payload[12] if sys.version_info[0] > 2 else ord(payload[12])
+ msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 1) + payload[4:12] + struct.pack('B', p12int & ~0x60) + payload[13:]
+ hapd.mgmt_tx(msg)
+
+ # reject request
+ msg['payload'] = struct.pack('BBBB', 17, 1, dialog, 1) + payload[4:]
+ hapd.mgmt_tx(msg)
+ ev = dev[0].wait_event(["TSPEC-REQ-FAILED"], timeout=10)
+ if ev is None:
+ raise Exception("Timeout on TSPEC-REQ-FAILED")
+ if "tsid=%d" % tsid not in ev:
+ raise Exception("Unexpected TSPEC-REQ-FAILED contents: " + ev)
+
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+@remote_compatible
+def test_tspec_not_enabled(dev, apdev):
+ """addts failing if AP does not support WMM"""
+ params = {"ssid": "wmm_no_ac",
+ "hw_mode": "g",
+ "channel": "11",
+ "wmm_enabled": "0"}
+ hapd = hostapd.add_ap(apdev[0], params)
+ dev[0].connect("wmm_no_ac", key_mgmt="NONE", scan_freq="2462")
+ status = dev[0].request("WMM_AC_STATUS")
+ if "Not associated to a WMM AP, WMM AC is Disabled" not in status:
+ raise Exception("Unexpected WMM_AC_STATUS: " + status)
+
+ try:
+ dev[0].add_ts(5, 6)
+ raise Exception("ADDTS succeeded although it should have failed")
+ except Exception as e:
+ if not str(e).startswith("ADDTS failed"):
+ raise
+
+ # attempt to delete non-existing tsid
+ try:
+ dev[0].del_ts(5)
+ raise Exception("DELTS succeeded although it should have failed")
+ except Exception as e:
+ if not str(e).startswith("DELTS failed"):
+ raise
+
+ # unexpected Action frame when WMM is disabled
+ MGMT_SUBTYPE_ACTION = 13
+ msg = {}
+ msg['fc'] = MGMT_SUBTYPE_ACTION << 4
+ msg['da'] = dev[0].p2p_interface_addr()
+ msg['sa'] = apdev[0]['bssid']
+ msg['bssid'] = apdev[0]['bssid']
+ msg['payload'] = struct.pack('BBBB', 17, 2, 0, 0)
+ hapd.mgmt_tx(msg)
+
+@remote_compatible
+def test_tspec_ap_roam_open(dev, apdev):
+ """Roam between two open APs while having tspecs"""
+ hapd0 = add_wmm_ap(apdev[0], ["VO", "VI"])
+ dev[0].connect("wmm_ac", key_mgmt="NONE")
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+ dev[0].add_ts(5, 6)
+
+ hapd1 = add_wmm_ap(apdev[1], ["VO", "VI"])
+ dev[0].scan_for_bss(apdev[1]['bssid'], freq=2462)
+ dev[0].roam(apdev[1]['bssid'])
+ hwsim_utils.test_connectivity(dev[0], hapd1)
+ if dev[0].tspecs():
+ raise Exception("TSPECs weren't deleted on roaming")
+
+ dev[0].scan_for_bss(apdev[0]['bssid'], freq=2462)
+ dev[0].roam(apdev[0]['bssid'])
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+
+@remote_compatible
+def test_tspec_reassoc(dev, apdev):
+ """Reassociation to same BSS while having tspecs"""
+ hapd0 = add_wmm_ap(apdev[0], ["VO", "VI"])
+ dev[0].connect("wmm_ac", key_mgmt="NONE")
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+ dev[0].add_ts(5, 6)
+ last_tspecs = dev[0].tspecs()
+
+ dev[0].request("REASSOCIATE")
+ dev[0].wait_connected()
+
+ hwsim_utils.test_connectivity(dev[0], hapd0)
+ if dev[0].tspecs() != last_tspecs:
+ raise Exception("TSPECs weren't saved on reassociation")
+
+def test_wmm_element(dev, apdev):
+ """hostapd FTM range request timeout"""
+ try:
+ run_wmm_element(dev, apdev)
+ finally:
+ dev[0].request("VENDOR_ELEM_REMOVE 13 *")
+
+def run_wmm_element(dev, apdev):
+ params = {"ssid": "wmm"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+
+ # Too short WMM IE
+ dev[0].request("VENDOR_ELEM_ADD 13 dd060050f2020001")
+ dev[0].scan_for_bss(bssid, freq=2412)
+ dev[0].connect("wmm", key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("Association not rejected")
+ dev[0].request("REMOVE_NETWORK all")
+
+ # Unsupported WMM IE Subtype/Version
+ dev[0].request("VENDOR_ELEM_ADD 13 dd070050f202000000")
+ dev[0].connect("wmm", key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("Association not rejected")
+ dev[0].request("REMOVE_NETWORK all")
+
+ # Unsupported WMM IE Subtype/Version
+ dev[0].request("VENDOR_ELEM_ADD 13 dd070050f202010100")
+ dev[0].connect("wmm", key_mgmt="NONE", scan_freq="2412", wait_connect=False)
+ ev = dev[0].wait_event(["CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("Association not rejected")
+ dev[0].request("REMOVE_NETWORK all")
+
+def test_tspec_ap_fail(dev, apdev):
+ """AP failing to send tspec response"""
+ # configure ap with VO and VI requiring admission-control
+ hapd = add_wmm_ap(apdev[0], ["VO", "VI"])
+ dev[0].connect("wmm_ac", key_mgmt="NONE", scan_freq="2462")
+ tsid = 5
+
+ with fail_test(hapd, 1, "wmm_send_action"):
+ try:
+ # add tspec for UP=6
+ dev[0].add_ts(tsid, 6)
+ except:
+ pass
+
+def test_tspec_ap_parsing(dev, apdev):
+ """TSPEC AP parsing tests"""
+ # configure ap with VO and VI requiring admission-control
+ hapd = add_wmm_ap(apdev[0], ["VO", "VI"])
+ bssid = hapd.own_addr()
+ dev[0].connect("wmm_ac", key_mgmt="NONE", scan_freq="2462")
+ addr = dev[0].own_addr()
+
+ tests = ["WMM_AC_ADDTS downlink tsid=5 up=6 nominal_msdu_size=1500 sba=9000 mean_data_rate=1500 min_phy_rate=600000",
+ "WMM_AC_ADDTS downlink tsid=5 up=6 nominal_msdu_size=1500 sba=8192 mean_data_rate=1500 min_phy_rate=6000000",
+ "WMM_AC_ADDTS downlink tsid=5 up=6 nominal_msdu_size=32767 sba=65535 mean_data_rate=1500 min_phy_rate=1000000",
+ "WMM_AC_ADDTS downlink tsid=5 up=6 nominal_msdu_size=10000 sba=65535 mean_data_rate=2147483647 min_phy_rate=1000000"]
+ for t in tests:
+ if "OK" not in dev[0].request(t):
+ raise Exception("WMM_AC_ADDTS failed")
+ ev = dev[0].wait_event(["TSPEC-REQ-FAILED"], timeout=1)
+ if ev is None:
+ raise Exception("No response")
+
+ tests = []
+ # WMM: Invalid Nominal MSDU Size (0)
+ tests += ["11000400dd3d0050f2020201aa300000000000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff0000"]
+ # hostapd_wmm_action - missing or wrong length tspec
+ tests += ["11000400dd3e0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff000000"]
+ # hostapd_wmm_action - could not parse wmm action
+ tests += ["11000400dd3d0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff00"]
+ # valid form
+ tests += ["11000400dd3d0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff0000"]
+
+ hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
+ hapd.set("ext_mgmt_frame_handling", "1")
+ for t in tests:
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + t):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ hapd.set("ext_mgmt_frame_handling", "0")
+
+def test_wmm_disabled(dev, apdev):
+ """WMM disabled and unexpected TSPEC"""
+ params = {"ssid": "no-wmm", "ieee80211n": "0", "wmm_enabled": "0"}
+ hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+ bssid = hapd.own_addr()
+ dev[0].connect("no-wmm", key_mgmt="NONE", scan_freq="2412")
+ addr = dev[0].own_addr()
+
+ # wmm action received is not from associated wmm station
+ hdr = "d0003a01" + bssid.replace(':', '') + addr.replace(':', '') + bssid.replace(':', '') + "1000"
+ hapd.set("ext_mgmt_frame_handling", "1")
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "11000400dd3d0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff0000"):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ # IEEE 802.11: Ignored Action frame (category=17) from unassociated STA
+ hdr = "d0003a01" + bssid.replace(':', '') + "112233445566" + bssid.replace(':', '') + "1000"
+ if "OK" not in hapd.request("MGMT_RX_PROCESS freq=2412 datarate=0 ssi_signal=-30 frame=" + hdr + "11000400dd3d0050f2020201aa300010270000000000000000000000000000000000000000000000000000ffffff7f00000000000000000000000040420f00ffff0000"):
+ raise Exception("MGMT_RX_PROCESS failed")
+
+ hapd.set("ext_mgmt_frame_handling", "0")
diff --git a/contrib/wpa/tests/hwsim/tnc/.gitignore b/contrib/wpa/tests/hwsim/tnc/.gitignore
new file mode 100644
index 000000000000..2f8896276f35
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/tnc/.gitignore
@@ -0,0 +1,4 @@
+libhostap2_imc.so
+libhostap2_imv.so
+libhostap_imc.so
+libhostap_imv.so
diff --git a/contrib/wpa/tests/hwsim/tnc/Makefile b/contrib/wpa/tests/hwsim/tnc/Makefile
new file mode 100644
index 000000000000..64ba0cac6242
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/tnc/Makefile
@@ -0,0 +1,23 @@
+CFLAGS += -I$(abspath ../../../src)
+CFLAGS += -I$(abspath ../../../src/utils)
+
+ALL=libhostap_imc.so libhostap_imv.so libhostap2_imc.so libhostap2_imv.so
+all: $(ALL)
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
+
+lib%.so: %.c
+ $(Q)$(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $<
+ @$(E) " CC " $@
+
+clean:
+ rm -f $(ALL)
diff --git a/contrib/wpa/tests/hwsim/tnc/hostap2_imc.c b/contrib/wpa/tests/hwsim/tnc/hostap2_imc.c
new file mode 100644
index 000000000000..3818c17d994a
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/tnc/hostap2_imc.c
@@ -0,0 +1,183 @@
+/*
+ * Example IMC for TNC testing
+ * Copyright (c) 2014, 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 "common.h"
+#include "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMCID my_id = -1;
+static TNC_TNCC_SendMessagePointer send_message = NULL;
+static TNC_TNCC_ReportMessageTypesPointer report_message_types = NULL;
+static TNC_TNCC_RequestHandshakeRetryPointer request_retry = NULL;
+
+static TNC_MessageType message_types[] =
+{
+ (TNC_VENDORID_ANY << 8) | TNC_SUBTYPE_ANY
+};
+
+
+TNC_Result TNC_IMC_Initialize(
+ /*in*/ TNC_IMCID imcID,
+ /*in*/ TNC_Version minVersion,
+ /*in*/ TNC_Version maxVersion,
+ /*out*/ TNC_Version *pOutActualVersion)
+{
+ wpa_printf(MSG_INFO,
+ "IMC(hostap2) %s(imcID=%u, minVersion=%u, maxVersion=%u)",
+ __func__, (unsigned) imcID, (unsigned) minVersion,
+ (unsigned) maxVersion);
+
+ if (initialized)
+ return TNC_RESULT_ALREADY_INITIALIZED;
+
+ if (minVersion < TNC_IFIMC_VERSION_1 ||
+ maxVersion > TNC_IFIMC_VERSION_1)
+ return TNC_RESULT_NO_COMMON_VERSION;
+
+ if (!pOutActualVersion)
+ return TNC_RESULT_INVALID_PARAMETER;
+ *pOutActualVersion = TNC_IFIMC_VERSION_1;
+ my_id = imcID;
+
+ initialized = 1;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_BeginHandshake(
+ /*in*/ TNC_IMCID imcID,
+ /*in*/ TNC_ConnectionID connectionID)
+{
+ char *msg = "hello";
+ TNC_Result res;
+
+ wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u, connectionID=%u)",
+ __func__, (unsigned) imcID, (unsigned) connectionID);
+
+ if (!initialized)
+ return TNC_RESULT_NOT_INITIALIZED;
+
+ if (imcID != my_id)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ if (!send_message)
+ return TNC_RESULT_FATAL;
+
+ res = send_message(imcID, connectionID, msg, os_strlen(msg), 1);
+ if (res != TNC_RESULT_SUCCESS)
+ return res;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_ProvideBindFunction(
+ /*in*/ TNC_IMCID imcID,
+ /*in*/ TNC_TNCC_BindFunctionPointer bindFunction)
+{
+ TNC_Result res;
+
+ wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u)",
+ __func__, (unsigned) imcID);
+
+ if (!initialized)
+ return TNC_RESULT_NOT_INITIALIZED;
+
+ if (imcID != my_id || !bindFunction)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ if (bindFunction(imcID, "TNC_TNCC_SendMessage",
+ (void **) &send_message) != TNC_RESULT_SUCCESS ||
+ !send_message)
+ return TNC_RESULT_FATAL;
+
+ if (bindFunction(imcID, "TNC_TNCC_ReportMessageTypes",
+ (void **) &report_message_types) !=
+ TNC_RESULT_SUCCESS ||
+ !report_message_types)
+ return TNC_RESULT_FATAL;
+
+ if (bindFunction(imcID, "TNC_TNCC_RequestHandshakeRetry",
+ (void **) &request_retry) != TNC_RESULT_SUCCESS ||
+ !request_retry)
+ return TNC_RESULT_FATAL;
+
+ res = report_message_types(imcID, message_types,
+ ARRAY_SIZE(message_types));
+ if (res != TNC_RESULT_SUCCESS)
+ return res;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_NotifyConnectionChange(
+ /*in*/ TNC_IMCID imcID,
+ /*in*/ TNC_ConnectionID connectionID,
+ /*in*/ TNC_ConnectionState newState)
+{
+ wpa_printf(MSG_INFO,
+ "IMC(hostap2) %s(imcID=%u, connectionID=%u, newState=%u)",
+ __func__, (unsigned) imcID, (unsigned) connectionID,
+ (unsigned) newState);
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_ReceiveMessage(
+ /*in*/ TNC_IMCID imcID,
+ /*in*/ TNC_ConnectionID connectionID,
+ /*in*/ TNC_BufferReference message,
+ /*in*/ TNC_UInt32 messageLength,
+ /*in*/ TNC_MessageType messageType)
+{
+ TNC_Result res;
+
+ wpa_printf(MSG_INFO,
+ "IMC(hostap2) %s(imcID=%u, connectionID=%u, messageType=%u)",
+ __func__, (unsigned) imcID, (unsigned) connectionID,
+ (unsigned) messageType);
+ wpa_hexdump_ascii(MSG_INFO, "IMC(hostap2) message",
+ message, messageLength);
+
+ if (messageType == 1 && messageLength == 5 &&
+ os_memcmp(message, "hello", 5) == 0) {
+ char *msg = "i'm fine";
+
+ res = send_message(imcID, connectionID, msg, os_strlen(msg), 1);
+ if (res != TNC_RESULT_SUCCESS)
+ return res;
+ }
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_BatchEnding(
+ /*in*/ TNC_IMCID imcID,
+ /*in*/ TNC_ConnectionID connectionID)
+{
+ wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u, connectionID=%u)",
+ __func__, (unsigned) imcID, (unsigned) connectionID);
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_Terminate(
+ /*in*/ TNC_IMCID imcID)
+{
+ wpa_printf(MSG_INFO, "IMC(hostap2) %s(imcID=%u)",
+ __func__, (unsigned) imcID);
+
+ return TNC_RESULT_SUCCESS;
+}
diff --git a/contrib/wpa/tests/hwsim/tnc/hostap2_imv.c b/contrib/wpa/tests/hwsim/tnc/hostap2_imv.c
new file mode 100644
index 000000000000..652888ab2865
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/tnc/hostap2_imv.c
@@ -0,0 +1,203 @@
+/*
+ * Example IMV for TNC testing
+ * Copyright (c) 2014, 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 "common.h"
+#include "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMVID my_id = -1;
+static TNC_TNCS_ReportMessageTypesPointer report_message_types = NULL;
+static TNC_TNCS_SendMessagePointer send_message = NULL;
+static TNC_TNCS_RequestHandshakeRetryPointer request_retry = NULL;
+TNC_TNCS_ProvideRecommendationPointer provide_recomm = NULL;
+
+static TNC_MessageType message_types[] =
+{
+ (TNC_VENDORID_ANY << 8) | TNC_SUBTYPE_ANY
+};
+
+
+TNC_Result TNC_IMV_Initialize(
+ /*in*/ TNC_IMVID imvID,
+ /*in*/ TNC_Version minVersion,
+ /*in*/ TNC_Version maxVersion,
+ /*out*/ TNC_Version *pOutActualVersion)
+{
+ wpa_printf(MSG_INFO,
+ "IMV(hostap2) %s(imvID=%u, minVersion=%u, maxVersion=%u)",
+ __func__, (unsigned) imvID, (unsigned) minVersion,
+ (unsigned) maxVersion);
+
+ if (initialized)
+ return TNC_RESULT_ALREADY_INITIALIZED;
+
+ if (minVersion < TNC_IFIMV_VERSION_1 ||
+ maxVersion > TNC_IFIMV_VERSION_1)
+ return TNC_RESULT_NO_COMMON_VERSION;
+
+ if (!pOutActualVersion)
+ return TNC_RESULT_INVALID_PARAMETER;
+ *pOutActualVersion = TNC_IFIMV_VERSION_1;
+
+ initialized = 1;
+ my_id = imvID;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_NotifyConnectionChange(
+ /*in*/ TNC_IMVID imvID,
+ /*in*/ TNC_ConnectionID connectionID,
+ /*in*/ TNC_ConnectionState newState)
+{
+ wpa_printf(MSG_INFO,
+ "IMV(hostap2) %s(imvID=%u, connectionID=%u, newState=%u)",
+ __func__, (unsigned) imvID, (unsigned) connectionID,
+ (unsigned) newState);
+
+ if (!initialized)
+ return TNC_RESULT_NOT_INITIALIZED;
+
+ if (imvID != my_id)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ /* TODO: call TNC_TNCS_ProvideRecommendation */
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_ReceiveMessage(
+ /*in*/ TNC_IMVID imvID,
+ /*in*/ TNC_ConnectionID connectionID,
+ /*in*/ TNC_BufferReference message,
+ /*in*/ TNC_UInt32 messageLength,
+ /*in*/ TNC_MessageType messageType)
+{
+ TNC_Result res;
+
+ wpa_printf(MSG_INFO,
+ "IMV(hostap2) %s(imvID=%u, connectionID=%u, messageType=%u)",
+ __func__, (unsigned) imvID, (unsigned) connectionID,
+ (unsigned) messageType);
+ wpa_hexdump_ascii(MSG_INFO, "IMV(hostap2) message",
+ message, messageLength);
+
+ if (!send_message)
+ return TNC_RESULT_FATAL;
+
+ if (messageType == 1 && messageLength == 5 &&
+ os_memcmp(message, "hello", 5) == 0) {
+ char *msg = "hello";
+
+ res = send_message(imvID, connectionID, msg, os_strlen(msg), 1);
+ if (res != TNC_RESULT_SUCCESS)
+ return res;
+ }
+
+ if (messageType == 1 && messageLength == 8 &&
+ os_memcmp(message, "i'm fine", 8) == 0) {
+ if (!provide_recomm)
+ return TNC_RESULT_FATAL;
+ res = provide_recomm(imvID, connectionID,
+ TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
+ TNC_IMV_EVALUATION_RESULT_COMPLIANT);
+ if (res != TNC_RESULT_SUCCESS)
+ return res;
+ }
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_SolicitRecommendation(
+ /*in*/ TNC_IMVID imvID,
+ /*in*/ TNC_ConnectionID connectionID)
+{
+ wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u, connectionID=%u)",
+ __func__, (unsigned) imvID, (unsigned) connectionID);
+
+ if (!initialized)
+ return TNC_RESULT_NOT_INITIALIZED;
+
+ if (imvID != my_id)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ /* TODO: call TNC_TNCS_ProvideRecommendation */
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_BatchEnding(
+ /*in*/ TNC_IMVID imvID,
+ /*in*/ TNC_ConnectionID connectionID)
+{
+ wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u, connectionID=%u)",
+ __func__, (unsigned) imvID, (unsigned) connectionID);
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_Terminate(
+ /*in*/ TNC_IMVID imvID)
+{
+ wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u)",
+ __func__, (unsigned) imvID);
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_ProvideBindFunction(
+ /*in*/ TNC_IMVID imvID,
+ /*in*/ TNC_TNCS_BindFunctionPointer bindFunction)
+{
+ TNC_Result res;
+
+ wpa_printf(MSG_INFO, "IMV(hostap2) %s(imvID=%u)",
+ __func__, (unsigned) imvID);
+
+ if (!initialized)
+ return TNC_RESULT_NOT_INITIALIZED;
+
+ if (imvID != my_id || !bindFunction)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ if (bindFunction(imvID, "TNC_TNCS_ReportMessageTypes",
+ (void **) &report_message_types) !=
+ TNC_RESULT_SUCCESS ||
+ !report_message_types)
+ return TNC_RESULT_FATAL;
+
+ if (bindFunction(imvID, "TNC_TNCS_SendMessage",
+ (void **) &send_message) != TNC_RESULT_SUCCESS ||
+ !send_message)
+ return TNC_RESULT_FATAL;
+
+ if (bindFunction(imvID, "TNC_TNCS_RequestHandshakeRetry",
+ (void **) &request_retry) != TNC_RESULT_SUCCESS ||
+ !request_retry)
+ return TNC_RESULT_FATAL;
+
+ if (bindFunction(imvID, "TNC_TNCS_ProvideRecommendation",
+ (void **) &provide_recomm) != TNC_RESULT_SUCCESS ||
+ !provide_recomm)
+ return TNC_RESULT_FATAL;
+
+ res = report_message_types(imvID, message_types,
+ ARRAY_SIZE(message_types));
+ if (res != TNC_RESULT_SUCCESS)
+ return res;
+
+ return TNC_RESULT_SUCCESS;
+}
diff --git a/contrib/wpa/tests/hwsim/tnc/hostap_imc.c b/contrib/wpa/tests/hwsim/tnc/hostap_imc.c
new file mode 100644
index 000000000000..d28183a016f5
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/tnc/hostap_imc.c
@@ -0,0 +1,72 @@
+/*
+ * Minimal example IMC for TNC testing
+ * Copyright (c) 2014, 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 "common.h"
+#include "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMCID my_id = -1;
+
+TNC_Result TNC_IMC_Initialize(
+ /*in*/ TNC_IMCID imcID,
+ /*in*/ TNC_Version minVersion,
+ /*in*/ TNC_Version maxVersion,
+ /*out*/ TNC_Version *pOutActualVersion)
+{
+ wpa_printf(MSG_INFO, "IMC(hostap) %s", __func__);
+
+ if (initialized)
+ return TNC_RESULT_ALREADY_INITIALIZED;
+
+ if (minVersion < TNC_IFIMC_VERSION_1 ||
+ maxVersion > TNC_IFIMC_VERSION_1)
+ return TNC_RESULT_NO_COMMON_VERSION;
+
+ if (!pOutActualVersion)
+ return TNC_RESULT_INVALID_PARAMETER;
+ *pOutActualVersion = TNC_IFIMC_VERSION_1;
+ my_id = imcID;
+
+ initialized = 1;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_BeginHandshake(
+ /*in*/ TNC_IMCID imcID,
+ /*in*/ TNC_ConnectionID connectionID)
+{
+ wpa_printf(MSG_INFO, "IMC(hostap) %s", __func__);
+
+ if (!initialized)
+ return TNC_RESULT_NOT_INITIALIZED;
+
+ if (imcID != my_id)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMC_ProvideBindFunction(
+ /*in*/ TNC_IMCID imcID,
+ /*in*/ TNC_TNCC_BindFunctionPointer bindFunction)
+{
+ wpa_printf(MSG_INFO, "IMC(hostap) %s", __func__);
+
+ if (!initialized)
+ return TNC_RESULT_NOT_INITIALIZED;
+
+ if (imcID != my_id)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ return TNC_RESULT_SUCCESS;
+}
diff --git a/contrib/wpa/tests/hwsim/tnc/hostap_imv.c b/contrib/wpa/tests/hwsim/tnc/hostap_imv.c
new file mode 100644
index 000000000000..0f4f9c8994c5
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/tnc/hostap_imv.c
@@ -0,0 +1,66 @@
+/*
+ * Minimal example IMV for TNC testing
+ * Copyright (c) 2014, 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 "common.h"
+#include "common/tnc.h"
+
+static int initialized = 0;
+static TNC_IMVID my_id = -1;
+
+TNC_Result TNC_IMV_Initialize(
+ /*in*/ TNC_IMVID imvID,
+ /*in*/ TNC_Version minVersion,
+ /*in*/ TNC_Version maxVersion,
+ /*out*/ TNC_Version *pOutActualVersion)
+{
+ if (initialized)
+ return TNC_RESULT_ALREADY_INITIALIZED;
+
+ if (minVersion < TNC_IFIMV_VERSION_1 ||
+ maxVersion > TNC_IFIMV_VERSION_1)
+ return TNC_RESULT_NO_COMMON_VERSION;
+
+ if (!pOutActualVersion)
+ return TNC_RESULT_INVALID_PARAMETER;
+ *pOutActualVersion = TNC_IFIMV_VERSION_1;
+
+ initialized = 1;
+ my_id = imvID;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_SolicitRecommendation(
+ /*in*/ TNC_IMVID imvID,
+ /*in*/ TNC_ConnectionID connectionID)
+{
+ if (!initialized)
+ return TNC_RESULT_NOT_INITIALIZED;
+
+ if (imvID != my_id)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ return TNC_RESULT_SUCCESS;
+}
+
+
+TNC_Result TNC_IMV_ProvideBindFunction(
+ /*in*/ TNC_IMVID imvID,
+ /*in*/ TNC_TNCS_BindFunctionPointer bindFunction)
+{
+ if (!initialized)
+ return TNC_RESULT_NOT_INITIALIZED;
+
+ if (imvID != my_id)
+ return TNC_RESULT_INVALID_PARAMETER;
+
+ return TNC_RESULT_SUCCESS;
+}
diff --git a/contrib/wpa/tests/hwsim/tnc/tnc_config b/contrib/wpa/tests/hwsim/tnc/tnc_config
new file mode 100644
index 000000000000..613783a66f5d
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/tnc/tnc_config
@@ -0,0 +1,4 @@
+IMC "hostap IMC" tnc/libhostap_imc.so
+IMV "hostap IMV" tnc/libhostap_imv.so
+IMC "hostap2 IMC" tnc/libhostap2_imc.so
+IMV "hostap2 IMV" tnc/libhostap2_imv.so
diff --git a/contrib/wpa/tests/hwsim/tshark.py b/contrib/wpa/tests/hwsim/tshark.py
new file mode 100644
index 000000000000..32cdf4701ec3
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/tshark.py
@@ -0,0 +1,124 @@
+#
+# tshark module - refactored from test_scan.py
+#
+# Copyright (c) 2014, Qualcomm Atheros, Inc.
+# Copyright (c) 2015, Intel Mobile Communications GmbH
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import subprocess
+import logging
+logger = logging.getLogger()
+
+from utils import *
+
+class UnknownFieldsException(Exception):
+ def __init__(self, fields):
+ Exception.__init__(self, "unknown tshark fields %s" % ','.join(fields))
+ self.fields = fields
+
+_tshark_filter_arg = '-Y'
+
+def _run_tshark(filename, filter, display=None, wait=True):
+ global _tshark_filter_arg
+
+ if wait:
+ # wait a bit to make it more likely for wlantest sniffer to have
+ # captured and written the results into a file that we can process here
+ time.sleep(0.1)
+
+ try:
+ arg = ["tshark", "-r", filename,
+ _tshark_filter_arg, filter]
+ if display:
+ arg.append('-Tfields')
+ for d in display:
+ arg.append('-e')
+ arg.append(d)
+ else:
+ arg.append('-V')
+ cmd = subprocess.Popen(arg, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ except Exception as e:
+ logger.info("Could run run tshark check: " + str(e))
+ if "No such file or directory: 'tshark'" in str(e):
+ raise HwsimSkip("No tshark available")
+ cmd = None
+ return None
+
+ output = cmd.communicate()
+ out = output[0].decode(errors='ignore')
+ out1 = output[1].decode()
+ res = cmd.wait()
+ if res == 1:
+ errmsg = "Some fields aren't valid"
+ if errmsg in out1:
+ errors = out1.split('\n')
+ fields = []
+ collect = False
+ for f in errors:
+ if collect:
+ f = f.strip()
+ if f:
+ fields.append(f)
+ continue
+ if errmsg in f:
+ collect = True
+ continue
+ raise UnknownFieldsException(fields)
+ # remember this for efficiency
+ _tshark_filter_arg = '-R'
+ arg[3] = '-R'
+ cmd = subprocess.Popen(arg, stdout=subprocess.PIPE,
+ stderr=open('/dev/null', 'w'))
+ out = cmd.communicate()[0].decode()
+ cmd.wait()
+ if res == 2:
+ if "tshark: Neither" in out1 and "are field or protocol names" in out1:
+ errors = out1.split('\n')
+ fields = []
+ for f in errors:
+ if f.startswith("tshark: Neither "):
+ f = f.split(' ')[2].strip('"')
+ if f:
+ fields.append(f)
+ continue
+ raise UnknownFieldsException(fields)
+
+ return out
+
+def run_tshark(filename, filter, display=None, wait=True):
+ if display is None: display = []
+ try:
+ return _run_tshark(filename, filter.replace('wlan_mgt', 'wlan'),
+ [x.replace('wlan_mgt', 'wlan') for x in display],
+ wait)
+ except UnknownFieldsException as e:
+ all_wlan_mgt = True
+ for f in e.fields:
+ if not f.startswith('wlan_mgt.'):
+ all_wlan_mgt = False
+ break
+ if not all_wlan_mgt:
+ raise
+ return _run_tshark(filename, filter, display, wait)
+
+def run_tshark_json(filename, filter):
+ arg = ["tshark", "-r", filename,
+ _tshark_filter_arg, filter]
+ arg.append('-Tjson')
+ arg.append('-x')
+ try:
+ cmd = subprocess.Popen(arg, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ except Exception as e:
+ logger.info("Could run run tshark: " + str(e))
+ if "No such file or directory: 'tshark'" in str(e):
+ raise HwsimSkip("No tshark available")
+ return None
+ output = cmd.communicate()
+ out = output[0].decode()
+ res = cmd.wait()
+ return out
diff --git a/contrib/wpa/tests/hwsim/utils.py b/contrib/wpa/tests/hwsim/utils.py
new file mode 100644
index 000000000000..4e88626615d5
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/utils.py
@@ -0,0 +1,314 @@
+# Testing utilities
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import os
+import socket
+import struct
+import subprocess
+import time
+import remotehost
+import logging
+logger = logging.getLogger()
+import hostapd
+
+def get_ifnames():
+ ifnames = []
+ with open("/proc/net/dev", "r") as f:
+ lines = f.readlines()
+ for l in lines:
+ val = l.split(':', 1)
+ if len(val) == 2:
+ ifnames.append(val[0].strip(' '))
+ return ifnames
+
+class HwsimSkip(Exception):
+ def __init__(self, reason):
+ self.reason = reason
+ def __str__(self):
+ return self.reason
+
+def long_duration_test(func):
+ func.long_duration_test = True
+ return func
+
+class alloc_fail(object):
+ def __init__(self, dev, count, funcs):
+ self._dev = dev
+ self._count = count
+ self._funcs = funcs
+ def __enter__(self):
+ cmd = "TEST_ALLOC_FAIL %d:%s" % (self._count, self._funcs)
+ if "OK" not in self._dev.request(cmd):
+ raise HwsimSkip("TEST_ALLOC_FAIL not supported")
+ def __exit__(self, type, value, traceback):
+ if type is None:
+ if self._dev.request("GET_ALLOC_FAIL") != "0:%s" % self._funcs:
+ raise Exception("Allocation failure did not trigger")
+
+class fail_test(object):
+ def __init__(self, dev, count, funcs):
+ self._dev = dev
+ self._count = count
+ self._funcs = funcs
+ def __enter__(self):
+ cmd = "TEST_FAIL %d:%s" % (self._count, self._funcs)
+ if "OK" not in self._dev.request(cmd):
+ raise HwsimSkip("TEST_FAIL not supported")
+ def __exit__(self, type, value, traceback):
+ if type is None:
+ if self._dev.request("GET_FAIL") != "0:%s" % self._funcs:
+ raise Exception("Test failure did not trigger")
+
+def wait_fail_trigger(dev, cmd, note="Failure not triggered", max_iter=40,
+ timeout=0.05):
+ for i in range(0, max_iter):
+ if dev.request(cmd).startswith("0:"):
+ break
+ if i == max_iter - 1:
+ raise Exception(note)
+ time.sleep(timeout)
+
+def require_under_vm():
+ with open('/proc/1/cmdline', 'r') as f:
+ cmd = f.read()
+ if "inside.sh" not in cmd:
+ raise HwsimSkip("Not running under VM")
+
+def iface_is_in_bridge(bridge, ifname):
+ fname = "/sys/class/net/"+ifname+"/brport/bridge"
+ if not os.path.exists(fname):
+ return False
+ if not os.path.islink(fname):
+ return False
+ truebridge = os.path.basename(os.readlink(fname))
+ if bridge == truebridge:
+ return True
+ return False
+
+def skip_with_fips(dev, reason="Not supported in FIPS mode"):
+ res = dev.get_capability("fips")
+ if res and 'FIPS' in res:
+ raise HwsimSkip(reason)
+
+def check_ext_key_id_capa(dev):
+ res = dev.get_driver_status_field('capa.flags')
+ if (int(res, 0) & 0x8000000000000000) == 0:
+ raise HwsimSkip("Extended Key ID not supported")
+
+def skip_without_tkip(dev):
+ res = dev.get_capability("fips")
+ if "TKIP" not in dev.get_capability("pairwise") or \
+ "TKIP" not in dev.get_capability("group"):
+ raise HwsimSkip("Cipher TKIP not supported")
+
+def check_wep_capa(dev):
+ if "WEP40" not in dev.get_capability("group"):
+ raise HwsimSkip("WEP not supported")
+
+def check_sae_capab(dev):
+ if "SAE" not in dev.get_capability("auth_alg"):
+ raise HwsimSkip("SAE not supported")
+
+def check_sae_pk_capab(dev):
+ capab = dev.get_capability("sae")
+ if capab is None or "PK" not in capab:
+ raise HwsimSkip("SAE-PK not supported")
+
+def check_erp_capa(dev):
+ capab = dev.get_capability("erp")
+ if not capab or 'ERP' not in capab:
+ raise HwsimSkip("ERP not supported in the build")
+
+def check_fils_capa(dev):
+ capa = dev.get_capability("fils")
+ if capa is None or "FILS" not in capa:
+ raise HwsimSkip("FILS not supported")
+
+def check_fils_sk_pfs_capa(dev):
+ capa = dev.get_capability("fils")
+ if capa is None or "FILS-SK-PFS" not in capa:
+ raise HwsimSkip("FILS-SK-PFS not supported")
+
+def check_tls_tod(dev):
+ tls = dev.request("GET tls_library")
+ if not tls.startswith("OpenSSL") and not tls.startswith("internal"):
+ raise HwsimSkip("TLS TOD-TOFU/STRICT not supported with this TLS library: " + tls)
+
+def vht_supported():
+ cmd = subprocess.Popen(["iw", "reg", "get"], stdout=subprocess.PIPE)
+ reg = cmd.stdout.read()
+ if "@ 80)" in reg or "@ 160)" in reg:
+ return True
+ return False
+
+# This function checks whether the provided dev, which may be either
+# WpaSupplicant or Hostapd supports CSA.
+def csa_supported(dev):
+ res = dev.get_driver_status()
+ if (int(res['capa.flags'], 0) & 0x80000000) == 0:
+ raise HwsimSkip("CSA not supported")
+
+def get_phy(ap, ifname=None):
+ phy = "phy3"
+ try:
+ hostname = ap['hostname']
+ except:
+ hostname = None
+ host = remotehost.Host(hostname)
+
+ if ifname == None:
+ ifname = ap['ifname']
+ status, buf = host.execute(["iw", "dev", ifname, "info"])
+ if status != 0:
+ raise Exception("iw " + ifname + " info failed")
+ lines = buf.split("\n")
+ for line in lines:
+ if "wiphy" in line:
+ words = line.split()
+ phy = "phy" + words[1]
+ break
+ return phy
+
+def parse_ie(buf):
+ ret = {}
+ data = binascii.unhexlify(buf)
+ while len(data) >= 2:
+ ie, elen = struct.unpack('BB', data[0:2])
+ data = data[2:]
+ if elen > len(data):
+ break
+ ret[ie] = data[0:elen]
+ data = data[elen:]
+ return ret
+
+def wait_regdom_changes(dev):
+ for i in range(10):
+ ev = dev.wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=0.1)
+ if ev is None:
+ break
+
+def clear_country(dev):
+ logger.info("Try to clear country")
+ id = dev[1].add_network()
+ dev[1].set_network(id, "mode", "2")
+ dev[1].set_network_quoted(id, "ssid", "country-clear")
+ dev[1].set_network(id, "key_mgmt", "NONE")
+ dev[1].set_network(id, "frequency", "2412")
+ dev[1].set_network(id, "scan_freq", "2412")
+ dev[1].select_network(id)
+ ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"])
+ if ev:
+ dev[0].connect("country-clear", key_mgmt="NONE", scan_freq="2412")
+ dev[1].request("DISCONNECT")
+ dev[0].wait_disconnected()
+ dev[0].request("DISCONNECT")
+ dev[0].request("ABORT_SCAN")
+ time.sleep(1)
+ dev[0].dump_monitor()
+ dev[1].dump_monitor()
+
+def clear_regdom(hapd, dev, count=1):
+ disable_hapd(hapd)
+ clear_regdom_dev(dev, count)
+
+def disable_hapd(hapd):
+ if hapd:
+ hapd.request("DISABLE")
+ time.sleep(0.1)
+
+def clear_regdom_dev(dev, count=1):
+ for i in range(count):
+ dev[i].request("DISCONNECT")
+ for i in range(count):
+ dev[i].disconnect_and_stop_scan()
+ dev[0].cmd_execute(['iw', 'reg', 'set', '00'])
+ wait_regdom_changes(dev[0])
+ country = dev[0].get_driver_status_field("country")
+ logger.info("Country code at the end: " + country)
+ if country != "00":
+ clear_country(dev)
+ for i in range(count):
+ dev[i].flush_scan_cache()
+
+def radiotap_build():
+ radiotap_payload = struct.pack('BB', 0x08, 0)
+ radiotap_payload += struct.pack('BB', 0, 0)
+ radiotap_payload += struct.pack('BB', 0, 0)
+ radiotap_hdr = struct.pack('<BBHL', 0, 0, 8 + len(radiotap_payload),
+ 0xc002)
+ return radiotap_hdr + radiotap_payload
+
+def start_monitor(ifname, freq=2412):
+ subprocess.check_call(["iw", ifname, "set", "type", "monitor"])
+ subprocess.call(["ip", "link", "set", "dev", ifname, "up"])
+ subprocess.check_call(["iw", ifname, "set", "freq", str(freq)])
+
+ ETH_P_ALL = 3
+ sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW,
+ socket.htons(ETH_P_ALL))
+ sock.bind((ifname, 0))
+ sock.settimeout(0.5)
+ return sock
+
+def stop_monitor(ifname):
+ subprocess.call(["ip", "link", "set", "dev", ifname, "down"])
+ subprocess.call(["iw", ifname, "set", "type", "managed"])
+
+def clear_scan_cache(apdev):
+ ifname = apdev['ifname']
+ hostapd.cmd_execute(apdev, ['ifconfig', ifname, 'up'])
+ hostapd.cmd_execute(apdev, ['iw', ifname, 'scan', 'trigger', 'freq', '2412',
+ 'flush'])
+ time.sleep(0.1)
+ hostapd.cmd_execute(apdev, ['ifconfig', ifname, 'down'])
+
+def set_world_reg(apdev0=None, apdev1=None, dev0=None):
+ if apdev0:
+ hostapd.cmd_execute(apdev0, ['iw', 'reg', 'set', '00'])
+ if apdev1:
+ hostapd.cmd_execute(apdev1, ['iw', 'reg', 'set', '00'])
+ if dev0:
+ dev0.cmd_execute(['iw', 'reg', 'set', '00'])
+ time.sleep(0.1)
+
+def sysctl_write(val):
+ subprocess.call(['sysctl', '-w', val], stdout=open('/dev/null', 'w'))
+
+def var_arg_call(fn, dev, apdev, params):
+ if fn.__code__.co_argcount > 2:
+ return fn(dev, apdev, params)
+ elif fn.__code__.co_argcount > 1:
+ return fn(dev, apdev)
+ return fn(dev)
+
+def cloned_wrapper(wrapper, fn):
+ # we need the name set right for selecting / printing etc.
+ wrapper.__name__ = fn.__name__
+ wrapper.__doc__ = fn.__doc__
+ # reparent to the right module for module filtering
+ wrapper.__module__ = fn.__module__
+ return wrapper
+
+def disable_ipv6(fn):
+ def wrapper(dev, apdev, params):
+ require_under_vm()
+ try:
+ sysctl_write('net.ipv6.conf.all.disable_ipv6=1')
+ sysctl_write('net.ipv6.conf.default.disable_ipv6=1')
+ var_arg_call(fn, dev, apdev, params)
+ finally:
+ sysctl_write('net.ipv6.conf.all.disable_ipv6=0')
+ sysctl_write('net.ipv6.conf.default.disable_ipv6=0')
+ return cloned_wrapper(wrapper, fn)
+
+def reset_ignore_old_scan_res(fn):
+ def wrapper(dev, apdev, params):
+ try:
+ var_arg_call(fn, dev, apdev, params)
+ finally:
+ dev[0].set("ignore_old_scan_res", "0")
+ return cloned_wrapper(wrapper, fn)
diff --git a/contrib/wpa/tests/hwsim/vm/.gitignore b/contrib/wpa/tests/hwsim/vm/.gitignore
new file mode 100644
index 000000000000..b1ce1b1050f5
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/.gitignore
@@ -0,0 +1 @@
+vm-config
diff --git a/contrib/wpa/tests/hwsim/vm/README b/contrib/wpa/tests/hwsim/vm/README
new file mode 100644
index 000000000000..224d65a26109
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/README
@@ -0,0 +1,80 @@
+These scripts allow you to run the hwsim tests inside a KVM virtual machine or
+as a UML (User Mode Linux) program.
+
+To set it up, first compile a kernel with the kernel-config[.uml] file as the
+.config. You can adjust it as needed, the configuration is for a 64-bit x86
+system and should be close to minimal. The architecture must be the same as
+your host since the host's filesystem is used.
+
+To build the regular x86_64 kernel, simply issue
+
+yes "" | make -j <n_cpus>
+
+or to build UML:
+
+yes "" | ARCH=um make -j <n_cpus>
+
+Running a UML kernel is recommended as it can optimize out any sleep()s or
+kernel timers by taking advantage of UML time travel mode, greatly increasing
+test efficiency (~3200 tests can be run in under 5 minutes using parallel-vm.py
+on a 24 core CPU).
+
+Install the required tools: at least 'kvm', if you want tracing trace-cmd,
+valgrind if you want, etc.
+
+Compile the hwsim tests as per the instructions given, you may have to
+install some extra development packages (e.g. binutils-dev for libbfd).
+
+Create a vm-config file and put the KERNELDIR option into it (see the
+vm-run.sh script). If you want valgrind, also increase the memory size.
+
+Now you can run the vm-run.sh script and it will execute the tests using
+your system's root filesystem (read-only) inside the VM. The options you
+give it are passed through to run-all.sh, see there.
+
+To speed up testing, it is possible to run multiple VMs concurrently and
+split the test cases between all the VMs. If the host system has enough
+memory and CPU resources, this can significantly speed up the full test
+cycle. For example, a 4 core system with 4 GB of RAM can easily run 8
+parallel VMs (assuming valgrind is not used with its higher memory
+requirements). This can be run with:
+
+./parallel-vm.py <number of VMs> [arguments..]
+
+
+--------------------------------------------------------------------------------
+
+Code Coverage Analysis for user space code
+
+Code coverage for wpa_supplicant and hostapd can be generated from the
+test run with following command line:
+
+./vm-run.sh --codecov [other arguments..]
+
+This builds a separate copies of wpa_supplicant and hostapd into a
+directory that is writable from the virtual machine to collect the gcov
+data. lcov is then used to prepare the reports at the end of the test
+run.
+
+
+Code Coverage Analysis for kernel code
+
+In order to do code coverage analysis, reconfigure the kernel to include
+
+CONFIG_GCOV_KERNEL=y
+CONFIG_GCOV_PROFILE_ALL=y
+
+Note that for gcc 4.7, kernel version 3.13-rc1 or higher is required.
+
+The scripts inside the VM will automatically copy the gcov data out of the
+VM into the logs directory. To post-process this data, you'll want to use
+lcov and run
+
+cd /tmp/hwsim-test-logs/<timestamp>
+lcov -b <path to kernel dir> -c -d gcov/ > gcov/data
+genhtml -o html/ gcov/data
+
+Then open html/index.html in your browser.
+
+Note that in this case you need to keep your build and source directories
+across the test run (otherwise, it's safe to only keep the kernel image.)
diff --git a/contrib/wpa/tests/hwsim/vm/bisect-run.sh b/contrib/wpa/tests/hwsim/vm/bisect-run.sh
new file mode 100755
index 000000000000..fa511073f0db
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/bisect-run.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+set -e
+
+path="$(dirname $0)"
+
+test="$1"
+makedir="$2"
+if [ -z $test ] ; then
+ echo "This script helps bisect test failures, given a test case."
+ echo ""
+ echo "Use it like this:"
+ echo " git bisect start"
+ echo " git bisect bad <commit>"
+ echo " git bisect good <commit>"
+ echo " git bisect run $0 <test name> [<compile directory>]"
+ echo ""
+ echo "(the compile directory is optional, use it if you want to"
+ echo "use an out-of-tree kernel build."
+ echo ""
+ echo "Note that, of course, you have to have a working vm-run setup."
+ exit 200 # exit git bisect run if called that way
+fi
+
+if [ -n "$makedir" ] ; then
+ cd "$makedir"
+fi
+
+yes '' | make oldconfig || exit 125
+make -j8 || exit 125
+
+output=$(mktemp)
+if [ $? -ne 0 ] ; then
+ exit 202
+fi
+finish() {
+ rm -f $output
+}
+trap finish EXIT
+
+"$path/vm-run.sh" $test 2>&1 | tee $output
+
+grep -q 'ALL-PASSED' $output && exit 0 || exit 1
diff --git a/contrib/wpa/tests/hwsim/vm/build-codecov.sh b/contrib/wpa/tests/hwsim/vm/build-codecov.sh
new file mode 100755
index 000000000000..e67ef2ea8e0e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/build-codecov.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+LOGDIR=$1
+DIR=$PWD
+TMPDIR=/tmp/logs
+
+if [ -e $TMPDIR ]; then
+ echo "$TMPDIR exists - cannot prepare build trees"
+ exit 1
+fi
+mkdir $TMPDIR
+echo "Preparing separate build trees for hostapd/wpa_supplicant"
+cd ../../..
+git archive --format=tar --prefix=hostap/ HEAD > $TMPDIR/hostap.tar
+cd $DIR
+cat ../../../wpa_supplicant/.config > $TMPDIR/wpa_supplicant.config
+echo "CONFIG_CODE_COVERAGE=y" >> $TMPDIR/wpa_supplicant.config
+cat ../../../hostapd/.config > $TMPDIR/hostapd.config
+echo "CONFIG_CODE_COVERAGE=y" >> $TMPDIR/hostapd.config
+
+cd $TMPDIR
+tar xf hostap.tar
+mv hostap alt-wpa_supplicant
+mv wpa_supplicant.config alt-wpa_supplicant/wpa_supplicant/.config
+tar xf hostap.tar
+mv hostap alt-hostapd
+cp hostapd.config alt-hostapd/hostapd/.config
+tar xf hostap.tar
+mv hostap alt-hostapd-as
+cp hostapd.config alt-hostapd-as/hostapd/.config
+tar xf hostap.tar
+mv hostap alt-hlr_auc_gw
+mv hostapd.config alt-hlr_auc_gw/hostapd/.config
+rm hostap.tar
+
+cd $TMPDIR/alt-wpa_supplicant/wpa_supplicant
+echo "Building wpa_supplicant"
+make -j8 > /dev/null
+
+cd $TMPDIR/alt-hostapd/hostapd
+echo "Building hostapd"
+make -j8 hostapd hostapd_cli > /dev/null
+
+cd $TMPDIR/alt-hostapd-as/hostapd
+echo "Building hostapd (AS)"
+make -j8 hostapd hostapd_cli > /dev/null
+
+cd $TMPDIR/alt-hlr_auc_gw/hostapd
+echo "Building hlr_auc_gw"
+make -j8 hlr_auc_gw > /dev/null
+
+cd $DIR
+
+mv $TMPDIR/alt-wpa_supplicant $LOGDIR
+mv $TMPDIR/alt-hostapd $LOGDIR
+mv $TMPDIR/alt-hostapd-as $LOGDIR
+mv $TMPDIR/alt-hlr_auc_gw $LOGDIR
diff --git a/contrib/wpa/tests/hwsim/vm/combine-codecov.sh b/contrib/wpa/tests/hwsim/vm/combine-codecov.sh
new file mode 100755
index 000000000000..309125f22b7b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/combine-codecov.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+LOGDIR=$1
+if [ -n "$2" ]; then
+ ODIR=$2
+else
+ ODIR=.
+fi
+TMPDIR=/tmp/logs
+
+mv $LOGDIR/alt-* $TMPDIR
+
+cd $TMPDIR
+args=""
+for i in lcov-*.info-*; do
+ args="$args -a $i"
+done
+
+lcov $args -o $LOGDIR/combined.info > $LOGDIR/combined-lcov.log 2>&1
+cat $LOGDIR/combined.info |
+ sed "/^TN:$/{N;s/TN:\n\(SF:.*\/bits\/byteswap.h$\)/\1/};/^SF:.*\/bits\/byteswap.h$/,/^end_of_record$/d" |
+ sed "/^TN:$/{N;s/TN:\n\(SF:.*\/openssl\/x509.h$\)/\1/};/^SF:.*\/openssl\/x509.h$/,/^end_of_record$/d" |
+ sed "/^TN:$/{N;s/TN:\n\(SF:.*\/openssl\/x509v3.h$\)/\1/};/^SF:.*\/openssl\/x509v3.h$/,/^end_of_record$/d" |
+ sed "/^TN:$/{N;s/TN:\n\(SF:.*\/common\/wpa_ctrl.c$\)/\1/};/^SF:.*\/common\/wpa_ctrl.c$/,/^end_of_record$/d" |
+ sed "/^TN:$/{N;s/TN:\n\(SF:.*\/common\/cli.c$\)/\1/};/^SF:.*\/common\/cli.c$/,/^end_of_record$/d" |
+ sed "/^TN:$/{N;s/TN:\n\(SF:.*\/utils\/edit.c$\)/\1/};/^SF:.*\/utils\/edit.c$/,/^end_of_record$/d" |
+ sed "/^TN:$/{N;s/TN:\n\(SF:.*_module_tests.c$\)/\1/};/^SF:.*_module_tests.c$/,/^end_of_record$/d" |
+ sed "/^TN:$/{N;s/TN:\n\(SF:.*\/hostapd\/hostapd_cli.c$\)/\1/};/^SF:.*\/hostapd\/hostapd_cli.c$/,/^end_of_record$/d" |
+ sed "/^TN:$/{N;s/TN:\n\(SF:.*wpa_supplicant\/wpa_cli.c$\)/\1/};/^SF:.*wpa_supplicant\/wpa_cli.c$/,/^end_of_record$/d" > $LOGDIR/combined.info.filtered
+
+cd $LOGDIR
+genhtml -t "wpa_supplicant/hostapd combined for hwsim test run $(date +%s)" combined.info.filtered --output-directory $ODIR > lcov.log 2>&1
+
+rm -r /tmp/logs/alt-wpa_supplicant
+rm -r /tmp/logs/alt-hostapd
+rm -r /tmp/logs/alt-hostapd-as
+rm -r /tmp/logs/alt-hlr_auc_gw
+rm /tmp/logs/lcov-*info-*
+rmdir /tmp/logs
diff --git a/contrib/wpa/tests/hwsim/vm/dbus.conf b/contrib/wpa/tests/hwsim/vm/dbus.conf
new file mode 100644
index 000000000000..1f3b56353c88
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/dbus.conf
@@ -0,0 +1,34 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <type>system</type>
+ <user>messagebus</user>
+ <fork/>
+ <standard_system_servicedirs/>
+ <servicehelper>/usr/lib/dbus-1.0/dbus-daemon-launch-helper</servicehelper>
+ <pidfile>/var/run/dbus/pid</pidfile>
+ <auth>EXTERNAL</auth>
+ <listen>unix:path=/var/run/dbus/system_bus_socket</listen>
+ <policy context="default">
+ <allow user="*"/>
+ <deny own="*"/>
+ <deny send_type="method_call"/>
+ <allow send_type="signal"/>
+ <allow send_requested_reply="true" send_type="method_return"/>
+ <allow send_requested_reply="true" send_type="error"/>
+ <allow receive_type="method_call"/>
+ <allow receive_type="method_return"/>
+ <allow receive_type="error"/>
+ <allow receive_type="signal"/>
+ <allow send_destination="org.freedesktop.DBus"/>
+ <deny send_destination="org.freedesktop.DBus"
+ send_interface="org.freedesktop.DBus"
+ send_member="UpdateActivationEnvironment"/>
+ </policy>
+ <policy user="root">
+ <allow own="fi.w1.wpa_supplicant1"/>
+ <allow send_destination="fi.w1.wpa_supplicant1"/>
+ <allow send_interface="fi.w1.wpa_supplicant1"/>
+ <allow receive_sender="fi.w1.wpa_supplicant1" receive_type="signal"/>
+ </policy>
+</busconfig>
diff --git a/contrib/wpa/tests/hwsim/vm/example-vm-setup.txt b/contrib/wpa/tests/hwsim/vm/example-vm-setup.txt
new file mode 100644
index 000000000000..81e2dfdb9ffe
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/example-vm-setup.txt
@@ -0,0 +1,95 @@
+Step-by-step guide for setting up hostapd/wpa_supplicant test framework (VM)
+----------------------------------------------------------------------------
+
+This document can be used as a quick guide for getting started with
+hostapd/wpa_supplicant test framework with mac80211_hwsim. While the
+example here uses Ubuntu 16.04.1 server to have a list of exact steps,
+there are no requirements for using that specific distribution in the
+testing setup.
+
+The steps here describe how to run a guest VM for testing on a Linux
+host system.
+
+
+Install Ubuntu Server 16.04.1 as the host system for VMs
+
+- download installation image, e.g.,
+ http://releases.ubuntu.com/16.04.1/ubuntu-16.04.1-server-amd64.iso
+- install the host system with default settings
+- boot to the installed system
+- update the installed packages:
+ sudo apt update
+ sudo apt upgrade
+
+
+Install the prerequisite packages that may not have been installed by default
+
+# kvm for running the VM guests
+sudo apt install qemu-kvm
+
+# build tools
+sudo apt install build-essential git libpcap-dev libsqlite3-dev binutils-dev \
+ bc pkg-config libssl-dev libiberty-dev libdbus-1-dev \
+ libnl-3-dev libnl-genl-3-dev libnl-route-3-dev
+
+# tools used be the test scripts
+sudo apt install python-minimal python-crypto python-pyrad python-netifaces \
+ python-dbus python-gobject python-openssl bridge-utils ebtables tshark
+
+
+Enable kvm use for the user
+
+sudo adduser $USER kvm
+
+
+Download a snapshot of the hostap.git repository and build the programs
+
+cd
+git clone git://w1.fi/hostap.git
+cd hostap/tests/hwsim
+./build.sh
+cd vm
+cat > vm-config <<EOF
+KERNELDIR=~/wireless-testing
+MEMORY=512
+KVMARGS="-cpu host"
+EOF
+
+
+Build a Linux kernel for testing
+
+cd
+git clone git://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-testing.git
+cd wireless-testing
+cp ~/hostap/tests/hwsim/vm/kernel-config .config
+make oldconfig
+make -j8
+
+
+Setup is now ready for testing. You can run a quick test to confirm that
+things work as expected:
+
+cd ~/hostap/tests/hwsim/vm
+./vm-run ap_open
+
+This should print out following style results:
+
+Starting test run in a virtual machine
+./run-all.sh: passing the following args to run-tests.py: ap_open
+START ap_open 1/1
+PASS ap_open 0.924019 2017-01-28 20:20:12.137717
+passed all 1 test case(s)
+ALL-PASSED
+
+Test run completed
+Logfiles are at /tmp/hwsim-test-logs/1485634801
+
+(If that "PASS ap_open" line does not show up, something unexpected has
+happened and the setup is not in working condition.)
+
+
+To run all available test cases in 7 parallel VMs, you can run
+following:
+
+cd ~/hostap/tests/hwsim/vm
+./parallel-vm.py 7
diff --git a/contrib/wpa/tests/hwsim/vm/inside.sh b/contrib/wpa/tests/hwsim/vm/inside.sh
new file mode 100755
index 000000000000..9d4a933fe729
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/inside.sh
@@ -0,0 +1,169 @@
+#!/bin/sh
+
+# keep old /etc
+mount tmpfs -t tmpfs /tmp
+mkdir /tmp/etc
+mount --bind /etc /tmp/etc
+# mount all kinds of things
+mount tmpfs -t tmpfs /etc
+# we need our own /dev/rfkill, and don't want device access
+mount tmpfs -t tmpfs /dev
+# some sockets go into /var/run, and / is read-only
+mount tmpfs -t tmpfs /var/run
+mount proc -t proc /proc
+mount sysfs -t sysfs /sys
+# needed for tracing
+mount debugfs -t debugfs /sys/kernel/debug
+
+mkdir /tmp/wireshark-share
+mount --bind /usr/share/wireshark /tmp/wireshark-share
+mount tmpfs -t tmpfs /usr/share/wireshark
+
+# for inside telnet
+mkdir /dev/pts
+mount devpts -t devpts /dev/pts
+
+export PATH=/usr/sbin:$PATH
+export HOME=/tmp
+
+# reboot on any sort of crash
+sysctl kernel.panic_on_oops=1
+sysctl kernel.panic=1
+
+# get extra command line variables from /proc/cmdline
+TESTDIR=$(sed 's/.*testdir=\([^ ]*\) .*/\1/' /proc/cmdline)
+TIMEWARP=$(sed 's/.*timewarp=\([^ ]*\) .*/\1/' /proc/cmdline)
+EPATH=$(sed 's/.*EPATH=\([^ ]*\) .*/\1/' /proc/cmdline)
+TELNET=$(sed 's/.*TELNET=\([^ ]*\) .*/\1/' /proc/cmdline)
+ARGS=$(sed 's/.*ARGS=\([^ ]*\)\( \|$\).*/\1/' /proc/cmdline)
+LOGDIR=$(sed 's/.*LOGDIR=\([^ ]*\)\( \|$\).*/\1/' /proc/cmdline)
+
+# create /dev entries we need
+mknod -m 660 /dev/ttyS0 c 4 64
+mknod -m 666 /dev/ptmx c 5 2
+mknod -m 660 /dev/random c 1 8
+mknod -m 660 /dev/urandom c 1 9
+mknod -m 666 /dev/null c 1 3
+mknod -m 666 /dev/kmsg c 1 11
+test -f /sys/class/misc/rfkill/dev && \
+ mknod -m 660 /dev/rfkill c $(cat /sys/class/misc/rfkill/dev | tr ':' ' ')
+ln -s /proc/self/fd/0 /dev/stdin
+ln -s /proc/self/fd/1 /dev/stdout
+ln -s /proc/self/fd/2 /dev/stderr
+
+echo "VM has started up" > /dev/ttyS0
+
+# create dummy sudo - everything runs as uid 0
+mkdir /tmp/bin
+cat > /tmp/bin/sudo << EOF
+#!/bin/bash
+
+exec "\$@"
+EOF
+chmod +x /tmp/bin/sudo
+# and put it into $PATH, as well as our extra-$PATH
+export PATH=/tmp/bin:$EPATH:$PATH
+
+# some tests assume adm/admin group(s) exist(s)
+cat > /etc/group <<EOF
+adm:x:0:
+admin:x:0:
+messagebus:x:106:
+EOF
+# root should exist
+cat > /etc/passwd <<EOF
+root:x:0:0:root:/tmp:/bin/bash
+messagebus:x:102:106::/var/run/dbus:/bin/false
+EOF
+cat > /etc/ethertypes <<EOF
+IPv4 0800 ip ip4
+ARP 0806 ether-arp
+IPv6 86DD ip6
+EOF
+cat > /etc/protocols <<EOF
+ip 0 IP
+icmp 1 ICMP
+tcp 6 TCP
+udp 17 UDP
+ipv6-icmp 58 IPv6-ICMP
+EOF
+
+# we may need /etc/alternatives, at least on Debian-based systems
+ln -s /tmp/etc/alternatives /etc/
+
+# local network is needed for some tests
+ip link set lo up
+
+# create logs mountpoint and mount the logshare
+mkdir /tmp/logs
+if grep -q rootfstype=hostfs /proc/cmdline; then
+ mount -t hostfs none /tmp/logs -o $LOGDIR
+else
+ mount -t 9p -o trans=virtio,rw logshare /tmp/logs
+fi
+
+# allow access to any outside directory (e.g. /tmp) we also have
+mkdir /tmp/host
+mount --bind / /tmp/host
+
+if [ "$TIMEWARP" = "1" ] ; then
+ (
+ while sleep 1 ; do
+ date --set "@$(($(date +%s) + 19))"
+ done
+ ) &
+fi
+
+echo hwsimvm > /proc/sys/kernel/hostname
+echo 8 8 8 8 > /proc/sys/kernel/printk
+
+cat > /tmp/bin/login <<EOF
+#!/bin/sh
+
+export PS1='\h:\w\$ '
+exec bash
+EOF
+chmod +x /tmp/bin/login
+
+if [ "$TELNET" = "1" ] ; then
+ ip link set eth0 up
+ ip addr add 172.16.0.15/24 dev eth0
+ which in.telnetd >/dev/null && (
+ while true ; do
+ in.telnetd -debug 23 -L /tmp/bin/login
+ done
+ ) &
+fi
+
+# check if we're rebooting due to a kernel panic ...
+if grep -q 'Kernel panic' /tmp/logs/console ; then
+ echo "KERNEL CRASHED!" >/dev/ttyS0
+else
+ # finally run the tests
+ export USER=0
+ export LOGDIR=/tmp/logs
+ export DBFILE=$LOGDIR/results.db
+ export PREFILL_DB=y
+
+ # some tests need CRDA, install a simple uevent helper
+ # and preload the 00 domain it will have asked for already
+ echo $TESTDIR/vm/uevent.sh > /sys/kernel/uevent_helper
+ COUNTRY=00 crda
+
+ mkdir -p /var/run/dbus
+ touch /var/run/dbus/hwsim-test
+ chown messagebus.messagebus /var/run/dbus
+ dbus-daemon --config-file=$TESTDIR/vm/dbus.conf --fork
+
+ cd $TESTDIR
+ ./run-all.sh --vm $(cat /tmp/host$ARGS) </dev/ttyS0 >/dev/ttyS0 2>&1
+ if test -d /sys/kernel/debug/gcov ; then
+ cp -ar /sys/kernel/debug/gcov /tmp/logs/
+ # these are broken as they're updated while being read ...
+ find /tmp/logs/gcov/ -wholename '*kernel/gcov/*' -print0 | xargs -0 rm
+ fi
+ #bash </dev/ttyS0 >/dev/ttyS0 2>&1
+fi
+
+# and shut down the machine again
+halt -f -p
diff --git a/contrib/wpa/tests/hwsim/vm/kernel-config b/contrib/wpa/tests/hwsim/vm/kernel-config
new file mode 100644
index 000000000000..2aff20af49ad
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/kernel-config
@@ -0,0 +1,175 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_KERNEL_BZIP2=y
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+# CONFIG_CROSS_MEMORY_ATTACH is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_PREEMPT=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_LOG_BUF_SHIFT=21
+CONFIG_NAMESPACES=y
+# CONFIG_FHANDLE is not set
+CONFIG_EMBEDDED=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SMP=y
+# CONFIG_X86_EXTENDED_PLATFORM is not set
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_PARAVIRT=y
+CONFIG_PARAVIRT_SPINLOCKS=y
+CONFIG_MCORE2=y
+CONFIG_GART_IOMMU=y
+CONFIG_NR_CPUS=4
+# CONFIG_X86_MCE is not set
+CONFIG_MICROCODE_OLD_INTERFACE=y
+# CONFIG_MTRR_SANITIZER is not set
+# CONFIG_SECCOMP is not set
+CONFIG_HZ_100=y
+# CONFIG_RELOCATABLE is not set
+CONFIG_PHYSICAL_ALIGN=0x1000000
+CONFIG_LEGACY_VSYSCALL_EMULATE=y
+# CONFIG_SUSPEND is not set
+# CONFIG_ACPI_AC is not set
+# CONFIG_ACPI_BATTERY is not set
+# CONFIG_ACPI_BUTTON is not set
+# CONFIG_ACPI_FAN is not set
+CONFIG_CPU_IDLE_GOV_LADDER=y
+# CONFIG_PCI_MMCONFIG is not set
+# CONFIG_ISA_DMA_API is not set
+# CONFIG_DMIID is not set
+# CONFIG_VIRTUALIZATION is not set
+CONFIG_JUMP_LABEL=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_MAC_PARTITION=y
+# CONFIG_COMPACTION is not set
+# CONFIG_BOUNCE is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+# CONFIG_INET_DIAG is not set
+CONFIG_NETFILTER=y
+CONFIG_BRIDGE_NETFILTER=y
+CONFIG_NF_TABLES=y
+CONFIG_NETFILTER_XTABLES=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NF_TABLES_BRIDGE=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_T_FILTER=y
+CONFIG_BRIDGE_EBT_ARP=y
+CONFIG_BRIDGE_EBT_IP=y
+CONFIG_BRIDGE_EBT_IP6=y
+CONFIG_BRIDGE_EBT_PKTTYPE=y
+CONFIG_BRIDGE_EBT_ARPREPLY=y
+CONFIG_BRIDGE=y
+CONFIG_VLAN_8021Q=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_DEVELOPER_WARNINGS=y
+CONFIG_CFG80211_DEBUGFS=y
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=y
+CONFIG_MAC80211_MESH=y
+CONFIG_MAC80211_DEBUGFS=y
+CONFIG_MAC80211_MESSAGE_TRACING=y
+CONFIG_MAC80211_DEBUG_MENU=y
+CONFIG_MAC80211_NOINLINE=y
+CONFIG_MAC80211_VERBOSE_DEBUG=y
+CONFIG_MAC80211_MLME_DEBUG=y
+CONFIG_MAC80211_STA_DEBUG=y
+CONFIG_MAC80211_HT_DEBUG=y
+CONFIG_MAC80211_IBSS_DEBUG=y
+CONFIG_MAC80211_PS_DEBUG=y
+CONFIG_MAC80211_TDLS_DEBUG=y
+CONFIG_RFKILL=y
+CONFIG_NET_9P=y
+CONFIG_NET_9P_VIRTIO=y
+CONFIG_PCI=y
+CONFIG_UEVENT_HELPER=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_PNP_DEBUG_MESSAGES is not set
+# CONFIG_BLK_DEV is not set
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_MACSEC=y
+CONFIG_VETH=y
+# CONFIG_ETHERNET is not set
+CONFIG_MAC80211_HWSIM=y
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_PNP is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_SERIAL_8250_MID is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_HWMON is not set
+CONFIG_FB=y
+CONFIG_FB_MODE_HELPERS=y
+CONFIG_FB_VESA=y
+CONFIG_VGACON_SOFT_SCROLLBACK=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_HIDRAW=y
+# CONFIG_USB_SUPPORT is not set
+CONFIG_VIRT_DRIVERS=y
+CONFIG_VIRTIO_PCI=y
+# CONFIG_X86_PLATFORM_DEVICES is not set
+# CONFIG_IOMMU_SUPPORT is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+CONFIG_ISO9660_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_CONFIGFS_FS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_9P_FS=y
+CONFIG_9P_FS_POSIX_ACL=y
+CONFIG_CRYPTO_ECHAINIV=y
+CONFIG_CRYPTO_CRCT10DIF=y
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_HW is not set
+CONFIG_PRINTK_TIME=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_REDUCED=y
+CONFIG_FRAME_WARN=1024
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_PAGE_EXTENSION=y
+CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_DEBUG_RODATA_TEST=y
+CONFIG_DEBUG_OBJECTS=y
+CONFIG_DEBUG_OBJECTS_SELFTEST=y
+CONFIG_DEBUG_OBJECTS_FREE=y
+CONFIG_DEBUG_OBJECTS_TIMERS=y
+CONFIG_DEBUG_OBJECTS_WORK=y
+CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
+CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
+CONFIG_SLUB_DEBUG_ON=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_STACK_USAGE=y
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_HARDLOCKUP_DETECTOR=y
+CONFIG_PROVE_LOCKING=y
+CONFIG_LOCK_STAT=y
+CONFIG_DEBUG_LOCKDEP=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+CONFIG_DEBUG_KOBJECT=y
+CONFIG_DEBUG_LIST=y
+CONFIG_DEBUG_NOTIFIERS=y
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+# CONFIG_RCU_TRACE is not set
+CONFIG_LATENCYTOP=y
+CONFIG_FUNCTION_TRACER=y
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_X86_VERBOSE_BOOTUP is not set
diff --git a/contrib/wpa/tests/hwsim/vm/kernel-config.uml b/contrib/wpa/tests/hwsim/vm/kernel-config.uml
new file mode 100644
index 000000000000..b0f2f65ac390
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/kernel-config.uml
@@ -0,0 +1,131 @@
+CONFIG_SYSVIPC=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_LOG_BUF_SHIFT=14
+CONFIG_CGROUPS=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+# CONFIG_PID_NS is not set
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SLAB=y
+CONFIG_HOSTFS=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_MMAPPER=y
+# CONFIG_SECCOMP is not set
+CONFIG_UML_TIME_TRAVEL_SUPPORT=y
+CONFIG_SSL=y
+CONFIG_NULL_CHAN=y
+CONFIG_PORT_CHAN=y
+CONFIG_PTY_CHAN=y
+CONFIG_TTY_CHAN=y
+CONFIG_XTERM_CHAN=y
+CONFIG_CON_CHAN="pts"
+CONFIG_SSL_CHAN="pts"
+CONFIG_UML_NET=y
+CONFIG_UML_NET_TUNTAP=y
+CONFIG_UML_NET_VECTOR=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_MQ_IOSCHED_DEADLINE is not set
+# CONFIG_MQ_IOSCHED_KYBER is not set
+CONFIG_BINFMT_MISC=y
+# CONFIG_COMPACTION is not set
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_BRIDGE_NETFILTER=y
+CONFIG_NF_TABLES=y
+CONFIG_NETFILTER_XTABLES=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NF_TABLES_BRIDGE=y
+CONFIG_BRIDGE_NF_EBTABLES=y
+CONFIG_BRIDGE_EBT_T_FILTER=y
+CONFIG_BRIDGE_EBT_ARP=y
+CONFIG_BRIDGE_EBT_IP=y
+CONFIG_BRIDGE_EBT_IP6=y
+CONFIG_BRIDGE_EBT_PKTTYPE=y
+CONFIG_BRIDGE_EBT_ARPREPLY=y
+CONFIG_BRIDGE=y
+CONFIG_BRIDGE_VLAN_FILTERING=y
+CONFIG_VLAN_8021Q=y
+CONFIG_CFG80211=y
+CONFIG_CFG80211_DEBUGFS=y
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=y
+CONFIG_MAC80211_MESH=y
+CONFIG_MAC80211_DEBUGFS=y
+CONFIG_MAC80211_MESSAGE_TRACING=y
+CONFIG_MAC80211_DEBUG_MENU=y
+CONFIG_MAC80211_VERBOSE_DEBUG=y
+CONFIG_MAC80211_MLME_DEBUG=y
+CONFIG_MAC80211_STA_DEBUG=y
+CONFIG_MAC80211_HT_DEBUG=y
+CONFIG_MAC80211_OCB_DEBUG=y
+CONFIG_MAC80211_IBSS_DEBUG=y
+CONFIG_MAC80211_PS_DEBUG=y
+CONFIG_MAC80211_TDLS_DEBUG=y
+CONFIG_MAC80211_DEBUG_COUNTERS=y
+CONFIG_RFKILL=y
+CONFIG_UEVENT_HELPER=y
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_BLK_DEV_UBD=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_DUMMY=y
+CONFIG_MACSEC=y
+CONFIG_VETH=y
+# CONFIG_ETHERNET is not set
+# CONFIG_WLAN_VENDOR_ADMTEK is not set
+# CONFIG_WLAN_VENDOR_ATH is not set
+# CONFIG_WLAN_VENDOR_ATMEL is not set
+# CONFIG_WLAN_VENDOR_BROADCOM is not set
+# CONFIG_WLAN_VENDOR_CISCO is not set
+# CONFIG_WLAN_VENDOR_INTEL is not set
+# CONFIG_WLAN_VENDOR_INTERSIL is not set
+# CONFIG_WLAN_VENDOR_MARVELL is not set
+# CONFIG_WLAN_VENDOR_MEDIATEK is not set
+# CONFIG_WLAN_VENDOR_RALINK is not set
+# CONFIG_WLAN_VENDOR_REALTEK is not set
+# CONFIG_WLAN_VENDOR_RSI is not set
+# CONFIG_WLAN_VENDOR_ST is not set
+# CONFIG_WLAN_VENDOR_TI is not set
+# CONFIG_WLAN_VENDOR_ZYDAS is not set
+# CONFIG_WLAN_VENDOR_QUANTENNA is not set
+CONFIG_MAC80211_HWSIM=y
+CONFIG_LEGACY_PTY_COUNT=32
+# CONFIG_HW_RANDOM is not set
+CONFIG_UML_RANDOM=y
+# CONFIG_IOMMU_SUPPORT is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS=y
+CONFIG_LSM="yama,loadpin,safesetid,integrity"
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRC16=y
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_FRAME_WARN=1024
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
+CONFIG_PREEMPTIRQ_EVENTS=y
+# CONFIG_RUNTIME_TESTING_MENU is not set
diff --git a/contrib/wpa/tests/hwsim/vm/parallel-vm.py b/contrib/wpa/tests/hwsim/vm/parallel-vm.py
new file mode 100755
index 000000000000..86565c677493
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/parallel-vm.py
@@ -0,0 +1,669 @@
+#!/usr/bin/env python3
+#
+# Parallel VM test case executor
+# Copyright (c) 2014-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from __future__ import print_function
+import curses
+import fcntl
+import logging
+import multiprocessing
+import os
+import selectors
+import subprocess
+import sys
+import time
+import errno
+
+logger = logging.getLogger()
+
+# Test cases that take significantly longer time to execute than average.
+long_tests = ["ap_roam_open",
+ "wpas_mesh_password_mismatch_retry",
+ "wpas_mesh_password_mismatch",
+ "hostapd_oom_wpa2_psk_connect",
+ "ap_hs20_fetch_osu_stop",
+ "ap_roam_wpa2_psk",
+ "ibss_wpa_none_ccmp",
+ "nfc_wps_er_handover_pk_hash_mismatch_sta",
+ "go_neg_peers_force_diff_freq",
+ "p2p_cli_invite",
+ "sta_ap_scan_2b",
+ "ap_pmf_sta_unprot_deauth_burst",
+ "ap_bss_add_remove_during_ht_scan",
+ "wext_scan_hidden",
+ "autoscan_exponential",
+ "nfc_p2p_client",
+ "wnm_bss_keep_alive",
+ "ap_inactivity_disconnect",
+ "scan_bss_expiration_age",
+ "autoscan_periodic",
+ "discovery_group_client",
+ "concurrent_p2pcli",
+ "ap_bss_add_remove",
+ "wpas_ap_wps",
+ "wext_pmksa_cache",
+ "ibss_wpa_none",
+ "ap_ht_40mhz_intolerant_ap",
+ "ibss_rsn",
+ "discovery_pd_retries",
+ "ap_wps_setup_locked_timeout",
+ "ap_vht160",
+ 'he160',
+ 'he160b',
+ "dfs_radar",
+ "dfs",
+ "dfs_ht40_minus",
+ "dfs_etsi",
+ "dfs_radar_vht80_downgrade",
+ "ap_acs_dfs",
+ "grpform_cred_ready_timeout",
+ "hostapd_oom_wpa2_eap_connect",
+ "wpas_ap_dfs",
+ "autogo_many",
+ "hostapd_oom_wpa2_eap",
+ "ibss_open",
+ "proxyarp_open_ebtables",
+ "proxyarp_open_ebtables_ipv6",
+ "radius_failover",
+ "obss_scan_40_intolerant",
+ "dbus_connect_oom",
+ "proxyarp_open",
+ "proxyarp_open_ipv6",
+ "ap_wps_iteration",
+ "ap_wps_iteration_error",
+ "ap_wps_pbc_timeout",
+ "ap_wps_pbc_ap_timeout",
+ "ap_wps_pin_ap_timeout",
+ "ap_wps_http_timeout",
+ "p2p_go_move_reg_change",
+ "p2p_go_move_active",
+ "p2p_go_move_scm",
+ "p2p_go_move_scm_peer_supports",
+ "p2p_go_move_scm_peer_does_not_support",
+ "p2p_go_move_scm_multi"]
+
+def get_failed(vm):
+ failed = []
+ for i in range(num_servers):
+ failed += vm[i]['failed']
+ return failed
+
+def vm_read_stdout(vm, test_queue):
+ global total_started, total_passed, total_failed, total_skipped
+ global rerun_failures
+ global first_run_failures
+
+ ready = False
+ try:
+ out = vm['proc'].stdout.read()
+ if out == None:
+ return False
+ out = out.decode()
+ except IOError as e:
+ if e.errno == errno.EAGAIN:
+ return False
+ raise
+ logger.debug("VM[%d] stdout.read[%s]" % (vm['idx'], out.rstrip()))
+ pending = vm['pending'] + out
+ lines = []
+ while True:
+ pos = pending.find('\n')
+ if pos < 0:
+ break
+ line = pending[0:pos].rstrip()
+ pending = pending[(pos + 1):]
+ logger.debug("VM[%d] stdout full line[%s]" % (vm['idx'], line))
+ if line.startswith("READY"):
+ vm['starting'] = False
+ vm['started'] = True
+ ready = True
+ elif line.startswith("PASS"):
+ ready = True
+ total_passed += 1
+ elif line.startswith("FAIL"):
+ ready = True
+ total_failed += 1
+ vals = line.split(' ')
+ if len(vals) < 2:
+ logger.info("VM[%d] incomplete FAIL line: %s" % (vm['idx'],
+ line))
+ name = line
+ else:
+ name = vals[1]
+ logger.debug("VM[%d] test case failed: %s" % (vm['idx'], name))
+ vm['failed'].append(name)
+ if name != vm['current_name']:
+ logger.info("VM[%d] test result mismatch: %s (expected %s)" % (vm['idx'], name, vm['current_name']))
+ else:
+ count = vm['current_count']
+ if count == 0:
+ first_run_failures.append(name)
+ if rerun_failures and count < 1:
+ logger.debug("Requeue test case %s" % name)
+ test_queue.append((name, vm['current_count'] + 1))
+ elif line.startswith("NOT-FOUND"):
+ ready = True
+ total_failed += 1
+ logger.info("VM[%d] test case not found" % vm['idx'])
+ elif line.startswith("SKIP"):
+ ready = True
+ total_skipped += 1
+ elif line.startswith("REASON"):
+ vm['skip_reason'].append(line[7:])
+ elif line.startswith("START"):
+ total_started += 1
+ if len(vm['failed']) == 0:
+ vals = line.split(' ')
+ if len(vals) >= 2:
+ vm['fail_seq'].append(vals[1])
+ vm['out'] += line + '\n'
+ lines.append(line)
+ vm['pending'] = pending
+ return ready
+
+def start_vm(vm, sel):
+ logger.info("VM[%d] starting up" % (vm['idx'] + 1))
+ vm['starting'] = True
+ vm['proc'] = subprocess.Popen(vm['cmd'],
+ stdin=subprocess.PIPE,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ vm['cmd'] = None
+ for stream in [vm['proc'].stdout, vm['proc'].stderr]:
+ fd = stream.fileno()
+ fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+ fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+ sel.register(stream, selectors.EVENT_READ, vm)
+
+def num_vm_starting():
+ count = 0
+ for i in range(num_servers):
+ if vm[i]['starting']:
+ count += 1
+ return count
+
+def vm_read_stderr(vm):
+ try:
+ err = vm['proc'].stderr.read()
+ if err != None:
+ err = err.decode()
+ if len(err) > 0:
+ vm['err'] += err
+ logger.info("VM[%d] stderr.read[%s]" % (vm['idx'], err))
+ except IOError as e:
+ if e.errno != errno.EAGAIN:
+ raise
+
+def vm_next_step(_vm, scr, test_queue):
+ scr.move(_vm['idx'] + 1, 10)
+ scr.clrtoeol()
+ if not test_queue:
+ _vm['proc'].stdin.write(b'\n')
+ _vm['proc'].stdin.flush()
+ scr.addstr("shutting down")
+ logger.info("VM[%d] shutting down" % _vm['idx'])
+ return
+ (name, count) = test_queue.pop(0)
+ _vm['current_name'] = name
+ _vm['current_count'] = count
+ _vm['proc'].stdin.write(name.encode() + b'\n')
+ _vm['proc'].stdin.flush()
+ scr.addstr(name)
+ logger.debug("VM[%d] start test %s" % (_vm['idx'], name))
+
+def check_vm_start(scr, sel, test_queue):
+ running = False
+ for i in range(num_servers):
+ if vm[i]['proc']:
+ running = True
+ continue
+
+ # Either not yet started or already stopped VM
+ max_start = multiprocessing.cpu_count()
+ if max_start > 4:
+ max_start /= 2
+ num_starting = num_vm_starting()
+ if vm[i]['cmd'] and len(test_queue) > num_starting and \
+ num_starting < max_start:
+ scr.move(i + 1, 10)
+ scr.clrtoeol()
+ scr.addstr(i + 1, 10, "starting VM")
+ start_vm(vm[i], sel)
+ return True, True
+
+ return running, False
+
+def vm_terminated(_vm, scr, sel, test_queue):
+ updated = False
+ for stream in [_vm['proc'].stdout, _vm['proc'].stderr]:
+ sel.unregister(stream)
+ _vm['proc'] = None
+ scr.move(_vm['idx'] + 1, 10)
+ scr.clrtoeol()
+ log = '{}/{}.srv.{}/console'.format(dir, timestamp, _vm['idx'] + 1)
+ with open(log, 'r') as f:
+ if "Kernel panic" in f.read():
+ scr.addstr("kernel panic")
+ logger.info("VM[%d] kernel panic" % _vm['idx'])
+ updated = True
+ if test_queue:
+ num_vm = 0
+ for i in range(num_servers):
+ if _vm['proc']:
+ num_vm += 1
+ if len(test_queue) > num_vm:
+ scr.addstr("unexpected exit")
+ logger.info("VM[%d] unexpected exit" % i)
+ updated = True
+ return updated
+
+def update_screen(scr, total_tests):
+ scr.move(num_servers + 1, 10)
+ scr.clrtoeol()
+ scr.addstr("{} %".format(int(100.0 * (total_passed + total_failed + total_skipped) / total_tests)))
+ scr.addstr(num_servers + 1, 20,
+ "TOTAL={} STARTED={} PASS={} FAIL={} SKIP={}".format(total_tests, total_started, total_passed, total_failed, total_skipped))
+ failed = get_failed(vm)
+ if len(failed) > 0:
+ scr.move(num_servers + 2, 0)
+ scr.clrtoeol()
+ scr.addstr("Failed test cases: ")
+ count = 0
+ for f in failed:
+ count += 1
+ if count > 30:
+ scr.addstr('...')
+ scr.clrtoeol()
+ break
+ scr.addstr(f)
+ scr.addstr(' ')
+ scr.refresh()
+
+def show_progress(scr):
+ global num_servers
+ global vm
+ global dir
+ global timestamp
+ global tests
+ global first_run_failures
+ global total_started, total_passed, total_failed, total_skipped
+ global rerun_failures
+
+ sel = selectors.DefaultSelector()
+ total_tests = len(tests)
+ logger.info("Total tests: %d" % total_tests)
+ test_queue = [(t, 0) for t in tests]
+ start_vm(vm[0], sel)
+
+ scr.leaveok(1)
+ scr.addstr(0, 0, "Parallel test execution status", curses.A_BOLD)
+ for i in range(0, num_servers):
+ scr.addstr(i + 1, 0, "VM %d:" % (i + 1), curses.A_BOLD)
+ status = "starting VM" if vm[i]['proc'] else "not yet started"
+ scr.addstr(i + 1, 10, status)
+ scr.addstr(num_servers + 1, 0, "Total:", curses.A_BOLD)
+ scr.addstr(num_servers + 1, 20, "TOTAL={} STARTED=0 PASS=0 FAIL=0 SKIP=0".format(total_tests))
+ scr.refresh()
+
+ while True:
+ updated = False
+ events = sel.select(timeout=1)
+ for key, mask in events:
+ _vm = key.data
+ if not _vm['proc']:
+ continue
+ vm_read_stderr(_vm)
+ if vm_read_stdout(_vm, test_queue):
+ vm_next_step(_vm, scr, test_queue)
+ updated = True
+ vm_read_stderr(_vm)
+ if _vm['proc'].poll() is not None:
+ if vm_terminated(_vm, scr, sel, test_queue):
+ updated = True
+
+ running, run_update = check_vm_start(scr, sel, test_queue)
+ if updated or run_update:
+ update_screen(scr, total_tests)
+ if not running:
+ break
+ sel.close()
+
+ for i in range(num_servers):
+ if not vm[i]['proc']:
+ continue
+ vm[i]['proc'] = None
+ scr.move(i + 1, 10)
+ scr.clrtoeol()
+ scr.addstr("still running")
+ logger.info("VM[%d] still running" % i)
+
+ scr.refresh()
+ time.sleep(0.3)
+
+def known_output(tests, line):
+ if not line:
+ return True
+ if line in tests:
+ return True
+ known = ["START ", "PASS ", "FAIL ", "SKIP ", "REASON ", "ALL-PASSED",
+ "READY",
+ " ", "Exception: ", "Traceback (most recent call last):",
+ "./run-all.sh: running",
+ "./run-all.sh: passing",
+ "Test run completed", "Logfiles are at", "Starting test run",
+ "passed all", "skipped ", "failed tests:"]
+ for k in known:
+ if line.startswith(k):
+ return True
+ return False
+
+def main():
+ import argparse
+ import os
+ global num_servers
+ global vm
+ global dir
+ global timestamp
+ global tests
+ global first_run_failures
+ global total_started, total_passed, total_failed, total_skipped
+ global rerun_failures
+
+ total_started = 0
+ total_passed = 0
+ total_failed = 0
+ total_skipped = 0
+
+ debug_level = logging.INFO
+ rerun_failures = True
+ timestamp = int(time.time())
+
+ scriptsdir = os.path.dirname(os.path.realpath(sys.argv[0]))
+
+ p = argparse.ArgumentParser(description='run multiple testing VMs in parallel')
+ p.add_argument('num_servers', metavar='number of VMs', type=int, choices=range(1, 100),
+ help="number of VMs to start")
+ p.add_argument('-f', dest='testmodules', metavar='<test module>',
+ help='execute only tests from these test modules',
+ type=str, nargs='+')
+ p.add_argument('-1', dest='no_retry', action='store_const', const=True, default=False,
+ help="don't retry failed tests automatically")
+ p.add_argument('--debug', dest='debug', action='store_const', const=True, default=False,
+ help="enable debug logging")
+ p.add_argument('--codecov', dest='codecov', action='store_const', const=True, default=False,
+ help="enable code coverage collection")
+ p.add_argument('--shuffle-tests', dest='shuffle', action='store_const', const=True, default=False,
+ help="shuffle test cases to randomize order")
+ p.add_argument('--short', dest='short', action='store_const', const=True,
+ default=False,
+ help="only run short-duration test cases")
+ p.add_argument('--long', dest='long', action='store_const', const=True,
+ default=False,
+ help="include long-duration test cases")
+ p.add_argument('--valgrind', dest='valgrind', action='store_const',
+ const=True, default=False,
+ help="run tests under valgrind")
+ p.add_argument('--telnet', dest='telnet', metavar='<baseport>', type=int,
+ help="enable telnet server inside VMs, specify the base port here")
+ p.add_argument('--nocurses', dest='nocurses', action='store_const',
+ const=True, default=False, help="Don't use curses for output")
+ p.add_argument('params', nargs='*')
+ args = p.parse_args()
+
+ dir = os.environ.get('HWSIM_TEST_LOG_DIR', '/tmp/hwsim-test-logs')
+ try:
+ os.makedirs(dir)
+ except OSError as e:
+ if e.errno != errno.EEXIST:
+ raise
+
+ num_servers = args.num_servers
+ rerun_failures = not args.no_retry
+ if args.debug:
+ debug_level = logging.DEBUG
+ extra_args = []
+ if args.valgrind:
+ extra_args += ['--valgrind']
+ if args.long:
+ extra_args += ['--long']
+ if args.codecov:
+ print("Code coverage - build separate binaries")
+ logdir = os.path.join(dir, str(timestamp))
+ os.makedirs(logdir)
+ subprocess.check_call([os.path.join(scriptsdir, 'build-codecov.sh'),
+ logdir])
+ codecov_args = ['--codecov_dir', logdir]
+ codecov = True
+ else:
+ codecov_args = []
+ codecov = False
+
+ first_run_failures = []
+ if args.params:
+ tests = args.params
+ else:
+ tests = []
+ cmd = [os.path.join(os.path.dirname(scriptsdir), 'run-tests.py'), '-L']
+ if args.testmodules:
+ cmd += ["-f"]
+ cmd += args.testmodules
+ lst = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ for l in lst.stdout.readlines():
+ name = l.decode().split(' ')[0]
+ tests.append(name)
+ if len(tests) == 0:
+ sys.exit("No test cases selected")
+
+ if args.shuffle:
+ from random import shuffle
+ shuffle(tests)
+ elif num_servers > 2 and len(tests) > 100:
+ # Move test cases with long duration to the beginning as an
+ # optimization to avoid last part of the test execution running a long
+ # duration test case on a single VM while all other VMs have already
+ # completed their work.
+ for l in long_tests:
+ if l in tests:
+ tests.remove(l)
+ tests.insert(0, l)
+ if args.short:
+ tests = [t for t in tests if t not in long_tests]
+
+ logger.setLevel(debug_level)
+ if not args.nocurses:
+ log_handler = logging.FileHandler('parallel-vm.log')
+ else:
+ log_handler = logging.StreamHandler(sys.stdout)
+ log_handler.setLevel(debug_level)
+ fmt = "%(asctime)s %(levelname)s %(message)s"
+ log_formatter = logging.Formatter(fmt)
+ log_handler.setFormatter(log_formatter)
+ logger.addHandler(log_handler)
+
+ vm = {}
+ for i in range(0, num_servers):
+ cmd = [os.path.join(scriptsdir, 'vm-run.sh'),
+ '--timestamp', str(timestamp),
+ '--ext', 'srv.%d' % (i + 1),
+ '-i'] + codecov_args + extra_args
+ if args.telnet:
+ cmd += ['--telnet', str(args.telnet + i)]
+ vm[i] = {}
+ vm[i]['idx'] = i
+ vm[i]['starting'] = False
+ vm[i]['started'] = False
+ vm[i]['cmd'] = cmd
+ vm[i]['proc'] = None
+ vm[i]['out'] = ""
+ vm[i]['pending'] = ""
+ vm[i]['err'] = ""
+ vm[i]['failed'] = []
+ vm[i]['fail_seq'] = []
+ vm[i]['skip_reason'] = []
+ print('')
+
+ if not args.nocurses:
+ curses.wrapper(show_progress)
+ else:
+ class FakeScreen:
+ def leaveok(self, n):
+ pass
+ def refresh(self):
+ pass
+ def addstr(self, *args, **kw):
+ pass
+ def move(self, x, y):
+ pass
+ def clrtoeol(self):
+ pass
+ show_progress(FakeScreen())
+
+ with open('{}/{}-parallel.log'.format(dir, timestamp), 'w') as f:
+ for i in range(0, num_servers):
+ f.write('VM {}\n{}\n{}\n'.format(i + 1, vm[i]['out'], vm[i]['err']))
+ first = True
+ for i in range(0, num_servers):
+ for line in vm[i]['out'].splitlines():
+ if line.startswith("FAIL "):
+ if first:
+ first = False
+ print("Logs for failed test cases:")
+ f.write("Logs for failed test cases:\n")
+ fname = "%s/%d.srv.%d/%s.log" % (dir, timestamp, i + 1,
+ line.split(' ')[1])
+ print(fname)
+ f.write("%s\n" % fname)
+
+ failed = get_failed(vm)
+
+ if first_run_failures:
+ print("To re-run same failure sequence(s):")
+ for i in range(0, num_servers):
+ if len(vm[i]['failed']) == 0:
+ continue
+ print("./vm-run.sh", end=' ')
+ if args.long:
+ print("--long", end=' ')
+ skip = len(vm[i]['fail_seq'])
+ skip -= min(skip, 30)
+ for t in vm[i]['fail_seq']:
+ if skip > 0:
+ skip -= 1
+ continue
+ print(t, end=' ')
+ print('')
+ print("Failed test cases:")
+ for f in first_run_failures:
+ print(f, end=' ')
+ logger.info("Failed: " + f)
+ print('')
+ double_failed = []
+ for name in failed:
+ double_failed.append(name)
+ for test in first_run_failures:
+ double_failed.remove(test)
+ if not rerun_failures:
+ pass
+ elif failed and not double_failed:
+ print("All failed cases passed on retry")
+ logger.info("All failed cases passed on retry")
+ elif double_failed:
+ print("Failed even on retry:")
+ for f in double_failed:
+ print(f, end=' ')
+ logger.info("Failed on retry: " + f)
+ print('')
+ res = "TOTAL={} PASS={} FAIL={} SKIP={}".format(total_started,
+ total_passed,
+ total_failed,
+ total_skipped)
+ print(res)
+ logger.info(res)
+ print("Logs: " + dir + '/' + str(timestamp))
+ logger.info("Logs: " + dir + '/' + str(timestamp))
+
+ skip_reason = []
+ for i in range(num_servers):
+ if not vm[i]['started']:
+ continue
+ skip_reason += vm[i]['skip_reason']
+ if len(vm[i]['pending']) > 0:
+ logger.info("Unprocessed stdout from VM[%d]: '%s'" %
+ (i, vm[i]['pending']))
+ log = '{}/{}.srv.{}/console'.format(dir, timestamp, i + 1)
+ with open(log, 'r') as f:
+ if "Kernel panic" in f.read():
+ print("Kernel panic in " + log)
+ logger.info("Kernel panic in " + log)
+ missing = {}
+ missing['OCV not supported'] = 'OCV'
+ missing['sigma_dut not available'] = 'sigma_dut'
+ missing['Skip test case with long duration due to --long not specified'] = 'long'
+ missing['TEST_ALLOC_FAIL not supported' ] = 'TEST_FAIL'
+ missing['TEST_ALLOC_FAIL not supported in the build'] = 'TEST_FAIL'
+ missing['TEST_FAIL not supported' ] = 'TEST_FAIL'
+ missing['veth not supported (kernel CONFIG_VETH)'] = 'KERNEL:CONFIG_VETH'
+ missing['WPA-EAP-SUITE-B-192 not supported'] = 'CONFIG_SUITEB192'
+ missing['WPA-EAP-SUITE-B not supported'] = 'CONFIG_SUITEB'
+ missing['wmediumd not available'] = 'wmediumd'
+ missing['DPP not supported'] = 'CONFIG_DPP'
+ missing['DPP version 2 not supported'] = 'CONFIG_DPP2'
+ missing['EAP method PWD not supported in the build'] = 'CONFIG_EAP_PWD'
+ missing['EAP method TEAP not supported in the build'] = 'CONFIG_EAP_TEAP'
+ missing['FILS not supported'] = 'CONFIG_FILS'
+ missing['FILS-SK-PFS not supported'] = 'CONFIG_FILS_SK_PFS'
+ missing['OWE not supported'] = 'CONFIG_OWE'
+ missing['SAE not supported'] = 'CONFIG_SAE'
+ missing['Not using OpenSSL'] = 'CONFIG_TLS=openssl'
+ missing['wpa_supplicant TLS library is not OpenSSL: internal'] = 'CONFIG_TLS=openssl'
+ missing_items = []
+ other_reasons = []
+ for reason in sorted(set(skip_reason)):
+ if reason in missing:
+ missing_items.append(missing[reason])
+ elif reason.startswith('OCSP-multi not supported with this TLS library'):
+ missing_items.append('OCSP-MULTI')
+ else:
+ other_reasons.append(reason)
+ if missing_items:
+ print("Missing items (SKIP):", missing_items)
+ if other_reasons:
+ print("Other skip reasons:", other_reasons)
+
+ for i in range(num_servers):
+ unknown = ""
+ for line in vm[i]['out'].splitlines():
+ if not known_output(tests, line):
+ unknown += line + "\n"
+ if unknown:
+ print("\nVM %d - unexpected stdout output:\n%s" % (i, unknown))
+ if vm[i]['err']:
+ print("\nVM %d - unexpected stderr output:\n%s\n" % (i, vm[i]['err']))
+
+ if codecov:
+ print("Code coverage - preparing report")
+ for i in range(num_servers):
+ subprocess.check_call([os.path.join(scriptsdir,
+ 'process-codecov.sh'),
+ logdir + ".srv.%d" % (i + 1),
+ str(i)])
+ subprocess.check_call([os.path.join(scriptsdir, 'combine-codecov.sh'),
+ logdir])
+ print("file://%s/index.html" % logdir)
+ logger.info("Code coverage report: file://%s/index.html" % logdir)
+
+ if double_failed or (failed and not rerun_failures):
+ logger.info("Test run complete - failures found")
+ sys.exit(2)
+ if failed:
+ logger.info("Test run complete - failures found on first run; passed on retry")
+ sys.exit(1)
+ logger.info("Test run complete - no failures")
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
diff --git a/contrib/wpa/tests/hwsim/vm/process-codecov.sh b/contrib/wpa/tests/hwsim/vm/process-codecov.sh
new file mode 100755
index 000000000000..d932aa2d011e
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/process-codecov.sh
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+LOGDIR=$1
+POSTFIX=$2
+RESTORE=$3
+
+DIR=$PWD
+TMPDIR=/tmp/logs
+
+mv $LOGDIR/alt-wpa_supplicant $TMPDIR
+mv $LOGDIR/alt-hostapd $TMPDIR
+mv $LOGDIR/alt-hostapd-as $TMPDIR
+mv $LOGDIR/alt-hlr_auc_gw $TMPDIR
+
+cd $TMPDIR/alt-wpa_supplicant/wpa_supplicant
+lcov -c -d .. 2> lcov.log | sed s%SF:/tmp/logs/alt-[^/]*/%SF:/tmp/logs/alt-wpa_supplicant/% > $TMPDIR/lcov-wpa_supplicant.info-$POSTFIX &
+
+cd $TMPDIR/alt-hostapd/hostapd
+lcov -c -d .. 2> lcov.log | sed s%SF:/tmp/logs/alt-[^/]*/%SF:/tmp/logs/alt-wpa_supplicant/% > $TMPDIR/lcov-hostapd.info-$POSTFIX &
+
+cd $TMPDIR/alt-hostapd-as/hostapd
+lcov -c -d .. 2> lcov.log | sed s%SF:/tmp/logs/alt-[^/]*/%SF:/tmp/logs/alt-wpa_supplicant/% > $TMPDIR/lcov-hostapd-as.info-$POSTFIX &
+
+cd $TMPDIR/alt-hlr_auc_gw/hostapd
+lcov -c -d .. 2> lcov.log | sed s%SF:/tmp/logs/alt-[^/]*/%SF:/tmp/logs/alt-wpa_supplicant/% > $TMPDIR/lcov-hlr_auc_gw.info-$POSTFIX &
+wait
+
+cd $DIR
+if [ "$RESTORE" == "restore" ]; then
+ mv $TMPDIR/alt-* $LOGDIR
+else
+ rm -r $TMPDIR/alt-wpa_supplicant
+ rm -r $TMPDIR/alt-hostapd
+ rm -r $TMPDIR/alt-hostapd-as
+ rm -r $TMPDIR/alt-hlr_auc_gw
+fi
diff --git a/contrib/wpa/tests/hwsim/vm/uevent.sh b/contrib/wpa/tests/hwsim/vm/uevent.sh
new file mode 100755
index 000000000000..76e31e76d3be
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/uevent.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+EPATH=$(sed 's/.*EPATH=\([^ ]*\) .*/\1/' /proc/cmdline)
+PATH=/tmp/bin:$EPATH:$PATH
+
+# assume this was a call for CRDA,
+# if not then it won't find a COUNTRY
+# environment variable and exit
+exec crda
diff --git a/contrib/wpa/tests/hwsim/vm/vm-run.sh b/contrib/wpa/tests/hwsim/vm/vm-run.sh
new file mode 100755
index 000000000000..06dee068960b
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/vm/vm-run.sh
@@ -0,0 +1,202 @@
+#!/bin/bash
+
+cd "$(dirname $0)"
+
+if [ -z "$TESTDIR" ] ; then
+ TESTDIR=$(pwd)/../
+fi
+
+if [ -n "$HWSIM_TEST_LOG_DIR" ] ; then
+ LOGS="$HWSIM_TEST_LOG_DIR"
+else
+ LOGS=/tmp/hwsim-test-logs
+fi
+
+# increase the memory size if you want to run with valgrind, 512 MB works
+MEMORY=256
+
+# Some ubuntu systems (notably 12.04) have issues with this - since the guest
+# mounts as read-only it should be safe to not specify ,readonly. Override in
+# vm-config if needed (see below)
+ROTAG=,readonly
+
+# set this to ttyS0 to see kvm messages (if something doesn't work)
+KVMOUT=ttyS1
+
+# you can set EPATH if you need anything extra in $PATH inside the VM
+#EPATH=/some/dir
+
+# extra KVM arguments, e.g., -s for gdbserver
+#KVMARGS=-s
+
+# number of channels each hwsim device supports
+CHANNELS=1
+
+test -f vm-config && . vm-config
+test -f ~/.wpas-vm-config && . ~/.wpas-vm-config
+
+if [ -z "$KERNEL" ] && [ -z "$KERNELDIR" ] ; then
+ echo "You need to set a KERNEL or KERNELDIR (in the environment or vm-config)"
+ exit 2
+fi
+if [ -z "$KERNEL" ] ; then
+ if [ -e $KERNELDIR/arch/x86_64/boot/bzImage ]; then
+ KERNEL=$KERNELDIR/arch/x86_64/boot/bzImage
+ elif [ -e $KERNELDIR/linux ]; then
+ KERNEL=$KERNELDIR/linux
+ else
+ echo "No suitable kernel image found from KERNELDIR"
+ exit 2
+ fi
+fi
+if [ ! -e $KERNEL ]; then
+ echo "Kernel image not found: $KERNEL"
+ exit 2
+fi
+
+
+CMD=$TESTDIR/vm/inside.sh
+
+unset RUN_TEST_ARGS
+TIMESTAMP=$(date +%s)
+DATE=$TIMESTAMP
+CODECOV=no
+TIMEWARP=0
+TELNET_QEMU=
+TELNET_ARG=0
+CODECOV_DIR=
+while [ "$1" != "" ]; do
+ case $1 in
+ --timestamp ) shift
+ TIMESTAMP=$1
+ shift
+ ;;
+ --ext ) shift
+ DATE=$TIMESTAMP.$1
+ shift
+ ;;
+ --codecov ) shift
+ CODECOV=yes
+ ;;
+ --codecov_dir ) shift
+ CODECOV_DIR=$1
+ shift
+ ;;
+ --timewrap ) shift
+ TIMEWARP=1
+ ;;
+ --telnet ) shift
+ TELNET_ARG=1
+ TELNET_QEMU="-net nic,model=virtio -net user,id=telnet,restrict=on,net=172.16.0.0/24,hostfwd=tcp:127.0.0.1:$1-:23"
+ shift
+ ;;
+ * )
+ RUN_TEST_ARGS="$RUN_TEST_ARGS$1 "
+ shift
+ ;;
+ esac
+done
+
+LOGDIR=$LOGS/$DATE
+mkdir -p $LOGDIR
+rm -f $LOGS/latest
+ln -s $LOGDIR $LOGS/latest
+
+if [ -n "$CODECOV_DIR" ]; then
+ cp -a $CODECOV_DIR/alt-wpa_supplicant $LOGDIR
+ cp -a $CODECOV_DIR/alt-hostapd $LOGDIR
+ cp -a $CODECOV_DIR/alt-hostapd-as $LOGDIR
+ cp -a $CODECOV_DIR/alt-hlr_auc_gw $LOGDIR
+elif [ $CODECOV = "yes" ]; then
+ ./build-codecov.sh $LOGDIR || exit 1
+else
+ CODECOV=no
+fi
+
+echo "Starting test run in a virtual machine"
+
+if [ -x $KERNEL ]; then
+ unset KVM
+else
+ KVM=kvm
+ for kvmprog in kvm qemu-kvm; do
+ if $kvmprog --version &> /dev/null; then
+ KVM=$kvmprog
+ break
+ fi
+ done
+fi
+
+argsfile=$(mktemp)
+if [ $? -ne 0 ] ; then
+ exit 2
+fi
+function finish {
+ rm -f $argsfile
+}
+trap finish EXIT
+
+if [ -z $KVM ]; then
+ RUN_TEST_ARGS="--long $RUN_TEST_ARGS"
+fi
+echo "$RUN_TEST_ARGS" > $argsfile
+
+A="mac80211_hwsim.support_p2p_device=0 "
+A+="mac80211_hwsim.channels=$CHANNELS "
+A+="mac80211_hwsim.radios=7 "
+A+="cfg80211.dyndbg=+p "
+A+="mac80211.dyndbg=+p "
+A+="mac80211_hwsim.dyndbg=+p "
+A+="init=$CMD "
+A+="testdir=$TESTDIR "
+A+="timewarp=$TIMEWARP "
+A+="TELNET=$TELNET_ARG "
+A+="EPATH=$EPATH "
+A+="ARGS=$argsfile "
+A+="console=$KVMOUT "
+A+="ro"
+
+if [ -z $KVM ]; then
+ $KERNEL \
+ mem=${MEMORY}M \
+ LOGDIR=$LOGDIR \
+ time-travel=inf-cpu \
+ $A \
+ root=none hostfs=/ rootfstype=hostfs rootflags=/ \
+ ssl0=fd:0,fd:1 \
+ ssl1=fd:100 \
+ ssl-non-raw \
+ 100<>$LOGDIR/console 2>&1 | \
+ sed -u '0,/VM has started up/d'
+else
+ $KVM \
+ -kernel $KERNEL \
+ -smp 4 \
+ $KVMARGS \
+ -m $MEMORY \
+ -nographic \
+ -fsdev local,security_model=none,id=fsdev-root,path=/$ROTAG \
+ -device virtio-9p-pci,id=fs-root,fsdev=fsdev-root,mount_tag=/dev/root \
+ -fsdev local,security_model=none,id=fsdev-logs,path="$LOGDIR",writeout=immediate \
+ -device virtio-9p-pci,id=fs-logs,fsdev=fsdev-logs,mount_tag=logshare \
+ -monitor null \
+ -serial stdio \
+ -serial file:$LOGDIR/console \
+ $TELNET_QEMU \
+ -append "$A root=/dev/root rootflags=trans=virtio,version=9p2000.u rootfstype=9p" | \
+ sed -u '0,/VM has started up/d'
+fi
+
+if [ $CODECOV = "yes" ]; then
+ echo "Preparing code coverage reports"
+ ./process-codecov.sh $LOGDIR "" restore
+ ./combine-codecov.sh $LOGDIR lcov
+fi
+
+echo
+echo "Test run completed"
+echo "Logfiles are at $LOGDIR ($LOGS/latest)"
+if [ $CODECOV = "yes" ]; then
+ echo "Code coverage report:"
+ echo "file://$LOGDIR/lcov/index.html"
+fi
diff --git a/contrib/wpa/tests/hwsim/w1fi_logo.png b/contrib/wpa/tests/hwsim/w1fi_logo.png
new file mode 100644
index 000000000000..ac7c259fff2e
Binary files /dev/null and b/contrib/wpa/tests/hwsim/w1fi_logo.png differ
diff --git a/contrib/wpa/tests/hwsim/wlantest.py b/contrib/wpa/tests/hwsim/wlantest.py
new file mode 100644
index 000000000000..16765d27a9de
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/wlantest.py
@@ -0,0 +1,277 @@
+# Python class for controlling wlantest
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import re
+import os
+import posixpath
+import time
+import subprocess
+import logging
+import wpaspy
+
+logger = logging.getLogger()
+
+class Wlantest:
+ remote_host = None
+ setup_params = None
+ exe_thread = None
+ exe_res = []
+ monitor_mod = None
+ setup_done = False
+
+ @classmethod
+ def stop_remote_wlantest(cls):
+ if cls.exe_thread is None:
+ # Local flow - no need for remote operations
+ return
+
+ cls.remote_host.execute(["killall", "-9", "wlantest"])
+ cls.remote_host.thread_wait(cls.exe_thread, 5)
+ cls.exe_thread = None
+ cls.exe_res = []
+
+ @classmethod
+ def reset_remote_wlantest(cls):
+ cls.stop_remote_wlantest()
+ cls.remote_host = None
+ cls.setup_params = None
+ cls.exe_thread = None
+ cls.exe_res = []
+ cls.monitor_mod = None
+ cls.setup_done = False
+
+ @classmethod
+ def start_remote_wlantest(cls):
+ if cls.remote_host is None:
+ # Local flow - no need for remote operations
+ return
+ if cls.exe_thread is not None:
+ raise Exception("Cannot start wlantest twice")
+
+ log_dir = cls.setup_params['log_dir']
+ ifaces = re.split('; | |, ', cls.remote_host.ifname)
+ ifname = ifaces[0]
+ exe = cls.setup_params["wlantest"]
+ tc_name = cls.setup_params["tc_name"]
+ base_log_name = tc_name + "_wlantest_" + \
+ cls.remote_host.name + "_" + ifname
+ log_file = posixpath.join(log_dir, base_log_name + ".log")
+ pcap_file = posixpath.join(log_dir, base_log_name + ".pcapng")
+ cmd = "{} -i {} -n {} -c -dtN -L {}".format(exe, ifname,
+ pcap_file, log_file)
+ cls.remote_host.add_log(log_file)
+ cls.remote_host.add_log(pcap_file)
+ cls.exe_thread = cls.remote_host.thread_run(cmd.split(), cls.exe_res)
+ # Give wlantest a chance to start working
+ time.sleep(1)
+
+ @classmethod
+ def register_remote_wlantest(cls, host, setup_params, monitor_mod):
+ if cls.remote_host is not None:
+ raise Exception("Cannot register remote wlantest twice")
+ cls.remote_host = host
+ cls.setup_params = setup_params
+ cls.monitor_mod = monitor_mod
+ status, buf = host.execute(["which", setup_params['wlantest']])
+ if status != 0:
+ raise Exception(host.name + " - wlantest: " + buf)
+ status, buf = host.execute(["which", setup_params['wlantest_cli']])
+ if status != 0:
+ raise Exception(host.name + " - wlantest_cli: " + buf)
+
+ @classmethod
+ def chan_from_wpa(cls, wpa, is_p2p=False):
+ if cls.monitor_mod is None:
+ return
+ m = cls.monitor_mod
+ return m.setup(cls.remote_host, [m.get_monitor_params(wpa, is_p2p)])
+
+ @classmethod
+ def setup(cls, wpa, is_p2p=False):
+ if wpa:
+ cls.chan_from_wpa(wpa, is_p2p)
+ cls.start_remote_wlantest()
+ cls.setup_done = True
+
+ def __init__(self):
+ if not self.setup_done:
+ raise Exception("Cannot create Wlantest instance before setup()")
+ if os.path.isfile('../../wlantest/wlantest_cli'):
+ self.wlantest_cli = '../../wlantest/wlantest_cli'
+ else:
+ self.wlantest_cli = 'wlantest_cli'
+
+ def cli_cmd(self, params):
+ if self.remote_host is not None:
+ exe = self.setup_params["wlantest_cli"]
+ ret = self.remote_host.execute([exe] + params)
+ if ret[0] != 0:
+ raise Exception("wlantest_cli failed")
+ return ret[1]
+ else:
+ return subprocess.check_output([self.wlantest_cli] + params).decode()
+
+ def flush(self):
+ res = self.cli_cmd(["flush"])
+ if "FAIL" in res:
+ raise Exception("wlantest_cli flush failed")
+
+ def relog(self):
+ res = self.cli_cmd(["relog"])
+ if "FAIL" in res:
+ raise Exception("wlantest_cli relog failed")
+
+ def add_passphrase(self, passphrase):
+ res = self.cli_cmd(["add_passphrase", passphrase])
+ if "FAIL" in res:
+ raise Exception("wlantest_cli add_passphrase failed")
+
+ def add_wepkey(self, key):
+ res = self.cli_cmd(["add_wepkey", key])
+ if "FAIL" in res:
+ raise Exception("wlantest_cli add_key failed")
+
+ def info_bss(self, field, bssid):
+ res = self.cli_cmd(["info_bss", field, bssid])
+ if "FAIL" in res:
+ raise Exception("Could not get BSS info from wlantest for " + bssid)
+ return res
+
+ def get_bss_counter(self, field, bssid):
+ try:
+ res = self.cli_cmd(["get_bss_counter", field, bssid])
+ except Exception as e:
+ return 0
+ if "FAIL" in res:
+ return 0
+ return int(res)
+
+ def clear_bss_counters(self, bssid):
+ self.cli_cmd(["clear_bss_counters", bssid])
+
+ def info_sta(self, field, bssid, addr):
+ res = self.cli_cmd(["info_sta", field, bssid, addr])
+ if "FAIL" in res:
+ raise Exception("Could not get STA info from wlantest for " + addr)
+ return res
+
+ def get_sta_counter(self, field, bssid, addr):
+ res = self.cli_cmd(["get_sta_counter", field, bssid, addr])
+ if "FAIL" in res:
+ raise Exception("wlantest_cli command failed")
+ return int(res)
+
+ def clear_sta_counters(self, bssid, addr):
+ res = self.cli_cmd(["clear_sta_counters", bssid, addr])
+ if "FAIL" in res:
+ raise Exception("wlantest_cli command failed")
+
+ def tdls_clear(self, bssid, addr1, addr2):
+ self.cli_cmd(["clear_tdls_counters", bssid, addr1, addr2])
+
+ def get_tdls_counter(self, field, bssid, addr1, addr2):
+ res = self.cli_cmd(["get_tdls_counter", field, bssid, addr1, addr2])
+ if "FAIL" in res:
+ raise Exception("wlantest_cli command failed")
+ return int(res)
+
+ def require_ap_pmf_mandatory(self, bssid):
+ res = self.info_bss("rsn_capab", bssid)
+ if "MFPR" not in res:
+ raise Exception("AP did not require PMF")
+ if "MFPC" not in res:
+ raise Exception("AP did not enable PMF")
+ res = self.info_bss("key_mgmt", bssid)
+ if "PSK-SHA256" not in res:
+ raise Exception("AP did not enable SHA256-based AKM for PMF")
+
+ def require_ap_pmf_optional(self, bssid):
+ res = self.info_bss("rsn_capab", bssid)
+ if "MFPR" in res:
+ raise Exception("AP required PMF")
+ if "MFPC" not in res:
+ raise Exception("AP did not enable PMF")
+
+ def require_ap_no_pmf(self, bssid):
+ res = self.info_bss("rsn_capab", bssid)
+ if "MFPR" in res:
+ raise Exception("AP required PMF")
+ if "MFPC" in res:
+ raise Exception("AP enabled PMF")
+
+ def require_sta_pmf_mandatory(self, bssid, addr):
+ res = self.info_sta("rsn_capab", bssid, addr)
+ if "MFPR" not in res:
+ raise Exception("STA did not require PMF")
+ if "MFPC" not in res:
+ raise Exception("STA did not enable PMF")
+
+ def require_sta_pmf(self, bssid, addr):
+ res = self.info_sta("rsn_capab", bssid, addr)
+ if "MFPC" not in res:
+ raise Exception("STA did not enable PMF")
+
+ def require_sta_no_pmf(self, bssid, addr):
+ res = self.info_sta("rsn_capab", bssid, addr)
+ if "MFPC" in res:
+ raise Exception("STA enabled PMF")
+
+ def require_sta_key_mgmt(self, bssid, addr, key_mgmt):
+ res = self.info_sta("key_mgmt", bssid, addr)
+ if key_mgmt not in res:
+ raise Exception("Unexpected STA key_mgmt")
+
+ def get_tx_tid(self, bssid, addr, tid):
+ res = self.cli_cmd(["get_tx_tid", bssid, addr, str(tid)])
+ if "FAIL" in res:
+ raise Exception("wlantest_cli command failed")
+ return int(res)
+
+ def get_rx_tid(self, bssid, addr, tid):
+ res = self.cli_cmd(["get_rx_tid", bssid, addr, str(tid)])
+ if "FAIL" in res:
+ raise Exception("wlantest_cli command failed")
+ return int(res)
+
+ def get_tid_counters(self, bssid, addr):
+ tx = {}
+ rx = {}
+ for tid in range(0, 17):
+ tx[tid] = self.get_tx_tid(bssid, addr, tid)
+ rx[tid] = self.get_rx_tid(bssid, addr, tid)
+ return [tx, rx]
+
+class WlantestCapture:
+ def __init__(self, ifname, output, netns=None):
+ self.cmd = None
+ self.ifname = ifname
+ if os.path.isfile('../../wlantest/wlantest'):
+ bin = '../../wlantest/wlantest'
+ else:
+ bin = 'wlantest'
+ logger.debug("wlantest[%s] starting" % ifname)
+ args = [bin, '-e', '-i', ifname, '-w', output]
+ if netns:
+ args = ['ip', 'netns', 'exec', netns] + args
+ self.cmd = subprocess.Popen(args,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+
+ def __del__(self):
+ if self.cmd:
+ self.close()
+
+ def close(self):
+ logger.debug("wlantest[%s] stopping" % self.ifname)
+ self.cmd.terminate()
+ res = self.cmd.communicate()
+ if len(res[0]) > 0:
+ logger.debug("wlantest[%s] stdout: %s" % (self.ifname,
+ res[0].decode().strip()))
+ if len(res[1]) > 0:
+ logger.debug("wlantest[%s] stderr: %s" % (self.ifname,
+ res[1].decode().strip()))
+ self.cmd = None
diff --git a/contrib/wpa/tests/hwsim/wpasupplicant.py b/contrib/wpa/tests/hwsim/wpasupplicant.py
new file mode 100644
index 000000000000..fdece92f66d4
--- /dev/null
+++ b/contrib/wpa/tests/hwsim/wpasupplicant.py
@@ -0,0 +1,1649 @@
+# Python class for controlling wpa_supplicant
+# Copyright (c) 2013-2019, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import time
+import logging
+import binascii
+import re
+import struct
+import wpaspy
+import remotehost
+import subprocess
+
+logger = logging.getLogger()
+wpas_ctrl = '/var/run/wpa_supplicant'
+
+class WpaSupplicant:
+ def __init__(self, ifname=None, global_iface=None, hostname=None,
+ port=9877, global_port=9878, monitor=True):
+ self.monitor = monitor
+ self.hostname = hostname
+ self.group_ifname = None
+ self.global_mon = None
+ self.global_ctrl = None
+ self.gctrl_mon = None
+ self.ctrl = None
+ self.mon = None
+ self.ifname = None
+ self.host = remotehost.Host(hostname, ifname)
+ self._group_dbg = None
+ if ifname:
+ self.set_ifname(ifname, hostname, port)
+ res = self.get_driver_status()
+ if 'capa.flags' in res and int(res['capa.flags'], 0) & 0x20000000:
+ self.p2p_dev_ifname = 'p2p-dev-' + self.ifname
+ else:
+ self.p2p_dev_ifname = ifname
+
+ self.global_iface = global_iface
+ if global_iface:
+ if hostname != None:
+ self.global_ctrl = wpaspy.Ctrl(hostname, global_port)
+ if self.monitor:
+ self.global_mon = wpaspy.Ctrl(hostname, global_port)
+ self.global_dbg = hostname + "/" + str(global_port) + "/"
+ else:
+ self.global_ctrl = wpaspy.Ctrl(global_iface)
+ if self.monitor:
+ self.global_mon = wpaspy.Ctrl(global_iface)
+ self.global_dbg = ""
+ if self.monitor:
+ self.global_mon.attach()
+
+ def __del__(self):
+ self.close_monitor()
+ self.close_control()
+
+ def close_control_ctrl(self):
+ if self.ctrl:
+ del self.ctrl
+ self.ctrl = None
+
+ def close_control_global(self):
+ if self.global_ctrl:
+ del self.global_ctrl
+ self.global_ctrl = None
+
+ def close_control(self):
+ self.close_control_ctrl()
+ self.close_control_global()
+
+ def close_monitor_mon(self):
+ if not self.mon:
+ return
+ try:
+ while self.mon.pending():
+ ev = self.mon.recv()
+ logger.debug(self.dbg + ": " + ev)
+ except:
+ pass
+ try:
+ self.mon.detach()
+ except ConnectionRefusedError:
+ pass
+ except Exception as e:
+ if str(e) == "DETACH failed":
+ pass
+ else:
+ raise
+ del self.mon
+ self.mon = None
+
+ def close_monitor_global(self):
+ if not self.global_mon:
+ return
+ try:
+ while self.global_mon.pending():
+ ev = self.global_mon.recv()
+ logger.debug(self.global_dbg + ": " + ev)
+ except:
+ pass
+ try:
+ self.global_mon.detach()
+ except ConnectionRefusedError:
+ pass
+ except Exception as e:
+ if str(e) == "DETACH failed":
+ pass
+ else:
+ raise
+ del self.global_mon
+ self.global_mon = None
+
+ def close_monitor_group(self):
+ if not self.gctrl_mon:
+ return
+ try:
+ while self.gctrl_mon.pending():
+ ev = self.gctrl_mon.recv()
+ logger.debug(self.dbg + ": " + ev)
+ except:
+ pass
+ try:
+ self.gctrl_mon.detach()
+ except:
+ pass
+ del self.gctrl_mon
+ self.gctrl_mon = None
+
+ def close_monitor(self):
+ self.close_monitor_mon()
+ self.close_monitor_global()
+ self.close_monitor_group()
+
+ def cmd_execute(self, cmd_array, shell=False):
+ if self.hostname is None:
+ if shell:
+ cmd = ' '.join(cmd_array)
+ else:
+ cmd = cmd_array
+ proc = subprocess.Popen(cmd, stderr=subprocess.STDOUT,
+ stdout=subprocess.PIPE, shell=shell)
+ out = proc.communicate()[0]
+ ret = proc.returncode
+ return ret, out.decode()
+ else:
+ return self.host.execute(cmd_array)
+
+ def terminate(self):
+ if self.global_mon:
+ self.close_monitor_global()
+ self.global_ctrl.terminate()
+ self.global_ctrl = None
+
+ def close_ctrl(self):
+ self.close_monitor_global()
+ self.close_control_global()
+ self.remove_ifname()
+
+ def set_ifname(self, ifname, hostname=None, port=9877):
+ self.remove_ifname()
+ self.ifname = ifname
+ if hostname != None:
+ self.ctrl = wpaspy.Ctrl(hostname, port)
+ if self.monitor:
+ self.mon = wpaspy.Ctrl(hostname, port)
+ self.host = remotehost.Host(hostname, ifname)
+ self.dbg = hostname + "/" + ifname
+ else:
+ self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+ if self.monitor:
+ self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+ self.dbg = ifname
+ if self.monitor:
+ self.mon.attach()
+
+ def remove_ifname(self):
+ self.close_monitor_mon()
+ self.close_control_ctrl()
+ self.ifname = None
+
+ def get_ctrl_iface_port(self, ifname):
+ if self.hostname is None:
+ return None
+
+ res = self.global_request("INTERFACES ctrl")
+ lines = res.splitlines()
+ found = False
+ for line in lines:
+ words = line.split()
+ if words[0] == ifname:
+ found = True
+ break
+ if not found:
+ raise Exception("Could not find UDP port for " + ifname)
+ res = line.find("ctrl_iface=udp:")
+ if res == -1:
+ raise Exception("Wrong ctrl_interface format")
+ words = line.split(":")
+ return int(words[1])
+
+ def interface_add(self, ifname, config="", driver="nl80211",
+ drv_params=None, br_ifname=None, create=False,
+ set_ifname=True, all_params=False, if_type=None):
+ status, groups = self.host.execute(["id"])
+ if status != 0:
+ group = "admin"
+ group = "admin" if "(admin)" in groups else "adm"
+ cmd = "INTERFACE_ADD " + ifname + "\t" + config + "\t" + driver + "\tDIR=/var/run/wpa_supplicant GROUP=" + group
+ if drv_params:
+ cmd = cmd + '\t' + drv_params
+ if br_ifname:
+ if not drv_params:
+ cmd += '\t'
+ cmd += '\t' + br_ifname
+ if create:
+ if not br_ifname:
+ cmd += '\t'
+ if not drv_params:
+ cmd += '\t'
+ cmd += '\tcreate'
+ if if_type:
+ cmd += '\t' + if_type
+ if all_params and not create:
+ if not br_ifname:
+ cmd += '\t'
+ if not drv_params:
+ cmd += '\t'
+ cmd += '\t'
+ if "FAIL" in self.global_request(cmd):
+ raise Exception("Failed to add a dynamic wpa_supplicant interface")
+ if not create and set_ifname:
+ port = self.get_ctrl_iface_port(ifname)
+ self.set_ifname(ifname, self.hostname, port)
+ res = self.get_driver_status()
+ if 'capa.flags' in res and int(res['capa.flags'], 0) & 0x20000000:
+ self.p2p_dev_ifname = 'p2p-dev-' + self.ifname
+ else:
+ self.p2p_dev_ifname = ifname
+
+ def interface_remove(self, ifname):
+ self.remove_ifname()
+ self.global_request("INTERFACE_REMOVE " + ifname)
+
+ def request(self, cmd, timeout=10):
+ logger.debug(self.dbg + ": CTRL: " + cmd)
+ return self.ctrl.request(cmd, timeout=timeout)
+
+ def global_request(self, cmd):
+ if self.global_iface is None:
+ return self.request(cmd)
+ else:
+ ifname = self.ifname or self.global_iface
+ logger.debug(self.global_dbg + ifname + ": CTRL(global): " + cmd)
+ return self.global_ctrl.request(cmd)
+
+ @property
+ def group_dbg(self):
+ if self._group_dbg is not None:
+ return self._group_dbg
+ if self.group_ifname is None:
+ raise Exception("Cannot have group_dbg without group_ifname")
+ if self.hostname is None:
+ self._group_dbg = self.group_ifname
+ else:
+ self._group_dbg = self.hostname + "/" + self.group_ifname
+ return self._group_dbg
+
+ def group_request(self, cmd):
+ if self.group_ifname and self.group_ifname != self.ifname:
+ if self.hostname is None:
+ gctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, self.group_ifname))
+ else:
+ port = self.get_ctrl_iface_port(self.group_ifname)
+ gctrl = wpaspy.Ctrl(self.hostname, port)
+ logger.debug(self.group_dbg + ": CTRL(group): " + cmd)
+ return gctrl.request(cmd)
+ return self.request(cmd)
+
+ def ping(self):
+ return "PONG" in self.request("PING")
+
+ def global_ping(self):
+ return "PONG" in self.global_request("PING")
+
+ def reset(self):
+ self.dump_monitor()
+ res = self.request("FLUSH")
+ if "OK" not in res:
+ logger.info("FLUSH to " + self.ifname + " failed: " + res)
+ self.global_request("REMOVE_NETWORK all")
+ self.global_request("SET p2p_no_group_iface 1")
+ self.global_request("P2P_FLUSH")
+ self.close_monitor_group()
+ self.group_ifname = None
+ self.dump_monitor()
+
+ iter = 0
+ while iter < 60:
+ state1 = self.get_driver_status_field("scan_state")
+ p2pdev = "p2p-dev-" + self.ifname
+ state2 = self.get_driver_status_field("scan_state", ifname=p2pdev)
+ states = str(state1) + " " + str(state2)
+ if "SCAN_STARTED" in states or "SCAN_REQUESTED" in states:
+ logger.info(self.ifname + ": Waiting for scan operation to complete before continuing")
+ time.sleep(1)
+ else:
+ break
+ iter = iter + 1
+ if iter == 60:
+ logger.error(self.ifname + ": Driver scan state did not clear")
+ print("Trying to clear cfg80211/mac80211 scan state")
+ status, buf = self.host.execute(["ifconfig", self.ifname, "down"])
+ if status != 0:
+ logger.info("ifconfig failed: " + buf)
+ logger.info(status)
+ status, buf = self.host.execute(["ifconfig", self.ifname, "up"])
+ if status != 0:
+ logger.info("ifconfig failed: " + buf)
+ logger.info(status)
+ if iter > 0:
+ # The ongoing scan could have discovered BSSes or P2P peers
+ logger.info("Run FLUSH again since scan was in progress")
+ self.request("FLUSH")
+ self.dump_monitor()
+
+ if not self.ping():
+ logger.info("No PING response from " + self.ifname + " after reset")
+
+ def set(self, field, value, allow_fail=False):
+ if "OK" not in self.request("SET " + field + " " + value):
+ if allow_fail:
+ return
+ raise Exception("Failed to set wpa_supplicant parameter " + field)
+
+ def add_network(self):
+ id = self.request("ADD_NETWORK")
+ if "FAIL" in id:
+ raise Exception("ADD_NETWORK failed")
+ return int(id)
+
+ def remove_network(self, id):
+ id = self.request("REMOVE_NETWORK " + str(id))
+ if "FAIL" in id:
+ raise Exception("REMOVE_NETWORK failed")
+ return None
+
+ def get_network(self, id, field):
+ res = self.request("GET_NETWORK " + str(id) + " " + field)
+ if res == "FAIL\n":
+ return None
+ return res
+
+ def set_network(self, id, field, value):
+ res = self.request("SET_NETWORK " + str(id) + " " + field + " " + value)
+ if "FAIL" in res:
+ raise Exception("SET_NETWORK failed")
+ return None
+
+ def set_network_quoted(self, id, field, value):
+ res = self.request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
+ if "FAIL" in res:
+ raise Exception("SET_NETWORK failed")
+ return None
+
+ def p2pdev_request(self, cmd):
+ return self.global_request("IFNAME=" + self.p2p_dev_ifname + " " + cmd)
+
+ def p2pdev_add_network(self):
+ id = self.p2pdev_request("ADD_NETWORK")
+ if "FAIL" in id:
+ raise Exception("p2pdev ADD_NETWORK failed")
+ return int(id)
+
+ def p2pdev_set_network(self, id, field, value):
+ res = self.p2pdev_request("SET_NETWORK " + str(id) + " " + field + " " + value)
+ if "FAIL" in res:
+ raise Exception("p2pdev SET_NETWORK failed")
+ return None
+
+ def p2pdev_set_network_quoted(self, id, field, value):
+ res = self.p2pdev_request("SET_NETWORK " + str(id) + " " + field + ' "' + value + '"')
+ if "FAIL" in res:
+ raise Exception("p2pdev SET_NETWORK failed")
+ return None
+
+ def list_networks(self, p2p=False):
+ if p2p:
+ res = self.global_request("LIST_NETWORKS")
+ else:
+ res = self.request("LIST_NETWORKS")
+ lines = res.splitlines()
+ networks = []
+ for l in lines:
+ if "network id" in l:
+ continue
+ [id, ssid, bssid, flags] = l.split('\t')
+ network = {}
+ network['id'] = id
+ network['ssid'] = ssid
+ network['bssid'] = bssid
+ network['flags'] = flags
+ networks.append(network)
+ return networks
+
+ def hs20_enable(self, auto_interworking=False):
+ self.request("SET interworking 1")
+ self.request("SET hs20 1")
+ if auto_interworking:
+ self.request("SET auto_interworking 1")
+ else:
+ self.request("SET auto_interworking 0")
+
+ def interworking_add_network(self, bssid):
+ id = self.request("INTERWORKING_ADD_NETWORK " + bssid)
+ if "FAIL" in id or "OK" in id:
+ raise Exception("INTERWORKING_ADD_NETWORK failed")
+ return int(id)
+
+ def add_cred(self):
+ id = self.request("ADD_CRED")
+ if "FAIL" in id:
+ raise Exception("ADD_CRED failed")
+ return int(id)
+
+ def remove_cred(self, id):
+ id = self.request("REMOVE_CRED " + str(id))
+ if "FAIL" in id:
+ raise Exception("REMOVE_CRED failed")
+ return None
+
+ def set_cred(self, id, field, value):
+ res = self.request("SET_CRED " + str(id) + " " + field + " " + value)
+ if "FAIL" in res:
+ raise Exception("SET_CRED failed")
+ return None
+
+ def set_cred_quoted(self, id, field, value):
+ res = self.request("SET_CRED " + str(id) + " " + field + ' "' + value + '"')
+ if "FAIL" in res:
+ raise Exception("SET_CRED failed")
+ return None
+
+ def get_cred(self, id, field):
+ return self.request("GET_CRED " + str(id) + " " + field)
+
+ def add_cred_values(self, params):
+ id = self.add_cred()
+
+ quoted = ["realm", "username", "password", "domain", "imsi",
+ "excluded_ssid", "milenage", "ca_cert", "client_cert",
+ "private_key", "domain_suffix_match", "provisioning_sp",
+ "roaming_partner", "phase1", "phase2", "private_key_passwd",
+ "roaming_consortiums"]
+ for field in quoted:
+ if field in params:
+ self.set_cred_quoted(id, field, params[field])
+
+ not_quoted = ["eap", "roaming_consortium", "priority",
+ "required_roaming_consortium", "sp_priority",
+ "max_bss_load", "update_identifier", "req_conn_capab",
+ "min_dl_bandwidth_home", "min_ul_bandwidth_home",
+ "min_dl_bandwidth_roaming", "min_ul_bandwidth_roaming"]
+ for field in not_quoted:
+ if field in params:
+ self.set_cred(id, field, params[field])
+
+ return id
+
+ def select_network(self, id, freq=None):
+ if freq:
+ extra = " freq=" + str(freq)
+ else:
+ extra = ""
+ id = self.request("SELECT_NETWORK " + str(id) + extra)
+ if "FAIL" in id:
+ raise Exception("SELECT_NETWORK failed")
+ return None
+
+ def mesh_group_add(self, id):
+ id = self.request("MESH_GROUP_ADD " + str(id))
+ if "FAIL" in id:
+ raise Exception("MESH_GROUP_ADD failed")
+ return None
+
+ def mesh_group_remove(self):
+ id = self.request("MESH_GROUP_REMOVE " + str(self.ifname))
+ if "FAIL" in id:
+ raise Exception("MESH_GROUP_REMOVE failed")
+ return None
+
+ def connect_network(self, id, timeout=None):
+ if timeout is None:
+ timeout = 10 if self.hostname is None else 60
+ self.dump_monitor()
+ self.select_network(id)
+ self.wait_connected(timeout=timeout)
+ self.dump_monitor()
+
+ def get_status(self, extra=None):
+ if extra:
+ extra = "-" + extra
+ else:
+ extra = ""
+ res = self.request("STATUS" + extra)
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ try:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ except ValueError as e:
+ logger.info(self.ifname + ": Ignore unexpected STATUS line: " + l)
+ return vals
+
+ def get_status_field(self, field, extra=None):
+ vals = self.get_status(extra)
+ if field in vals:
+ return vals[field]
+ return None
+
+ def get_group_status(self, extra=None):
+ if extra:
+ extra = "-" + extra
+ else:
+ extra = ""
+ res = self.group_request("STATUS" + extra)
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ try:
+ [name, value] = l.split('=', 1)
+ except ValueError:
+ logger.info(self.ifname + ": Ignore unexpected status line: " + l)
+ continue
+ vals[name] = value
+ return vals
+
+ def get_group_status_field(self, field, extra=None):
+ vals = self.get_group_status(extra)
+ if field in vals:
+ return vals[field]
+ return None
+
+ def get_driver_status(self, ifname=None):
+ if ifname is None:
+ res = self.request("STATUS-DRIVER")
+ else:
+ res = self.global_request("IFNAME=%s STATUS-DRIVER" % ifname)
+ if res.startswith("FAIL"):
+ return dict()
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ try:
+ [name, value] = l.split('=', 1)
+ except ValueError:
+ logger.info(self.ifname + ": Ignore unexpected status-driver line: " + l)
+ continue
+ vals[name] = value
+ return vals
+
+ def get_driver_status_field(self, field, ifname=None):
+ vals = self.get_driver_status(ifname)
+ if field in vals:
+ return vals[field]
+ return None
+
+ def get_mcc(self):
+ mcc = int(self.get_driver_status_field('capa.num_multichan_concurrent'))
+ return 1 if mcc < 2 else mcc
+
+ def get_mib(self):
+ res = self.request("MIB")
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ try:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ except ValueError as e:
+ logger.info(self.ifname + ": Ignore unexpected MIB line: " + l)
+ return vals
+
+ def p2p_dev_addr(self):
+ return self.get_status_field("p2p_device_address")
+
+ def p2p_interface_addr(self):
+ return self.get_group_status_field("address")
+
+ def own_addr(self):
+ try:
+ res = self.p2p_interface_addr()
+ except:
+ res = self.p2p_dev_addr()
+ return res
+
+ def get_addr(self, group=False):
+ dev_addr = self.own_addr()
+ if not group:
+ addr = self.get_status_field('address')
+ if addr:
+ dev_addr = addr
+
+ return dev_addr
+
+ def p2p_listen(self):
+ return self.global_request("P2P_LISTEN")
+
+ def p2p_ext_listen(self, period, interval):
+ return self.global_request("P2P_EXT_LISTEN %d %d" % (period, interval))
+
+ def p2p_cancel_ext_listen(self):
+ return self.global_request("P2P_EXT_LISTEN")
+
+ def p2p_find(self, social=False, progressive=False, dev_id=None,
+ dev_type=None, delay=None, freq=None):
+ cmd = "P2P_FIND"
+ if social:
+ cmd = cmd + " type=social"
+ elif progressive:
+ cmd = cmd + " type=progressive"
+ if dev_id:
+ cmd = cmd + " dev_id=" + dev_id
+ if dev_type:
+ cmd = cmd + " dev_type=" + dev_type
+ if delay:
+ cmd = cmd + " delay=" + str(delay)
+ if freq:
+ cmd = cmd + " freq=" + str(freq)
+ return self.global_request(cmd)
+
+ def p2p_stop_find(self):
+ return self.global_request("P2P_STOP_FIND")
+
+ def wps_read_pin(self):
+ self.pin = self.request("WPS_PIN get").rstrip("\n")
+ if "FAIL" in self.pin:
+ raise Exception("Could not generate PIN")
+ return self.pin
+
+ def peer_known(self, peer, full=True):
+ res = self.global_request("P2P_PEER " + peer)
+ if peer.lower() not in res.lower():
+ return False
+ if not full:
+ return True
+ return "[PROBE_REQ_ONLY]" not in res
+
+ def discover_peer(self, peer, full=True, timeout=15, social=True,
+ force_find=False, freq=None):
+ logger.info(self.ifname + ": Trying to discover peer " + peer)
+ if not force_find and self.peer_known(peer, full):
+ return True
+ self.p2p_find(social, freq=freq)
+ count = 0
+ while count < timeout * 4:
+ time.sleep(0.25)
+ count = count + 1
+ if self.peer_known(peer, full):
+ return True
+ return False
+
+ def get_peer(self, peer):
+ res = self.global_request("P2P_PEER " + peer)
+ if peer.lower() not in res.lower():
+ raise Exception("Peer information not available")
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ if '=' in l:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ return vals
+
+ def group_form_result(self, ev, expect_failure=False, go_neg_res=None):
+ if expect_failure:
+ if "P2P-GROUP-STARTED" in ev:
+ raise Exception("Group formation succeeded when expecting failure")
+ exp = r'<.>(P2P-GO-NEG-FAILURE) status=([0-9]*)'
+ s = re.split(exp, ev)
+ if len(s) < 3:
+ return None
+ res = {}
+ res['result'] = 'go-neg-failed'
+ res['status'] = int(s[2])
+ return res
+
+ if "P2P-GROUP-STARTED" not in ev:
+ raise Exception("No P2P-GROUP-STARTED event seen")
+
+ exp = r'<.>(P2P-GROUP-STARTED) ([^ ]*) ([^ ]*) ssid="(.*)" freq=([0-9]*) ((?:psk=.*)|(?:passphrase=".*")) go_dev_addr=([0-9a-f:]*) ip_addr=([0-9.]*) ip_mask=([0-9.]*) go_ip_addr=([0-9.]*)'
+ s = re.split(exp, ev)
+ if len(s) < 11:
+ exp = r'<.>(P2P-GROUP-STARTED) ([^ ]*) ([^ ]*) ssid="(.*)" freq=([0-9]*) ((?:psk=.*)|(?:passphrase=".*")) go_dev_addr=([0-9a-f:]*)'
+ s = re.split(exp, ev)
+ if len(s) < 8:
+ raise Exception("Could not parse P2P-GROUP-STARTED")
+ res = {}
+ res['result'] = 'success'
+ res['ifname'] = s[2]
+ self.group_ifname = s[2]
+ try:
+ if self.hostname is None:
+ self.gctrl_mon = wpaspy.Ctrl(os.path.join(wpas_ctrl,
+ self.group_ifname))
+ else:
+ port = self.get_ctrl_iface_port(self.group_ifname)
+ self.gctrl_mon = wpaspy.Ctrl(self.hostname, port)
+ if self.monitor:
+ self.gctrl_mon.attach()
+ except:
+ logger.debug("Could not open monitor socket for group interface")
+ self.gctrl_mon = None
+ res['role'] = s[3]
+ res['ssid'] = s[4]
+ res['freq'] = s[5]
+ if "[PERSISTENT]" in ev:
+ res['persistent'] = True
+ else:
+ res['persistent'] = False
+ p = re.match(r'psk=([0-9a-f]*)', s[6])
+ if p:
+ res['psk'] = p.group(1)
+ p = re.match(r'passphrase="(.*)"', s[6])
+ if p:
+ res['passphrase'] = p.group(1)
+ res['go_dev_addr'] = s[7]
+
+ if len(s) > 8 and len(s[8]) > 0 and "[PERSISTENT]" not in s[8]:
+ res['ip_addr'] = s[8]
+ if len(s) > 9:
+ res['ip_mask'] = s[9]
+ if len(s) > 10:
+ res['go_ip_addr'] = s[10]
+
+ if go_neg_res:
+ exp = r'<.>(P2P-GO-NEG-SUCCESS) role=(GO|client) freq=([0-9]*)'
+ s = re.split(exp, go_neg_res)
+ if len(s) < 4:
+ raise Exception("Could not parse P2P-GO-NEG-SUCCESS")
+ res['go_neg_role'] = s[2]
+ res['go_neg_freq'] = s[3]
+
+ return res
+
+ def p2p_go_neg_auth(self, peer, pin, method, go_intent=None,
+ persistent=False, freq=None, freq2=None,
+ max_oper_chwidth=None, ht40=False, vht=False):
+ if not self.discover_peer(peer):
+ raise Exception("Peer " + peer + " not found")
+ self.dump_monitor()
+ if pin:
+ cmd = "P2P_CONNECT " + peer + " " + pin + " " + method + " auth"
+ else:
+ cmd = "P2P_CONNECT " + peer + " " + method + " auth"
+ if go_intent:
+ cmd = cmd + ' go_intent=' + str(go_intent)
+ if freq:
+ cmd = cmd + ' freq=' + str(freq)
+ if freq2:
+ cmd = cmd + ' freq2=' + str(freq2)
+ if max_oper_chwidth:
+ cmd = cmd + ' max_oper_chwidth=' + str(max_oper_chwidth)
+ if ht40:
+ cmd = cmd + ' ht40'
+ if vht:
+ cmd = cmd + ' vht'
+ if persistent:
+ cmd = cmd + " persistent"
+ if "OK" in self.global_request(cmd):
+ return None
+ raise Exception("P2P_CONNECT (auth) failed")
+
+ def p2p_go_neg_auth_result(self, timeout=None, expect_failure=False):
+ if timeout is None:
+ timeout = 1 if expect_failure else 5
+ go_neg_res = None
+ ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS",
+ "P2P-GO-NEG-FAILURE"], timeout)
+ if ev is None:
+ if expect_failure:
+ return None
+ raise Exception("Group formation timed out")
+ if "P2P-GO-NEG-SUCCESS" in ev:
+ go_neg_res = ev
+ ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout)
+ if ev is None:
+ if expect_failure:
+ return None
+ raise Exception("Group formation timed out")
+ self.dump_monitor()
+ return self.group_form_result(ev, expect_failure, go_neg_res)
+
+ def p2p_go_neg_init(self, peer, pin, method, timeout=0, go_intent=None,
+ expect_failure=False, persistent=False,
+ persistent_id=None, freq=None, provdisc=False,
+ wait_group=True, freq2=None, max_oper_chwidth=None,
+ ht40=False, vht=False):
+ if not self.discover_peer(peer):
+ raise Exception("Peer " + peer + " not found")
+ self.dump_monitor()
+ if pin:
+ cmd = "P2P_CONNECT " + peer + " " + pin + " " + method
+ else:
+ cmd = "P2P_CONNECT " + peer + " " + method
+ if go_intent is not None:
+ cmd = cmd + ' go_intent=' + str(go_intent)
+ if freq:
+ cmd = cmd + ' freq=' + str(freq)
+ if freq2:
+ cmd = cmd + ' freq2=' + str(freq2)
+ if max_oper_chwidth:
+ cmd = cmd + ' max_oper_chwidth=' + str(max_oper_chwidth)
+ if ht40:
+ cmd = cmd + ' ht40'
+ if vht:
+ cmd = cmd + ' vht'
+ if persistent:
+ cmd = cmd + " persistent"
+ elif persistent_id:
+ cmd = cmd + " persistent=" + persistent_id
+ if provdisc:
+ cmd = cmd + " provdisc"
+ if "OK" in self.global_request(cmd):
+ if timeout == 0:
+ return None
+ go_neg_res = None
+ ev = self.wait_global_event(["P2P-GO-NEG-SUCCESS",
+ "P2P-GO-NEG-FAILURE"], timeout)
+ if ev is None:
+ if expect_failure:
+ return None
+ raise Exception("Group formation timed out")
+ if "P2P-GO-NEG-SUCCESS" in ev:
+ if not wait_group:
+ return ev
+ go_neg_res = ev
+ ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout)
+ if ev is None:
+ if expect_failure:
+ return None
+ raise Exception("Group formation timed out")
+ self.dump_monitor()
+ return self.group_form_result(ev, expect_failure, go_neg_res)
+ raise Exception("P2P_CONNECT failed")
+
+ def _wait_event(self, mon, pfx, events, timeout):
+ if not isinstance(events, list):
+ raise Exception("WpaSupplicant._wait_event() called with incorrect events argument type")
+ start = os.times()[4]
+ while True:
+ while mon.pending():
+ ev = mon.recv()
+ logger.debug(self.dbg + pfx + ev)
+ for event in events:
+ if event in ev:
+ return ev
+ now = os.times()[4]
+ remaining = start + timeout - now
+ if remaining <= 0:
+ break
+ if not mon.pending(timeout=remaining):
+ break
+ return None
+
+ def wait_event(self, events, timeout=10):
+ return self._wait_event(self.mon, ": ", events, timeout)
+
+ def wait_global_event(self, events, timeout):
+ if self.global_iface is None:
+ return self.wait_event(events, timeout)
+ return self._wait_event(self.global_mon, "(global): ",
+ events, timeout)
+
+ def wait_group_event(self, events, timeout=10):
+ if not isinstance(events, list):
+ raise Exception("WpaSupplicant.wait_group_event() called with incorrect events argument type")
+ if self.group_ifname and self.group_ifname != self.ifname:
+ if self.gctrl_mon is None:
+ return None
+ start = os.times()[4]
+ while True:
+ while self.gctrl_mon.pending():
+ ev = self.gctrl_mon.recv()
+ logger.debug(self.group_dbg + "(group): " + ev)
+ for event in events:
+ if event in ev:
+ return ev
+ now = os.times()[4]
+ remaining = start + timeout - now
+ if remaining <= 0:
+ break
+ if not self.gctrl_mon.pending(timeout=remaining):
+ break
+ return None
+
+ return self.wait_event(events, timeout)
+
+ def wait_go_ending_session(self):
+ self.close_monitor_group()
+ timeout = 3 if self.hostname is None else 10
+ ev = self.wait_global_event(["P2P-GROUP-REMOVED"], timeout=timeout)
+ if ev is None:
+ raise Exception("Group removal event timed out")
+ if "reason=GO_ENDING_SESSION" not in ev:
+ raise Exception("Unexpected group removal reason")
+
+ def dump_monitor(self, mon=True, global_mon=True):
+ count_iface = 0
+ count_global = 0
+ while mon and self.monitor and self.mon.pending():
+ ev = self.mon.recv()
+ logger.debug(self.dbg + ": " + ev)
+ count_iface += 1
+ while global_mon and self.monitor and self.global_mon and self.global_mon.pending():
+ ev = self.global_mon.recv()
+ logger.debug(self.global_dbg + self.ifname + "(global): " + ev)
+ count_global += 1
+ return (count_iface, count_global)
+
+ def remove_group(self, ifname=None):
+ self.close_monitor_group()
+ if ifname is None:
+ ifname = self.group_ifname if self.group_ifname else self.ifname
+ if "OK" not in self.global_request("P2P_GROUP_REMOVE " + ifname):
+ raise Exception("Group could not be removed")
+ self.group_ifname = None
+
+ def p2p_start_go(self, persistent=None, freq=None, no_event_clear=False):
+ self.dump_monitor()
+ cmd = "P2P_GROUP_ADD"
+ if persistent is None:
+ pass
+ elif persistent is True:
+ cmd = cmd + " persistent"
+ else:
+ cmd = cmd + " persistent=" + str(persistent)
+ if freq:
+ cmd = cmd + " freq=" + str(freq)
+ if "OK" in self.global_request(cmd):
+ ev = self.wait_global_event(["P2P-GROUP-STARTED"], timeout=5)
+ if ev is None:
+ raise Exception("GO start up timed out")
+ if not no_event_clear:
+ self.dump_monitor()
+ return self.group_form_result(ev)
+ raise Exception("P2P_GROUP_ADD failed")
+
+ def p2p_go_authorize_client(self, pin):
+ cmd = "WPS_PIN any " + pin
+ if "FAIL" in self.group_request(cmd):
+ raise Exception("Failed to authorize client connection on GO")
+ return None
+
+ def p2p_go_authorize_client_pbc(self):
+ cmd = "WPS_PBC"
+ if "FAIL" in self.group_request(cmd):
+ raise Exception("Failed to authorize client connection on GO")
+ return None
+
+ def p2p_connect_group(self, go_addr, pin, timeout=0, social=False,
+ freq=None):
+ self.dump_monitor()
+ if not self.discover_peer(go_addr, social=social, freq=freq):
+ if social or not self.discover_peer(go_addr, social=social):
+ raise Exception("GO " + go_addr + " not found")
+ self.p2p_stop_find()
+ self.dump_monitor()
+ cmd = "P2P_CONNECT " + go_addr + " " + pin + " join"
+ if freq:
+ cmd += " freq=" + str(freq)
+ if "OK" in self.global_request(cmd):
+ if timeout == 0:
+ self.dump_monitor()
+ return None
+ ev = self.wait_global_event(["P2P-GROUP-STARTED",
+ "P2P-GROUP-FORMATION-FAILURE"],
+ timeout)
+ if ev is None:
+ raise Exception("Joining the group timed out")
+ if "P2P-GROUP-STARTED" not in ev:
+ raise Exception("Failed to join the group")
+ self.dump_monitor()
+ return self.group_form_result(ev)
+ raise Exception("P2P_CONNECT(join) failed")
+
+ def tdls_setup(self, peer):
+ cmd = "TDLS_SETUP " + peer
+ if "FAIL" in self.group_request(cmd):
+ raise Exception("Failed to request TDLS setup")
+ return None
+
+ def tdls_teardown(self, peer):
+ cmd = "TDLS_TEARDOWN " + peer
+ if "FAIL" in self.group_request(cmd):
+ raise Exception("Failed to request TDLS teardown")
+ return None
+
+ def tdls_link_status(self, peer):
+ cmd = "TDLS_LINK_STATUS " + peer
+ ret = self.group_request(cmd)
+ if "FAIL" in ret:
+ raise Exception("Failed to request TDLS link status")
+ return ret
+
+ def tspecs(self):
+ """Return (tsid, up) tuples representing current tspecs"""
+ res = self.request("WMM_AC_STATUS")
+ tspecs = re.findall(r"TSID=(\d+) UP=(\d+)", res)
+ tspecs = [tuple(map(int, tspec)) for tspec in tspecs]
+
+ logger.debug("tspecs: " + str(tspecs))
+ return tspecs
+
+ def add_ts(self, tsid, up, direction="downlink", expect_failure=False,
+ extra=None):
+ params = {
+ "sba": 9000,
+ "nominal_msdu_size": 1500,
+ "min_phy_rate": 6000000,
+ "mean_data_rate": 1500,
+ }
+ cmd = "WMM_AC_ADDTS %s tsid=%d up=%d" % (direction, tsid, up)
+ for (key, value) in params.items():
+ cmd += " %s=%d" % (key, value)
+ if extra:
+ cmd += " " + extra
+
+ if self.request(cmd).strip() != "OK":
+ raise Exception("ADDTS failed (tsid=%d up=%d)" % (tsid, up))
+
+ if expect_failure:
+ ev = self.wait_event(["TSPEC-REQ-FAILED"], timeout=2)
+ if ev is None:
+ raise Exception("ADDTS failed (time out while waiting failure)")
+ if "tsid=%d" % (tsid) not in ev:
+ raise Exception("ADDTS failed (invalid tsid in TSPEC-REQ-FAILED")
+ return
+
+ ev = self.wait_event(["TSPEC-ADDED"], timeout=1)
+ if ev is None:
+ raise Exception("ADDTS failed (time out)")
+ if "tsid=%d" % (tsid) not in ev:
+ raise Exception("ADDTS failed (invalid tsid in TSPEC-ADDED)")
+
+ if (tsid, up) not in self.tspecs():
+ raise Exception("ADDTS failed (tsid not in tspec list)")
+
+ def del_ts(self, tsid):
+ if self.request("WMM_AC_DELTS %d" % (tsid)).strip() != "OK":
+ raise Exception("DELTS failed")
+
+ ev = self.wait_event(["TSPEC-REMOVED"], timeout=1)
+ if ev is None:
+ raise Exception("DELTS failed (time out)")
+ if "tsid=%d" % (tsid) not in ev:
+ raise Exception("DELTS failed (invalid tsid in TSPEC-REMOVED)")
+
+ tspecs = [(t, u) for (t, u) in self.tspecs() if t == tsid]
+ if tspecs:
+ raise Exception("DELTS failed (still in tspec list)")
+
+ def connect(self, ssid=None, ssid2=None, **kwargs):
+ logger.info("Connect STA " + self.ifname + " to AP")
+ id = self.add_network()
+ if ssid:
+ self.set_network_quoted(id, "ssid", ssid)
+ elif ssid2:
+ self.set_network(id, "ssid", ssid2)
+
+ quoted = ["psk", "identity", "anonymous_identity", "password",
+ "machine_identity", "machine_password",
+ "ca_cert", "client_cert", "private_key",
+ "private_key_passwd", "ca_cert2", "client_cert2",
+ "private_key2", "phase1", "phase2", "domain_suffix_match",
+ "altsubject_match", "subject_match", "pac_file", "dh_file",
+ "bgscan", "ht_mcs", "id_str", "openssl_ciphers",
+ "domain_match", "dpp_connector", "sae_password",
+ "sae_password_id", "check_cert_subject",
+ "machine_ca_cert", "machine_client_cert",
+ "machine_private_key", "machine_phase2"]
+ for field in quoted:
+ if field in kwargs and kwargs[field]:
+ self.set_network_quoted(id, field, kwargs[field])
+
+ not_quoted = ["proto", "key_mgmt", "ieee80211w", "pairwise",
+ "group", "wep_key0", "wep_key1", "wep_key2", "wep_key3",
+ "wep_tx_keyidx", "scan_freq", "freq_list", "eap",
+ "eapol_flags", "fragment_size", "scan_ssid", "auth_alg",
+ "wpa_ptk_rekey", "disable_ht", "disable_vht", "bssid",
+ "disable_he",
+ "disable_max_amsdu", "ampdu_factor", "ampdu_density",
+ "disable_ht40", "disable_sgi", "disable_ldpc",
+ "ht40_intolerant", "update_identifier", "mac_addr",
+ "erp", "bg_scan_period", "bssid_ignore",
+ "bssid_accept", "mem_only_psk", "eap_workaround",
+ "engine", "fils_dh_group", "bssid_hint",
+ "dpp_csign", "dpp_csign_expiry",
+ "dpp_netaccesskey", "dpp_netaccesskey_expiry", "dpp_pfs",
+ "group_mgmt", "owe_group", "owe_only",
+ "owe_ptk_workaround",
+ "transition_disable", "sae_pk",
+ "roaming_consortium_selection", "ocv",
+ "multi_ap_backhaul_sta", "rx_stbc", "tx_stbc",
+ "ft_eap_pmksa_caching", "beacon_prot",
+ "wpa_deny_ptk0_rekey"]
+ for field in not_quoted:
+ if field in kwargs and kwargs[field]:
+ self.set_network(id, field, kwargs[field])
+
+ known_args = {"raw_psk", "password_hex", "peerkey", "okc", "ocsp",
+ "only_add_network", "wait_connect"}
+ unknown = set(kwargs.keys())
+ unknown -= set(quoted)
+ unknown -= set(not_quoted)
+ unknown -= known_args
+ if unknown:
+ raise Exception("Unknown WpaSupplicant::connect() arguments: " + str(unknown))
+
+ if "raw_psk" in kwargs and kwargs['raw_psk']:
+ self.set_network(id, "psk", kwargs['raw_psk'])
+ if "password_hex" in kwargs and kwargs['password_hex']:
+ self.set_network(id, "password", kwargs['password_hex'])
+ if "peerkey" in kwargs and kwargs['peerkey']:
+ self.set_network(id, "peerkey", "1")
+ if "okc" in kwargs and kwargs['okc']:
+ self.set_network(id, "proactive_key_caching", "1")
+ if "ocsp" in kwargs and kwargs['ocsp']:
+ self.set_network(id, "ocsp", str(kwargs['ocsp']))
+ if "only_add_network" in kwargs and kwargs['only_add_network']:
+ return id
+ if "wait_connect" not in kwargs or kwargs['wait_connect']:
+ if "eap" in kwargs:
+ self.connect_network(id, timeout=20)
+ else:
+ self.connect_network(id)
+ else:
+ self.dump_monitor()
+ self.select_network(id)
+ return id
+
+ def scan(self, type=None, freq=None, no_wait=False, only_new=False,
+ passive=False):
+ if not no_wait:
+ self.dump_monitor()
+ if type:
+ cmd = "SCAN TYPE=" + type
+ else:
+ cmd = "SCAN"
+ if freq:
+ cmd = cmd + " freq=" + str(freq)
+ if only_new:
+ cmd += " only_new=1"
+ if passive:
+ cmd += " passive=1"
+ if not no_wait:
+ self.dump_monitor()
+ res = self.request(cmd)
+ if "OK" not in res:
+ raise Exception("Failed to trigger scan: " + str(res))
+ if no_wait:
+ return
+ ev = self.wait_event(["CTRL-EVENT-SCAN-RESULTS",
+ "CTRL-EVENT-SCAN-FAILED"], 15)
+ if ev is None:
+ raise Exception("Scan timed out")
+ if "CTRL-EVENT-SCAN-FAILED" in ev:
+ raise Exception("Scan failed: " + ev)
+
+ def scan_for_bss(self, bssid, freq=None, force_scan=False, only_new=False,
+ passive=False):
+ if not force_scan and self.get_bss(bssid) is not None:
+ return
+ for i in range(0, 10):
+ self.scan(freq=freq, type="ONLY", only_new=only_new,
+ passive=passive)
+ if self.get_bss(bssid) is not None:
+ return
+ raise Exception("Could not find BSS " + bssid + " in scan")
+
+ def flush_scan_cache(self, freq=2417):
+ self.request("BSS_FLUSH 0")
+ self.scan(freq=freq, only_new=True)
+ res = self.request("SCAN_RESULTS")
+ if len(res.splitlines()) > 1:
+ logger.debug("Scan results remaining after first attempt to flush the results:\n" + res)
+ self.request("BSS_FLUSH 0")
+ self.scan(freq=2422, only_new=True)
+ res = self.request("SCAN_RESULTS")
+ if len(res.splitlines()) > 1:
+ logger.info("flush_scan_cache: Could not clear all BSS entries. These remain:\n" + res)
+
+ def disconnect_and_stop_scan(self):
+ self.request("DISCONNECT")
+ res = self.request("ABORT_SCAN")
+ for i in range(2 if "OK" in res else 1):
+ self.wait_event(["CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-SCAN-RESULTS"], timeout=0.5)
+ self.dump_monitor()
+
+ def roam(self, bssid, fail_test=False, assoc_reject_ok=False,
+ check_bssid=True):
+ self.dump_monitor()
+ if "OK" not in self.request("ROAM " + bssid):
+ raise Exception("ROAM failed")
+ if fail_test:
+ if assoc_reject_ok:
+ ev = self.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-ASSOC-REJECT"], timeout=1)
+ else:
+ ev = self.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=1)
+ if ev and "CTRL-EVENT-DISCONNECTED" in ev:
+ self.dump_monitor()
+ return
+ if ev is not None and "CTRL-EVENT-ASSOC-REJECT" not in ev:
+ raise Exception("Unexpected connection")
+ self.dump_monitor()
+ return
+ if assoc_reject_ok:
+ ev = self.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED"], timeout=10)
+ else:
+ ev = self.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-DISCONNECTED",
+ "CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Roaming association rejected")
+ if "CTRL-EVENT-DISCONNECTED" in ev:
+ raise Exception("Unexpected disconnection when waiting for roam to complete")
+ self.dump_monitor()
+ if check_bssid and self.get_status_field('bssid') != bssid:
+ raise Exception("Did not roam to correct BSSID")
+
+ def roam_over_ds(self, bssid, fail_test=False):
+ self.dump_monitor()
+ if "OK" not in self.request("FT_DS " + bssid):
+ raise Exception("FT_DS failed")
+ if fail_test:
+ ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=1)
+ if ev is not None:
+ raise Exception("Unexpected connection")
+ self.dump_monitor()
+ return
+ ev = self.wait_event(["CTRL-EVENT-CONNECTED",
+ "CTRL-EVENT-ASSOC-REJECT"], timeout=10)
+ if ev is None:
+ raise Exception("Roaming with the AP timed out")
+ if "CTRL-EVENT-ASSOC-REJECT" in ev:
+ raise Exception("Roaming association rejected")
+ self.dump_monitor()
+
+ def wps_reg(self, bssid, pin, new_ssid=None, key_mgmt=None, cipher=None,
+ new_passphrase=None, no_wait=False):
+ self.dump_monitor()
+ if new_ssid:
+ self.request("WPS_REG " + bssid + " " + pin + " " +
+ binascii.hexlify(new_ssid.encode()).decode() + " " +
+ key_mgmt + " " + cipher + " " +
+ binascii.hexlify(new_passphrase.encode()).decode())
+ if no_wait:
+ return
+ ev = self.wait_event(["WPS-SUCCESS"], timeout=15)
+ else:
+ self.request("WPS_REG " + bssid + " " + pin)
+ if no_wait:
+ return
+ ev = self.wait_event(["WPS-CRED-RECEIVED"], timeout=15)
+ if ev is None:
+ raise Exception("WPS cred timed out")
+ ev = self.wait_event(["WPS-FAIL"], timeout=15)
+ if ev is None:
+ raise Exception("WPS timed out")
+ self.wait_connected(timeout=15)
+
+ def relog(self):
+ self.global_request("RELOG")
+
+ def wait_completed(self, timeout=10):
+ for i in range(0, timeout * 2):
+ if self.get_status_field("wpa_state") == "COMPLETED":
+ return
+ time.sleep(0.5)
+ raise Exception("Timeout while waiting for COMPLETED state")
+
+ def get_capability(self, field):
+ res = self.request("GET_CAPABILITY " + field)
+ if "FAIL" in res:
+ return None
+ return res.split(' ')
+
+ def get_bss(self, bssid, ifname=None):
+ if not ifname or ifname == self.ifname:
+ res = self.request("BSS " + bssid)
+ elif ifname == self.group_ifname:
+ res = self.group_request("BSS " + bssid)
+ else:
+ return None
+
+ if "FAIL" in res:
+ return None
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ if len(vals) == 0:
+ return None
+ return vals
+
+ def get_pmksa(self, bssid):
+ res = self.request("PMKSA")
+ lines = res.splitlines()
+ for l in lines:
+ if bssid not in l:
+ continue
+ vals = dict()
+ try:
+ [index, aa, pmkid, expiration, opportunistic] = l.split(' ')
+ cache_id = None
+ except ValueError:
+ [index, aa, pmkid, expiration, opportunistic, cache_id] = l.split(' ')
+ vals['index'] = index
+ vals['pmkid'] = pmkid
+ vals['expiration'] = expiration
+ vals['opportunistic'] = opportunistic
+ if cache_id != None:
+ vals['cache_id'] = cache_id
+ return vals
+ return None
+
+ def get_pmk(self, network_id):
+ bssid = self.get_status_field('bssid')
+ res = self.request("PMKSA_GET %d" % network_id)
+ for val in res.splitlines():
+ if val.startswith(bssid):
+ return val.split(' ')[2]
+ return None
+
+ def get_sta(self, addr, info=None, next=False):
+ cmd = "STA-NEXT " if next else "STA "
+ if addr is None:
+ res = self.request("STA-FIRST")
+ elif info:
+ res = self.request(cmd + addr + " " + info)
+ else:
+ res = self.request(cmd + addr)
+ lines = res.splitlines()
+ vals = dict()
+ first = True
+ for l in lines:
+ if first:
+ vals['addr'] = l
+ first = False
+ else:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ return vals
+
+ def mgmt_rx(self, timeout=5):
+ ev = self.wait_event(["MGMT-RX"], timeout=timeout)
+ if ev is None:
+ return None
+ msg = {}
+ items = ev.split(' ')
+ field, val = items[1].split('=')
+ if field != "freq":
+ raise Exception("Unexpected MGMT-RX event format: " + ev)
+ msg['freq'] = val
+
+ field, val = items[2].split('=')
+ if field != "datarate":
+ raise Exception("Unexpected MGMT-RX event format: " + ev)
+ msg['datarate'] = val
+
+ field, val = items[3].split('=')
+ if field != "ssi_signal":
+ raise Exception("Unexpected MGMT-RX event format: " + ev)
+ msg['ssi_signal'] = val
+
+ frame = binascii.unhexlify(items[4])
+ msg['frame'] = frame
+
+ hdr = struct.unpack('<HH6B6B6BH', frame[0:24])
+ msg['fc'] = hdr[0]
+ msg['subtype'] = (hdr[0] >> 4) & 0xf
+ hdr = hdr[1:]
+ msg['duration'] = hdr[0]
+ hdr = hdr[1:]
+ msg['da'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+ hdr = hdr[6:]
+ msg['sa'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+ hdr = hdr[6:]
+ msg['bssid'] = "%02x:%02x:%02x:%02x:%02x:%02x" % hdr[0:6]
+ hdr = hdr[6:]
+ msg['seq_ctrl'] = hdr[0]
+ msg['payload'] = frame[24:]
+
+ return msg
+
+ def wait_connected(self, timeout=10, error="Connection timed out"):
+ ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=timeout)
+ if ev is None:
+ raise Exception(error)
+ return ev
+
+ def wait_disconnected(self, timeout=None, error="Disconnection timed out"):
+ if timeout is None:
+ timeout = 10 if self.hostname is None else 30
+ ev = self.wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=timeout)
+ if ev is None:
+ raise Exception(error)
+ return ev
+
+ def get_group_ifname(self):
+ return self.group_ifname if self.group_ifname else self.ifname
+
+ def get_config(self):
+ res = self.request("DUMP")
+ if res.startswith("FAIL"):
+ raise Exception("DUMP failed")
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ [name, value] = l.split('=', 1)
+ vals[name] = value
+ return vals
+
+ def asp_provision(self, peer, adv_id, adv_mac, session_id, session_mac,
+ method="1000", info="", status=None, cpt=None, role=None):
+ if status is None:
+ cmd = "P2P_ASP_PROVISION"
+ params = "info='%s' method=%s" % (info, method)
+ else:
+ cmd = "P2P_ASP_PROVISION_RESP"
+ params = "status=%d" % status
+
+ if role is not None:
+ params += " role=" + role
+ if cpt is not None:
+ params += " cpt=" + cpt
+
+ if "OK" not in self.global_request("%s %s adv_id=%s adv_mac=%s session=%d session_mac=%s %s" %
+ (cmd, peer, adv_id, adv_mac, session_id, session_mac, params)):
+ raise Exception("%s request failed" % cmd)
+
+ def note(self, txt):
+ self.request("NOTE " + txt)
+
+ def save_config(self):
+ if "OK" not in self.request("SAVE_CONFIG"):
+ raise Exception("Failed to save configuration file")
+
+ def wait_regdom(self, country_ie=False):
+ for i in range(5):
+ ev = self.wait_event(["CTRL-EVENT-REGDOM-CHANGE"], timeout=1)
+ if ev is None:
+ break
+ if country_ie:
+ if "init=COUNTRY_IE" in ev:
+ break
+ else:
+ break
+
+ def dpp_qr_code(self, uri):
+ res = self.request("DPP_QR_CODE " + uri)
+ if "FAIL" in res:
+ raise Exception("Failed to parse QR Code URI")
+ return int(res)
+
+ def dpp_nfc_uri(self, uri):
+ res = self.request("DPP_NFC_URI " + uri)
+ if "FAIL" in res:
+ raise Exception("Failed to parse NFC URI")
+ return int(res)
+
+ def dpp_bootstrap_gen(self, type="qrcode", chan=None, mac=None, info=None,
+ curve=None, key=None):
+ cmd = "DPP_BOOTSTRAP_GEN type=" + type
+ if chan:
+ cmd += " chan=" + chan
+ if mac:
+ if mac is True:
+ mac = self.own_addr()
+ cmd += " mac=" + mac.replace(':', '')
+ if info:
+ cmd += " info=" + info
+ if curve:
+ cmd += " curve=" + curve
+ if key:
+ cmd += " key=" + key
+ res = self.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to generate bootstrapping info")
+ return int(res)
+
+ def dpp_bootstrap_set(self, id, conf=None, configurator=None, ssid=None,
+ extra=None):
+ cmd = "DPP_BOOTSTRAP_SET %d" % id
+ if ssid:
+ cmd += " ssid=" + binascii.hexlify(ssid.encode()).decode()
+ if extra:
+ cmd += " " + extra
+ if conf:
+ cmd += " conf=" + conf
+ if configurator is not None:
+ cmd += " configurator=%d" % configurator
+ if "OK" not in self.request(cmd):
+ raise Exception("Failed to set bootstrapping parameters")
+
+ def dpp_listen(self, freq, netrole=None, qr=None, role=None):
+ cmd = "DPP_LISTEN " + str(freq)
+ if netrole:
+ cmd += " netrole=" + netrole
+ if qr:
+ cmd += " qr=" + qr
+ if role:
+ cmd += " role=" + role
+ if "OK" not in self.request(cmd):
+ raise Exception("Failed to start listen operation")
+
+ def dpp_auth_init(self, peer=None, uri=None, conf=None, configurator=None,
+ extra=None, own=None, role=None, neg_freq=None,
+ ssid=None, passphrase=None, expect_fail=False,
+ tcp_addr=None, tcp_port=None, conn_status=False,
+ ssid_charset=None, nfc_uri=None, netrole=None,
+ csrattrs=None):
+ cmd = "DPP_AUTH_INIT"
+ if peer is None:
+ if nfc_uri:
+ peer = self.dpp_nfc_uri(nfc_uri)
+ else:
+ peer = self.dpp_qr_code(uri)
+ cmd += " peer=%d" % peer
+ if own is not None:
+ cmd += " own=%d" % own
+ if role:
+ cmd += " role=" + role
+ if extra:
+ cmd += " " + extra
+ if conf:
+ cmd += " conf=" + conf
+ if configurator is not None:
+ cmd += " configurator=%d" % configurator
+ if neg_freq:
+ cmd += " neg_freq=%d" % neg_freq
+ if ssid:
+ cmd += " ssid=" + binascii.hexlify(ssid.encode()).decode()
+ if ssid_charset:
+ cmd += " ssid_charset=%d" % ssid_charset
+ if passphrase:
+ cmd += " pass=" + binascii.hexlify(passphrase.encode()).decode()
+ if tcp_addr:
+ cmd += " tcp_addr=" + tcp_addr
+ if tcp_port:
+ cmd += " tcp_port=" + tcp_port
+ if conn_status:
+ cmd += " conn_status=1"
+ if netrole:
+ cmd += " netrole=" + netrole
+ if csrattrs:
+ cmd += " csrattrs=" + csrattrs
+ res = self.request(cmd)
+ if expect_fail:
+ if "FAIL" not in res:
+ raise Exception("DPP authentication started unexpectedly")
+ return
+ if "OK" not in res:
+ raise Exception("Failed to initiate DPP Authentication")
+ return int(peer)
+
+ def dpp_pkex_init(self, identifier, code, role=None, key=None, curve=None,
+ extra=None, use_id=None, allow_fail=False):
+ if use_id is None:
+ id1 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve)
+ else:
+ id1 = use_id
+ cmd = "own=%d " % id1
+ if identifier:
+ cmd += "identifier=%s " % identifier
+ cmd += "init=1 "
+ if role:
+ cmd += "role=%s " % role
+ if extra:
+ cmd += extra + " "
+ cmd += "code=%s" % code
+ res = self.request("DPP_PKEX_ADD " + cmd)
+ if allow_fail:
+ return id1
+ if "FAIL" in res:
+ raise Exception("Failed to set PKEX data (initiator)")
+ return id1
+
+ def dpp_pkex_resp(self, freq, identifier, code, key=None, curve=None,
+ listen_role=None, use_id=None):
+ if use_id is None:
+ id0 = self.dpp_bootstrap_gen(type="pkex", key=key, curve=curve)
+ else:
+ id0 = use_id
+ cmd = "own=%d " % id0
+ if identifier:
+ cmd += "identifier=%s " % identifier
+ cmd += "code=%s" % code
+ res = self.request("DPP_PKEX_ADD " + cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to set PKEX data (responder)")
+ self.dpp_listen(freq, role=listen_role)
+ return id0
+
+ def dpp_configurator_add(self, curve=None, key=None):
+ cmd = "DPP_CONFIGURATOR_ADD"
+ if curve:
+ cmd += " curve=" + curve
+ if key:
+ cmd += " key=" + key
+ res = self.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to add configurator")
+ return int(res)
+
+ def dpp_configurator_remove(self, conf_id):
+ res = self.request("DPP_CONFIGURATOR_REMOVE %d" % conf_id)
+ if "OK" not in res:
+ raise Exception("DPP_CONFIGURATOR_REMOVE failed")
+
+ def get_ptksa(self, bssid, cipher):
+ res = self.request("PTKSA_CACHE_LIST")
+ lines = res.splitlines()
+ for l in lines:
+ if bssid not in l or cipher not in l:
+ continue
+
+ vals = dict()
+ [index, addr, cipher, expiration, tk, kdk] = l.split(' ', 5)
+ vals['index'] = index
+ vals['addr'] = addr
+ vals['cipher'] = cipher
+ vals['expiration'] = expiration
+ vals['tk'] = tk
+ vals['kdk'] = kdk
+ return vals
+ return None
diff --git a/contrib/wpa/tests/hwsim/wps-ctrl-cred b/contrib/wpa/tests/hwsim/wps-ctrl-cred
new file mode 100644
index 000000000000..b02b783b8b58
Binary files /dev/null and b/contrib/wpa/tests/hwsim/wps-ctrl-cred differ
diff --git a/contrib/wpa/tests/hwsim/wps-ctrl-cred2 b/contrib/wpa/tests/hwsim/wps-ctrl-cred2
new file mode 100644
index 000000000000..696a576f0012
Binary files /dev/null and b/contrib/wpa/tests/hwsim/wps-ctrl-cred2 differ
diff --git a/contrib/wpa/tests/hwsim/wps-mixed-cred b/contrib/wpa/tests/hwsim/wps-mixed-cred
new file mode 100644
index 000000000000..fca2871fd210
Binary files /dev/null and b/contrib/wpa/tests/hwsim/wps-mixed-cred differ
diff --git a/contrib/wpa/tests/hwsim/wps-wep-cred b/contrib/wpa/tests/hwsim/wps-wep-cred
new file mode 100644
index 000000000000..407cf4143ba1
Binary files /dev/null and b/contrib/wpa/tests/hwsim/wps-wep-cred differ
diff --git a/contrib/wpa/tests/remote/config.py b/contrib/wpa/tests/remote/config.py
new file mode 100644
index 000000000000..1ac362ead3f3
--- /dev/null
+++ b/contrib/wpa/tests/remote/config.py
@@ -0,0 +1,87 @@
+# Environment configuration
+# Copyright (c) 2016, Tieto Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+#
+# Currently static definition, in the future this could be a config file,
+# or even common database with host management.
+#
+
+import logging
+logger = logging.getLogger()
+
+#
+# You can put your settings in cfg.py file with setup_params, devices
+# definitions in the format as below. In other case HWSIM cfg will be used.
+#
+setup_params = {"setup_hw" : "./tests/setup_hw.sh",
+ "hostapd" : "./tests/hostapd-rt",
+ "wpa_supplicant" : "./tests/wpa_supplicant-rt",
+ "iperf" : "iperf",
+ "wlantest" : "./tests/wlantest",
+ "wlantest_cli" : "./tests/wlantest_cli",
+ "country" : "US",
+ "log_dir" : "/tmp/",
+ "ipv4_test_net" : "192.168.12.0",
+ "trace_start" : "./tests/trace_start.sh",
+ "trace_stop" : "./tests/trace_stop.sh",
+ "perf_start" : "./tests/perf_start.sh",
+ "perf_stop" : "./tests/perf_stop.sh"}
+
+#
+#devices = [{"hostname": "192.168.254.58", "ifname" : "wlan0", "port": "9877", "name" : "t2-ath9k", "flags" : "AP_HT40 STA_HT40"},
+# {"hostname": "192.168.254.58", "ifname" : "wlan1", "port": "9877", "name" : "t2-ath10k", "flags" : "AP_VHT80"},
+# {"hostname": "192.168.254.58", "ifname" : "wlan3", "port": "9877", "name" : "t2-intel7260", "flags" : "STA_VHT80"},
+# {"hostname": "192.168.254.55", "ifname" : "wlan0, wlan1, wlan2", "port": "", "name" : "t3-monitor"},
+# {"hostname": "192.168.254.50", "ifname" : "wlan0", "port": "9877", "name" : "t1-ath9k"},
+# {"hostname": "192.168.254.50", "ifname" : "wlan1", "port": "9877", "name" : "t1-ath10k"}]
+
+#
+# HWSIM - ifaces available after modprobe mac80211_hwsim
+#
+devices = [{"hostname": "localhost", "ifname": "wlan0", "port": "9868", "name": "hwsim0", "flags": "AP_VHT80 STA_VHT80"},
+ {"hostname": "localhost", "ifname": "wlan1", "port": "9878", "name": "hwsim1", "flags": "AP_VHT80 STA_VHT80"},
+ {"hostname": "localhost", "ifname": "wlan2", "port": "9888", "name": "hwsim2", "flags": "AP_VHT80 STA_VHT80"},
+ {"hostname": "localhost", "ifname": "wlan3", "port": "9898", "name": "hwsim3", "flags": "AP_VHT80 STA_VHT80"},
+ {"hostname": "localhost", "ifname": "wlan4", "port": "9908", "name": "hwsim4", "flags": "AP_VHT80 STA_VHT80"}]
+
+
+def get_setup_params(filename="cfg.py"):
+ try:
+ mod = __import__(filename.split(".")[0])
+ return mod.setup_params
+ except:
+ logger.debug("__import__(" + filename + ") failed, using static settings")
+ pass
+ return setup_params
+
+def get_devices(filename="cfg.py"):
+ try:
+ mod = __import__(filename.split(".")[0])
+ return mod.devices
+ except:
+ logger.debug("__import__(" + filename + ") failed, using static settings")
+ pass
+ return devices
+
+def get_device(devices, name=None, flags=None, lock=False):
+ if name is None and flags is None:
+ raise Exception("Failed to get device")
+ word = name.split(":")
+ name = word[0]
+ for device in devices:
+ if device['name'] == name:
+ return device
+ for device in devices:
+ try:
+ device_flags = device['flags']
+ if device_flags.find(flags) != -1:
+ return device
+ except:
+ pass
+ raise Exception("Failed to get device " + name)
+
+def put_device(devices, name):
+ pass
diff --git a/contrib/wpa/tests/remote/hwsim_wrapper.py b/contrib/wpa/tests/remote/hwsim_wrapper.py
new file mode 100644
index 000000000000..38f927f6e820
--- /dev/null
+++ b/contrib/wpa/tests/remote/hwsim_wrapper.py
@@ -0,0 +1,126 @@
+# Hwsim wrapper
+# Copyright (c) 2016, Tieto Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import remotehost
+from wpasupplicant import WpaSupplicant
+import hostapd
+import config
+import rutils
+import monitor
+import traceback
+import wlantest
+
+import logging
+logger = logging.getLogger()
+
+def run_hwsim_test(devices, setup_params, refs, duts, monitors, hwsim_test):
+ try:
+ ref_hosts = []
+ dut_hosts = []
+ dev = []
+ apdev = []
+
+ # get hosts
+ for ref in refs:
+ ref_host = rutils.get_host(devices, ref)
+ ref_hosts.append(ref_host)
+ for dut in duts:
+ dut_host = rutils.get_host(devices, dut)
+ dut_hosts.append(dut_host)
+
+ # setup log dir
+ local_log_dir = setup_params['local_log_dir']
+
+ # setup hw before test
+ rutils.setup_hw(ref_hosts, setup_params)
+ rutils.setup_hw(dut_hosts, setup_params)
+
+ # run monitors if requested/possible
+ for ref_host in ref_hosts:
+ monitor.add(ref_host, monitors)
+ monitor.run(ref_host, setup_params)
+ for dut_host in dut_hosts:
+ monitor.add(dut_host, monitors)
+ monitor.run(dut_host, setup_params)
+
+ monitor_hosts = monitor.create(devices, setup_params, refs, duts,
+ monitors)
+ mon = None
+ if len(monitor_hosts) > 0:
+ mon = monitor_hosts[0]
+ wlantest.Wlantest.reset_remote_wlantest()
+ wlantest.Wlantest.register_remote_wlantest(mon, setup_params,
+ monitor)
+
+ # run hostapd/wpa_supplicant
+ for ref_host in ref_hosts:
+ rutils.run_wpasupplicant(ref_host, setup_params)
+ wpas = WpaSupplicant(hostname=ref_host.host, global_iface="udp",
+ global_port=ref_host.port)
+ wpas.interface_add(ref_host.ifname)
+ dev.append(wpas)
+ for dut_host in dut_hosts:
+ rutils.run_hostapd(dut_host, setup_params)
+ dut_host.dev['bssid'] = rutils.get_mac_addr(dut_host)
+ apdev.append(dut_host.dev)
+
+ if hwsim_test.__code__.co_argcount == 1:
+ hwsim_test(dev)
+ elif hwsim_test.__code__.co_argcount == 2:
+ hwsim_test(dev, apdev)
+ else:
+ params = {}
+ params['long'] = 1
+ params['logdir'] = local_log_dir
+ hwsim_test(dev, apdev, params)
+
+ # hostapd/wpa_supplicant cleanup
+ for wpas in dev:
+ wpas.interface_remove(wpas.host.ifname)
+ wpas.terminate()
+ dev = []
+
+ # remove monitors
+ for ref_host in ref_hosts:
+ monitor.remove(ref_host)
+ for dut_host in dut_hosts:
+ monitor.remove(dut_host)
+
+ for ref_host in ref_hosts:
+ rutils.kill_wpasupplicant(ref_host, setup_params)
+ ref_host.get_logs(local_log_dir)
+ for dut_host in dut_hosts:
+ rutils.kill_hostapd(dut_host, setup_params)
+ dut_host.get_logs(local_log_dir)
+ if mon is not None:
+ wlantest.Wlantest.reset_remote_wlantest()
+ mon.get_logs(local_log_dir)
+
+ return ""
+ except:
+ logger.info(traceback.format_exc())
+ for wpas in dev:
+ try:
+ wpas.interface_remove(wpas.host.ifname)
+ wpas.terminate()
+ except:
+ pass
+
+ for ref_host in ref_hosts:
+ monitor.remove(ref_host)
+ for dut_host in dut_hosts:
+ monitor.remove(dut_host)
+
+ for ref_host in ref_hosts:
+ rutils.kill_wpasupplicant(ref_host, setup_params)
+ ref_host.get_logs(local_log_dir)
+ for dut_host in dut_hosts:
+ rutils.kill_hostapd(dut_host, setup_params)
+ dut_host.get_logs(local_log_dir)
+ if mon is not None:
+ wlantest.Wlantest.reset_remote_wlantest()
+ mon.get_logs(local_log_dir)
+ raise
diff --git a/contrib/wpa/tests/remote/monitor.py b/contrib/wpa/tests/remote/monitor.py
new file mode 100644
index 000000000000..0f77d500bf30
--- /dev/null
+++ b/contrib/wpa/tests/remote/monitor.py
@@ -0,0 +1,193 @@
+# Monitor support
+# Copyright (c) 2016, Tieto Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+from remotehost import Host
+import config
+import rutils
+import re
+import traceback
+import logging
+logger = logging.getLogger()
+import hostapd
+
+# standalone monitor with multi iface support
+def create(devices, setup_params, refs, duts, monitors):
+ mons = []
+ mhosts = []
+ hosts = duts + refs
+
+ # choose only standalone monitors
+ for monitor in monitors:
+ if monitor not in hosts and monitor != "all":
+ mons.append(monitor)
+
+ for mon in mons:
+ word = mon.split(":")
+ dev = config.get_device(devices, word[0])
+ if dev is None:
+ continue
+
+ host = Host(host=dev['hostname'],
+ ifname=dev['ifname'],
+ port=dev['port'],
+ name=dev['name'])
+
+ for iface_param in word[1:]:
+ params = iface_param.split(",")
+ if len(params) > 3:
+ monitor_param = { "freq" : rutils.c2f(params[0]),
+ "bw" : params[1],
+ "center_freq1" : rutils.c2f(params[2]),
+ "center_freq2" : rutils.c2f(params[3]) }
+ host.monitor_params.append(monitor_param)
+
+ try:
+ host.execute(["iw", "reg", "set", setup_params['country']])
+ rutils.setup_hw_host(host, setup_params, True)
+ except:
+ pass
+ mhosts.append(host)
+
+ return mhosts
+
+def destroy(devices, hosts):
+ for host in hosts:
+ stop(host)
+ for monitor in host.monitors:
+ host.execute(["ifconfig", monitor, "down"])
+ host.monitor_params = []
+
+def setup(host, monitor_params=None):
+ if host is None:
+ return
+
+ if monitor_params == None:
+ monitor_params = host.monitor_params
+
+ ifaces = re.split('; | |, ', host.ifname)
+ count = 0
+ for param in monitor_params:
+ try:
+ iface = ifaces[count]
+ except:
+ logger.debug(traceback.format_exc())
+ break
+ host.execute(["ifconfig", iface, " down"])
+ host.execute(["rfkill", "unblock", "wifi"])
+ host.execute(["iw", iface, "set type monitor"])
+ host.execute(["ifconfig", iface, "up"])
+ status, buf = host.execute(["iw", iface, "set", "freq", param['freq'],
+ param['bw'], param['center_freq1'],
+ param['center_freq2']])
+ if status != 0:
+ logger.debug("Could not setup monitor interface: " + buf)
+ continue
+ host.monitors.append(iface)
+ count = count + 1
+
+def run(host, setup_params):
+ monitor_res = []
+ log_monitor = ""
+ if host is None:
+ return None
+ if len(host.monitors) == 0:
+ return None
+ try:
+ log_dir = setup_params['log_dir']
+ tc_name = setup_params['tc_name']
+ except:
+ return None
+
+ tshark = "tshark"
+ for monitor in host.monitors:
+ host.execute(["ifconfig", monitor, "up"])
+ tshark = tshark + " -i " + monitor
+ log_monitor = log_monitor + "_" + monitor
+
+ log = log_dir + tc_name + "_" + host.name + log_monitor + ".pcap"
+ host.add_log(log)
+ thread = host.thread_run([tshark, "-w", log], monitor_res)
+ host.thread = thread
+
+
+def stop(host):
+ if host is None:
+ return
+ if len(host.monitors) == 0:
+ return
+ if host.thread is None:
+ return
+
+ host.thread_stop(host.thread)
+ host.thread = None
+
+# Add monitor to existing interface
+def add(host, monitors):
+ if host is None:
+ return
+
+ for monitor in monitors:
+ if monitor != "all" and monitor != host.name:
+ continue
+ mon = "mon_" + host.ifname
+ status, buf = host.execute(["iw", host.ifname, "interface", "add", mon,
+ "type", "monitor"])
+ if status == 0:
+ host.monitors.append(mon)
+ host.execute(["ifconfig", mon, "up"])
+ else:
+ logger.debug("Could not add monitor for " + host.name)
+
+def remove(host):
+ stop(host)
+ for monitor in host.monitors:
+ host.execute(["iw", monitor, "del"])
+ host.monitors.remove(monitor)
+
+
+# get monitor params from hostapd/wpa_supplicant
+def get_monitor_params(wpa, is_p2p=False):
+ if is_p2p:
+ get_status_field_f = wpa.get_group_status_field
+ else:
+ get_status_field_f = wpa.get_status_field
+ freq = get_status_field_f("freq")
+ bw = "20"
+ center_freq1 = ""
+ center_freq2 = ""
+
+ vht_oper_chwidth = get_status_field_f("vht_oper_chwidth")
+ secondary_channel = get_status_field_f("secondary_channel")
+ vht_oper_centr_freq_seg0_idx = get_status_field_f("vht_oper_centr_freq_seg0_idx")
+ vht_oper_centr_freq_seg1_idx = get_status_field_f("vht_oper_centr_freq_seg1_idx")
+ if vht_oper_chwidth == "0" or vht_oper_chwidth is None:
+ if secondary_channel == "1":
+ bw = "40"
+ center_freq1 = str(int(freq) + 10)
+ elif secondary_channel == "-1":
+ center_freq1 = str(int(freq) - 10)
+ else:
+ pass
+ elif vht_oper_chwidth == "1":
+ bw = "80"
+ center_freq1 = str(int(vht_oper_centr_freq_seg0_idx) * 5 + 5000)
+ elif vht_oper_chwidth == "2":
+ bw = "160"
+ center_freq1 = str(int(vht_oper_centr_freq_seg0_idx) * 5 + 5000)
+ elif vht_oper_chwidth == "3":
+ bw = "80+80"
+ center_freq1 = str(int(vht_oper_centr_freq_seg0_idx) * 5 + 5000)
+ center_freq2 = str(int(vht_oper_centr_freq_seg1_idx) * 5 + 5000)
+ else:
+ pass
+
+ monitor_params = {"freq" : freq,
+ "bw" : bw,
+ "center_freq1" : center_freq1,
+ "center_freq2" : center_freq2}
+
+ return monitor_params
diff --git a/contrib/wpa/tests/remote/run-tests.py b/contrib/wpa/tests/remote/run-tests.py
new file mode 100755
index 000000000000..67993a3c2a34
--- /dev/null
+++ b/contrib/wpa/tests/remote/run-tests.py
@@ -0,0 +1,408 @@
+#!/usr/bin/env python3
+#
+# Remote test case executor
+# Copyright (c) 2016, Tieto Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import re
+import sys
+import time
+import traceback
+import getopt
+from datetime import datetime
+from random import shuffle
+
+import logging
+logger = logging.getLogger()
+
+scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
+sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
+sys.path.append(os.path.join(scriptsdir, '..', 'hwsim'))
+
+import wpaspy
+import config
+from test_devices import show_devices
+from test_devices import check_devices
+from rutils import TestSkip
+from utils import HwsimSkip
+from hwsim_wrapper import run_hwsim_test
+
+def usage():
+ print("USAGE: " + sys.argv[0] + " -t devices")
+ print("USAGE: " + sys.argv[0] + " -t check_devices")
+ print("USAGE: " + sys.argv[0] + " -d <dut_name> -t <all|sanity|tests_to_run> [-r <ref_name>] [-c <cfg_file.py>] [-m <all|monitor_name>] [-h hwsim_tests] [-f hwsim_modules][-R][-T][-P][-S][-v]")
+ print("USAGE: " + sys.argv[0])
+
+def get_devices(devices, duts, refs, monitors):
+ for dut in duts:
+ config.get_device(devices, dut, lock=True)
+ for ref in refs:
+ config.get_device(devices, ref, lock=True)
+ for monitor in monitors:
+ if monitor == "all":
+ continue
+ if monitor in duts:
+ continue
+ if monitor in refs:
+ continue
+ config.get_device(devices, monitor, lock=True)
+
+def put_devices(devices, duts, refs, monitors):
+ for dut in duts:
+ config.put_device(devices, dut)
+ for ref in refs:
+ config.put_device(devices, ref)
+ for monitor in monitors:
+ if monitor == "all":
+ continue
+ if monitor in duts:
+ continue
+ if monitor in refs:
+ continue
+ config.put_device(devices, monitor)
+
+def main():
+ duts = []
+ refs = []
+ monitors = []
+ filter_keys = []
+ requested_tests = ["help"]
+ requested_hwsim_tests = []
+ hwsim_tests = []
+ requested_modules = []
+ modules_tests = []
+ cfg_file = "cfg.py"
+ log_dir = "./logs/"
+ verbose = False
+ trace = False
+ restart = False
+ perf = False
+ shuffle_tests = False
+
+ # parse input parameters
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], "d:f:r:t:l:k:c:m:h:vRPTS",
+ ["dut=", "modules=", "ref=", "tests=",
+ "log-dir=",
+ "cfg=", "key=", "monitor=", "hwsim="])
+ except getopt.GetoptError as err:
+ print(err)
+ usage()
+ sys.exit(2)
+
+ for option, argument in opts:
+ if option == "-v":
+ verbose = True
+ elif option == "-R":
+ restart = True
+ elif option == "-T":
+ trace = True
+ elif option == "-P":
+ perf = True
+ elif option == "-S":
+ shuffle_tests = True
+ elif option in ("-d", "--dut"):
+ duts.append(argument)
+ elif option in ("-r", "--ref"):
+ refs.append(argument)
+ elif option in ("-t", "--tests"):
+ requested_tests = re.split('; | |, ', argument)
+ elif option in ("-l", "--log-dir"):
+ log_dir = argument
+ elif option in ("-k", "--key"):
+ filter_keys.append(argument)
+ elif option in ("-m", "--monitor"):
+ monitors.append(argument)
+ elif option in ("-c", "--cfg"):
+ cfg_file = argument
+ elif option in ("-h", "--hwsim"):
+ requested_hwsim_tests = re.split('; | |, ', argument)
+ elif option in ("-f", "--modules"):
+ requested_modules = re.split('; | |, ', argument)
+ else:
+ assert False, "unhandled option"
+
+ # get env configuration
+ setup_params = config.get_setup_params(cfg_file)
+ devices = config.get_devices(cfg_file)
+
+ # put logs in log_dir
+ symlink = os.path.join(log_dir, "current");
+ if os.path.exists(symlink):
+ os.unlink(symlink)
+ log_dir = os.path.join(log_dir, time.strftime("%Y_%m_%d_%H_%M_%S"))
+ if not os.path.exists(log_dir):
+ os.makedirs(log_dir)
+ os.symlink(os.path.join("../", log_dir), symlink)
+
+ # setup restart/trace/perf request
+ setup_params['local_log_dir'] = log_dir
+ setup_params['restart_device'] = restart
+ setup_params['trace'] = trace
+ setup_params['perf'] = perf
+
+ # configure logger
+ logger.setLevel(logging.DEBUG)
+
+ stdout_handler = logging.StreamHandler()
+ stdout_handler.setLevel(logging.WARNING)
+ if verbose:
+ stdout_handler.setLevel(logging.DEBUG)
+ logger.addHandler(stdout_handler)
+
+ formatter = logging.Formatter('%(asctime)s - %(message)s')
+ file_name = os.path.join(log_dir, 'run-tests.log')
+ log_handler = logging.FileHandler(file_name)
+ log_handler.setLevel(logging.DEBUG)
+ log_handler.setFormatter(formatter)
+ logger.addHandler(log_handler)
+
+ # import available tests
+ tests = []
+ failed = []
+ test_modules = []
+ files = os.listdir(scriptsdir)
+ for t in files:
+ m = re.match(r'(test_.*)\.py$', t)
+ if m:
+ mod = __import__(m.group(1))
+ test_modules.append(mod.__name__.replace('test_', '', 1))
+ for key, val in mod.__dict__.items():
+ if key.startswith("test_"):
+ tests.append(val)
+ test_names = list(set([t.__name__.replace('test_', '', 1) for t in tests]))
+
+ # import test_*
+ files = os.listdir("../hwsim/")
+ for t in files:
+ m = re.match(r'(test_.*)\.py$', t)
+ if m:
+ mod = __import__(m.group(1))
+ test_modules.append(mod.__name__.replace('test_', '', 1))
+ for key, val in mod.__dict__.items():
+ if key.startswith("test_"):
+ hwsim_tests.append(val)
+
+ # setup hwsim tests
+ hwsim_tests_to_run = []
+ if len(requested_hwsim_tests) > 0:
+ # apply filters
+ for filter_key in filter_keys:
+ filtered_tests = []
+ for hwsim_test in hwsim_tests:
+ if re.search(filter_key, hwsim_test.__name__):
+ filtered_tests.append(hwsim_test)
+ hwsim_tests = filtered_tests
+
+ # setup hwsim_test we should run
+ if requested_hwsim_tests[0] == "all":
+ hwsim_tests_to_run = hwsim_tests
+ elif requested_hwsim_tests[0] == "remote":
+ hwsim_tests_to_run = [t for t in hwsim_tests
+ if hasattr(t, "remote_compatible") and
+ t.remote_compatible]
+ else:
+ for test in requested_hwsim_tests:
+ t = None
+ for tt in hwsim_tests:
+ name = tt.__name__.replace('test_', '', 1)
+ if name == test:
+ t = tt
+ break
+ if not t:
+ logger.warning("hwsim test case: " + test + " NOT-FOUND")
+ continue
+ hwsim_tests_to_run.append(t)
+
+ # import test_* from modules
+ files = os.listdir("../hwsim/")
+ for t in files:
+ m = re.match(r'(test_.*)\.py$', t)
+ if m:
+ mod = __import__(m.group(1))
+ if mod.__name__.replace('test_', '', 1) not in requested_modules:
+ continue
+ for key, val in mod.__dict__.items():
+ if key.startswith("test_"):
+ modules_tests.append(val)
+
+ if len(requested_modules) > 0:
+ requested_hwsim_tests = modules_tests
+ hwsim_tests_to_run = modules_tests
+
+ # sort the list
+ test_names.sort()
+ tests.sort(key=lambda t: t.__name__)
+
+ # print help
+ if requested_tests[0] == "help" and len(requested_hwsim_tests) == 0:
+ usage()
+ print("\nAvailable Devices:")
+ for device in devices:
+ print("\t", device['name'])
+ print("\nAvailable tests:")
+ for test in test_names:
+ print("\t", test)
+ print("\nAvailable hwsim tests:")
+ for hwsim_test in hwsim_tests:
+ print("\t", hwsim_test.__name__.replace('test_', '', 1))
+ return
+
+ # show/check devices
+ if requested_tests[0] == "devices":
+ show_devices(devices, setup_params)
+ return
+
+ # apply filters
+ for filter_key in filter_keys:
+ filtered_tests = []
+ for test in tests:
+ if re.search(filter_key, test.__name__):
+ filtered_tests.append(test)
+ tests = filtered_tests
+
+ # setup test we should run
+ tests_to_run = []
+ if requested_tests[0] == "all":
+ tests_to_run = tests
+ if requested_tests[0] == "help":
+ pass
+ elif requested_tests[0] == "sanity":
+ for test in tests:
+ if test.__name__.startswith("test_sanity_"):
+ tests_to_run.append(test)
+ else:
+ for test in requested_tests:
+ t = None
+ for tt in tests:
+ name = tt.__name__.replace('test_', '', 1)
+ if name == test:
+ t = tt
+ break
+ if not t:
+ logger.warning("test case: " + test + " NOT-FOUND")
+ continue
+ tests_to_run.append(t)
+
+ if shuffle_tests:
+ shuffle(tests_to_run)
+ shuffle(hwsim_tests_to_run)
+
+ # lock devices
+ try:
+ get_devices(devices, duts, refs, monitors)
+ except Exception as e:
+ logger.warning("get devices failed: " + str(e))
+ logger.info(traceback.format_exc())
+ put_devices(devices, duts, refs, monitors)
+ return
+ except:
+ logger.warning("get devices failed")
+ logger.info(traceback.format_exc())
+ put_devices(devices, duts, refs, monitors)
+ return
+
+ # now run test cases
+ for dut in duts:
+ if len(requested_hwsim_tests) > 0:
+ logger.warning("DUT (apdev): " + str(dut))
+ else:
+ logger.warning("DUT: " + str(dut))
+ for ref in refs:
+ if len(requested_hwsim_tests) > 0:
+ logger.warning("REF (dev): " + str(ref))
+ else:
+ logger.warning("REF: " + str(ref))
+ for monitor in monitors:
+ logger.warning("MON: " + str(monitor))
+
+ # run check_devices at beginning
+ logger.warning("RUN check_devices")
+ try:
+ check_devices(devices, setup_params, refs, duts, monitors)
+ except Exception as e:
+ logger.warning("FAILED: " + str(e))
+ logger.info(traceback.format_exc())
+ put_devices(devices, duts, refs, monitors)
+ return
+ except:
+ logger.warning("FAILED")
+ logger.info(traceback.format_exc())
+ put_devices(devices, duts, refs, monitors)
+ return
+ logger.warning("PASS")
+
+ test_no = 1
+ for test in tests_to_run:
+ try:
+ start = datetime.now()
+ setup_params['tc_name'] = test.__name__.replace('test_', '', 1)
+ logger.warning("START - " + setup_params['tc_name'] + " (" + str(test_no) + "/" + str(len(tests_to_run)) + ")")
+ if test.__doc__:
+ logger.info("Test: " + test.__doc__)
+
+ # run tc
+ res = test(devices, setup_params, refs, duts, monitors)
+
+ end = datetime.now()
+ logger.warning("PASS (" + res + ") - " + str((end - start).total_seconds()) + "s")
+ except KeyboardInterrupt:
+ put_devices(devices, duts, refs, monitors)
+ raise
+ except TestSkip as e:
+ end = datetime.now()
+ logger.warning("SKIP (" + str(e) + ") - " + str((end - start).total_seconds()) + "s")
+ except Exception as e:
+ end = datetime.now()
+ logger.warning("FAILED (" + str(e) + ") - " + str((end - start).total_seconds()) + "s")
+ logger.info(traceback.format_exc())
+ failed.append(test.__name__.replace('test_', '', 1))
+ except:
+ end = datetime.now()
+ logger.warning("FAILED - " + str((end - start).total_seconds()) + "s")
+ logger.info(traceback.format_exc())
+ failed.append(test.__name__.replace('test_', '', 1))
+ test_no += 1
+
+ test_no = 1
+ for hwsim_test in hwsim_tests_to_run:
+ try:
+ start = datetime.now()
+ setup_params['tc_name'] = hwsim_test.__name__.replace('test_', '', 1)
+ logger.warning("START - " + setup_params['tc_name'] + " (" + str(test_no) + "/" + str(len(hwsim_tests_to_run)) + ")")
+ res = run_hwsim_test(devices, setup_params, refs, duts, monitors, hwsim_test)
+ end = datetime.now()
+ logger.warning("PASS (" + res + ") - " + str((end - start).total_seconds()) + "s")
+ except KeyboardInterrupt:
+ put_devices(devices, duts, refs, monitors)
+ raise
+ except HwsimSkip as e:
+ end = datetime.now()
+ logger.warning("SKIP (" + str(e) + ") - " + str((end - start).total_seconds()) + "s")
+ failed.append(hwsim_test.__name__.replace('test_', '', 1))
+ except Exception as e:
+ end = datetime.now()
+ logger.warning("FAILED (" + str(e) + ") - " + str((end - start).total_seconds()) + "s")
+ logger.info(traceback.format_exc())
+ failed.append(hwsim_test.__name__.replace('test_', '', 1))
+ except:
+ end = datetime.now()
+ logger.warning("FAILED - " + str((end - start).total_seconds()) + "s")
+ logger.info(traceback.format_exc())
+ failed.append(hwsim_test.__name__.replace('test_', '', 1))
+ test_no += 1
+
+ # unlock devices
+ put_devices(devices, duts, refs, monitors)
+
+ if len(failed) > 0:
+ logger.warning("Failed test cases:")
+ for test in failed:
+ logger.warning("\t" + test)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/contrib/wpa/tests/remote/rutils.py b/contrib/wpa/tests/remote/rutils.py
new file mode 100644
index 000000000000..6902991124c8
--- /dev/null
+++ b/contrib/wpa/tests/remote/rutils.py
@@ -0,0 +1,567 @@
+# Utils
+# Copyright (c) 2016, Tieto Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import re
+import time
+from remotehost import Host
+import hostapd
+import config
+
+class TestSkip(Exception):
+ def __init__(self, reason):
+ self.reason = reason
+ def __str__(self):
+ return self.reason
+
+# get host based on name
+def get_host(devices, dev_name):
+ dev = config.get_device(devices, dev_name)
+ host = Host(host=dev['hostname'],
+ ifname=dev['ifname'],
+ port=dev['port'],
+ name=dev['name'])
+ host.dev = dev
+ return host
+
+# Run setup_hw - hardware specific
+def setup_hw_host_iface(host, iface, setup_params, force_restart=False):
+ try:
+ setup_hw = setup_params['setup_hw']
+ restart = ""
+ try:
+ if setup_params['restart_device'] == True:
+ restart = "-R"
+ except:
+ pass
+
+ if force_restart:
+ restart = "-R"
+
+ host.execute([setup_hw, "-I", iface, restart])
+ except:
+ pass
+
+def setup_hw_host(host, setup_params, force_restart=False):
+ ifaces = re.split('; | |, ', host.ifname)
+ for iface in ifaces:
+ setup_hw_host_iface(host, iface, setup_params, force_restart)
+
+def setup_hw(hosts, setup_params, force_restart=False):
+ for host in hosts:
+ setup_hw_host(host, setup_params, force_restart)
+
+# get traces - hw specific
+def trace_start(hosts, setup_params):
+ for host in hosts:
+ trace_start_stop(host, setup_params, start=True)
+
+def trace_stop(hosts, setup_params):
+ for host in hosts:
+ trace_start_stop(host, setup_params, start=False)
+
+def trace_start_stop(host, setup_params, start):
+ if setup_params['trace'] == False:
+ return
+ try:
+ start_trace = setup_params['trace_start']
+ stop_trace = setup_params['trace_stop']
+ if start:
+ cmd = start_trace
+ else:
+ cmd = stop_trace
+ trace_dir = setup_params['log_dir'] + host.ifname + "/remote_traces"
+ host.add_log(trace_dir + "/*")
+ host.execute([cmd, "-I", host.ifname, "-D", trace_dir])
+ except:
+ pass
+
+# get perf
+def perf_start(hosts, setup_params):
+ for host in hosts:
+ perf_start_stop(host, setup_params, start=True)
+
+def perf_stop(hosts, setup_params):
+ for host in hosts:
+ perf_start_stop(host, setup_params, start=False)
+
+def perf_start_stop(host, setup_params, start):
+ if setup_params['perf'] == False:
+ return
+ try:
+ perf_start = setup_params['perf_start']
+ perf_stop = setup_params['perf_stop']
+ if start:
+ cmd = perf_start
+ else:
+ cmd = perf_stop
+ perf_dir = setup_params['log_dir'] + host.ifname + "/remote_perf"
+ host.add_log(perf_dir + "/*")
+ host.execute([cmd, "-I", host.ifname, "-D", perf_dir])
+ except:
+ pass
+
+# hostapd/wpa_supplicant helpers
+def run_hostapd(host, setup_params):
+ log_file = None
+ try:
+ tc_name = setup_params['tc_name']
+ log_dir = setup_params['log_dir']
+ log_file = log_dir + tc_name + "_hostapd_" + host.name + "_" + host.ifname + ".log"
+ host.execute(["rm", log_file])
+ log = " -f " + log_file
+ except:
+ log = ""
+
+ if log_file:
+ host.add_log(log_file)
+ pidfile = setup_params['log_dir'] + "hostapd_" + host.ifname + "_" + setup_params['tc_name'] + ".pid"
+ status, buf = host.execute([setup_params['hostapd'], "-B", "-ddt", "-g", "udp:" + host.port, "-P", pidfile, log])
+ if status != 0:
+ raise Exception("Could not run hostapd: " + buf)
+
+def run_wpasupplicant(host, setup_params):
+ log_file = None
+ try:
+ tc_name = setup_params['tc_name']
+ log_dir = setup_params['log_dir']
+ log_file = log_dir + tc_name + "_wpa_supplicant_" + host.name + "_" + host.ifname + ".log"
+ host.execute(["rm", log_file])
+ log = " -f " + log_file
+ except:
+ log = ""
+
+ if log_file:
+ host.add_log(log_file)
+ pidfile = setup_params['log_dir'] + "wpa_supplicant_" + host.ifname + "_" + setup_params['tc_name'] + ".pid"
+ status, buf = host.execute([setup_params['wpa_supplicant'], "-B", "-ddt", "-g", "udp:" + host.port, "-P", pidfile, log])
+ if status != 0:
+ raise Exception("Could not run wpa_supplicant: " + buf)
+
+def kill_wpasupplicant(host, setup_params):
+ pidfile = setup_params['log_dir'] + "wpa_supplicant_" + host.ifname + "_" + setup_params['tc_name'] + ".pid"
+ host.execute(["kill `cat " + pidfile + "`"])
+
+def kill_hostapd(host, setup_params):
+ pidfile = setup_params['log_dir'] + "hostapd_" + host.ifname + "_" + setup_params['tc_name'] + ".pid"
+ host.execute(["kill `cat " + pidfile + "`"])
+
+def get_ap_params(channel="1", bw="HT20", country="US", security="open", ht_capab=None, vht_capab=None):
+ ssid = "test_" + channel + "_" + security + "_" + bw
+
+ if bw == "b_only":
+ params = hostapd.b_only_params(channel, ssid, country)
+ elif bw == "g_only":
+ params = hostapd.g_only_params(channel, ssid, country)
+ elif bw == "g_only_wmm":
+ params = hostapd.g_only_params(channel, ssid, country)
+ params['wmm_enabled'] = "1"
+ elif bw == "a_only":
+ params = hostapd.a_only_params(channel, ssid, country)
+ elif bw == "a_only_wmm":
+ params = hostapd.a_only_params(channel, ssid, country)
+ params['wmm_enabled'] = "1"
+ elif bw == "HT20":
+ params = hostapd.ht20_params(channel, ssid, country)
+ if ht_capab:
+ try:
+ params['ht_capab'] = params['ht_capab'] + ht_capab
+ except:
+ params['ht_capab'] = ht_capab
+ elif bw == "HT40+":
+ params = hostapd.ht40_plus_params(channel, ssid, country)
+ if ht_capab:
+ params['ht_capab'] = params['ht_capab'] + ht_capab
+ elif bw == "HT40-":
+ params = hostapd.ht40_minus_params(channel, ssid, country)
+ if ht_capab:
+ params['ht_capab'] = params['ht_capab'] + ht_capab
+ elif bw == "VHT80":
+ params = hostapd.ht40_plus_params(channel, ssid, country)
+ if ht_capab:
+ params['ht_capab'] = params['ht_capab'] + ht_capab
+ if vht_capab:
+ try:
+ params['vht_capab'] = params['vht_capab'] + vht_capab
+ except:
+ params['vht_capab'] = vht_capab
+ params['ieee80211ac'] = "1"
+ params['vht_oper_chwidth'] = "1"
+ params['vht_oper_centr_freq_seg0_idx'] = str(int(channel) + 6)
+ else:
+ params = {}
+
+ # now setup security params
+ if security == "tkip":
+ sec_params = hostapd.wpa_params(passphrase="testtest")
+ elif security == "ccmp":
+ sec_params = hostapd.wpa2_params(passphrase="testtest")
+ elif security == "mixed":
+ sec_params = hostapd.wpa_mixed_params(passphrase="testtest")
+ elif security == "wep":
+ sec_params = {"wep_key0" : "123456789a",
+ "wep_default_key" : "0",
+ "auth_algs" : "1"}
+ elif security == "wep_shared":
+ sec_params = {"wep_key0" : "123456789a",
+ "wep_default_key" : "0",
+ "auth_algs" : "2"}
+ else:
+ sec_params = {}
+
+ params.update(sec_params)
+
+ return params
+
+# ip helpers
+def get_ipv4(client, ifname=None):
+ if ifname is None:
+ ifname = client.ifname
+ status, buf = client.execute(["ifconfig", ifname])
+ lines = buf.splitlines()
+
+ for line in lines:
+ res = line.find("inet addr:")
+ if res != -1:
+ break
+
+ if res != -1:
+ words = line.split()
+ addr = words[1].split(":")
+ return addr[1]
+
+ return "unknown"
+
+def get_ipv6(client, ifname=None):
+ res = -1
+ if ifname is None:
+ ifname = client.ifname
+ status, buf = client.execute(["ifconfig", ifname])
+ lines = buf.splitlines()
+
+ for line in lines:
+ res = line.find("Scope:Link")
+ if res == -1:
+ res = line.find("<link>")
+ if res != -1:
+ break
+
+ if res != -1:
+ words = line.split()
+ if words[0] == "inet6" and words[1] == "addr:":
+ addr_mask = words[2]
+ addr = addr_mask.split("/")
+ return addr[0]
+ if words[0] == "inet6":
+ return words[1]
+
+ return "unknown"
+
+def get_ip(client, addr_type="ipv6", iface=None):
+ if addr_type == "ipv6":
+ return get_ipv6(client, iface)
+ elif addr_type == "ipv4":
+ return get_ipv4(client, iface)
+ else:
+ return "unknown addr_type: " + addr_type
+
+def get_ipv4_addr(setup_params, number):
+ try:
+ ipv4_base = setup_params['ipv4_test_net']
+ except:
+ ipv4_base = "172.16.12.0"
+
+ parts = ipv4_base.split('.')
+ ipv4 = parts[0] + "." + parts[1] + "." + parts[2] + "." + str(number)
+
+ return ipv4
+
+def get_mac_addr(host, iface=None):
+ if iface == None:
+ iface = host.ifname
+ status, buf = host.execute(["ifconfig", iface])
+ if status != 0:
+ raise Exception("ifconfig " + iface)
+ words = buf.split()
+ found = 0
+ for word in words:
+ if found == 1:
+ return word
+ if word == "HWaddr" or word == "ether":
+ found = 1
+ raise Exception("Could not find HWaddr")
+
+# connectivity/ping helpers
+def get_ping_packet_loss(ping_res):
+ loss_line = ""
+ lines = ping_res.splitlines()
+ for line in lines:
+ if line.find("packet loss") != -1:
+ loss_line = line
+ break;
+
+ if loss_line == "":
+ return "100%"
+
+ sections = loss_line.split(",")
+
+ for section in sections:
+ if section.find("packet loss") != -1:
+ words = section.split()
+ return words[0]
+
+ return "100%"
+
+def ac_to_ping_ac(qos):
+ if qos == "be":
+ qos_param = "0x00"
+ elif qos == "bk":
+ qos_param = "0x20"
+ elif qos == "vi":
+ qos_param = "0xA0"
+ elif qos == "vo":
+ qos_param = "0xE0"
+ else:
+ qos_param = "0x00"
+ return qos_param
+
+def ping_run(host, ip, result, ifname=None, addr_type="ipv4", deadline="5", qos=None):
+ if ifname is None:
+ ifname = host.ifname
+ if addr_type == "ipv6":
+ ping = ["ping6"]
+ else:
+ ping = ["ping"]
+
+ ping = ping + ["-w", deadline, "-I", ifname]
+ if qos:
+ ping = ping + ["-Q", ac_to_ping_ac(qos)]
+ ping = ping + [ip]
+
+ flush_arp_cache(host)
+
+ thread = host.thread_run(ping, result)
+ return thread
+
+def ping_wait(host, thread, timeout=None):
+ host.thread_wait(thread, timeout)
+ if thread.is_alive():
+ raise Exception("ping thread still alive")
+
+def flush_arp_cache(host):
+ host.execute(["ip", "-s", "-s", "neigh", "flush", "all"])
+
+def check_connectivity(a, b, addr_type="ipv4", deadline="5", qos=None):
+ addr_a = get_ip(a, addr_type)
+ addr_b = get_ip(b, addr_type)
+
+ if addr_type == "ipv4":
+ ping = ["ping"]
+ else:
+ ping = ["ping6"]
+
+ ping_a_b = ping + ["-w", deadline, "-I", a.ifname]
+ ping_b_a = ping + ["-w", deadline, "-I", b.ifname]
+ if qos:
+ ping_a_b = ping_a_b + ["-Q", ac_to_ping_ac(qos)]
+ ping_b_a = ping_b_a + ["-Q", ac_to_ping_ac(qos)]
+ ping_a_b = ping_a_b + [addr_b]
+ ping_b_a = ping_b_a + [addr_a]
+
+ # Clear arp cache
+ flush_arp_cache(a)
+ flush_arp_cache(b)
+
+ status, buf = a.execute(ping_a_b)
+ if status == 2 and ping == "ping6":
+ # tentative possible for a while, try again
+ time.sleep(3)
+ status, buf = a.execute(ping_a_b)
+ if status != 0:
+ raise Exception("ping " + a.name + "/" + a.ifname + " >> " + b.name + "/" + b.ifname)
+
+ a_b = get_ping_packet_loss(buf)
+
+ # Clear arp cache
+ flush_arp_cache(a)
+ flush_arp_cache(b)
+
+ status, buf = b.execute(ping_b_a)
+ if status != 0:
+ raise Exception("ping " + b.name + "/" + b.ifname + " >> " + a.name + "/" + a.ifname)
+
+ b_a = get_ping_packet_loss(buf)
+
+ if int(a_b[:-1]) > 40:
+ raise Exception("Too high packet lost: " + a_b)
+
+ if int(b_a[:-1]) > 40:
+ raise Exception("Too high packet lost: " + b_a)
+
+ return a_b, b_a
+
+
+# iperf helpers
+def get_iperf_speed(iperf_res, pattern="Mbits/sec"):
+ lines = iperf_res.splitlines()
+ sum_line = ""
+ last_line = ""
+ count = 0
+ res = -1
+
+ # first find last SUM line
+ for line in lines:
+ res = line.find("[SUM]")
+ if res != -1:
+ sum_line = line
+
+ # next check SUM status
+ if sum_line != "":
+ words = sum_line.split()
+ for word in words:
+ res = word.find(pattern)
+ if res != -1:
+ return words[count - 1] + " " + pattern
+ count = count + 1
+
+ # no SUM - one thread - find last line
+ for line in lines:
+ res = line.find(pattern)
+ if res != -1:
+ last_line = line
+
+ if last_line == "":
+ return "0 " + pattern
+
+ count = 0
+ words = last_line.split()
+ for word in words:
+ res = word.find(pattern)
+ if res != -1:
+ return words[count - 1] + " " + pattern
+ break;
+ count = count + 1
+ return "0 " + pattern
+
+def ac_to_iperf_ac(qos):
+ if qos == "be":
+ qos_param = "0x00"
+ elif qos == "bk":
+ qos_param = "0x20"
+ elif qos == "vi":
+ qos_param = "0xA0"
+ elif qos == "vo":
+ qos_param = "0xE0"
+ else:
+ qos_param = "0x00"
+ return qos_param
+
+def iperf_run(server, client, server_ip, client_res, server_res,
+ l4="udp", bw="30M", test_time="30", parallel="5",
+ qos="be", param=" -i 5 ", ifname=None, l3="ipv4",
+ port="5001", iperf="iperf"):
+ if ifname == None:
+ ifname = client.ifname
+
+ if iperf == "iperf":
+ iperf_server = [iperf]
+ elif iperf == "iperf3":
+ iperf_server = [iperf, "-1"]
+
+ if l3 == "ipv4":
+ iperf_client = [iperf, "-c", server_ip, "-p", port]
+ iperf_server = iperf_server + ["-p", port]
+ elif l3 == "ipv6":
+ iperf_client = [iperf, "-V", "-c", server_ip + "%" + ifname, "-p", port]
+ iperf_server = iperf_server + ["-V", "-p", port]
+ else:
+ return -1, -1
+
+ iperf_server = iperf_server + ["-s", "-f", "m", param]
+ iperf_client = iperf_client + ["-f", "m", "-t", test_time]
+
+ if parallel != "1":
+ iperf_client = iperf_client + ["-P", parallel]
+
+ if l4 == "udp":
+ if iperf != "iperf3":
+ iperf_server = iperf_server + ["-u"]
+ iperf_client = iperf_client + ["-u", "-b", bw]
+
+ if qos:
+ iperf_client = iperf_client + ["-Q", ac_to_iperf_ac(qos)]
+
+ flush_arp_cache(server)
+ flush_arp_cache(client)
+
+ server_thread = server.thread_run(iperf_server, server_res)
+ time.sleep(1)
+ client_thread = client.thread_run(iperf_client, client_res)
+
+ return server_thread, client_thread
+
+def iperf_wait(server, client, server_thread, client_thread, timeout=None, iperf="iperf"):
+ client.thread_wait(client_thread, timeout)
+ if client_thread.is_alive():
+ raise Exception("iperf client thread still alive")
+
+ server.thread_wait(server_thread, 5)
+ if server_thread.is_alive():
+ server.execute(["killall", "-s", "INT", iperf])
+ time.sleep(1)
+
+ server.thread_wait(server_thread, 5)
+ if server_thread.is_alive():
+ raise Exception("iperf server thread still alive")
+
+ return
+
+def run_tp_test(server, client, l3="ipv4", iperf="iperf", l4="tcp", test_time="10", parallel="5",
+ qos="be", bw="30M", ifname=None, port="5001"):
+ client_res = []
+ server_res = []
+
+ server_ip = get_ip(server, l3)
+ time.sleep(1)
+ server_thread, client_thread = iperf_run(server, client, server_ip, client_res, server_res,
+ l3=l3, iperf=iperf, l4=l4, test_time=test_time,
+ parallel=parallel, qos=qos, bw=bw, ifname=ifname,
+ port=port)
+ iperf_wait(server, client, server_thread, client_thread, iperf=iperf, timeout=int(test_time) + 10)
+
+ if client_res[0] != 0:
+ raise Exception(iperf + " client: " + client_res[1])
+ if server_res[0] != 0:
+ raise Exception(iperf + " server: " + server_res[1])
+ if client_res[1] is None:
+ raise Exception(iperf + " client result issue")
+ if server_res[1] is None:
+ raise Exception(iperf + " server result issue")
+
+ if iperf == "iperf":
+ result = server_res[1]
+ if iperf == "iperf3":
+ result = client_res[1]
+
+ speed = get_iperf_speed(result)
+ return speed
+
+def get_iperf_bw(bw, parallel, spacial_streams=2):
+ if bw == "b_only":
+ max_tp = 11
+ elif bw == "g_only" or bw == "g_only_wmm" or bw == "a_only" or bw == "a_only_wmm":
+ max_tp = 54
+ elif bw == "HT20":
+ max_tp = 72 * spacial_streams
+ elif bw == "HT40+" or bw == "HT40-":
+ max_tp = 150 * spacial_streams
+ elif bw == "VHT80":
+ max_tp = 433 * spacial_streams
+ else:
+ max_tp = 150
+
+ max_tp = 1.2 * max_tp
+
+ return str(int(max_tp/int(parallel))) + "M"
diff --git a/contrib/wpa/tests/remote/test_devices.py b/contrib/wpa/tests/remote/test_devices.py
new file mode 100644
index 000000000000..ccd9984a25f4
--- /dev/null
+++ b/contrib/wpa/tests/remote/test_devices.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python2
+#
+# Show/check devices
+# Copyright (c) 2016, Tieto Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import time
+import traceback
+import config
+import os
+import sys
+import getopt
+import re
+
+import logging
+logger = logging.getLogger()
+
+import rutils
+from remotehost import Host
+from wpasupplicant import WpaSupplicant
+import hostapd
+
+def show_devices(devices, setup_params):
+ """Show/check available devices"""
+ print("Devices:")
+ for device in devices:
+ host = rutils.get_host(devices, device['name'])
+ # simple check if authorized_keys works correctly
+ status, buf = host.execute(["id"])
+ if status != 0:
+ print("[" + host.name + "] - ssh communication: FAILED")
+ continue
+ else:
+ print("[" + host.name + "] - ssh communication: OK")
+ # check setup_hw works correctly
+ rutils.setup_hw_host(host, setup_params)
+
+ # show uname
+ status, buf = host.execute(["uname", "-s", "-n", "-r", "-m", "-o"])
+ print("\t" + buf)
+ # show ifconfig
+ ifaces = re.split('; | |, ', host.ifname)
+ for iface in ifaces:
+ status, buf = host.execute(["ifconfig", iface])
+ if status != 0:
+ print("\t" + iface + " failed\n")
+ continue
+ lines = buf.splitlines()
+ for line in lines:
+ print("\t" + line)
+ # check hostapd, wpa_supplicant, iperf exist
+ status, buf = host.execute([setup_params['wpa_supplicant'], "-v"])
+ if status != 0:
+ print("\t" + setup_params['wpa_supplicant'] + " not find\n")
+ continue
+ lines = buf.splitlines()
+ for line in lines:
+ print("\t" + line)
+ print("")
+ status, buf = host.execute([setup_params['hostapd'], "-v"])
+ if status != 1:
+ print("\t" + setup_params['hostapd'] + " not find\n")
+ continue
+ lines = buf.splitlines()
+ for line in lines:
+ print("\t" + line)
+ print("")
+ status, buf = host.execute([setup_params['iperf'], "-v"])
+ if status != 0 and status != 1:
+ print("\t" + setup_params['iperf'] + " not find\n")
+ continue
+ lines = buf.splitlines()
+ for line in lines:
+ print("\t" + line)
+ print("")
+
+def check_device(devices, setup_params, dev_name, monitor=False):
+ host = rutils.get_host(devices, dev_name)
+ # simple check if authorized_keys works correctly
+ status, buf = host.execute(["id"])
+ if status != 0:
+ raise Exception(dev_name + " - ssh communication FAILED: " + buf)
+
+ rutils.setup_hw_host(host, setup_params)
+
+ ifaces = re.split('; | |, ', host.ifname)
+ # check interfaces (multi for monitor)
+ for iface in ifaces:
+ status, buf = host.execute(["ifconfig", iface])
+ if status != 0:
+ raise Exception(dev_name + " ifconfig " + iface + " failed: " + buf)
+
+ # monitor doesn't need wpa_supplicant/hostapd ...
+ if monitor == True:
+ return
+
+ status, buf = host.execute(["ls", "-l", setup_params['wpa_supplicant']])
+ if status != 0:
+ raise Exception(dev_name + " - wpa_supplicant: " + buf)
+
+ status, buf = host.execute(["ls", "-l", setup_params['hostapd']])
+ if status != 0:
+ raise Exception(dev_name + " - hostapd: " + buf)
+
+ status, buf = host.execute(["which", setup_params['iperf']])
+ if status != 0:
+ raise Exception(dev_name + " - iperf: " + buf)
+
+ status, buf = host.execute(["which", "tshark"])
+ if status != 0:
+ logger.debug(dev_name + " - tshark: " + buf)
+
+def check_devices(devices, setup_params, refs, duts, monitors):
+ """Check duts/refs/monitors devices"""
+ for dut in duts:
+ check_device(devices, setup_params, dut)
+ for ref in refs:
+ check_device(devices, setup_params, ref)
+ for monitor in monitors:
+ if monitor == "all":
+ continue
+ check_device(devices, setup_params, monitor, monitor=True)
diff --git a/contrib/wpa/tests/remote/test_example.py b/contrib/wpa/tests/remote/test_example.py
new file mode 100644
index 000000000000..1550665c30c4
--- /dev/null
+++ b/contrib/wpa/tests/remote/test_example.py
@@ -0,0 +1,141 @@
+# Example test case
+# Copyright (c) 2016, Tieto Corporation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import remotehost
+from wpasupplicant import WpaSupplicant
+import hostapd
+import config
+import rutils
+import monitor
+
+import logging
+logger = logging.getLogger()
+
+def test_example(devices, setup_params, refs, duts, monitors):
+ """TC example - simple connect and ping test"""
+ try:
+ sta = None
+ ap = None
+ hapd = None
+ wpas = None
+ mon = None
+
+ # get hosts based on name
+ sta = rutils.get_host(devices, duts[0])
+ ap = rutils.get_host(devices, refs[0])
+
+ # setup log dir
+ local_log_dir = setup_params['local_log_dir']
+
+ # setup hw before test
+ rutils.setup_hw([sta, ap], setup_params)
+
+ # run traces if requested
+ rutils.trace_start([sta], setup_params)
+
+ # run perf if requested
+ rutils.perf_start([sta], setup_params)
+
+ # run hostapd/wpa_supplicant
+ rutils.run_wpasupplicant(sta, setup_params)
+ rutils.run_hostapd(ap, setup_params)
+
+ # get ap_params
+ ap_params = rutils.get_ap_params(channel="1", bw="HT20", country="US",
+ security="open")
+
+ # Add monitors if requested
+ monitor_hosts = monitor.create(devices, setup_params, refs, duts,
+ monitors)
+ if len(monitor_hosts) > 0:
+ mon = monitor_hosts[0]
+ monitor.add(sta, monitors)
+ monitor.add(ap, monitors)
+
+ # connect to hostapd/wpa_supplicant UDP CTRL iface
+ hapd = hostapd.add_ap(ap.dev, ap_params)
+ freq = hapd.get_status_field("freq")
+ wpas = WpaSupplicant(hostname=sta.host, global_iface="udp",
+ global_port=sta.port)
+ wpas.interface_add(sta.ifname)
+
+ # setup standalone monitor based on hapd; could be multi interface
+ # monitor
+ monitor_param = monitor.get_monitor_params(hapd)
+ monitor.setup(mon, [monitor_param])
+
+ # run monitors
+ monitor.run(sta, setup_params)
+ monitor.run(ap, setup_params)
+ monitor.run(mon, setup_params)
+
+ # connect wpa_supplicant to hostapd
+ wpas.connect(ap_params['ssid'], key_mgmt="NONE", scan_freq=freq)
+
+ # run ping test
+ ap_sta, sta_ap = rutils.check_connectivity(ap, sta, "ipv6")
+
+ # remove/destroy monitors
+ monitor.remove(sta)
+ monitor.remove(ap)
+ monitor.destroy(devices, monitor_hosts)
+
+ # hostapd/wpa_supplicant cleanup
+ wpas.interface_remove(sta.ifname)
+ wpas.terminate()
+
+ hapd.close_ctrl()
+ hostapd.remove_bss(ap.dev)
+ hostapd.terminate(ap.dev)
+
+ # stop perf
+ rutils.perf_stop([sta], setup_params)
+
+ # stop traces
+ rutils.trace_stop([sta], setup_params)
+
+ # get wpa_supplicant/hostapd/tshark logs
+ sta.get_logs(local_log_dir)
+ ap.get_logs(local_log_dir)
+ if mon:
+ mon.get_logs(local_log_dir)
+
+ return "packet_loss: " + ap_sta + ", " + sta_ap
+ except:
+ rutils.perf_stop([sta], setup_params)
+ rutils.trace_stop([sta], setup_params)
+ if wpas:
+ try:
+ wpas.interface_remove(sta.ifname)
+ wpas.terminate()
+ except:
+ pass
+ if hapd:
+ try:
+ hapd.close_ctrl()
+ hostapd.remove_bss(ap.dev)
+ hostapd.terminate(ap.dev)
+ except:
+ pass
+ if mon:
+ monitor.destroy(devices, monitor_hosts)
+ mon.get_logs(local_log_dir)
+
+ if sta:
+ monitor.remove(sta)
+ dmesg = setup_params['log_dir'] + setup_params['tc_name'] + "_" + sta.name + "_" + sta.ifname + ".dmesg"
+ sta.execute(["dmesg", "-c", ">", dmesg])
+ sta.add_log(dmesg)
+ sta.get_logs(local_log_dir)
+ sta.execute(["ifconfig", sta.ifname, "down"])
+ if ap:
+ monitor.remove(ap)
+ dmesg = setup_params['log_dir'] + setup_params['tc_name'] + "_" + ap.name + "_" + ap.ifname + ".dmesg"
+ ap.execute(["dmesg", "-c", ">", dmesg])
+ ap.add_log(dmesg)
+ ap.get_logs(local_log_dir)
+ ap.execute(["ifconfig", ap.ifname, " down"])
+ raise
diff --git a/contrib/wpa/tests/remote/test_monitor.py b/contrib/wpa/tests/remote/test_monitor.py
new file mode 100644
index 000000000000..c8b88d4bec43
--- /dev/null
+++ b/contrib/wpa/tests/remote/test_monitor.py
@@ -0,0 +1,52 @@
+# Monitor support
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import remotehost
+import config
+import rutils
+import monitor
+import time
+import os
+
+import logging
+logger = logging.getLogger()
+
+def run_monitor(devices, setup_params, refs, duts, monitors, seconds=None):
+ try:
+ air_monitor = []
+ output = "\n\tPCAP files:\n"
+ # setup log dir
+ local_log_dir = setup_params['local_log_dir']
+
+ # add/run monitors if requested
+ air_monitors = monitor.create(devices, setup_params, refs, duts,
+ monitors)
+ for air_monitor in air_monitors:
+ monitor.setup(air_monitor)
+ monitor.run(air_monitor, setup_params)
+ logger.warning(air_monitor.name + " - monitor started ...")
+
+ if seconds != None:
+ time.sleep(int(seconds))
+ else:
+ input("\tPress Enter to end capturing...")
+
+ # destroy monitor / get pcap
+ monitor.destroy(devices, air_monitors)
+ for air_monitor in air_monitors:
+ for log in air_monitor.logs:
+ head, tail = os.path.split(log)
+ output = output + "\t" + local_log_dir + "/" + tail + "\n"
+ air_monitor.get_logs(local_log_dir)
+ return output
+ except:
+ for air_monitor in air_monitors:
+ monitor.destroy(devices, air_monitors)
+ air_monitor.get_logs(local_log_dir)
+ raise
+
+def test_run_monitor(devices, setup_params, refs, duts, monitors):
+ """TC run standalone monitor"""
+ return run_monitor(devices, setup_params, refs, duts, monitors)
diff --git a/contrib/wpa/tests/test-aes.c b/contrib/wpa/tests/test-aes.c
new file mode 100644
index 000000000000..9d76c07b56e6
--- /dev/null
+++ b/contrib/wpa/tests/test-aes.c
@@ -0,0 +1,624 @@
+/*
+ * Test program for AES
+ * Copyright (c) 2003-2012, 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 "common.h"
+#include "crypto/crypto.h"
+#include "crypto/aes_wrap.h"
+
+#define BLOCK_SIZE 16
+
+static void test_aes_perf(void)
+{
+#if 0 /* this did not seem to work with new compiler?! */
+#ifdef __i386__
+#define rdtscll(val) \
+ __asm__ __volatile__("rdtsc" : "=A" (val))
+ const int num_iters = 10;
+ int i;
+ unsigned int start, end;
+ u8 key[16], pt[16], ct[16];
+ void *ctx;
+
+ printf("keySetupEnc:");
+ for (i = 0; i < num_iters; i++) {
+ rdtscll(start);
+ ctx = aes_encrypt_init(key, 16);
+ rdtscll(end);
+ aes_encrypt_deinit(ctx);
+ printf(" %d", end - start);
+ }
+ printf("\n");
+
+ printf("Encrypt:");
+ ctx = aes_encrypt_init(key, 16);
+ for (i = 0; i < num_iters; i++) {
+ rdtscll(start);
+ aes_encrypt(ctx, pt, ct);
+ rdtscll(end);
+ printf(" %d", end - start);
+ }
+ aes_encrypt_deinit(ctx);
+ printf("\n");
+#endif /* __i386__ */
+#endif
+}
+
+
+/*
+ * GCM test vectors from
+ * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
+ */
+struct gcm_test_vector {
+ char *k;
+ char *p;
+ char *aad;
+ char *iv;
+ char *c;
+ char *t;
+};
+
+static const struct gcm_test_vector gcm_tests[] = {
+ {
+ /* Test Case 1 */
+ "00000000000000000000000000000000",
+ "",
+ "",
+ "000000000000000000000000",
+ "",
+ "58e2fccefa7e3061367f1d57a4e7455a"
+ },
+ {
+ /* Test Case 2 */
+ "00000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "",
+ "000000000000000000000000",
+ "0388dace60b6a392f328c2b971b2fe78",
+ "ab6e47d42cec13bdf53a67b21257bddf"
+ },
+ {
+ /* Test Case 3 */
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+ "",
+ "cafebabefacedbaddecaf888",
+ "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091473f5985",
+ "4d5c2af327cd64a62cf35abd2ba6fab4"
+ },
+ {
+ /* Test Case 4 */
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbaddecaf888",
+ "42831ec2217774244b7221b784d0d49ce3aa212f2c02a4e035c17e2329aca12e21d514b25466931c7d8f6a5aac84aa051ba30b396a0aac973d58e091",
+ "5bc94fbc3221a5db94fae95ae7121a47"
+ },
+ {
+ /* Test Case 5 */
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbad",
+ "61353b4c2806934a777ff51fa22a4755699b2a714fcdc6f83766e5f97b6c742373806900e49f24b22b097544d4896b424989b5e1ebac0f07c23f4598",
+ "3612d2e79e3b0785561be14aaca2fccb"
+ },
+ {
+ /* Test Case 6 */
+ "feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+ "8ce24998625615b603a033aca13fb894be9112a5c3a211a8ba262a3cca7e2ca701e4a9a4fba43c90ccdcb281d48c7c6fd62875d2aca417034c34aee5",
+ "619cc5aefffe0bfa462af43c1699d050"
+ },
+ {
+ /* Test Case 7 */
+ "000000000000000000000000000000000000000000000000",
+ "",
+ "",
+ "000000000000000000000000",
+ "",
+ "cd33b28ac773f74ba00ed1f312572435"
+ },
+ {
+ /* Test Case 8 */
+ "000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "",
+ "000000000000000000000000",
+ "98e7247c07f0fe411c267e4384b0f600",
+ "2ff58d80033927ab8ef4d4587514f0fb"
+ },
+ {
+ /* Test Case 9 */
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+ "",
+ "cafebabefacedbaddecaf888",
+ "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710acade256",
+ "9924a7c8587336bfb118024db8674a14"
+ },
+ {
+ /* Test Case 10 */
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbaddecaf888",
+ "3980ca0b3c00e841eb06fac4872a2757859e1ceaa6efd984628593b40ca1e19c7d773d00c144c525ac619d18c84a3f4718e2448b2fe324d9ccda2710",
+ "2519498e80f1478f37ba55bd6d27618c"
+ },
+ {
+ /* Test Case 11 */
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbad",
+ "0f10f599ae14a154ed24b36e25324db8c566632ef2bbb34f8347280fc4507057fddc29df9a471f75c66541d4d4dad1c9e93a19a58e8b473fa0f062f7",
+ "65dcc57fcf623a24094fcca40d3533f8"
+ },
+ {
+ /* Test Case 12 */
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+ "d27e88681ce3243c4830165a8fdcf9ff1de9a1d8e6b447ef6ef7b79828666e4581e79012af34ddd9e2f037589b292db3e67c036745fa22e7e9b7373b",
+ "dcf566ff291c25bbb8568fc3d376a6d9"
+ },
+ {
+ /* Test Case 13 */
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "",
+ "",
+ "000000000000000000000000",
+ "",
+ "530f8afbc74536b9a963b4f1c4cb738b"
+ },
+ {
+ /* Test Case 14 */
+ "0000000000000000000000000000000000000000000000000000000000000000",
+ "00000000000000000000000000000000",
+ "",
+ "000000000000000000000000",
+ "cea7403d4d606b6e074ec5d3baf39d18",
+ "d0d1c8a799996bf0265b98b5d48ab919"
+ },
+ {
+ /* Test Case 15 */
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b391aafd255",
+ "",
+ "cafebabefacedbaddecaf888",
+ "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662898015ad",
+ "b094dac5d93471bdec1a502270e3cc6c"
+ },
+ {
+ /* Test Case 16 */
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbaddecaf888",
+ "522dc1f099567d07f47f37a32a84427d643a8cdcbfe5c0c97598a2bd2555d1aa8cb08e48590dbb3da7b08b1056828838c5f61e6393ba7a0abcc9f662",
+ "76fc6ece0f4e1768cddf8853bb2d551b"
+ },
+ {
+ /* Test Case 17 */
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "cafebabefacedbad",
+ "c3762df1ca787d32ae47c13bf19844cbaf1ae14d0b976afac52ff7d79bba9de0feb582d33934a4f0954cc2363bc73f7862ac430e64abe499f47c9b1f",
+ "3a337dbf46a792c45e454913fe2ea8f2"
+ },
+ {
+ /* Test Case 18 */
+ "feffe9928665731c6d6a8f9467308308feffe9928665731c6d6a8f9467308308",
+ "d9313225f88406e5a55909c5aff5269a86a7a9531534f7da2e4c303d8a318a721c3c0c95956809532fcf0e2449a6b525b16aedf5aa0de657ba637b39",
+ "feedfacedeadbeeffeedfacedeadbeefabaddad2",
+ "9313225df88406e555909c5aff5269aa6a7a9538534f7da1e4c303d2a318a728c3c0c95156809539fcf0e2429a6b525416aedbf5a0de6a57a637b39b",
+ "5a8def2f0c9e53f1f75d7853659e2a20eeb2b22aafde6419a058ab4f6f746bf40fc0c3b780f244452da3ebf1c5d82cdea2418997200ef82e44ae7e3f",
+ "a44a8266ee1c8eb0c8b5d4cf5ae9f19a"
+ }
+};
+
+
+static int test_gcm(void)
+{
+ int ret = 0;
+ int i;
+ u8 k[32], aad[32], iv[64], t[16], tag[16];
+ u8 p[64], c[64], tmp[64];
+ size_t k_len, p_len, aad_len, iv_len;
+
+ for (i = 0; i < ARRAY_SIZE(gcm_tests); i++) {
+ const struct gcm_test_vector *tc = &gcm_tests[i];
+
+ k_len = os_strlen(tc->k) / 2;
+ if (hexstr2bin(tc->k, k, k_len)) {
+ printf("Invalid GCM test vector %d (k)\n", i);
+ ret++;
+ continue;
+ }
+
+ p_len = os_strlen(tc->p) / 2;
+ if (hexstr2bin(tc->p, p, p_len)) {
+ printf("Invalid GCM test vector %d (p)\n", i);
+ ret++;
+ continue;
+ }
+
+ aad_len = os_strlen(tc->aad) / 2;
+ if (hexstr2bin(tc->aad, aad, aad_len)) {
+ printf("Invalid GCM test vector %d (aad)\n", i);
+ ret++;
+ continue;
+ }
+
+ iv_len = os_strlen(tc->iv) / 2;
+ if (hexstr2bin(tc->iv, iv, iv_len)) {
+ printf("Invalid GCM test vector %d (iv)\n", i);
+ ret++;
+ continue;
+ }
+
+ if (hexstr2bin(tc->c, c, p_len)) {
+ printf("Invalid GCM test vector %d (c)\n", i);
+ ret++;
+ continue;
+ }
+
+ if (hexstr2bin(tc->t, t, sizeof(t))) {
+ printf("Invalid GCM test vector %d (t)\n", i);
+ ret++;
+ continue;
+ }
+
+ if (aes_gcm_ae(k, k_len, iv, iv_len, p, p_len, aad, aad_len,
+ tmp, tag) < 0) {
+ printf("GCM-AE failed (test case %d)\n", i);
+ ret++;
+ continue;
+ }
+
+ if (os_memcmp(c, tmp, p_len) != 0) {
+ printf("GCM-AE mismatch (test case %d)\n", i);
+ ret++;
+ }
+
+ if (os_memcmp(tag, t, sizeof(tag)) != 0) {
+ printf("GCM-AE tag mismatch (test case %d)\n", i);
+ ret++;
+ }
+
+ if (p_len == 0) {
+ if (aes_gmac(k, k_len, iv, iv_len, aad, aad_len, tag) <
+ 0) {
+ printf("GMAC failed (test case %d)\n", i);
+ ret++;
+ continue;
+ }
+
+ if (os_memcmp(tag, t, sizeof(tag)) != 0) {
+ printf("GMAC tag mismatch (test case %d)\n", i);
+ ret++;
+ }
+ }
+
+ if (aes_gcm_ad(k, k_len, iv, iv_len, c, p_len, aad, aad_len,
+ t, tmp) < 0) {
+ printf("GCM-AD failed (test case %d)\n", i);
+ ret++;
+ continue;
+ }
+
+ if (os_memcmp(p, tmp, p_len) != 0) {
+ printf("GCM-AD mismatch (test case %d)\n", i);
+ ret++;
+ }
+ }
+
+ return ret;
+}
+
+
+static int test_nist_key_wrap_ae(const char *fname)
+{
+ FILE *f;
+ int ret = 0;
+ char buf[15000], *pos, *pos2;
+ u8 bin[2000], k[32], p[1024], c[1024 + 8], result[1024 + 8];
+ size_t bin_len, k_len = 0, p_len = 0, c_len = 0;
+ int ok = 0;
+
+ printf("NIST KW AE tests from %s\n", fname);
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ printf("%s does not exist - cannot validate test vectors\n",
+ fname);
+ return 1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ if (buf[0] == '#')
+ continue;
+ pos = os_strchr(buf, '=');
+ if (pos == NULL)
+ continue;
+ pos2 = pos - 1;
+ while (pos2 >= buf && *pos2 == ' ')
+ *pos2-- = '\0';
+ *pos++ = '\0';
+ while (*pos == ' ')
+ *pos++ = '\0';
+ pos2 = os_strchr(pos, '\r');
+ if (!pos2)
+ pos2 = os_strchr(pos, '\n');
+ if (pos2)
+ *pos2 = '\0';
+ else
+ pos2 = pos + os_strlen(pos);
+
+ if (buf[0] == '[') {
+ printf("%s = %s\n", buf, pos);
+ continue;
+ }
+
+ if (os_strcmp(buf, "COUNT") == 0) {
+ printf("Test %s - ", pos);
+ continue;
+ }
+
+ bin_len = os_strlen(pos);
+ if (bin_len > sizeof(bin) * 2) {
+ printf("Too long binary data (%s)\n", buf);
+ return 1;
+ }
+ if (bin_len & 0x01) {
+ printf("Odd number of hexstring values (%s)\n",
+ buf);
+ return 1;
+ }
+ bin_len /= 2;
+ if (hexstr2bin(pos, bin, bin_len) < 0) {
+ printf("Invalid hex string '%s' (%s)\n", pos, buf);
+ return 1;
+ }
+
+ if (os_strcmp(buf, "K") == 0) {
+ if (bin_len > sizeof(k)) {
+ printf("Too long K (%u)\n", (unsigned) bin_len);
+ return 1;
+ }
+ os_memcpy(k, bin, bin_len);
+ k_len = bin_len;
+ continue;
+ }
+
+ if (os_strcmp(buf, "P") == 0) {
+ if (bin_len > sizeof(p)) {
+ printf("Too long P (%u)\n", (unsigned) bin_len);
+ return 1;
+ }
+ os_memcpy(p, bin, bin_len);
+ p_len = bin_len;
+ continue;
+ }
+
+ if (os_strcmp(buf, "C") != 0) {
+ printf("Unexpected field '%s'\n", buf);
+ continue;
+ }
+
+ if (bin_len > sizeof(c)) {
+ printf("Too long C (%u)\n", (unsigned) bin_len);
+ return 1;
+ }
+ os_memcpy(c, bin, bin_len);
+ c_len = bin_len;
+
+ if (p_len % 8 != 0 || c_len % 8 != 0 || c_len - p_len != 8) {
+ printf("invalid parameter length (p_len=%u c_len=%u)\n",
+ (unsigned) p_len, (unsigned) c_len);
+ continue;
+ }
+
+ if (aes_wrap(k, k_len, p_len / 8, p, result)) {
+ printf("aes_wrap() failed\n");
+ ret++;
+ continue;
+ }
+
+ if (os_memcmp(c, result, c_len) == 0) {
+ printf("OK\n");
+ ok++;
+ } else {
+ printf("FAIL\n");
+ ret++;
+ }
+ }
+
+ fclose(f);
+
+ if (ret)
+ printf("Test case failed\n");
+ else
+ printf("%d test vectors OK\n", ok);
+
+ return ret;
+}
+
+
+static int test_nist_key_wrap_ad(const char *fname)
+{
+ FILE *f;
+ int ret = 0;
+ char buf[15000], *pos, *pos2;
+ u8 bin[2000], k[32], p[1024], c[1024 + 8], result[1024 + 8];
+ size_t bin_len, k_len = 0, p_len = 0, c_len = 0;
+ int ok = 0;
+ int fail;
+
+ printf("NIST KW AD tests from %s\n", fname);
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ printf("%s does not exist - cannot validate test vectors\n",
+ fname);
+ return 1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ if (buf[0] == '#')
+ continue;
+ fail = 0;
+ pos = os_strchr(buf, '=');
+ if (pos == NULL) {
+ if (os_strncmp(buf, "FAIL", 4) == 0) {
+ fail = 1;
+ goto skip_val_parse;
+ }
+ continue;
+ }
+ pos2 = pos - 1;
+ while (pos2 >= buf && *pos2 == ' ')
+ *pos2-- = '\0';
+ *pos++ = '\0';
+ while (*pos == ' ')
+ *pos++ = '\0';
+ pos2 = os_strchr(pos, '\r');
+ if (!pos2)
+ pos2 = os_strchr(pos, '\n');
+ if (pos2)
+ *pos2 = '\0';
+ else
+ pos2 = pos + os_strlen(pos);
+
+ if (buf[0] == '[') {
+ printf("%s = %s\n", buf, pos);
+ continue;
+ }
+
+ if (os_strcmp(buf, "COUNT") == 0) {
+ printf("Test %s - ", pos);
+ continue;
+ }
+
+ bin_len = os_strlen(pos);
+ if (bin_len > sizeof(bin) * 2) {
+ printf("Too long binary data (%s)\n", buf);
+ return 1;
+ }
+ if (bin_len & 0x01) {
+ printf("Odd number of hexstring values (%s)\n",
+ buf);
+ return 1;
+ }
+ bin_len /= 2;
+ if (hexstr2bin(pos, bin, bin_len) < 0) {
+ printf("Invalid hex string '%s' (%s)\n", pos, buf);
+ return 1;
+ }
+
+ if (os_strcmp(buf, "K") == 0) {
+ if (bin_len > sizeof(k)) {
+ printf("Too long K (%u)\n", (unsigned) bin_len);
+ return 1;
+ }
+ os_memcpy(k, bin, bin_len);
+ k_len = bin_len;
+ continue;
+ }
+
+ if (os_strcmp(buf, "C") == 0) {
+ if (bin_len > sizeof(c)) {
+ printf("Too long C (%u)\n", (unsigned) bin_len);
+ return 1;
+ }
+ os_memcpy(c, bin, bin_len);
+ c_len = bin_len;
+ continue;
+ }
+
+ skip_val_parse:
+ if (!fail) {
+ if (os_strcmp(buf, "P") != 0) {
+ printf("Unexpected field '%s'\n", buf);
+ continue;
+ }
+
+ if (bin_len > sizeof(p)) {
+ printf("Too long P (%u)\n", (unsigned) bin_len);
+ return 1;
+ }
+ os_memcpy(p, bin, bin_len);
+ p_len = bin_len;
+
+ if (p_len % 8 != 0 || c_len % 8 != 0 ||
+ c_len - p_len != 8) {
+ printf("invalid parameter length (p_len=%u c_len=%u)\n",
+ (unsigned) p_len, (unsigned) c_len);
+ continue;
+ }
+ }
+
+ if (aes_unwrap(k, k_len, (c_len / 8) - 1, c, result)) {
+ if (fail) {
+ printf("OK (fail reported)\n");
+ ok++;
+ continue;
+ }
+ printf("aes_unwrap() failed\n");
+ ret++;
+ continue;
+ }
+
+ if (fail) {
+ printf("FAIL (mismatch not reported)\n");
+ ret++;
+ } else if (os_memcmp(p, result, p_len) == 0) {
+ printf("OK\n");
+ ok++;
+ } else {
+ printf("FAIL\n");
+ ret++;
+ }
+ }
+
+ fclose(f);
+
+ if (ret)
+ printf("Test case failed\n");
+ else
+ printf("%d test vectors OK\n", ok);
+
+ return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+
+ if (argc >= 3 && os_strcmp(argv[1], "NIST-KW-AE") == 0)
+ ret += test_nist_key_wrap_ae(argv[2]);
+ else if (argc >= 3 && os_strcmp(argv[1], "NIST-KW-AD") == 0)
+ ret += test_nist_key_wrap_ad(argv[2]);
+
+ test_aes_perf();
+
+ ret += test_gcm();
+
+ if (ret)
+ printf("FAILED!\n");
+
+ return ret;
+}
diff --git a/contrib/wpa/tests/test-base64.c b/contrib/wpa/tests/test-base64.c
new file mode 100644
index 000000000000..99943f0db3cf
--- /dev/null
+++ b/contrib/wpa/tests/test-base64.c
@@ -0,0 +1,42 @@
+/*
+ * Base64 encoding/decoding (RFC1341) - test program
+ * Copyright (c) 2005, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/os.h"
+#include "utils/base64.h"
+
+int main(int argc, char *argv[])
+{
+ FILE *f;
+ size_t len, elen;
+ unsigned char *buf, *e;
+
+ if (argc != 4) {
+ printf("Usage: base64 <encode|decode> <in file> <out file>\n");
+ return -1;
+ }
+
+ buf = (unsigned char *) os_readfile(argv[2], &len);
+ if (buf == NULL)
+ return -1;
+
+ if (strcmp(argv[1], "encode") == 0)
+ e = (unsigned char *) base64_encode(buf, len, &elen);
+ else
+ e = base64_decode((const char *) buf, len, &elen);
+ if (e == NULL)
+ return -2;
+ f = fopen(argv[3], "w");
+ if (f == NULL)
+ return -3;
+ fwrite(e, 1, elen, f);
+ fclose(f);
+ free(e);
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/test-https.c b/contrib/wpa/tests/test-https.c
new file mode 100644
index 000000000000..a72e56f9d21c
--- /dev/null
+++ b/contrib/wpa/tests/test-https.c
@@ -0,0 +1,225 @@
+/*
+ * Testing tool for TLSv1 client routines using HTTPS
+ * Copyright (c) 2011, 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 <netdb.h>
+
+#include "common.h"
+#include "crypto/tls.h"
+
+
+static void https_tls_event_cb(void *ctx, enum tls_event ev,
+ union tls_event_data *data)
+{
+ wpa_printf(MSG_DEBUG, "HTTPS: TLS event %d", ev);
+}
+
+
+static struct wpabuf * https_recv(int s)
+{
+ struct wpabuf *in;
+ int len, ret;
+ fd_set rfds;
+ struct timeval tv;
+
+ in = wpabuf_alloc(20000);
+ if (in == NULL)
+ return NULL;
+
+ FD_ZERO(&rfds);
+ FD_SET(s, &rfds);
+ tv.tv_sec = 5;
+ tv.tv_usec = 0;
+
+ wpa_printf(MSG_DEBUG, "Waiting for more data");
+ ret = select(s + 1, &rfds, NULL, NULL, &tv);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
+ wpabuf_free(in);
+ return NULL;
+ }
+ if (ret == 0) {
+ /* timeout */
+ wpa_printf(MSG_INFO, "Timeout on waiting for data");
+ wpabuf_free(in);
+ return NULL;
+ }
+
+ len = recv(s, wpabuf_put(in, 0), wpabuf_tailroom(in), 0);
+ if (len < 0) {
+ wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+ wpabuf_free(in);
+ return NULL;
+ }
+ if (len == 0) {
+ wpa_printf(MSG_DEBUG, "No more data available");
+ wpabuf_free(in);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "Received %d bytes", len);
+ wpabuf_put(in, len);
+
+ return in;
+}
+
+
+static int https_client(int s, const char *path)
+{
+ struct tls_config conf;
+ void *tls;
+ struct tls_connection *conn;
+ struct wpabuf *in, *out, *appl;
+ int res = -1;
+ int need_more_data;
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.event_cb = https_tls_event_cb;
+ tls = tls_init(&conf);
+ if (tls == NULL)
+ return -1;
+
+ conn = tls_connection_init(tls);
+ if (conn == NULL) {
+ tls_deinit(tls);
+ return -1;
+ }
+
+ in = NULL;
+
+ for (;;) {
+ appl = NULL;
+ out = tls_connection_handshake2(tls, conn, in, &appl,
+ &need_more_data);
+ wpabuf_free(in);
+ in = NULL;
+ if (out == NULL) {
+ if (need_more_data)
+ goto read_more;
+ goto done;
+ }
+ if (tls_connection_get_failed(tls, conn)) {
+ wpa_printf(MSG_ERROR, "TLS handshake failed");
+ goto done;
+ }
+ if (tls_connection_established(tls, conn))
+ break;
+ wpa_printf(MSG_DEBUG, "Sending %d bytes",
+ (int) wpabuf_len(out));
+ if (send(s, wpabuf_head(out), wpabuf_len(out), 0) < 0) {
+ wpa_printf(MSG_ERROR, "send: %s", strerror(errno));
+ goto done;
+ }
+ wpabuf_free(out);
+ out = NULL;
+
+ read_more:
+ in = https_recv(s);
+ if (in == NULL)
+ goto done;
+ }
+ wpabuf_free(out);
+ out = NULL;
+
+ wpa_printf(MSG_INFO, "TLS connection established");
+ if (appl)
+ wpa_hexdump_buf(MSG_DEBUG, "Received application data", appl);
+
+ in = wpabuf_alloc(100 + os_strlen(path));
+ if (in == NULL)
+ goto done;
+ wpabuf_put_str(in, "GET ");
+ wpabuf_put_str(in, path);
+ wpabuf_put_str(in, " HTTP/1.0\r\n\r\n");
+ out = tls_connection_encrypt(tls, conn, in);
+ wpabuf_free(in);
+ in = NULL;
+ if (out == NULL)
+ goto done;
+
+ wpa_printf(MSG_INFO, "Sending HTTP request: %d bytes",
+ (int) wpabuf_len(out));
+ if (send(s, wpabuf_head(out), wpabuf_len(out), 0) < 0) {
+ wpa_printf(MSG_ERROR, "send: %s", strerror(errno));
+ goto done;
+ }
+ wpabuf_free(out);
+ out = NULL;
+
+ wpa_printf(MSG_INFO, "Reading HTTP response");
+ for (;;) {
+ int need_more_data;
+ in = https_recv(s);
+ if (in == NULL)
+ goto done;
+ out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
+ if (need_more_data)
+ wpa_printf(MSG_DEBUG, "HTTP: Need more data");
+ wpabuf_free(in);
+ in = NULL;
+ if (out == NULL)
+ goto done;
+ wpa_hexdump_ascii(MSG_INFO, "Response", wpabuf_head(out),
+ wpabuf_len(out));
+ wpabuf_free(out);
+ out = NULL;
+ }
+
+ res = 0;
+done:
+ wpabuf_free(out);
+ wpabuf_free(in);
+ wpabuf_free(appl);
+ tls_connection_deinit(tls, conn);
+ tls_deinit(tls);
+
+ return res;
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct addrinfo hints, *result, *rp;
+ int res, s;
+
+ wpa_debug_level = 0;
+ wpa_debug_show_keys = 1;
+
+ if (argc < 4) {
+ wpa_printf(MSG_INFO, "usage: test-https server port path");
+ return -1;
+ }
+
+ os_memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ res = getaddrinfo(argv[1], argv[2], &hints, &result);
+ if (res) {
+ wpa_printf(MSG_ERROR, "getaddrinfo: %s", gai_strerror(res));
+ return -1;
+ }
+
+ for (rp = result; rp; rp = rp->ai_next) {
+ s = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (s < 0)
+ continue;
+ if (connect(s, rp->ai_addr, rp->ai_addrlen) == 0)
+ break;
+ close(s);
+ }
+ freeaddrinfo(result);
+
+ if (rp == NULL) {
+ wpa_printf(MSG_ERROR, "Could not connect");
+ return -1;
+ }
+
+ https_client(s, argv[3]);
+ close(s);
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/test-https_server.c b/contrib/wpa/tests/test-https_server.c
new file mode 100644
index 000000000000..33b448682478
--- /dev/null
+++ b/contrib/wpa/tests/test-https_server.c
@@ -0,0 +1,275 @@
+/*
+ * Testing tool for TLSv1 server routines using HTTPS
+ * Copyright (c) 2011-2019, 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 "common.h"
+#include "crypto/tls.h"
+
+
+static void https_tls_event_cb(void *ctx, enum tls_event ev,
+ union tls_event_data *data)
+{
+ wpa_printf(MSG_DEBUG, "HTTPS: TLS event %d", ev);
+}
+
+
+static struct wpabuf * https_recv(int s, int timeout_ms)
+{
+ struct wpabuf *in;
+ int len, ret;
+ fd_set rfds;
+ struct timeval tv;
+
+ in = wpabuf_alloc(20000);
+ if (in == NULL)
+ return NULL;
+
+ FD_ZERO(&rfds);
+ FD_SET(s, &rfds);
+ tv.tv_sec = timeout_ms / 1000;
+ tv.tv_usec = timeout_ms % 1000;
+
+ wpa_printf(MSG_DEBUG, "Waiting for more data");
+ ret = select(s + 1, &rfds, NULL, NULL, &tv);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
+ wpabuf_free(in);
+ return NULL;
+ }
+ if (ret == 0) {
+ /* timeout */
+ wpa_printf(MSG_INFO, "Timeout on waiting for data");
+ wpabuf_free(in);
+ return NULL;
+ }
+
+ len = recv(s, wpabuf_put(in, 0), wpabuf_tailroom(in), 0);
+ if (len < 0) {
+ wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
+ wpabuf_free(in);
+ return NULL;
+ }
+ if (len == 0) {
+ wpa_printf(MSG_DEBUG, "No more data available");
+ wpabuf_free(in);
+ return NULL;
+ }
+ wpa_printf(MSG_DEBUG, "Received %d bytes", len);
+ wpabuf_put(in, len);
+
+ return in;
+}
+
+
+static void https_tls_log_cb(void *ctx, const char *msg)
+{
+ wpa_printf(MSG_DEBUG, "TLS: %s", msg);
+}
+
+
+static int https_server(int s)
+{
+ struct tls_config conf;
+ void *tls;
+ struct tls_connection_params params;
+ struct tls_connection *conn;
+ struct wpabuf *in, *out, *appl;
+ int res = -1;
+
+ os_memset(&conf, 0, sizeof(conf));
+ conf.event_cb = https_tls_event_cb;
+ tls = tls_init(&conf);
+ if (!tls)
+ return -1;
+
+ os_memset(&params, 0, sizeof(params));
+ params.ca_cert = "hwsim/auth_serv/ca.pem";
+ params.client_cert = "hwsim/auth_serv/server.pem";
+ params.private_key = "hwsim/auth_serv/server.key";
+ params.dh_file = "hwsim/auth_serv/dh.conf";
+
+ if (tls_global_set_params(tls, &params)) {
+ wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
+ tls_deinit(tls);
+ return -1;
+ }
+
+ conn = tls_connection_init(tls);
+ if (!conn) {
+ tls_deinit(tls);
+ return -1;
+ }
+
+ tls_connection_set_log_cb(conn, https_tls_log_cb, NULL);
+
+ for (;;) {
+ in = https_recv(s, 5000);
+ if (!in)
+ goto done;
+
+ appl = NULL;
+ out = tls_connection_server_handshake(tls, conn, in, &appl);
+ wpabuf_free(in);
+ in = NULL;
+ if (!out) {
+ if (!tls_connection_get_failed(tls, conn) &&
+ !tls_connection_established(tls, conn))
+ continue;
+ goto done;
+ }
+ wpa_printf(MSG_DEBUG, "Sending %d bytes",
+ (int) wpabuf_len(out));
+ if (send(s, wpabuf_head(out), wpabuf_len(out), 0) < 0) {
+ wpa_printf(MSG_ERROR, "send: %s", strerror(errno));
+ goto done;
+ }
+ wpabuf_free(out);
+ out = NULL;
+ if (tls_connection_get_failed(tls, conn)) {
+ wpa_printf(MSG_ERROR, "TLS handshake failed");
+ goto done;
+ }
+ if (tls_connection_established(tls, conn))
+ break;
+ }
+ wpabuf_free(out);
+ out = NULL;
+
+ wpa_printf(MSG_INFO, "TLS connection established");
+ if (appl)
+ wpa_hexdump_buf(MSG_DEBUG, "Received application data", appl);
+
+ wpa_printf(MSG_INFO, "Reading HTTP request");
+ for (;;) {
+ int need_more_data;
+
+ in = https_recv(s, 5000);
+ if (!in)
+ goto done;
+ out = tls_connection_decrypt2(tls, conn, in, &need_more_data);
+ wpabuf_free(in);
+ in = NULL;
+ if (need_more_data) {
+ wpa_printf(MSG_DEBUG, "HTTP: Need more data");
+ continue;
+ }
+ if (!out)
+ goto done;
+ wpa_hexdump_ascii(MSG_INFO, "Request",
+ wpabuf_head(out), wpabuf_len(out));
+ wpabuf_free(out);
+ out = NULL;
+ break;
+ }
+
+ in = wpabuf_alloc(1000);
+ if (!in)
+ goto done;
+ wpabuf_put_str(in, "HTTP/1.1 200 OK\r\n"
+ "Server: test-https_server\r\n"
+ "\r\n"
+ "<HTML><BODY>HELLO</BODY></HTML>\n");
+ wpa_hexdump_ascii(MSG_DEBUG, "Response",
+ wpabuf_head(in), wpabuf_len(in));
+ out = tls_connection_encrypt(tls, conn, in);
+ wpabuf_free(in);
+ in = NULL;
+ wpa_hexdump_buf(MSG_DEBUG, "Encrypted response", out);
+ if (!out)
+ goto done;
+
+ wpa_printf(MSG_INFO, "Sending HTTP response: %d bytes",
+ (int) wpabuf_len(out));
+ if (send(s, wpabuf_head(out), wpabuf_len(out), 0) < 0) {
+ wpa_printf(MSG_ERROR, "send: %s", strerror(errno));
+ goto done;
+ }
+ wpabuf_free(out);
+ out = NULL;
+
+ res = 0;
+done:
+ wpabuf_free(out);
+ wpabuf_free(in);
+ wpabuf_free(appl);
+ tls_connection_deinit(tls, conn);
+ tls_deinit(tls);
+ close(s);
+
+ return res;
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in sin;
+ int port, s, conn;
+ int on = 1;
+
+ wpa_debug_level = 0;
+ wpa_debug_show_keys = 1;
+
+ if (argc < 2) {
+ wpa_printf(MSG_INFO, "usage: test-https_server port");
+ return -1;
+ }
+
+ port = atoi(argv[1]);
+
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "HTTP: setsockopt(SO_REUSEADDR) failed: %s",
+ strerror(errno));
+ /* try to continue anyway */
+ }
+
+ os_memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(port);
+ if (bind(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ perror("bind");
+ close(s);
+ return -1;
+ }
+
+ if (listen(s, 10) < 0) {
+ perror("listen");
+ close(s);
+ return -1;
+ }
+
+ for (;;) {
+ struct sockaddr_in addr;
+ socklen_t addr_len = sizeof(addr);
+
+ conn = accept(s, (struct sockaddr *) &addr, &addr_len);
+ if (conn < 0) {
+ perror("accept");
+ break;
+ }
+
+ wpa_printf(MSG_DEBUG, "-------------------------------------");
+ wpa_printf(MSG_DEBUG, "Connection from %s:%d",
+ inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
+
+ https_server(conn);
+ wpa_printf(MSG_DEBUG, "Done with the connection");
+ wpa_printf(MSG_DEBUG, "-------------------------------------");
+ }
+
+ close(s);
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/test-list.c b/contrib/wpa/tests/test-list.c
new file mode 100644
index 000000000000..01bcbf640940
--- /dev/null
+++ b/contrib/wpa/tests/test-list.c
@@ -0,0 +1,72 @@
+/*
+ * Doubly-linked list - test program
+ * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/os.h"
+#include "utils/list.h"
+
+struct test {
+ struct dl_list list;
+ int value;
+};
+
+static void dump_list(struct dl_list *head)
+{
+ struct test *t;
+ printf("dump:");
+ dl_list_for_each(t, head, struct test, list)
+ printf(" %d", t->value);
+ printf(" (len=%d%s)\n", dl_list_len(head),
+ dl_list_empty(head) ? " empty" : "");
+}
+
+int main(int argc, char *argv[])
+{
+ struct dl_list head;
+ struct test *t, *tmp;
+ int i;
+
+ dl_list_init(&head);
+ dump_list(&head);
+
+ for (i = 0; i < 5; i++) {
+ t = os_zalloc(sizeof(*t));
+ if (t == NULL)
+ return -1;
+ t->value = i;
+ dl_list_add(&head, &t->list);
+ dump_list(&head);
+ }
+
+ for (i = 10; i > 5; i--) {
+ t = os_zalloc(sizeof(*t));
+ if (t == NULL)
+ return -1;
+ t->value = i;
+ dl_list_add_tail(&head, &t->list);
+ dump_list(&head);
+ }
+
+ i = 0;
+ dl_list_for_each(t, &head, struct test, list)
+ if (++i == 5)
+ break;
+ printf("move: %d\n", t->value);
+ dl_list_del(&t->list);
+ dl_list_add(&head, &t->list);
+ dump_list(&head);
+
+ dl_list_for_each_safe(t, tmp, &head, struct test, list) {
+ printf("delete: %d\n", t->value);
+ dl_list_del(&t->list);
+ os_free(t);
+ dump_list(&head);
+ }
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/test-md4.c b/contrib/wpa/tests/test-md4.c
new file mode 100644
index 000000000000..e3e63edf14a2
--- /dev/null
+++ b/contrib/wpa/tests/test-md4.c
@@ -0,0 +1,93 @@
+/*
+ * Test program for MD4 (test vectors from RFC 1320)
+ * Copyright (c) 2006-2009, 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 "common.h"
+#include "crypto/crypto.h"
+
+int main(int argc, char *argv[])
+{
+ struct {
+ char *data;
+ char *hash;
+ } tests[] = {
+ {
+ "",
+ "\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31"
+ "\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0"
+ },
+ {
+ "a",
+ "\xbd\xe5\x2c\xb3\x1d\xe3\x3e\x46"
+ "\x24\x5e\x05\xfb\xdb\xd6\xfb\x24"
+ },
+ {
+ "abc",
+ "\xa4\x48\x01\x7a\xaf\x21\xd8\x52"
+ "\x5f\xc1\x0a\xe8\x7a\xa6\x72\x9d"
+ },
+ {
+ "message digest",
+ "\xd9\x13\x0a\x81\x64\x54\x9f\xe8"
+ "\x18\x87\x48\x06\xe1\xc7\x01\x4b"
+ },
+ {
+ "abcdefghijklmnopqrstuvwxyz",
+ "\xd7\x9e\x1c\x30\x8a\xa5\xbb\xcd"
+ "\xee\xa8\xed\x63\xdf\x41\x2d\xa9"
+ },
+ {
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
+ "0123456789",
+ "\x04\x3f\x85\x82\xf2\x41\xdb\x35"
+ "\x1c\xe6\x27\xe1\x53\xe7\xf0\xe4"
+ },
+ {
+ "12345678901234567890123456789012345678901234567890"
+ "123456789012345678901234567890",
+ "\xe3\x3b\x4d\xdc\x9c\x38\xf2\x19"
+ "\x9c\x3e\x7b\x16\x4f\xcc\x05\x36"
+ }
+ };
+ unsigned int i;
+ u8 hash[16];
+ const u8 *addr[2];
+ size_t len[2];
+ int errors = 0;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ printf("MD4 test case %d:", i);
+
+ addr[0] = (u8 *) tests[i].data;
+ len[0] = strlen(tests[i].data);
+ md4_vector(1, addr, len, hash);
+ if (memcmp(hash, tests[i].hash, 16) != 0) {
+ printf(" FAIL");
+ errors++;
+ } else
+ printf(" OK");
+
+ if (len[0]) {
+ addr[0] = (u8 *) tests[i].data;
+ len[0] = strlen(tests[i].data);
+ addr[1] = (u8 *) tests[i].data + 1;
+ len[1] = strlen(tests[i].data) - 1;
+ md4_vector(1, addr, len, hash);
+ if (memcmp(hash, tests[i].hash, 16) != 0) {
+ printf(" FAIL");
+ errors++;
+ } else
+ printf(" OK");
+ }
+
+ printf("\n");
+ }
+
+ return errors;
+}
diff --git a/contrib/wpa/tests/test-milenage.c b/contrib/wpa/tests/test-milenage.c
new file mode 100644
index 000000000000..7c4be09020a1
--- /dev/null
+++ b/contrib/wpa/tests/test-milenage.c
@@ -0,0 +1,814 @@
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/milenage.h"
+
+
+/**
+ * milenage_opc - Determine OPc from OP and K
+ * @op: OP = 128-bit operator variant algorithm configuration field
+ * @k: K = 128-bit subscriber key
+ * @opc: Buffer for OPc = 128-bit value derived from OP and K
+ */
+static int milenage_opc(const u8 *op, const u8 *k, u8 *opc)
+{
+ int i;
+ /* OP_C = OP XOR E_K(OP) */
+ if (aes_128_encrypt_block(k, op, opc) < 0)
+ return -1;
+ for (i = 0; i < 16; i++)
+ opc[i] ^= op[i];
+ return 0;
+}
+
+
+struct gsm_milenage_test_set {
+ u8 ki[16];
+ u8 rand[16];
+ u8 opc[16];
+ u8 sres1[4];
+ u8 sres2[4];
+ u8 kc[8];
+};
+
+static const struct gsm_milenage_test_set gsm_test_sets[] =
+{
+ {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 1 */
+ { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f,
+ 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc },
+ { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d,
+ 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 },
+ { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e,
+ 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf },
+ { 0x46, 0xf8, 0x41, 0x6a },
+ { 0xa5, 0x42, 0x11, 0xd5 },
+ { 0xea, 0xe4, 0xbe, 0x82, 0x3a, 0xf9, 0xa0, 0x8b }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 2 */
+ { 0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0,
+ 0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f },
+ { 0x9f, 0x7c, 0x8d, 0x02, 0x1a, 0xcc, 0xf4, 0xdb,
+ 0x21, 0x3c, 0xcf, 0xf0, 0xc7, 0xf7, 0x1a, 0x6a },
+ { 0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6,
+ 0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3 },
+ { 0x8c, 0x30, 0x8a, 0x5e },
+ { 0x80, 0x11, 0xc4, 0x8c },
+ { 0xaa, 0x01, 0x73, 0x9b, 0x8c, 0xaa, 0x97, 0x6d }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 3 */
+ { 0x9e, 0x59, 0x44, 0xae, 0xa9, 0x4b, 0x81, 0x16,
+ 0x5c, 0x82, 0xfb, 0xf9, 0xf3, 0x2d, 0xb7, 0x51 },
+ { 0xce, 0x83, 0xdb, 0xc5, 0x4a, 0xc0, 0x27, 0x4a,
+ 0x15, 0x7c, 0x17, 0xf8, 0x0d, 0x01, 0x7b, 0xd6 },
+ { 0xa6, 0x4a, 0x50, 0x7a, 0xe1, 0xa2, 0xa9, 0x8b,
+ 0xb8, 0x8e, 0xb4, 0x21, 0x01, 0x35, 0xdc, 0x87 },
+ { 0xcf, 0xbc, 0xe3, 0xfe },
+ { 0xf3, 0x65, 0xcd, 0x68 },
+ { 0x9a, 0x8e, 0xc9, 0x5f, 0x40, 0x8c, 0xc5, 0x07 }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 4 */
+ { 0x4a, 0xb1, 0xde, 0xb0, 0x5c, 0xa6, 0xce, 0xb0,
+ 0x51, 0xfc, 0x98, 0xe7, 0x7d, 0x02, 0x6a, 0x84 },
+ { 0x74, 0xb0, 0xcd, 0x60, 0x31, 0xa1, 0xc8, 0x33,
+ 0x9b, 0x2b, 0x6c, 0xe2, 0xb8, 0xc4, 0xa1, 0x86 },
+ { 0xdc, 0xf0, 0x7c, 0xbd, 0x51, 0x85, 0x52, 0x90,
+ 0xb9, 0x2a, 0x07, 0xa9, 0x89, 0x1e, 0x52, 0x3e },
+ { 0x96, 0x55, 0xe2, 0x65 },
+ { 0x58, 0x60, 0xfc, 0x1b },
+ { 0xcd, 0xc1, 0xdc, 0x08, 0x41, 0xb8, 0x1a, 0x22 }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 5 */
+ { 0x6c, 0x38, 0xa1, 0x16, 0xac, 0x28, 0x0c, 0x45,
+ 0x4f, 0x59, 0x33, 0x2e, 0xe3, 0x5c, 0x8c, 0x4f },
+ { 0xee, 0x64, 0x66, 0xbc, 0x96, 0x20, 0x2c, 0x5a,
+ 0x55, 0x7a, 0xbb, 0xef, 0xf8, 0xba, 0xbf, 0x63 },
+ { 0x38, 0x03, 0xef, 0x53, 0x63, 0xb9, 0x47, 0xc6,
+ 0xaa, 0xa2, 0x25, 0xe5, 0x8f, 0xae, 0x39, 0x34 },
+ { 0x13, 0x68, 0x8f, 0x17 },
+ { 0x16, 0xc8, 0x23, 0x3f },
+ { 0xdf, 0x75, 0xbc, 0x5e, 0xa8, 0x99, 0x87, 0x9f }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 6 */
+ { 0x2d, 0x60, 0x9d, 0x4d, 0xb0, 0xac, 0x5b, 0xf0,
+ 0xd2, 0xc0, 0xde, 0x26, 0x70, 0x14, 0xde, 0x0d },
+ { 0x19, 0x4a, 0xa7, 0x56, 0x01, 0x38, 0x96, 0xb7,
+ 0x4b, 0x4a, 0x2a, 0x3b, 0x0a, 0xf4, 0x53, 0x9e },
+ { 0xc3, 0x5a, 0x0a, 0xb0, 0xbc, 0xbf, 0xc9, 0x25,
+ 0x2c, 0xaf, 0xf1, 0x5f, 0x24, 0xef, 0xbd, 0xe0 },
+ { 0x55, 0x3d, 0x00, 0xb3 },
+ { 0x8c, 0x25, 0xa1, 0x6c },
+ { 0x84, 0xb4, 0x17, 0xae, 0x3a, 0xea, 0xb4, 0xf3 }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 7 */
+ { 0xa5, 0x30, 0xa7, 0xfe, 0x42, 0x8f, 0xad, 0x10,
+ 0x82, 0xc4, 0x5e, 0xdd, 0xfc, 0xe1, 0x38, 0x84 },
+ { 0x3a, 0x4c, 0x2b, 0x32, 0x45, 0xc5, 0x0e, 0xb5,
+ 0xc7, 0x1d, 0x08, 0x63, 0x93, 0x95, 0x76, 0x4d },
+ { 0x27, 0x95, 0x3e, 0x49, 0xbc, 0x8a, 0xf6, 0xdc,
+ 0xc6, 0xe7, 0x30, 0xeb, 0x80, 0x28, 0x6b, 0xe3 },
+ { 0x59, 0xf1, 0xa4, 0x4a },
+ { 0xa6, 0x32, 0x41, 0xe1 },
+ { 0x3b, 0x4e, 0x24, 0x4c, 0xdc, 0x60, 0xce, 0x03 }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 8 */
+ { 0xd9, 0x15, 0x1c, 0xf0, 0x48, 0x96, 0xe2, 0x58,
+ 0x30, 0xbf, 0x2e, 0x08, 0x26, 0x7b, 0x83, 0x60 },
+ { 0xf7, 0x61, 0xe5, 0xe9, 0x3d, 0x60, 0x3f, 0xeb,
+ 0x73, 0x0e, 0x27, 0x55, 0x6c, 0xb8, 0xa2, 0xca },
+ { 0xc4, 0xc9, 0x3e, 0xff, 0xe8, 0xa0, 0x81, 0x38,
+ 0xc2, 0x03, 0xd4, 0xc2, 0x7c, 0xe4, 0xe3, 0xd9 },
+ { 0x50, 0x58, 0x88, 0x61 },
+ { 0x4a, 0x90, 0xb2, 0x17 },
+ { 0x8d, 0x4e, 0xc0, 0x1d, 0xe5, 0x97, 0xac, 0xfe }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 9 */
+ { 0xa0, 0xe2, 0x97, 0x1b, 0x68, 0x22, 0xe8, 0xd3,
+ 0x54, 0xa1, 0x8c, 0xc2, 0x35, 0x62, 0x4e, 0xcb },
+ { 0x08, 0xef, 0xf8, 0x28, 0xb1, 0x3f, 0xdb, 0x56,
+ 0x27, 0x22, 0xc6, 0x5c, 0x7f, 0x30, 0xa9, 0xb2 },
+ { 0x82, 0xa2, 0x6f, 0x22, 0xbb, 0xa9, 0xe9, 0x48,
+ 0x8f, 0x94, 0x9a, 0x10, 0xd9, 0x8e, 0x9c, 0xc4 },
+ { 0xcd, 0xe6, 0xb0, 0x27 },
+ { 0x4b, 0xc2, 0x21, 0x2d },
+ { 0xd8, 0xde, 0xbc, 0x4f, 0xfb, 0xcd, 0x60, 0xaa }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 10 */
+ { 0x0d, 0xa6, 0xf7, 0xba, 0x86, 0xd5, 0xea, 0xc8,
+ 0xa1, 0x9c, 0xf5, 0x63, 0xac, 0x58, 0x64, 0x2d },
+ { 0x67, 0x9a, 0xc4, 0xdb, 0xac, 0xd7, 0xd2, 0x33,
+ 0xff, 0x9d, 0x68, 0x06, 0xf4, 0x14, 0x9c, 0xe3 },
+ { 0x0d, 0xb1, 0x07, 0x1f, 0x87, 0x67, 0x56, 0x2c,
+ 0xa4, 0x3a, 0x0a, 0x64, 0xc4, 0x1e, 0x8d, 0x08 },
+ { 0x02, 0xd1, 0x3a, 0xcd },
+ { 0x6f, 0xc3, 0x0f, 0xee },
+ { 0xf0, 0xea, 0xa5, 0x0a, 0x1e, 0xdc, 0xeb, 0xb7 }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 11 */
+ { 0x77, 0xb4, 0x58, 0x43, 0xc8, 0x8e, 0x58, 0xc1,
+ 0x0d, 0x20, 0x26, 0x84, 0x51, 0x5e, 0xd4, 0x30 },
+ { 0x4c, 0x47, 0xeb, 0x30, 0x76, 0xdc, 0x55, 0xfe,
+ 0x51, 0x06, 0xcb, 0x20, 0x34, 0xb8, 0xcd, 0x78 },
+ { 0xd4, 0x83, 0xaf, 0xae, 0x56, 0x24, 0x09, 0xa3,
+ 0x26, 0xb5, 0xbb, 0x0b, 0x20, 0xc4, 0xd7, 0x62 },
+ { 0x44, 0x38, 0x9d, 0x01 },
+ { 0xae, 0xfa, 0x35, 0x7b },
+ { 0x82, 0xdb, 0xab, 0x7f, 0x83, 0xf0, 0x63, 0xda }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 12 */
+ { 0x72, 0x9b, 0x17, 0x72, 0x92, 0x70, 0xdd, 0x87,
+ 0xcc, 0xdf, 0x1b, 0xfe, 0x29, 0xb4, 0xe9, 0xbb },
+ { 0x31, 0x1c, 0x4c, 0x92, 0x97, 0x44, 0xd6, 0x75,
+ 0xb7, 0x20, 0xf3, 0xb7, 0xe9, 0xb1, 0xcb, 0xd0 },
+ { 0x22, 0x8c, 0x2f, 0x2f, 0x06, 0xac, 0x32, 0x68,
+ 0xa9, 0xe6, 0x16, 0xee, 0x16, 0xdb, 0x4b, 0xa1 },
+ { 0x03, 0xe0, 0xfd, 0x84 },
+ { 0x98, 0xdb, 0xbd, 0x09 },
+ { 0x3c, 0x66, 0xcb, 0x98, 0xca, 0xb2, 0xd3, 0x3d }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 13 */
+ { 0xd3, 0x2d, 0xd2, 0x3e, 0x89, 0xdc, 0x66, 0x23,
+ 0x54, 0xca, 0x12, 0xeb, 0x79, 0xdd, 0x32, 0xfa },
+ { 0xcf, 0x7d, 0x0a, 0xb1, 0xd9, 0x43, 0x06, 0x95,
+ 0x0b, 0xf1, 0x20, 0x18, 0xfb, 0xd4, 0x68, 0x87 },
+ { 0xd2, 0x2a, 0x4b, 0x41, 0x80, 0xa5, 0x32, 0x57,
+ 0x08, 0xa5, 0xff, 0x70, 0xd9, 0xf6, 0x7e, 0xc7 },
+ { 0xbe, 0x73, 0xb3, 0xdc },
+ { 0xaf, 0x4a, 0x41, 0x1e },
+ { 0x96, 0x12, 0xb5, 0xd8, 0x8a, 0x41, 0x30, 0xbb }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 14 */
+ { 0xaf, 0x7c, 0x65, 0xe1, 0x92, 0x72, 0x21, 0xde,
+ 0x59, 0x11, 0x87, 0xa2, 0xc5, 0x98, 0x7a, 0x53 },
+ { 0x1f, 0x0f, 0x85, 0x78, 0x46, 0x4f, 0xd5, 0x9b,
+ 0x64, 0xbe, 0xd2, 0xd0, 0x94, 0x36, 0xb5, 0x7a },
+ { 0xa4, 0xcf, 0x5c, 0x81, 0x55, 0xc0, 0x8a, 0x7e,
+ 0xff, 0x41, 0x8e, 0x54, 0x43, 0xb9, 0x8e, 0x55 },
+ { 0x8f, 0xe0, 0x19, 0xc7 },
+ { 0x7b, 0xff, 0xa5, 0xc2 },
+ { 0x75, 0xa1, 0x50, 0xdf, 0x3c, 0x6a, 0xed, 0x08 }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 15 */
+ { 0x5b, 0xd7, 0xec, 0xd3, 0xd3, 0x12, 0x7a, 0x41,
+ 0xd1, 0x25, 0x39, 0xbe, 0xd4, 0xe7, 0xcf, 0x71 },
+ { 0x59, 0xb7, 0x5f, 0x14, 0x25, 0x1c, 0x75, 0x03,
+ 0x1d, 0x0b, 0xcb, 0xac, 0x1c, 0x2c, 0x04, 0xc7 },
+ { 0x76, 0x08, 0x9d, 0x3c, 0x0f, 0xf3, 0xef, 0xdc,
+ 0x6e, 0x36, 0x72, 0x1d, 0x4f, 0xce, 0xb7, 0x47 },
+ { 0x27, 0x20, 0x2b, 0x82 },
+ { 0x7e, 0x3f, 0x44, 0xc7 },
+ { 0xb7, 0xf9, 0x2e, 0x42, 0x6a, 0x36, 0xfe, 0xc5 }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 16 */
+ { 0x6c, 0xd1, 0xc6, 0xce, 0xb1, 0xe0, 0x1e, 0x14,
+ 0xf1, 0xb8, 0x23, 0x16, 0xa9, 0x0b, 0x7f, 0x3d },
+ { 0xf6, 0x9b, 0x78, 0xf3, 0x00, 0xa0, 0x56, 0x8b,
+ 0xce, 0x9f, 0x0c, 0xb9, 0x3c, 0x4b, 0xe4, 0xc9 },
+ { 0xa2, 0x19, 0xdc, 0x37, 0xf1, 0xdc, 0x7d, 0x66,
+ 0x73, 0x8b, 0x58, 0x43, 0xc7, 0x99, 0xf2, 0x06 },
+ { 0xdd, 0xd7, 0xef, 0xe6 },
+ { 0x70, 0xf6, 0xbd, 0xb9 },
+ { 0x88, 0xd9, 0xde, 0x10, 0xa2, 0x20, 0x04, 0xc5 }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 17 */
+ { 0xb7, 0x3a, 0x90, 0xcb, 0xcf, 0x3a, 0xfb, 0x62,
+ 0x2d, 0xba, 0x83, 0xc5, 0x8a, 0x84, 0x15, 0xdf },
+ { 0xb1, 0x20, 0xf1, 0xc1, 0xa0, 0x10, 0x2a, 0x2f,
+ 0x50, 0x7d, 0xd5, 0x43, 0xde, 0x68, 0x28, 0x1f },
+ { 0xdf, 0x0c, 0x67, 0x86, 0x8f, 0xa2, 0x5f, 0x74,
+ 0x8b, 0x70, 0x44, 0xc6, 0xe7, 0xc2, 0x45, 0xb8 },
+ { 0x67, 0xe4, 0xff, 0x3f },
+ { 0x47, 0x9d, 0xd2, 0x5c },
+ { 0xa8, 0x19, 0xe5, 0x77, 0xa8, 0xd6, 0x17, 0x5b }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 18 */
+ { 0x51, 0x22, 0x25, 0x02, 0x14, 0xc3, 0x3e, 0x72,
+ 0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0 },
+ { 0x81, 0xe9, 0x2b, 0x6c, 0x0e, 0xe0, 0xe1, 0x2e,
+ 0xbc, 0xeb, 0xa8, 0xd9, 0x2a, 0x99, 0xdf, 0xa5 },
+ { 0x98, 0x1d, 0x46, 0x4c, 0x7c, 0x52, 0xeb, 0x6e,
+ 0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf },
+ { 0x8a, 0x3b, 0x8d, 0x17 },
+ { 0x28, 0xd7, 0xb0, 0xf2 },
+ { 0x9a, 0x8d, 0x0e, 0x88, 0x3f, 0xf0, 0x88, 0x7a }
+ }, {
+ /* 3GPP TS 55.205 v6.0.0 - Test Set 19 */
+ { 0x90, 0xdc, 0xa4, 0xed, 0xa4, 0x5b, 0x53, 0xcf,
+ 0x0f, 0x12, 0xd7, 0xc9, 0xc3, 0xbc, 0x6a, 0x89 },
+ { 0x9f, 0xdd, 0xc7, 0x20, 0x92, 0xc6, 0xad, 0x03,
+ 0x6b, 0x6e, 0x46, 0x47, 0x89, 0x31, 0x5b, 0x78 },
+ { 0xcb, 0x9c, 0xcc, 0xc4, 0xb9, 0x25, 0x8e, 0x6d,
+ 0xca, 0x47, 0x60, 0x37, 0x9f, 0xb8, 0x25, 0x81 },
+ { 0xdf, 0x58, 0x52, 0x2f },
+ { 0xa9, 0x51, 0x00, 0xe2 },
+ { 0xed, 0x29, 0xb2, 0xf1, 0xc2, 0x7f, 0x9f, 0x34 }
+ }
+};
+
+#define NUM_GSM_TESTS ARRAY_SIZE(gsm_test_sets)
+
+
+struct milenage_test_set {
+ u8 k[16];
+ u8 rand[16];
+ u8 sqn[6];
+ u8 amf[2];
+ u8 op[16];
+ u8 opc[16];
+ u8 f1[8];
+ u8 f1star[8];
+ u8 f2[8];
+ u8 f3[16];
+ u8 f4[16];
+ u8 f5[6];
+ u8 f5star[6];
+};
+
+static const struct milenage_test_set test_sets[] =
+{
+ {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.1 Test Set 1 */
+ { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f,
+ 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc },
+ { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d,
+ 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 },
+ { 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 },
+ { 0xb9, 0xb9 },
+ { 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6,
+ 0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18 },
+ { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e,
+ 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf },
+ { 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 },
+ { 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 },
+ { 0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf },
+ { 0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05,
+ 0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb },
+ { 0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04,
+ 0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41 },
+ { 0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70 },
+ { 0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.2 Test Set 2 */
+ { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f,
+ 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc },
+ { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d,
+ 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 },
+ { 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 },
+ { 0xb9, 0xb9 },
+ { 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6,
+ 0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18 },
+ { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e,
+ 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf },
+ { 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 },
+ { 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 },
+ { 0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf },
+ { 0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05,
+ 0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb },
+ { 0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04,
+ 0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41 },
+ { 0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70 },
+ { 0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.3 Test Set 3 */
+ { 0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0,
+ 0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f },
+ { 0x9f, 0x7c, 0x8d, 0x02, 0x1a, 0xcc, 0xf4, 0xdb,
+ 0x21, 0x3c, 0xcf, 0xf0, 0xc7, 0xf7, 0x1a, 0x6a },
+ { 0x9d, 0x02, 0x77, 0x59, 0x5f, 0xfc },
+ { 0x72, 0x5c },
+ { 0xdb, 0xc5, 0x9a, 0xdc, 0xb6, 0xf9, 0xa0, 0xef,
+ 0x73, 0x54, 0x77, 0xb7, 0xfa, 0xdf, 0x83, 0x74 },
+ { 0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6,
+ 0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3 },
+ { 0x9c, 0xab, 0xc3, 0xe9, 0x9b, 0xaf, 0x72, 0x81 },
+ { 0x95, 0x81, 0x4b, 0xa2, 0xb3, 0x04, 0x43, 0x24 },
+ { 0x80, 0x11, 0xc4, 0x8c, 0x0c, 0x21, 0x4e, 0xd2 },
+ { 0x5d, 0xbd, 0xbb, 0x29, 0x54, 0xe8, 0xf3, 0xcd,
+ 0xe6, 0x65, 0xb0, 0x46, 0x17, 0x9a, 0x50, 0x98 },
+ { 0x59, 0xa9, 0x2d, 0x3b, 0x47, 0x6a, 0x04, 0x43,
+ 0x48, 0x70, 0x55, 0xcf, 0x88, 0xb2, 0x30, 0x7b },
+ { 0x33, 0x48, 0x4d, 0xc2, 0x13, 0x6b },
+ { 0xde, 0xac, 0xdd, 0x84, 0x8c, 0xc6 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.4 Test Set 4 */
+ { 0x9e, 0x59, 0x44, 0xae, 0xa9, 0x4b, 0x81, 0x16,
+ 0x5c, 0x82, 0xfb, 0xf9, 0xf3, 0x2d, 0xb7, 0x51 },
+ { 0xce, 0x83, 0xdb, 0xc5, 0x4a, 0xc0, 0x27, 0x4a,
+ 0x15, 0x7c, 0x17, 0xf8, 0x0d, 0x01, 0x7b, 0xd6 },
+ { 0x0b, 0x60, 0x4a, 0x81, 0xec, 0xa8 },
+ { 0x9e, 0x09 },
+ { 0x22, 0x30, 0x14, 0xc5, 0x80, 0x66, 0x94, 0xc0,
+ 0x07, 0xca, 0x1e, 0xee, 0xf5, 0x7f, 0x00, 0x4f },
+ { 0xa6, 0x4a, 0x50, 0x7a, 0xe1, 0xa2, 0xa9, 0x8b,
+ 0xb8, 0x8e, 0xb4, 0x21, 0x01, 0x35, 0xdc, 0x87 },
+ { 0x74, 0xa5, 0x82, 0x20, 0xcb, 0xa8, 0x4c, 0x49 },
+ { 0xac, 0x2c, 0xc7, 0x4a, 0x96, 0x87, 0x18, 0x37 },
+ { 0xf3, 0x65, 0xcd, 0x68, 0x3c, 0xd9, 0x2e, 0x96 },
+ { 0xe2, 0x03, 0xed, 0xb3, 0x97, 0x15, 0x74, 0xf5,
+ 0xa9, 0x4b, 0x0d, 0x61, 0xb8, 0x16, 0x34, 0x5d },
+ { 0x0c, 0x45, 0x24, 0xad, 0xea, 0xc0, 0x41, 0xc4,
+ 0xdd, 0x83, 0x0d, 0x20, 0x85, 0x4f, 0xc4, 0x6b },
+ { 0xf0, 0xb9, 0xc0, 0x8a, 0xd0, 0x2e },
+ { 0x60, 0x85, 0xa8, 0x6c, 0x6f, 0x63 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.5 Test Set 5 */
+ { 0x4a, 0xb1, 0xde, 0xb0, 0x5c, 0xa6, 0xce, 0xb0,
+ 0x51, 0xfc, 0x98, 0xe7, 0x7d, 0x02, 0x6a, 0x84 },
+ { 0x74, 0xb0, 0xcd, 0x60, 0x31, 0xa1, 0xc8, 0x33,
+ 0x9b, 0x2b, 0x6c, 0xe2, 0xb8, 0xc4, 0xa1, 0x86 },
+ { 0xe8, 0x80, 0xa1, 0xb5, 0x80, 0xb6 },
+ { 0x9f, 0x07 },
+ { 0x2d, 0x16, 0xc5, 0xcd, 0x1f, 0xdf, 0x6b, 0x22,
+ 0x38, 0x35, 0x84, 0xe3, 0xbe, 0xf2, 0xa8, 0xd8 },
+ { 0xdc, 0xf0, 0x7c, 0xbd, 0x51, 0x85, 0x52, 0x90,
+ 0xb9, 0x2a, 0x07, 0xa9, 0x89, 0x1e, 0x52, 0x3e },
+ { 0x49, 0xe7, 0x85, 0xdd, 0x12, 0x62, 0x6e, 0xf2 },
+ { 0x9e, 0x85, 0x79, 0x03, 0x36, 0xbb, 0x3f, 0xa2 },
+ { 0x58, 0x60, 0xfc, 0x1b, 0xce, 0x35, 0x1e, 0x7e },
+ { 0x76, 0x57, 0x76, 0x6b, 0x37, 0x3d, 0x1c, 0x21,
+ 0x38, 0xf3, 0x07, 0xe3, 0xde, 0x92, 0x42, 0xf9 },
+ { 0x1c, 0x42, 0xe9, 0x60, 0xd8, 0x9b, 0x8f, 0xa9,
+ 0x9f, 0x27, 0x44, 0xe0, 0x70, 0x8c, 0xcb, 0x53 },
+ { 0x31, 0xe1, 0x1a, 0x60, 0x91, 0x18 },
+ { 0xfe, 0x25, 0x55, 0xe5, 0x4a, 0xa9 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.6 Test Set 6 */
+ { 0x6c, 0x38, 0xa1, 0x16, 0xac, 0x28, 0x0c, 0x45,
+ 0x4f, 0x59, 0x33, 0x2e, 0xe3, 0x5c, 0x8c, 0x4f },
+ { 0xee, 0x64, 0x66, 0xbc, 0x96, 0x20, 0x2c, 0x5a,
+ 0x55, 0x7a, 0xbb, 0xef, 0xf8, 0xba, 0xbf, 0x63 },
+ { 0x41, 0x4b, 0x98, 0x22, 0x21, 0x81 },
+ { 0x44, 0x64 },
+ { 0x1b, 0xa0, 0x0a, 0x1a, 0x7c, 0x67, 0x00, 0xac,
+ 0x8c, 0x3f, 0xf3, 0xe9, 0x6a, 0xd0, 0x87, 0x25 },
+ { 0x38, 0x03, 0xef, 0x53, 0x63, 0xb9, 0x47, 0xc6,
+ 0xaa, 0xa2, 0x25, 0xe5, 0x8f, 0xae, 0x39, 0x34 },
+ { 0x07, 0x8a, 0xdf, 0xb4, 0x88, 0x24, 0x1a, 0x57 },
+ { 0x80, 0x24, 0x6b, 0x8d, 0x01, 0x86, 0xbc, 0xf1 },
+ { 0x16, 0xc8, 0x23, 0x3f, 0x05, 0xa0, 0xac, 0x28 },
+ { 0x3f, 0x8c, 0x75, 0x87, 0xfe, 0x8e, 0x4b, 0x23,
+ 0x3a, 0xf6, 0x76, 0xae, 0xde, 0x30, 0xba, 0x3b },
+ { 0xa7, 0x46, 0x6c, 0xc1, 0xe6, 0xb2, 0xa1, 0x33,
+ 0x7d, 0x49, 0xd3, 0xb6, 0x6e, 0x95, 0xd7, 0xb4 },
+ { 0x45, 0xb0, 0xf6, 0x9a, 0xb0, 0x6c },
+ { 0x1f, 0x53, 0xcd, 0x2b, 0x11, 0x13 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.7 Test Set 7 */
+ { 0x2d, 0x60, 0x9d, 0x4d, 0xb0, 0xac, 0x5b, 0xf0,
+ 0xd2, 0xc0, 0xde, 0x26, 0x70, 0x14, 0xde, 0x0d },
+ { 0x19, 0x4a, 0xa7, 0x56, 0x01, 0x38, 0x96, 0xb7,
+ 0x4b, 0x4a, 0x2a, 0x3b, 0x0a, 0xf4, 0x53, 0x9e },
+ { 0x6b, 0xf6, 0x94, 0x38, 0xc2, 0xe4 },
+ { 0x5f, 0x67 },
+ { 0x46, 0x0a, 0x48, 0x38, 0x54, 0x27, 0xaa, 0x39,
+ 0x26, 0x4a, 0xac, 0x8e, 0xfc, 0x9e, 0x73, 0xe8 },
+ { 0xc3, 0x5a, 0x0a, 0xb0, 0xbc, 0xbf, 0xc9, 0x25,
+ 0x2c, 0xaf, 0xf1, 0x5f, 0x24, 0xef, 0xbd, 0xe0 },
+ { 0xbd, 0x07, 0xd3, 0x00, 0x3b, 0x9e, 0x5c, 0xc3 },
+ { 0xbc, 0xb6, 0xc2, 0xfc, 0xad, 0x15, 0x22, 0x50 },
+ { 0x8c, 0x25, 0xa1, 0x6c, 0xd9, 0x18, 0xa1, 0xdf },
+ { 0x4c, 0xd0, 0x84, 0x60, 0x20, 0xf8, 0xfa, 0x07,
+ 0x31, 0xdd, 0x47, 0xcb, 0xdc, 0x6b, 0xe4, 0x11 },
+ { 0x88, 0xab, 0x80, 0xa4, 0x15, 0xf1, 0x5c, 0x73,
+ 0x71, 0x12, 0x54, 0xa1, 0xd3, 0x88, 0xf6, 0x96 },
+ { 0x7e, 0x64, 0x55, 0xf3, 0x4c, 0xf3 },
+ { 0xdc, 0x6d, 0xd0, 0x1e, 0x8f, 0x15 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.8 Test Set 8 */
+ { 0xa5, 0x30, 0xa7, 0xfe, 0x42, 0x8f, 0xad, 0x10,
+ 0x82, 0xc4, 0x5e, 0xdd, 0xfc, 0xe1, 0x38, 0x84 },
+ { 0x3a, 0x4c, 0x2b, 0x32, 0x45, 0xc5, 0x0e, 0xb5,
+ 0xc7, 0x1d, 0x08, 0x63, 0x93, 0x95, 0x76, 0x4d },
+ { 0xf6, 0x3f, 0x5d, 0x76, 0x87, 0x84 },
+ { 0xb9, 0x0e },
+ { 0x51, 0x1c, 0x6c, 0x4e, 0x83, 0xe3, 0x8c, 0x89,
+ 0xb1, 0xc5, 0xd8, 0xdd, 0xe6, 0x24, 0x26, 0xfa },
+ { 0x27, 0x95, 0x3e, 0x49, 0xbc, 0x8a, 0xf6, 0xdc,
+ 0xc6, 0xe7, 0x30, 0xeb, 0x80, 0x28, 0x6b, 0xe3 },
+ { 0x53, 0x76, 0x1f, 0xbd, 0x67, 0x9b, 0x0b, 0xad },
+ { 0x21, 0xad, 0xfd, 0x33, 0x4a, 0x10, 0xe7, 0xce },
+ { 0xa6, 0x32, 0x41, 0xe1, 0xff, 0xc3, 0xe5, 0xab },
+ { 0x10, 0xf0, 0x5b, 0xab, 0x75, 0xa9, 0x9a, 0x5f,
+ 0xbb, 0x98, 0xa9, 0xc2, 0x87, 0x67, 0x9c, 0x3b },
+ { 0xf9, 0xec, 0x08, 0x65, 0xeb, 0x32, 0xf2, 0x23,
+ 0x69, 0xca, 0xde, 0x40, 0xc5, 0x9c, 0x3a, 0x44 },
+ { 0x88, 0x19, 0x6c, 0x47, 0x98, 0x6f },
+ { 0xc9, 0x87, 0xa3, 0xd2, 0x31, 0x15 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.9 Test Set 9 */
+ { 0xd9, 0x15, 0x1c, 0xf0, 0x48, 0x96, 0xe2, 0x58,
+ 0x30, 0xbf, 0x2e, 0x08, 0x26, 0x7b, 0x83, 0x60 },
+ { 0xf7, 0x61, 0xe5, 0xe9, 0x3d, 0x60, 0x3f, 0xeb,
+ 0x73, 0x0e, 0x27, 0x55, 0x6c, 0xb8, 0xa2, 0xca },
+ { 0x47, 0xee, 0x01, 0x99, 0x82, 0x0a },
+ { 0x91, 0x13 },
+ { 0x75, 0xfc, 0x22, 0x33, 0xa4, 0x42, 0x94, 0xee,
+ 0x8e, 0x6d, 0xe2, 0x5c, 0x43, 0x53, 0xd2, 0x6b },
+ { 0xc4, 0xc9, 0x3e, 0xff, 0xe8, 0xa0, 0x81, 0x38,
+ 0xc2, 0x03, 0xd4, 0xc2, 0x7c, 0xe4, 0xe3, 0xd9 },
+ { 0x66, 0xcc, 0x4b, 0xe4, 0x48, 0x62, 0xaf, 0x1f },
+ { 0x7a, 0x4b, 0x8d, 0x7a, 0x87, 0x53, 0xf2, 0x46 },
+ { 0x4a, 0x90, 0xb2, 0x17, 0x1a, 0xc8, 0x3a, 0x76 },
+ { 0x71, 0x23, 0x6b, 0x71, 0x29, 0xf9, 0xb2, 0x2a,
+ 0xb7, 0x7e, 0xa7, 0xa5, 0x4c, 0x96, 0xda, 0x22 },
+ { 0x90, 0x52, 0x7e, 0xba, 0xa5, 0x58, 0x89, 0x68,
+ 0xdb, 0x41, 0x72, 0x73, 0x25, 0xa0, 0x4d, 0x9e },
+ { 0x82, 0xa0, 0xf5, 0x28, 0x7a, 0x71 },
+ { 0x52, 0x7d, 0xbf, 0x41, 0xf3, 0x5f }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.10 Test Set 10 */
+ { 0xa0, 0xe2, 0x97, 0x1b, 0x68, 0x22, 0xe8, 0xd3,
+ 0x54, 0xa1, 0x8c, 0xc2, 0x35, 0x62, 0x4e, 0xcb },
+ { 0x08, 0xef, 0xf8, 0x28, 0xb1, 0x3f, 0xdb, 0x56,
+ 0x27, 0x22, 0xc6, 0x5c, 0x7f, 0x30, 0xa9, 0xb2 },
+ { 0xdb, 0x5c, 0x06, 0x64, 0x81, 0xe0 },
+ { 0x71, 0x6b },
+ { 0x32, 0x37, 0x92, 0xfa, 0xca, 0x21, 0xfb, 0x4d,
+ 0x5d, 0x6f, 0x13, 0xc1, 0x45, 0xa9, 0xd2, 0xc1 },
+ { 0x82, 0xa2, 0x6f, 0x22, 0xbb, 0xa9, 0xe9, 0x48,
+ 0x8f, 0x94, 0x9a, 0x10, 0xd9, 0x8e, 0x9c, 0xc4 },
+ { 0x94, 0x85, 0xfe, 0x24, 0x62, 0x1c, 0xb9, 0xf6 },
+ { 0xbc, 0xe3, 0x25, 0xce, 0x03, 0xe2, 0xe9, 0xb9 },
+ { 0x4b, 0xc2, 0x21, 0x2d, 0x86, 0x24, 0x91, 0x0a },
+ { 0x08, 0xce, 0xf6, 0xd0, 0x04, 0xec, 0x61, 0x47,
+ 0x1a, 0x3c, 0x3c, 0xda, 0x04, 0x81, 0x37, 0xfa },
+ { 0xed, 0x03, 0x18, 0xca, 0x5d, 0xeb, 0x92, 0x06,
+ 0x27, 0x2f, 0x6e, 0x8f, 0xa6, 0x4b, 0xa4, 0x11 },
+ { 0xa2, 0xf8, 0x58, 0xaa, 0x9e, 0x5d },
+ { 0x74, 0xe7, 0x6f, 0xbb, 0xec, 0x38 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.11 Test Set 11 */
+ { 0x0d, 0xa6, 0xf7, 0xba, 0x86, 0xd5, 0xea, 0xc8,
+ 0xa1, 0x9c, 0xf5, 0x63, 0xac, 0x58, 0x64, 0x2d },
+ { 0x67, 0x9a, 0xc4, 0xdb, 0xac, 0xd7, 0xd2, 0x33,
+ 0xff, 0x9d, 0x68, 0x06, 0xf4, 0x14, 0x9c, 0xe3 },
+ { 0x6e, 0x23, 0x31, 0xd6, 0x92, 0xad },
+ { 0x22, 0x4a },
+ { 0x4b, 0x9a, 0x26, 0xfa, 0x45, 0x9e, 0x3a, 0xcb,
+ 0xff, 0x36, 0xf4, 0x01, 0x5d, 0xe3, 0xbd, 0xc1 },
+ { 0x0d, 0xb1, 0x07, 0x1f, 0x87, 0x67, 0x56, 0x2c,
+ 0xa4, 0x3a, 0x0a, 0x64, 0xc4, 0x1e, 0x8d, 0x08 },
+ { 0x28, 0x31, 0xd7, 0xae, 0x90, 0x88, 0xe4, 0x92 },
+ { 0x9b, 0x2e, 0x16, 0x95, 0x11, 0x35, 0xd5, 0x23 },
+ { 0x6f, 0xc3, 0x0f, 0xee, 0x6d, 0x12, 0x35, 0x23 },
+ { 0x69, 0xb1, 0xca, 0xe7, 0xc7, 0x42, 0x9d, 0x97,
+ 0x5e, 0x24, 0x5c, 0xac, 0xb0, 0x5a, 0x51, 0x7c },
+ { 0x74, 0xf2, 0x4e, 0x8c, 0x26, 0xdf, 0x58, 0xe1,
+ 0xb3, 0x8d, 0x7d, 0xcd, 0x4f, 0x1b, 0x7f, 0xbd },
+ { 0x4c, 0x53, 0x9a, 0x26, 0xe1, 0xfa },
+ { 0x07, 0x86, 0x1e, 0x12, 0x69, 0x28 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.12 Test Set 12 */
+ { 0x77, 0xb4, 0x58, 0x43, 0xc8, 0x8e, 0x58, 0xc1,
+ 0x0d, 0x20, 0x26, 0x84, 0x51, 0x5e, 0xd4, 0x30 },
+ { 0x4c, 0x47, 0xeb, 0x30, 0x76, 0xdc, 0x55, 0xfe,
+ 0x51, 0x06, 0xcb, 0x20, 0x34, 0xb8, 0xcd, 0x78 },
+ { 0xfe, 0x1a, 0x87, 0x31, 0x00, 0x5d },
+ { 0xad, 0x25 },
+ { 0xbf, 0x32, 0x86, 0xc7, 0xa5, 0x14, 0x09, 0xce,
+ 0x95, 0x72, 0x4d, 0x50, 0x3b, 0xfe, 0x6e, 0x70 },
+ { 0xd4, 0x83, 0xaf, 0xae, 0x56, 0x24, 0x09, 0xa3,
+ 0x26, 0xb5, 0xbb, 0x0b, 0x20, 0xc4, 0xd7, 0x62 },
+ { 0x08, 0x33, 0x2d, 0x7e, 0x9f, 0x48, 0x45, 0x70 },
+ { 0xed, 0x41, 0xb7, 0x34, 0x48, 0x9d, 0x52, 0x07 },
+ { 0xae, 0xfa, 0x35, 0x7b, 0xea, 0xc2, 0xa8, 0x7a },
+ { 0x90, 0x8c, 0x43, 0xf0, 0x56, 0x9c, 0xb8, 0xf7,
+ 0x4b, 0xc9, 0x71, 0xe7, 0x06, 0xc3, 0x6c, 0x5f },
+ { 0xc2, 0x51, 0xdf, 0x0d, 0x88, 0x8d, 0xd9, 0x32,
+ 0x9b, 0xcf, 0x46, 0x65, 0x5b, 0x22, 0x6e, 0x40 },
+ { 0x30, 0xff, 0x25, 0xcd, 0xad, 0xf6 },
+ { 0xe8, 0x4e, 0xd0, 0xd4, 0x67, 0x7e }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.13 Test Set 13 */
+ { 0x72, 0x9b, 0x17, 0x72, 0x92, 0x70, 0xdd, 0x87,
+ 0xcc, 0xdf, 0x1b, 0xfe, 0x29, 0xb4, 0xe9, 0xbb },
+ { 0x31, 0x1c, 0x4c, 0x92, 0x97, 0x44, 0xd6, 0x75,
+ 0xb7, 0x20, 0xf3, 0xb7, 0xe9, 0xb1, 0xcb, 0xd0 },
+ { 0xc8, 0x5c, 0x4c, 0xf6, 0x59, 0x16 },
+ { 0x5b, 0xb2 },
+ { 0xd0, 0x4c, 0x9c, 0x35, 0xbd, 0x22, 0x62, 0xfa,
+ 0x81, 0x0d, 0x29, 0x24, 0xd0, 0x36, 0xfd, 0x13 },
+ { 0x22, 0x8c, 0x2f, 0x2f, 0x06, 0xac, 0x32, 0x68,
+ 0xa9, 0xe6, 0x16, 0xee, 0x16, 0xdb, 0x4b, 0xa1 },
+ { 0xff, 0x79, 0x4f, 0xe2, 0xf8, 0x27, 0xeb, 0xf8 },
+ { 0x24, 0xfe, 0x4d, 0xc6, 0x1e, 0x87, 0x4b, 0x52 },
+ { 0x98, 0xdb, 0xbd, 0x09, 0x9b, 0x3b, 0x40, 0x8d },
+ { 0x44, 0xc0, 0xf2, 0x3c, 0x54, 0x93, 0xcf, 0xd2,
+ 0x41, 0xe4, 0x8f, 0x19, 0x7e, 0x1d, 0x10, 0x12 },
+ { 0x0c, 0x9f, 0xb8, 0x16, 0x13, 0x88, 0x4c, 0x25,
+ 0x35, 0xdd, 0x0e, 0xab, 0xf3, 0xb4, 0x40, 0xd8 },
+ { 0x53, 0x80, 0xd1, 0x58, 0xcf, 0xe3 },
+ { 0x87, 0xac, 0x3b, 0x55, 0x9f, 0xb6 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.14 Test Set 14 */
+ { 0xd3, 0x2d, 0xd2, 0x3e, 0x89, 0xdc, 0x66, 0x23,
+ 0x54, 0xca, 0x12, 0xeb, 0x79, 0xdd, 0x32, 0xfa },
+ { 0xcf, 0x7d, 0x0a, 0xb1, 0xd9, 0x43, 0x06, 0x95,
+ 0x0b, 0xf1, 0x20, 0x18, 0xfb, 0xd4, 0x68, 0x87 },
+ { 0x48, 0x41, 0x07, 0xe5, 0x6a, 0x43 },
+ { 0xb5, 0xe6 },
+ { 0xfe, 0x75, 0x90, 0x5b, 0x9d, 0xa4, 0x7d, 0x35,
+ 0x62, 0x36, 0xd0, 0x31, 0x4e, 0x09, 0xc3, 0x2e },
+ { 0xd2, 0x2a, 0x4b, 0x41, 0x80, 0xa5, 0x32, 0x57,
+ 0x08, 0xa5, 0xff, 0x70, 0xd9, 0xf6, 0x7e, 0xc7 },
+ { 0xcf, 0x19, 0xd6, 0x2b, 0x6a, 0x80, 0x98, 0x66 },
+ { 0x5d, 0x26, 0x95, 0x37, 0xe4, 0x5e, 0x2c, 0xe6 },
+ { 0xaf, 0x4a, 0x41, 0x1e, 0x11, 0x39, 0xf2, 0xc2 },
+ { 0x5a, 0xf8, 0x6b, 0x80, 0xed, 0xb7, 0x0d, 0xf5,
+ 0x29, 0x2c, 0xc1, 0x12, 0x1c, 0xba, 0xd5, 0x0c },
+ { 0x7f, 0x4d, 0x6a, 0xe7, 0x44, 0x0e, 0x18, 0x78,
+ 0x9a, 0x8b, 0x75, 0xad, 0x3f, 0x42, 0xf0, 0x3a },
+ { 0x21, 0x7a, 0xf4, 0x92, 0x72, 0xad },
+ { 0x90, 0x0e, 0x10, 0x1c, 0x67, 0x7e }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.15 Test Set 15 */
+ { 0xaf, 0x7c, 0x65, 0xe1, 0x92, 0x72, 0x21, 0xde,
+ 0x59, 0x11, 0x87, 0xa2, 0xc5, 0x98, 0x7a, 0x53 },
+ { 0x1f, 0x0f, 0x85, 0x78, 0x46, 0x4f, 0xd5, 0x9b,
+ 0x64, 0xbe, 0xd2, 0xd0, 0x94, 0x36, 0xb5, 0x7a },
+ { 0x3d, 0x62, 0x7b, 0x01, 0x41, 0x8d },
+ { 0x84, 0xf6 },
+ { 0x0c, 0x7a, 0xcb, 0x8d, 0x95, 0xb7, 0xd4, 0xa3,
+ 0x1c, 0x5a, 0xca, 0x6d, 0x26, 0x34, 0x5a, 0x88 },
+ { 0xa4, 0xcf, 0x5c, 0x81, 0x55, 0xc0, 0x8a, 0x7e,
+ 0xff, 0x41, 0x8e, 0x54, 0x43, 0xb9, 0x8e, 0x55 },
+ { 0xc3, 0x7c, 0xae, 0x78, 0x05, 0x64, 0x20, 0x32 },
+ { 0x68, 0xcd, 0x09, 0xa4, 0x52, 0xd8, 0xdb, 0x7c },
+ { 0x7b, 0xff, 0xa5, 0xc2, 0xf4, 0x1f, 0xbc, 0x05 },
+ { 0x3f, 0x8c, 0x3f, 0x3c, 0xcf, 0x76, 0x25, 0xbf,
+ 0x77, 0xfc, 0x94, 0xbc, 0xfd, 0x22, 0xfd, 0x26 },
+ { 0xab, 0xcb, 0xae, 0x8f, 0xd4, 0x61, 0x15, 0xe9,
+ 0x96, 0x1a, 0x55, 0xd0, 0xda, 0x5f, 0x20, 0x78 },
+ { 0x83, 0x7f, 0xd7, 0xb7, 0x44, 0x19 },
+ { 0x56, 0xe9, 0x7a, 0x60, 0x90, 0xb1 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.16 Test Set 16 */
+ { 0x5b, 0xd7, 0xec, 0xd3, 0xd3, 0x12, 0x7a, 0x41,
+ 0xd1, 0x25, 0x39, 0xbe, 0xd4, 0xe7, 0xcf, 0x71 },
+ { 0x59, 0xb7, 0x5f, 0x14, 0x25, 0x1c, 0x75, 0x03,
+ 0x1d, 0x0b, 0xcb, 0xac, 0x1c, 0x2c, 0x04, 0xc7 },
+ { 0xa2, 0x98, 0xae, 0x89, 0x29, 0xdc },
+ { 0xd0, 0x56 },
+ { 0xf9, 0x67, 0xf7, 0x60, 0x38, 0xb9, 0x20, 0xa9,
+ 0xcd, 0x25, 0xe1, 0x0c, 0x08, 0xb4, 0x99, 0x24 },
+ { 0x76, 0x08, 0x9d, 0x3c, 0x0f, 0xf3, 0xef, 0xdc,
+ 0x6e, 0x36, 0x72, 0x1d, 0x4f, 0xce, 0xb7, 0x47 },
+ { 0xc3, 0xf2, 0x5c, 0xd9, 0x43, 0x09, 0x10, 0x7e },
+ { 0xb0, 0xc8, 0xba, 0x34, 0x36, 0x65, 0xaf, 0xcc },
+ { 0x7e, 0x3f, 0x44, 0xc7, 0x59, 0x1f, 0x6f, 0x45 },
+ { 0xd4, 0x2b, 0x2d, 0x61, 0x5e, 0x49, 0xa0, 0x3a,
+ 0xc2, 0x75, 0xa5, 0xae, 0xf9, 0x7a, 0xf8, 0x92 },
+ { 0x0b, 0x3f, 0x8d, 0x02, 0x4f, 0xe6, 0xbf, 0xaf,
+ 0xaa, 0x98, 0x2b, 0x8f, 0x82, 0xe3, 0x19, 0xc2 },
+ { 0x5b, 0xe1, 0x14, 0x95, 0x52, 0x5d },
+ { 0x4d, 0x6a, 0x34, 0xa1, 0xe4, 0xeb }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.17 Test Set 17 */
+ { 0x6c, 0xd1, 0xc6, 0xce, 0xb1, 0xe0, 0x1e, 0x14,
+ 0xf1, 0xb8, 0x23, 0x16, 0xa9, 0x0b, 0x7f, 0x3d },
+ { 0xf6, 0x9b, 0x78, 0xf3, 0x00, 0xa0, 0x56, 0x8b,
+ 0xce, 0x9f, 0x0c, 0xb9, 0x3c, 0x4b, 0xe4, 0xc9 },
+ { 0xb4, 0xfc, 0xe5, 0xfe, 0xb0, 0x59 },
+ { 0xe4, 0xbb },
+ { 0x07, 0x8b, 0xfc, 0xa9, 0x56, 0x46, 0x59, 0xec,
+ 0xd8, 0x85, 0x1e, 0x84, 0xe6, 0xc5, 0x9b, 0x48 },
+ { 0xa2, 0x19, 0xdc, 0x37, 0xf1, 0xdc, 0x7d, 0x66,
+ 0x73, 0x8b, 0x58, 0x43, 0xc7, 0x99, 0xf2, 0x06 },
+ { 0x69, 0xa9, 0x08, 0x69, 0xc2, 0x68, 0xcb, 0x7b },
+ { 0x2e, 0x0f, 0xdc, 0xf9, 0xfd, 0x1c, 0xfa, 0x6a },
+ { 0x70, 0xf6, 0xbd, 0xb9, 0xad, 0x21, 0x52, 0x5f },
+ { 0x6e, 0xda, 0xf9, 0x9e, 0x5b, 0xd9, 0xf8, 0x5d,
+ 0x5f, 0x36, 0xd9, 0x1c, 0x12, 0x72, 0xfb, 0x4b },
+ { 0xd6, 0x1c, 0x85, 0x3c, 0x28, 0x0d, 0xd9, 0xc4,
+ 0x6f, 0x29, 0x7b, 0xae, 0xc3, 0x86, 0xde, 0x17 },
+ { 0x1c, 0x40, 0x8a, 0x85, 0x8b, 0x3e },
+ { 0xaa, 0x4a, 0xe5, 0x2d, 0xaa, 0x30 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.18 Test Set 18 */
+ { 0xb7, 0x3a, 0x90, 0xcb, 0xcf, 0x3a, 0xfb, 0x62,
+ 0x2d, 0xba, 0x83, 0xc5, 0x8a, 0x84, 0x15, 0xdf },
+ { 0xb1, 0x20, 0xf1, 0xc1, 0xa0, 0x10, 0x2a, 0x2f,
+ 0x50, 0x7d, 0xd5, 0x43, 0xde, 0x68, 0x28, 0x1f },
+ { 0xf1, 0xe8, 0xa5, 0x23, 0xa3, 0x6d },
+ { 0x47, 0x1b },
+ { 0xb6, 0x72, 0x04, 0x7e, 0x00, 0x3b, 0xb9, 0x52,
+ 0xdc, 0xa6, 0xcb, 0x8a, 0xf0, 0xe5, 0xb7, 0x79 },
+ { 0xdf, 0x0c, 0x67, 0x86, 0x8f, 0xa2, 0x5f, 0x74,
+ 0x8b, 0x70, 0x44, 0xc6, 0xe7, 0xc2, 0x45, 0xb8 },
+ { 0xeb, 0xd7, 0x03, 0x41, 0xbc, 0xd4, 0x15, 0xb0 },
+ { 0x12, 0x35, 0x9f, 0x5d, 0x82, 0x22, 0x0c, 0x14 },
+ { 0x47, 0x9d, 0xd2, 0x5c, 0x20, 0x79, 0x2d, 0x63 },
+ { 0x66, 0x19, 0x5d, 0xbe, 0xd0, 0x31, 0x32, 0x74,
+ 0xc5, 0xca, 0x77, 0x66, 0x61, 0x5f, 0xa2, 0x5e },
+ { 0x66, 0xbe, 0xc7, 0x07, 0xeb, 0x2a, 0xfc, 0x47,
+ 0x6d, 0x74, 0x08, 0xa8, 0xf2, 0x92, 0x7b, 0x36 },
+ { 0xae, 0xfd, 0xaa, 0x5d, 0xdd, 0x99 },
+ { 0x12, 0xec, 0x2b, 0x87, 0xfb, 0xb1 }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.19 Test Set 19 */
+ { 0x51, 0x22, 0x25, 0x02, 0x14, 0xc3, 0x3e, 0x72,
+ 0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0 },
+ { 0x81, 0xe9, 0x2b, 0x6c, 0x0e, 0xe0, 0xe1, 0x2e,
+ 0xbc, 0xeb, 0xa8, 0xd9, 0x2a, 0x99, 0xdf, 0xa5 },
+ { 0x16, 0xf3, 0xb3, 0xf7, 0x0f, 0xc2 },
+ { 0xc3, 0xab },
+ { 0xc9, 0xe8, 0x76, 0x32, 0x86, 0xb5, 0xb9, 0xff,
+ 0xbd, 0xf5, 0x6e, 0x12, 0x97, 0xd0, 0x88, 0x7b },
+ { 0x98, 0x1d, 0x46, 0x4c, 0x7c, 0x52, 0xeb, 0x6e,
+ 0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf },
+ { 0x2a, 0x5c, 0x23, 0xd1, 0x5e, 0xe3, 0x51, 0xd5 },
+ { 0x62, 0xda, 0xe3, 0x85, 0x3f, 0x3a, 0xf9, 0xd2 },
+ { 0x28, 0xd7, 0xb0, 0xf2, 0xa2, 0xec, 0x3d, 0xe5 },
+ { 0x53, 0x49, 0xfb, 0xe0, 0x98, 0x64, 0x9f, 0x94,
+ 0x8f, 0x5d, 0x2e, 0x97, 0x3a, 0x81, 0xc0, 0x0f },
+ { 0x97, 0x44, 0x87, 0x1a, 0xd3, 0x2b, 0xf9, 0xbb,
+ 0xd1, 0xdd, 0x5c, 0xe5, 0x4e, 0x3e, 0x2e, 0x5a },
+ { 0xad, 0xa1, 0x5a, 0xeb, 0x7b, 0xb8 },
+ { 0xd4, 0x61, 0xbc, 0x15, 0x47, 0x5d }
+ }, {
+ /* 3GPP TS 35.208 v6.0.0 - 4.3.20 Test Set 20 */
+ { 0x90, 0xdc, 0xa4, 0xed, 0xa4, 0x5b, 0x53, 0xcf,
+ 0x0f, 0x12, 0xd7, 0xc9, 0xc3, 0xbc, 0x6a, 0x89 },
+ { 0x9f, 0xdd, 0xc7, 0x20, 0x92, 0xc6, 0xad, 0x03,
+ 0x6b, 0x6e, 0x46, 0x47, 0x89, 0x31, 0x5b, 0x78 },
+ { 0x20, 0xf8, 0x13, 0xbd, 0x41, 0x41 },
+ { 0x61, 0xdf },
+ { 0x3f, 0xfc, 0xfe, 0x5b, 0x7b, 0x11, 0x11, 0x58,
+ 0x99, 0x20, 0xd3, 0x52, 0x8e, 0x84, 0xe6, 0x55 },
+ { 0xcb, 0x9c, 0xcc, 0xc4, 0xb9, 0x25, 0x8e, 0x6d,
+ 0xca, 0x47, 0x60, 0x37, 0x9f, 0xb8, 0x25, 0x81 },
+ { 0x09, 0xdb, 0x94, 0xea, 0xb4, 0xf8, 0x14, 0x9e },
+ { 0xa2, 0x94, 0x68, 0xaa, 0x97, 0x75, 0xb5, 0x27 },
+ { 0xa9, 0x51, 0x00, 0xe2, 0x76, 0x09, 0x52, 0xcd },
+ { 0xb5, 0xf2, 0xda, 0x03, 0x88, 0x3b, 0x69, 0xf9,
+ 0x6b, 0xf5, 0x2e, 0x02, 0x9e, 0xd9, 0xac, 0x45 },
+ { 0xb4, 0x72, 0x13, 0x68, 0xbc, 0x16, 0xea, 0x67,
+ 0x87, 0x5c, 0x55, 0x98, 0x68, 0x8b, 0xb0, 0xef },
+ { 0x83, 0xcf, 0xd5, 0x4d, 0xb9, 0x13 },
+ { 0x4f, 0x20, 0x39, 0x39, 0x2d, 0xdc }
+ }
+};
+
+#define NUM_TESTS ARRAY_SIZE(test_sets)
+
+
+int main(int argc, char *argv[])
+{
+ u8 buf[16], buf2[16], buf3[16], buf4[16], buf5[16], opc[16];
+ u8 auts[14], sqn[6], _rand[16];
+ int ret = 0, res, i;
+ const struct milenage_test_set *t;
+ size_t res_len;
+
+ wpa_debug_level = 0;
+
+ printf("Milenage test sets\n");
+ for (i = 0; i < NUM_TESTS; i++) {
+ t = &test_sets[i];
+ printf("Test Set %d\n", i + 1);
+
+ milenage_opc(t->op, t->k, opc);
+ if (memcmp(opc, t->opc, 16) != 0) {
+ printf("- milenage_opc failed\n");
+ ret++;
+ }
+
+ if (milenage_f1(opc, t->k, t->rand, t->sqn, t->amf, buf, buf2)
+ || memcmp(buf, t->f1, 8) != 0) {
+ printf("- milenage_f1 failed\n");
+ ret++;
+ }
+ if (memcmp(buf2, t->f1star, 8) != 0) {
+ printf("- milenage_f1* failed\n");
+ ret++;
+ }
+
+ if (milenage_f2345(opc, t->k, t->rand, buf, buf2, buf3, buf4,
+ buf5) ||
+ memcmp(buf, t->f2, 8) != 0) {
+ printf("- milenage_f2 failed\n");
+ ret++;
+ }
+ if (memcmp(buf2, t->f3, 16) != 0) {
+ printf("- milenage_f3 failed\n");
+ ret++;
+ }
+ if (memcmp(buf3, t->f4, 16) != 0) {
+ printf("- milenage_f4 failed\n");
+ ret++;
+ }
+ if (memcmp(buf4, t->f5, 6) != 0) {
+ printf("- milenage_f5 failed\n");
+ ret++;
+ }
+ if (memcmp(buf5, t->f5star, 6) != 0) {
+ printf("- milenage_f5* failed\n");
+ ret++;
+ }
+ }
+
+ printf("milenage_auts test:\n");
+ os_memcpy(auts, "\x4f\x20\x39\x39\x2d\xdd", 6);
+ os_memcpy(auts + 6, "\x4b\xb4\x31\x6e\xd4\xa1\x46\x88", 8);
+ res = milenage_auts(t->opc, t->k, t->rand, auts, buf);
+ printf("AUTS for test set %d: %d / SQN=%02x%02x%02x%02x%02x%02x\n",
+ i, res, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+ if (res)
+ ret++;
+
+ os_memset(_rand, 0xaa, sizeof(_rand));
+ os_memcpy(auts,
+ "\x43\x68\x1a\xd3\xda\xf0\x06\xbc\xde\x40\x5a\x20\x72\x67",
+ 14);
+ res = milenage_auts(t->opc, t->k, _rand, auts, buf);
+ printf("AUTS from a test USIM: %d / SQN=%02x%02x%02x%02x%02x%02x\n",
+ res, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
+ if (res)
+ ret++;
+
+ printf("milenage_generate test:\n");
+ os_memcpy(sqn, "\x00\x00\x00\x00\x40\x44", 6);
+ os_memcpy(_rand, "\x12\x69\xb8\x23\x41\x39\x35\x66\xfb\x99\x41\xe9\x84"
+ "\x4f\xe6\x2f", 16);
+ res_len = 8;
+ milenage_generate(t->opc, t->amf, t->k, sqn, _rand, buf, buf2, buf3,
+ buf4, &res_len);
+ wpa_hexdump(MSG_DEBUG, "SQN", sqn, 6);
+ wpa_hexdump(MSG_DEBUG, "RAND", _rand, 16);
+ wpa_hexdump(MSG_DEBUG, "AUTN", buf, 16);
+ wpa_hexdump(MSG_DEBUG, "IK", buf2, 16);
+ wpa_hexdump(MSG_DEBUG, "CK", buf3, 16);
+ wpa_hexdump(MSG_DEBUG, "RES", buf4, res_len);
+
+ printf("GSM-Milenage test sets\n");
+ for (i = 0; i < NUM_GSM_TESTS; i++) {
+ const struct gsm_milenage_test_set *g;
+ u8 sres[4], kc[8];
+ g = &gsm_test_sets[i];
+ printf("Test Set %d\n", i + 1);
+ gsm_milenage(g->opc, g->ki, g->rand, sres, kc);
+ if (memcmp(g->kc, kc, 8) != 0) {
+ printf("- gsm_milenage Kc failed\n");
+ ret++;
+ }
+#ifdef GSM_MILENAGE_ALT_SRES
+ if (memcmp(g->sres2, sres, 4) != 0) {
+ printf("- gsm_milenage SRES#2 failed\n");
+ ret++;
+ }
+#else /* GSM_MILENAGE_ALT_SRES */
+ if (memcmp(g->sres1, sres, 4) != 0) {
+ printf("- gsm_milenage SRES#1 failed\n");
+ ret++;
+ }
+#endif /* GSM_MILENAGE_ALT_SRES */
+ }
+
+ if (ret)
+ printf("Something failed\n");
+ else
+ printf("OK\n");
+
+ return ret;
+}
diff --git a/contrib/wpa/tests/test-rc4.c b/contrib/wpa/tests/test-rc4.c
new file mode 100644
index 000000000000..99f559274a06
--- /dev/null
+++ b/contrib/wpa/tests/test-rc4.c
@@ -0,0 +1,250 @@
+/*
+ * Test program for RC4
+ * Copyright (c) 2011, 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 "common.h"
+#include "crypto/crypto.h"
+
+
+struct rc4_test_vector {
+ size_t key_len;
+ const u8 *key;
+ const u8 *stream0;
+ const u8 *stream240;
+ const u8 *stream496;
+ const u8 *stream752;
+ const u8 *stream1008;
+ const u8 *stream1520;
+ const u8 *stream2032;
+ const u8 *stream3056;
+ const u8 *stream4080;
+};
+
+/* RFC 6229 test vectors */
+static const struct rc4_test_vector tests[] = {
+ {
+ 5, (u8 *) "\x01\x02\x03\x04\x05",
+ (u8 *) "\xb2\x39\x63\x05\xf0\x3d\xc0\x27\xcc\xc3\x52\x4a\x0a\x11\x18\xa8\x69\x82\x94\x4f\x18\xfc\x82\xd5\x89\xc4\x03\xa4\x7a\x0d\x09\x19",
+ (u8 *) "\x28\xcb\x11\x32\xc9\x6c\xe2\x86\x42\x1d\xca\xad\xb8\xb6\x9e\xae\x1c\xfc\xf6\x2b\x03\xed\xdb\x64\x1d\x77\xdf\xcf\x7f\x8d\x8c\x93",
+ (u8 *) "\x42\xb7\xd0\xcd\xd9\x18\xa8\xa3\x3d\xd5\x17\x81\xc8\x1f\x40\x41\x64\x59\x84\x44\x32\xa7\xda\x92\x3c\xfb\x3e\xb4\x98\x06\x61\xf6",
+ (u8 *) "\xec\x10\x32\x7b\xde\x2b\xee\xfd\x18\xf9\x27\x76\x80\x45\x7e\x22\xeb\x62\x63\x8d\x4f\x0b\xa1\xfe\x9f\xca\x20\xe0\x5b\xf8\xff\x2b",
+ (u8 *) "\x45\x12\x90\x48\xe6\xa0\xed\x0b\x56\xb4\x90\x33\x8f\x07\x8d\xa5\x30\xab\xbc\xc7\xc2\x0b\x01\x60\x9f\x23\xee\x2d\x5f\x6b\xb7\xdf",
+ (u8 *) "\x32\x94\xf7\x44\xd8\xf9\x79\x05\x07\xe7\x0f\x62\xe5\xbb\xce\xea\xd8\x72\x9d\xb4\x18\x82\x25\x9b\xee\x4f\x82\x53\x25\xf5\xa1\x30",
+ (u8 *) "\x1e\xb1\x4a\x0c\x13\xb3\xbf\x47\xfa\x2a\x0b\xa9\x3a\xd4\x5b\x8b\xcc\x58\x2f\x8b\xa9\xf2\x65\xe2\xb1\xbe\x91\x12\xe9\x75\xd2\xd7",
+ (u8 *) "\xf2\xe3\x0f\x9b\xd1\x02\xec\xbf\x75\xaa\xad\xe9\xbc\x35\xc4\x3c\xec\x0e\x11\xc4\x79\xdc\x32\x9d\xc8\xda\x79\x68\xfe\x96\x56\x81",
+ (u8 *) "\x06\x83\x26\xa2\x11\x84\x16\xd2\x1f\x9d\x04\xb2\xcd\x1c\xa0\x50\xff\x25\xb5\x89\x95\x99\x67\x07\xe5\x1f\xbd\xf0\x8b\x34\xd8\x75"
+ },
+ {
+ 7, (u8 *) "\x01\x02\x03\x04\x05\x06\x07",
+ (u8 *) "\x29\x3f\x02\xd4\x7f\x37\xc9\xb6\x33\xf2\xaf\x52\x85\xfe\xb4\x6b\xe6\x20\xf1\x39\x0d\x19\xbd\x84\xe2\xe0\xfd\x75\x20\x31\xaf\xc1",
+ (u8 *) "\x91\x4f\x02\x53\x1c\x92\x18\x81\x0d\xf6\x0f\x67\xe3\x38\x15\x4c\xd0\xfd\xb5\x83\x07\x3c\xe8\x5a\xb8\x39\x17\x74\x0e\xc0\x11\xd5",
+ (u8 *) "\x75\xf8\x14\x11\xe8\x71\xcf\xfa\x70\xb9\x0c\x74\xc5\x92\xe4\x54\x0b\xb8\x72\x02\x93\x8d\xad\x60\x9e\x87\xa5\xa1\xb0\x79\xe5\xe4",
+ (u8 *) "\xc2\x91\x12\x46\xb6\x12\xe7\xe7\xb9\x03\xdf\xed\xa1\xda\xd8\x66\x32\x82\x8f\x91\x50\x2b\x62\x91\x36\x8d\xe8\x08\x1d\xe3\x6f\xc2",
+ (u8 *) "\xf3\xb9\xa7\xe3\xb2\x97\xbf\x9a\xd8\x04\x51\x2f\x90\x63\xef\xf1\x8e\xcb\x67\xa9\xba\x1f\x55\xa5\xa0\x67\xe2\xb0\x26\xa3\x67\x6f",
+ (u8 *) "\xd2\xaa\x90\x2b\xd4\x2d\x0d\x7c\xfd\x34\x0c\xd4\x58\x10\x52\x9f\x78\xb2\x72\xc9\x6e\x42\xea\xb4\xc6\x0b\xd9\x14\xe3\x9d\x06\xe3",
+ (u8 *) "\xf4\x33\x2f\xd3\x1a\x07\x93\x96\xee\x3c\xee\x3f\x2a\x4f\xf0\x49\x05\x45\x97\x81\xd4\x1f\xda\x7f\x30\xc1\xbe\x7e\x12\x46\xc6\x23",
+ (u8 *) "\xad\xfd\x38\x68\xb8\xe5\x14\x85\xd5\xe6\x10\x01\x7e\x3d\xd6\x09\xad\x26\x58\x1c\x0c\x5b\xe4\x5f\x4c\xea\x01\xdb\x2f\x38\x05\xd5",
+ (u8 *) "\xf3\x17\x2c\xef\xfc\x3b\x3d\x99\x7c\x85\xcc\xd5\xaf\x1a\x95\x0c\xe7\x4b\x0b\x97\x31\x22\x7f\xd3\x7c\x0e\xc0\x8a\x47\xdd\xd8\xb8"
+ },
+ {
+ 8, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08",
+ (u8 *) "\x97\xab\x8a\x1b\xf0\xaf\xb9\x61\x32\xf2\xf6\x72\x58\xda\x15\xa8\x82\x63\xef\xdb\x45\xc4\xa1\x86\x84\xef\x87\xe6\xb1\x9e\x5b\x09",
+ (u8 *) "\x96\x36\xeb\xc9\x84\x19\x26\xf4\xf7\xd1\xf3\x62\xbd\xdf\x6e\x18\xd0\xa9\x90\xff\x2c\x05\xfe\xf5\xb9\x03\x73\xc9\xff\x4b\x87\x0a",
+ (u8 *) "\x73\x23\x9f\x1d\xb7\xf4\x1d\x80\xb6\x43\xc0\xc5\x25\x18\xec\x63\x16\x3b\x31\x99\x23\xa6\xbd\xb4\x52\x7c\x62\x61\x26\x70\x3c\x0f",
+ (u8 *) "\x49\xd6\xc8\xaf\x0f\x97\x14\x4a\x87\xdf\x21\xd9\x14\x72\xf9\x66\x44\x17\x3a\x10\x3b\x66\x16\xc5\xd5\xad\x1c\xee\x40\xc8\x63\xd0",
+ (u8 *) "\x27\x3c\x9c\x4b\x27\xf3\x22\xe4\xe7\x16\xef\x53\xa4\x7d\xe7\xa4\xc6\xd0\xe7\xb2\x26\x25\x9f\xa9\x02\x34\x90\xb2\x61\x67\xad\x1d",
+ (u8 *) "\x1f\xe8\x98\x67\x13\xf0\x7c\x3d\x9a\xe1\xc1\x63\xff\x8c\xf9\xd3\x83\x69\xe1\xa9\x65\x61\x0b\xe8\x87\xfb\xd0\xc7\x91\x62\xaa\xfb",
+ (u8 *) "\x0a\x01\x27\xab\xb4\x44\x84\xb9\xfb\xef\x5a\xbc\xae\x1b\x57\x9f\xc2\xcd\xad\xc6\x40\x2e\x8e\xe8\x66\xe1\xf3\x7b\xdb\x47\xe4\x2c",
+ (u8 *) "\x26\xb5\x1e\xa3\x7d\xf8\xe1\xd6\xf7\x6f\xc3\xb6\x6a\x74\x29\xb3\xbc\x76\x83\x20\x5d\x4f\x44\x3d\xc1\xf2\x9d\xda\x33\x15\xc8\x7b",
+ (u8 *) "\xd5\xfa\x5a\x34\x69\xd2\x9a\xaa\xf8\x3d\x23\x58\x9d\xb8\xc8\x5b\x3f\xb4\x6e\x2c\x8f\x0f\x06\x8e\xdc\xe8\xcd\xcd\x7d\xfc\x58\x62"
+ },
+ {
+ 10, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a",
+ (u8 *) "\xed\xe3\xb0\x46\x43\xe5\x86\xcc\x90\x7d\xc2\x18\x51\x70\x99\x02\x03\x51\x6b\xa7\x8f\x41\x3b\xeb\x22\x3a\xa5\xd4\xd2\xdf\x67\x11",
+ (u8 *) "\x3c\xfd\x6c\xb5\x8e\xe0\xfd\xde\x64\x01\x76\xad\x00\x00\x04\x4d\x48\x53\x2b\x21\xfb\x60\x79\xc9\x11\x4c\x0f\xfd\x9c\x04\xa1\xad",
+ (u8 *) "\x3e\x8c\xea\x98\x01\x71\x09\x97\x90\x84\xb1\xef\x92\xf9\x9d\x86\xe2\x0f\xb4\x9b\xdb\x33\x7e\xe4\x8b\x8d\x8d\xc0\xf4\xaf\xef\xfe",
+ (u8 *) "\x5c\x25\x21\xea\xcd\x79\x66\xf1\x5e\x05\x65\x44\xbe\xa0\xd3\x15\xe0\x67\xa7\x03\x19\x31\xa2\x46\xa6\xc3\x87\x5d\x2f\x67\x8a\xcb",
+ (u8 *) "\xa6\x4f\x70\xaf\x88\xae\x56\xb6\xf8\x75\x81\xc0\xe2\x3e\x6b\x08\xf4\x49\x03\x1d\xe3\x12\x81\x4e\xc6\xf3\x19\x29\x1f\x4a\x05\x16",
+ (u8 *) "\xbd\xae\x85\x92\x4b\x3c\xb1\xd0\xa2\xe3\x3a\x30\xc6\xd7\x95\x99\x8a\x0f\xed\xdb\xac\x86\x5a\x09\xbc\xd1\x27\xfb\x56\x2e\xd6\x0a",
+ (u8 *) "\xb5\x5a\x0a\x5b\x51\xa1\x2a\x8b\xe3\x48\x99\xc3\xe0\x47\x51\x1a\xd9\xa0\x9c\xea\x3c\xe7\x5f\xe3\x96\x98\x07\x03\x17\xa7\x13\x39",
+ (u8 *) "\x55\x22\x25\xed\x11\x77\xf4\x45\x84\xac\x8c\xfa\x6c\x4e\xb5\xfc\x7e\x82\xcb\xab\xfc\x95\x38\x1b\x08\x09\x98\x44\x21\x29\xc2\xf8",
+ (u8 *) "\x1f\x13\x5e\xd1\x4c\xe6\x0a\x91\x36\x9d\x23\x22\xbe\xf2\x5e\x3c\x08\xb6\xbe\x45\x12\x4a\x43\xe2\xeb\x77\x95\x3f\x84\xdc\x85\x53"
+ },
+ {
+ 16, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10",
+ (u8 *) "\x9a\xc7\xcc\x9a\x60\x9d\x1e\xf7\xb2\x93\x28\x99\xcd\xe4\x1b\x97\x52\x48\xc4\x95\x90\x14\x12\x6a\x6e\x8a\x84\xf1\x1d\x1a\x9e\x1c",
+ (u8 *) "\x06\x59\x02\xe4\xb6\x20\xf6\xcc\x36\xc8\x58\x9f\x66\x43\x2f\x2b\xd3\x9d\x56\x6b\xc6\xbc\xe3\x01\x07\x68\x15\x15\x49\xf3\x87\x3f",
+ (u8 *) "\xb6\xd1\xe6\xc4\xa5\xe4\x77\x1c\xad\x79\x53\x8d\xf2\x95\xfb\x11\xc6\x8c\x1d\x5c\x55\x9a\x97\x41\x23\xdf\x1d\xbc\x52\xa4\x3b\x89",
+ (u8 *) "\xc5\xec\xf8\x8d\xe8\x97\xfd\x57\xfe\xd3\x01\x70\x1b\x82\xa2\x59\xec\xcb\xe1\x3d\xe1\xfc\xc9\x1c\x11\xa0\xb2\x6c\x0b\xc8\xfa\x4d",
+ (u8 *) "\xe7\xa7\x25\x74\xf8\x78\x2a\xe2\x6a\xab\xcf\x9e\xbc\xd6\x60\x65\xbd\xf0\x32\x4e\x60\x83\xdc\xc6\xd3\xce\xdd\x3c\xa8\xc5\x3c\x16",
+ (u8 *) "\xb4\x01\x10\xc4\x19\x0b\x56\x22\xa9\x61\x16\xb0\x01\x7e\xd2\x97\xff\xa0\xb5\x14\x64\x7e\xc0\x4f\x63\x06\xb8\x92\xae\x66\x11\x81",
+ (u8 *) "\xd0\x3d\x1b\xc0\x3c\xd3\x3d\x70\xdf\xf9\xfa\x5d\x71\x96\x3e\xbd\x8a\x44\x12\x64\x11\xea\xa7\x8b\xd5\x1e\x8d\x87\xa8\x87\x9b\xf5",
+ (u8 *) "\xfa\xbe\xb7\x60\x28\xad\xe2\xd0\xe4\x87\x22\xe4\x6c\x46\x15\xa3\xc0\x5d\x88\xab\xd5\x03\x57\xf9\x35\xa6\x3c\x59\xee\x53\x76\x23",
+ (u8 *) "\xff\x38\x26\x5c\x16\x42\xc1\xab\xe8\xd3\xc2\xfe\x5e\x57\x2b\xf8\xa3\x6a\x4c\x30\x1a\xe8\xac\x13\x61\x0c\xcb\xc1\x22\x56\xca\xcc"
+ },
+ {
+ 24, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18",
+ (u8 *) "\x05\x95\xe5\x7f\xe5\xf0\xbb\x3c\x70\x6e\xda\xc8\xa4\xb2\xdb\x11\xdf\xde\x31\x34\x4a\x1a\xf7\x69\xc7\x4f\x07\x0a\xee\x9e\x23\x26",
+ (u8 *) "\xb0\x6b\x9b\x1e\x19\x5d\x13\xd8\xf4\xa7\x99\x5c\x45\x53\xac\x05\x6b\xd2\x37\x8e\xc3\x41\xc9\xa4\x2f\x37\xba\x79\xf8\x8a\x32\xff",
+ (u8 *) "\xe7\x0b\xce\x1d\xf7\x64\x5a\xdb\x5d\x2c\x41\x30\x21\x5c\x35\x22\x9a\x57\x30\xc7\xfc\xb4\xc9\xaf\x51\xff\xda\x89\xc7\xf1\xad\x22",
+ (u8 *) "\x04\x85\x05\x5f\xd4\xf6\xf0\xd9\x63\xef\x5a\xb9\xa5\x47\x69\x82\x59\x1f\xc6\x6b\xcd\xa1\x0e\x45\x2b\x03\xd4\x55\x1f\x6b\x62\xac",
+ (u8 *) "\x27\x53\xcc\x83\x98\x8a\xfa\x3e\x16\x88\xa1\xd3\xb4\x2c\x9a\x02\x93\x61\x0d\x52\x3d\x1d\x3f\x00\x62\xb3\xc2\xa3\xbb\xc7\xc7\xf0",
+ (u8 *) "\x96\xc2\x48\x61\x0a\xad\xed\xfe\xaf\x89\x78\xc0\x3d\xe8\x20\x5a\x0e\x31\x7b\x3d\x1c\x73\xb9\xe9\xa4\x68\x8f\x29\x6d\x13\x3a\x19",
+ (u8 *) "\xbd\xf0\xe6\xc3\xcc\xa5\xb5\xb9\xd5\x33\xb6\x9c\x56\xad\xa1\x20\x88\xa2\x18\xb6\xe2\xec\xe1\xe6\x24\x6d\x44\xc7\x59\xd1\x9b\x10",
+ (u8 *) "\x68\x66\x39\x7e\x95\xc1\x40\x53\x4f\x94\x26\x34\x21\x00\x6e\x40\x32\xcb\x0a\x1e\x95\x42\xc6\xb3\xb8\xb3\x98\xab\xc3\xb0\xf1\xd5",
+ (u8 *) "\x29\xa0\xb8\xae\xd5\x4a\x13\x23\x24\xc6\x2e\x42\x3f\x54\xb4\xc8\x3c\xb0\xf3\xb5\x02\x0a\x98\xb8\x2a\xf9\xfe\x15\x44\x84\xa1\x68"
+ },
+ {
+ 32, (u8 *) "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20",
+ (u8 *) "\xea\xa6\xbd\x25\x88\x0b\xf9\x3d\x3f\x5d\x1e\x4c\xa2\x61\x1d\x91\xcf\xa4\x5c\x9f\x7e\x71\x4b\x54\xbd\xfa\x80\x02\x7c\xb1\x43\x80",
+ (u8 *) "\x11\x4a\xe3\x44\xde\xd7\x1b\x35\xf2\xe6\x0f\xeb\xad\x72\x7f\xd8\x02\xe1\xe7\x05\x6b\x0f\x62\x39\x00\x49\x64\x22\x94\x3e\x97\xb6",
+ (u8 *) "\x91\xcb\x93\xc7\x87\x96\x4e\x10\xd9\x52\x7d\x99\x9c\x6f\x93\x6b\x49\xb1\x8b\x42\xf8\xe8\x36\x7c\xbe\xb5\xef\x10\x4b\xa1\xc7\xcd",
+ (u8 *) "\x87\x08\x4b\x3b\xa7\x00\xba\xde\x95\x56\x10\x67\x27\x45\xb3\x74\xe7\xa7\xb9\xe9\xec\x54\x0d\x5f\xf4\x3b\xdb\x12\x79\x2d\x1b\x35",
+ (u8 *) "\xc7\x99\xb5\x96\x73\x8f\x6b\x01\x8c\x76\xc7\x4b\x17\x59\xbd\x90\x7f\xec\x5b\xfd\x9f\x9b\x89\xce\x65\x48\x30\x90\x92\xd7\xe9\x58",
+ (u8 *) "\x40\xf2\x50\xb2\x6d\x1f\x09\x6a\x4a\xfd\x4c\x34\x0a\x58\x88\x15\x3e\x34\x13\x5c\x79\xdb\x01\x02\x00\x76\x76\x51\xcf\x26\x30\x73",
+ (u8 *) "\xf6\x56\xab\xcc\xf8\x8d\xd8\x27\x02\x7b\x2c\xe9\x17\xd4\x64\xec\x18\xb6\x25\x03\xbf\xbc\x07\x7f\xba\xbb\x98\xf2\x0d\x98\xab\x34",
+ (u8 *) "\x8a\xed\x95\xee\x5b\x0d\xcb\xfb\xef\x4e\xb2\x1d\x3a\x3f\x52\xf9\x62\x5a\x1a\xb0\x0e\xe3\x9a\x53\x27\x34\x6b\xdd\xb0\x1a\x9c\x18",
+ (u8 *) "\xa1\x3a\x7c\x79\xc7\xe1\x19\xb5\xab\x02\x96\xab\x28\xc3\x00\xb9\xf3\xe4\xc0\xa2\xe0\x2d\x1d\x01\xf7\xf0\xa7\x46\x18\xaf\x2b\x48"
+ },
+ {
+ 5, (u8 *) "\x83\x32\x22\x77\x2a",
+ (u8 *) "\x80\xad\x97\xbd\xc9\x73\xdf\x8a\x2e\x87\x9e\x92\xa4\x97\xef\xda\x20\xf0\x60\xc2\xf2\xe5\x12\x65\x01\xd3\xd4\xfe\xa1\x0d\x5f\xc0",
+ (u8 *) "\xfa\xa1\x48\xe9\x90\x46\x18\x1f\xec\x6b\x20\x85\xf3\xb2\x0e\xd9\xf0\xda\xf5\xba\xb3\xd5\x96\x83\x98\x57\x84\x6f\x73\xfb\xfe\x5a",
+ (u8 *) "\x1c\x7e\x2f\xc4\x63\x92\x32\xfe\x29\x75\x84\xb2\x96\x99\x6b\xc8\x3d\xb9\xb2\x49\x40\x6c\xc8\xed\xff\xac\x55\xcc\xd3\x22\xba\x12",
+ (u8 *) "\xe4\xf9\xf7\xe0\x06\x61\x54\xbb\xd1\x25\xb7\x45\x56\x9b\xc8\x97\x75\xd5\xef\x26\x2b\x44\xc4\x1a\x9c\xf6\x3a\xe1\x45\x68\xe1\xb9",
+ (u8 *) "\x6d\xa4\x53\xdb\xf8\x1e\x82\x33\x4a\x3d\x88\x66\xcb\x50\xa1\xe3\x78\x28\xd0\x74\x11\x9c\xab\x5c\x22\xb2\x94\xd7\xa9\xbf\xa0\xbb",
+ (u8 *) "\xad\xb8\x9c\xea\x9a\x15\xfb\xe6\x17\x29\x5b\xd0\x4b\x8c\xa0\x5c\x62\x51\xd8\x7f\xd4\xaa\xae\x9a\x7e\x4a\xd5\xc2\x17\xd3\xf3\x00",
+ (u8 *) "\xe7\x11\x9b\xd6\xdd\x9b\x22\xaf\xe8\xf8\x95\x85\x43\x28\x81\xe2\x78\x5b\x60\xfd\x7e\xc4\xe9\xfc\xb6\x54\x5f\x35\x0d\x66\x0f\xab",
+ (u8 *) "\xaf\xec\xc0\x37\xfd\xb7\xb0\x83\x8e\xb3\xd7\x0b\xcd\x26\x83\x82\xdb\xc1\xa7\xb4\x9d\x57\x35\x8c\xc9\xfa\x6d\x61\xd7\x3b\x7c\xf0",
+ (u8 *) "\x63\x49\xd1\x26\xa3\x7a\xfc\xba\x89\x79\x4f\x98\x04\x91\x4f\xdc\xbf\x42\xc3\x01\x8c\x2f\x7c\x66\xbf\xde\x52\x49\x75\x76\x81\x15"
+ },
+ {
+ 7, (u8 *) "\x19\x10\x83\x32\x22\x77\x2a",
+ (u8 *) "\xbc\x92\x22\xdb\xd3\x27\x4d\x8f\xc6\x6d\x14\xcc\xbd\xa6\x69\x0b\x7a\xe6\x27\x41\x0c\x9a\x2b\xe6\x93\xdf\x5b\xb7\x48\x5a\x63\xe3",
+ (u8 *) "\x3f\x09\x31\xaa\x03\xde\xfb\x30\x0f\x06\x01\x03\x82\x6f\x2a\x64\xbe\xaa\x9e\xc8\xd5\x9b\xb6\x81\x29\xf3\x02\x7c\x96\x36\x11\x81",
+ (u8 *) "\x74\xe0\x4d\xb4\x6d\x28\x64\x8d\x7d\xee\x8a\x00\x64\xb0\x6c\xfe\x9b\x5e\x81\xc6\x2f\xe0\x23\xc5\x5b\xe4\x2f\x87\xbb\xf9\x32\xb8",
+ (u8 *) "\xce\x17\x8f\xc1\x82\x6e\xfe\xcb\xc1\x82\xf5\x79\x99\xa4\x61\x40\x8b\xdf\x55\xcd\x55\x06\x1c\x06\xdb\xa6\xbe\x11\xde\x4a\x57\x8a",
+ (u8 *) "\x62\x6f\x5f\x4d\xce\x65\x25\x01\xf3\x08\x7d\x39\xc9\x2c\xc3\x49\x42\xda\xac\x6a\x8f\x9a\xb9\xa7\xfd\x13\x7c\x60\x37\x82\x56\x82",
+ (u8 *) "\xcc\x03\xfd\xb7\x91\x92\xa2\x07\x31\x2f\x53\xf5\xd4\xdc\x33\xd9\xf7\x0f\x14\x12\x2a\x1c\x98\xa3\x15\x5d\x28\xb8\xa0\xa8\xa4\x1d",
+ (u8 *) "\x2a\x3a\x30\x7a\xb2\x70\x8a\x9c\x00\xfe\x0b\x42\xf9\xc2\xd6\xa1\x86\x26\x17\x62\x7d\x22\x61\xea\xb0\xb1\x24\x65\x97\xca\x0a\xe9",
+ (u8 *) "\x55\xf8\x77\xce\x4f\x2e\x1d\xdb\xbf\x8e\x13\xe2\xcd\xe0\xfd\xc8\x1b\x15\x56\xcb\x93\x5f\x17\x33\x37\x70\x5f\xbb\x5d\x50\x1f\xc1",
+ (u8 *) "\xec\xd0\xe9\x66\x02\xbe\x7f\x8d\x50\x92\x81\x6c\xcc\xf2\xc2\xe9\x02\x78\x81\xfa\xb4\x99\x3a\x1c\x26\x20\x24\xa9\x4f\xff\x3f\x61"
+ },
+ {
+ 8, (u8 *) "\x64\x19\x10\x83\x32\x22\x77\x2a",
+ (u8 *) "\xbb\xf6\x09\xde\x94\x13\x17\x2d\x07\x66\x0c\xb6\x80\x71\x69\x26\x46\x10\x1a\x6d\xab\x43\x11\x5d\x6c\x52\x2b\x4f\xe9\x36\x04\xa9",
+ (u8 *) "\xcb\xe1\xff\xf2\x1c\x96\xf3\xee\xf6\x1e\x8f\xe0\x54\x2c\xbd\xf0\x34\x79\x38\xbf\xfa\x40\x09\xc5\x12\xcf\xb4\x03\x4b\x0d\xd1\xa7",
+ (u8 *) "\x78\x67\xa7\x86\xd0\x0a\x71\x47\x90\x4d\x76\xdd\xf1\xe5\x20\xe3\x8d\x3e\x9e\x1c\xae\xfc\xcc\xb3\xfb\xf8\xd1\x8f\x64\x12\x0b\x32",
+ (u8 *) "\x94\x23\x37\xf8\xfd\x76\xf0\xfa\xe8\xc5\x2d\x79\x54\x81\x06\x72\xb8\x54\x8c\x10\xf5\x16\x67\xf6\xe6\x0e\x18\x2f\xa1\x9b\x30\xf7",
+ (u8 *) "\x02\x11\xc7\xc6\x19\x0c\x9e\xfd\x12\x37\xc3\x4c\x8f\x2e\x06\xc4\xbd\xa6\x4f\x65\x27\x6d\x2a\xac\xb8\xf9\x02\x12\x20\x3a\x80\x8e",
+ (u8 *) "\xbd\x38\x20\xf7\x32\xff\xb5\x3e\xc1\x93\xe7\x9d\x33\xe2\x7c\x73\xd0\x16\x86\x16\x86\x19\x07\xd4\x82\xe3\x6c\xda\xc8\xcf\x57\x49",
+ (u8 *) "\x97\xb0\xf0\xf2\x24\xb2\xd2\x31\x71\x14\x80\x8f\xb0\x3a\xf7\xa0\xe5\x96\x16\xe4\x69\x78\x79\x39\xa0\x63\xce\xea\x9a\xf9\x56\xd1",
+ (u8 *) "\xc4\x7e\x0d\xc1\x66\x09\x19\xc1\x11\x01\x20\x8f\x9e\x69\xaa\x1f\x5a\xe4\xf1\x28\x96\xb8\x37\x9a\x2a\xad\x89\xb5\xb5\x53\xd6\xb0",
+ (u8 *) "\x6b\x6b\x09\x8d\x0c\x29\x3b\xc2\x99\x3d\x80\xbf\x05\x18\xb6\xd9\x81\x70\xcc\x3c\xcd\x92\xa6\x98\x62\x1b\x93\x9d\xd3\x8f\xe7\xb9"
+ },
+ {
+ 10, (u8 *) "\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a",
+ (u8 *) "\xab\x65\xc2\x6e\xdd\xb2\x87\x60\x0d\xb2\xfd\xa1\x0d\x1e\x60\x5c\xbb\x75\x90\x10\xc2\x96\x58\xf2\xc7\x2d\x93\xa2\xd1\x6d\x29\x30",
+ (u8 *) "\xb9\x01\xe8\x03\x6e\xd1\xc3\x83\xcd\x3c\x4c\x4d\xd0\xa6\xab\x05\x3d\x25\xce\x49\x22\x92\x4c\x55\xf0\x64\x94\x33\x53\xd7\x8a\x6c",
+ (u8 *) "\x12\xc1\xaa\x44\xbb\xf8\x7e\x75\xe6\x11\xf6\x9b\x2c\x38\xf4\x9b\x28\xf2\xb3\x43\x4b\x65\xc0\x98\x77\x47\x00\x44\xc6\xea\x17\x0d",
+ (u8 *) "\xbd\x9e\xf8\x22\xde\x52\x88\x19\x61\x34\xcf\x8a\xf7\x83\x93\x04\x67\x55\x9c\x23\xf0\x52\x15\x84\x70\xa2\x96\xf7\x25\x73\x5a\x32",
+ (u8 *) "\x8b\xab\x26\xfb\xc2\xc1\x2b\x0f\x13\xe2\xab\x18\x5e\xab\xf2\x41\x31\x18\x5a\x6d\x69\x6f\x0c\xfa\x9b\x42\x80\x8b\x38\xe1\x32\xa2",
+ (u8 *) "\x56\x4d\x3d\xae\x18\x3c\x52\x34\xc8\xaf\x1e\x51\x06\x1c\x44\xb5\x3c\x07\x78\xa7\xb5\xf7\x2d\x3c\x23\xa3\x13\x5c\x7d\x67\xb9\xf4",
+ (u8 *) "\xf3\x43\x69\x89\x0f\xcf\x16\xfb\x51\x7d\xca\xae\x44\x63\xb2\xdd\x02\xf3\x1c\x81\xe8\x20\x07\x31\xb8\x99\xb0\x28\xe7\x91\xbf\xa7",
+ (u8 *) "\x72\xda\x64\x62\x83\x22\x8c\x14\x30\x08\x53\x70\x17\x95\x61\x6f\x4e\x0a\x8c\x6f\x79\x34\xa7\x88\xe2\x26\x5e\x81\xd6\xd0\xc8\xf4",
+ (u8 *) "\x43\x8d\xd5\xea\xfe\xa0\x11\x1b\x6f\x36\xb4\xb9\x38\xda\x2a\x68\x5f\x6b\xfc\x73\x81\x58\x74\xd9\x71\x00\xf0\x86\x97\x93\x57\xd8"
+ },
+ {
+ 16, (u8 *) "\xeb\xb4\x62\x27\xc6\xcc\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a",
+ (u8 *) "\x72\x0c\x94\xb6\x3e\xdf\x44\xe1\x31\xd9\x50\xca\x21\x1a\x5a\x30\xc3\x66\xfd\xea\xcf\x9c\xa8\x04\x36\xbe\x7c\x35\x84\x24\xd2\x0b",
+ (u8 *) "\xb3\x39\x4a\x40\xaa\xbf\x75\xcb\xa4\x22\x82\xef\x25\xa0\x05\x9f\x48\x47\xd8\x1d\xa4\x94\x2d\xbc\x24\x9d\xef\xc4\x8c\x92\x2b\x9f",
+ (u8 *) "\x08\x12\x8c\x46\x9f\x27\x53\x42\xad\xda\x20\x2b\x2b\x58\xda\x95\x97\x0d\xac\xef\x40\xad\x98\x72\x3b\xac\x5d\x69\x55\xb8\x17\x61",
+ (u8 *) "\x3c\xb8\x99\x93\xb0\x7b\x0c\xed\x93\xde\x13\xd2\xa1\x10\x13\xac\xef\x2d\x67\x6f\x15\x45\xc2\xc1\x3d\xc6\x80\xa0\x2f\x4a\xdb\xfe",
+ (u8 *) "\xb6\x05\x95\x51\x4f\x24\xbc\x9f\xe5\x22\xa6\xca\xd7\x39\x36\x44\xb5\x15\xa8\xc5\x01\x17\x54\xf5\x90\x03\x05\x8b\xdb\x81\x51\x4e",
+ (u8 *) "\x3c\x70\x04\x7e\x8c\xbc\x03\x8e\x3b\x98\x20\xdb\x60\x1d\xa4\x95\x11\x75\xda\x6e\xe7\x56\xde\x46\xa5\x3e\x2b\x07\x56\x60\xb7\x70",
+ (u8 *) "\x00\xa5\x42\xbb\xa0\x21\x11\xcc\x2c\x65\xb3\x8e\xbd\xba\x58\x7e\x58\x65\xfd\xbb\x5b\x48\x06\x41\x04\xe8\x30\xb3\x80\xf2\xae\xde",
+ (u8 *) "\x34\xb2\x1a\xd2\xad\x44\xe9\x99\xdb\x2d\x7f\x08\x63\xf0\xd9\xb6\x84\xa9\x21\x8f\xc3\x6e\x8a\x5f\x2c\xcf\xbe\xae\x53\xa2\x7d\x25",
+ (u8 *) "\xa2\x22\x1a\x11\xb8\x33\xcc\xb4\x98\xa5\x95\x40\xf0\x54\x5f\x4a\x5b\xbe\xb4\x78\x7d\x59\xe5\x37\x3f\xdb\xea\x6c\x6f\x75\xc2\x9b"
+ },
+ {
+ 24, (u8 *) "\xc1\x09\x16\x39\x08\xeb\xe5\x1d\xeb\xb4\x62\x27\xc6\xcc\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a",
+ (u8 *) "\x54\xb6\x4e\x6b\x5a\x20\xb5\xe2\xec\x84\x59\x3d\xc7\x98\x9d\xa7\xc1\x35\xee\xe2\x37\xa8\x54\x65\xff\x97\xdc\x03\x92\x4f\x45\xce",
+ (u8 *) "\xcf\xcc\x92\x2f\xb4\xa1\x4a\xb4\x5d\x61\x75\xaa\xbb\xf2\xd2\x01\x83\x7b\x87\xe2\xa4\x46\xad\x0e\xf7\x98\xac\xd0\x2b\x94\x12\x4f",
+ (u8 *) "\x17\xa6\xdb\xd6\x64\x92\x6a\x06\x36\xb3\xf4\xc3\x7a\x4f\x46\x94\x4a\x5f\x9f\x26\xae\xee\xd4\xd4\xa2\x5f\x63\x2d\x30\x52\x33\xd9",
+ (u8 *) "\x80\xa3\xd0\x1e\xf0\x0c\x8e\x9a\x42\x09\xc1\x7f\x4e\xeb\x35\x8c\xd1\x5e\x7d\x5f\xfa\xaa\xbc\x02\x07\xbf\x20\x0a\x11\x77\x93\xa2",
+ (u8 *) "\x34\x96\x82\xbf\x58\x8e\xaa\x52\xd0\xaa\x15\x60\x34\x6a\xea\xfa\xf5\x85\x4c\xdb\x76\xc8\x89\xe3\xad\x63\x35\x4e\x5f\x72\x75\xe3",
+ (u8 *) "\x53\x2c\x7c\xec\xcb\x39\xdf\x32\x36\x31\x84\x05\xa4\xb1\x27\x9c\xba\xef\xe6\xd9\xce\xb6\x51\x84\x22\x60\xe0\xd1\xe0\x5e\x3b\x90",
+ (u8 *) "\xe8\x2d\x8c\x6d\xb5\x4e\x3c\x63\x3f\x58\x1c\x95\x2b\xa0\x42\x07\x4b\x16\xe5\x0a\xbd\x38\x1b\xd7\x09\x00\xa9\xcd\x9a\x62\xcb\x23",
+ (u8 *) "\x36\x82\xee\x33\xbd\x14\x8b\xd9\xf5\x86\x56\xcd\x8f\x30\xd9\xfb\x1e\x5a\x0b\x84\x75\x04\x5d\x9b\x20\xb2\x62\x86\x24\xed\xfd\x9e",
+ (u8 *) "\x63\xed\xd6\x84\xfb\x82\x62\x82\xfe\x52\x8f\x9c\x0e\x92\x37\xbc\xe4\xdd\x2e\x98\xd6\x96\x0f\xae\x0b\x43\x54\x54\x56\x74\x33\x91"
+ },
+ {
+ 32, (u8 *) "\x1a\xda\x31\xd5\xcf\x68\x82\x21\xc1\x09\x16\x39\x08\xeb\xe5\x1d\xeb\xb4\x62\x27\xc6\xcc\x8b\x37\x64\x19\x10\x83\x32\x22\x77\x2a",
+ (u8 *) "\xdd\x5b\xcb\x00\x18\xe9\x22\xd4\x94\x75\x9d\x7c\x39\x5d\x02\xd3\xc8\x44\x6f\x8f\x77\xab\xf7\x37\x68\x53\x53\xeb\x89\xa1\xc9\xeb",
+ (u8 *) "\xaf\x3e\x30\xf9\xc0\x95\x04\x59\x38\x15\x15\x75\xc3\xfb\x90\x98\xf8\xcb\x62\x74\xdb\x99\xb8\x0b\x1d\x20\x12\xa9\x8e\xd4\x8f\x0e",
+ (u8 *) "\x25\xc3\x00\x5a\x1c\xb8\x5d\xe0\x76\x25\x98\x39\xab\x71\x98\xab\x9d\xcb\xc1\x83\xe8\xcb\x99\x4b\x72\x7b\x75\xbe\x31\x80\x76\x9c",
+ (u8 *) "\xa1\xd3\x07\x8d\xfa\x91\x69\x50\x3e\xd9\xd4\x49\x1d\xee\x4e\xb2\x85\x14\xa5\x49\x58\x58\x09\x6f\x59\x6e\x4b\xcd\x66\xb1\x06\x65",
+ (u8 *) "\x5f\x40\xd5\x9e\xc1\xb0\x3b\x33\x73\x8e\xfa\x60\xb2\x25\x5d\x31\x34\x77\xc7\xf7\x64\xa4\x1b\xac\xef\xf9\x0b\xf1\x4f\x92\xb7\xcc",
+ (u8 *) "\xac\x4e\x95\x36\x8d\x99\xb9\xeb\x78\xb8\xda\x8f\x81\xff\xa7\x95\x8c\x3c\x13\xf8\xc2\x38\x8b\xb7\x3f\x38\x57\x6e\x65\xb7\xc4\x46",
+ (u8 *) "\x13\xc4\xb9\xc1\xdf\xb6\x65\x79\xed\xdd\x8a\x28\x0b\x9f\x73\x16\xdd\xd2\x78\x20\x55\x01\x26\x69\x8e\xfa\xad\xc6\x4b\x64\xf6\x6e",
+ (u8 *) "\xf0\x8f\x2e\x66\xd2\x8e\xd1\x43\xf3\xa2\x37\xcf\x9d\xe7\x35\x59\x9e\xa3\x6c\x52\x55\x31\xb8\x80\xba\x12\x43\x34\xf5\x7b\x0b\x70",
+ (u8 *) "\xd5\xa3\x9e\x3d\xfc\xc5\x02\x80\xba\xc4\xa6\xb5\xaa\x0d\xca\x7d\x37\x0b\x1c\x1f\xe6\x55\x91\x6d\x97\xfd\x0d\x47\xca\x1d\x72\xb8"
+ }
+};
+
+#define NUM_TESTS ARRAY_SIZE(tests)
+
+
+static int run_test(unsigned int i, const u8 *key, size_t key_len,
+ const u8 *stream, int offset)
+{
+ u8 res[32];
+ os_memset(res, 0, sizeof(res));
+ if (rc4_skip(key, key_len, offset, res, sizeof(res)) < 0 ||
+ os_memcmp(res, stream, 32) != 0) {
+ printf("RC4 test case %d (offset %d) - FAILED!\n",
+ i + 1, offset);
+ return 1;
+ }
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ unsigned int i;
+
+ for (i = 0; i < NUM_TESTS; i++) {
+ const struct rc4_test_vector *test = &tests[i];
+ ret += run_test(i, test->key, test->key_len,
+ test->stream0, 0);
+ ret += run_test(i, test->key, test->key_len,
+ test->stream240, 240);
+ ret += run_test(i, test->key, test->key_len,
+ test->stream496, 496);
+ ret += run_test(i, test->key, test->key_len,
+ test->stream752, 752);
+ ret += run_test(i, test->key, test->key_len,
+ test->stream1008, 1008);
+ ret += run_test(i, test->key, test->key_len,
+ test->stream1520, 1520);
+ ret += run_test(i, test->key, test->key_len,
+ test->stream2032, 2032);
+ ret += run_test(i, test->key, test->key_len,
+ test->stream3056, 3056);
+ ret += run_test(i, test->key, test->key_len,
+ test->stream4080, 4080);
+ }
+
+ if (ret == 0)
+ printf("All RC4 test cases passed\n");
+
+ return ret;
+}
diff --git a/contrib/wpa/tests/test-rsa-sig-ver.c b/contrib/wpa/tests/test-rsa-sig-ver.c
new file mode 100644
index 000000000000..0cb398af15a8
--- /dev/null
+++ b/contrib/wpa/tests/test-rsa-sig-ver.c
@@ -0,0 +1,206 @@
+/*
+ * Testing tool for RSA PKCS #1 v1.5 signature verification
+ * Copyright (c) 2014, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "crypto/crypto.h"
+#include "tls/rsa.h"
+#include "tls/asn1.h"
+#include "tls/pkcs1.h"
+
+
+static int cavp_rsa_sig_ver(const char *fname)
+{
+ FILE *f;
+ int ret = 0;
+ char buf[15000], *pos, *pos2;
+ u8 msg[200], n[512], s[512], em[512], e[512];
+ size_t msg_len = 0, n_len = 0, s_len = 0, em_len, e_len = 0;
+ size_t tmp_len;
+ char sha_alg[20];
+ int ok = 0;
+
+ printf("CAVP RSA SigVer test vectors from %s\n", fname);
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ printf("%s does not exist - cannot validate CAVP RSA SigVer test vectors\n",
+ fname);
+ return 0;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ pos = os_strchr(buf, '=');
+ if (pos == NULL)
+ continue;
+ pos2 = pos - 1;
+ while (pos2 >= buf && *pos2 == ' ')
+ *pos2-- = '\0';
+ *pos++ = '\0';
+ while (*pos == ' ')
+ *pos++ = '\0';
+ pos2 = os_strchr(pos, '\r');
+ if (!pos2)
+ pos2 = os_strchr(pos, '\n');
+ if (pos2)
+ *pos2 = '\0';
+ else
+ pos2 = pos + os_strlen(pos);
+
+ if (os_strcmp(buf, "SHAAlg") == 0) {
+ os_strlcpy(sha_alg, pos, sizeof(sha_alg));
+ } else if (os_strcmp(buf, "Msg") == 0) {
+ tmp_len = os_strlen(pos);
+ if (tmp_len > sizeof(msg) * 2) {
+ printf("Too long Msg\n");
+ fclose(f);
+ return -1;
+ }
+ msg_len = tmp_len / 2;
+ if (hexstr2bin(pos, msg, msg_len) < 0) {
+ printf("Invalid hex string '%s'\n", pos);
+ ret++;
+ break;
+ }
+ } else if (os_strcmp(buf, "n") == 0) {
+ tmp_len = os_strlen(pos);
+ if (tmp_len > sizeof(n) * 2) {
+ printf("Too long n\n");
+ fclose(f);
+ return -1;
+ }
+ n_len = tmp_len / 2;
+ if (hexstr2bin(pos, n, n_len) < 0) {
+ printf("Invalid hex string '%s'\n", pos);
+ ret++;
+ break;
+ }
+ } else if (os_strcmp(buf, "e") == 0) {
+ tmp_len = os_strlen(pos);
+ if (tmp_len > sizeof(e) * 2) {
+ printf("Too long e\n");
+ fclose(f);
+ return -1;
+ }
+ e_len = tmp_len / 2;
+ if (hexstr2bin(pos, e, e_len) < 0) {
+ printf("Invalid hex string '%s'\n", pos);
+ ret++;
+ break;
+ }
+ } else if (os_strcmp(buf, "S") == 0) {
+ tmp_len = os_strlen(pos);
+ if (tmp_len > sizeof(s) * 2) {
+ printf("Too long S\n");
+ fclose(f);
+ return -1;
+ }
+ s_len = tmp_len / 2;
+ if (hexstr2bin(pos, s, s_len) < 0) {
+ printf("Invalid hex string '%s'\n", pos);
+ ret++;
+ break;
+ }
+ } else if (os_strncmp(buf, "EM", 2) == 0) {
+ tmp_len = os_strlen(pos);
+ if (tmp_len > sizeof(em) * 2) {
+ fclose(f);
+ return -1;
+ }
+ em_len = tmp_len / 2;
+ if (hexstr2bin(pos, em, em_len) < 0) {
+ printf("Invalid hex string '%s'\n", pos);
+ ret++;
+ break;
+ }
+ } else if (os_strcmp(buf, "Result") == 0) {
+ const u8 *addr[1];
+ size_t len[1];
+ struct crypto_public_key *pk;
+ int res;
+ u8 hash[32];
+ size_t hash_len;
+ const struct asn1_oid *alg;
+
+ addr[0] = msg;
+ len[0] = msg_len;
+ if (os_strcmp(sha_alg, "SHA1") == 0) {
+ if (sha1_vector(1, addr, len, hash) < 0) {
+ fclose(f);
+ return -1;
+ }
+ hash_len = 20;
+ alg = &asn1_sha1_oid;
+ } else if (os_strcmp(sha_alg, "SHA256") == 0) {
+ if (sha256_vector(1, addr, len, hash) < 0) {
+ fclose(f);
+ return -1;
+ }
+ hash_len = 32;
+ alg = &asn1_sha256_oid;
+ } else {
+ continue;
+ }
+
+ printf("\nExpected result: %s\n", pos);
+ wpa_hexdump(MSG_INFO, "Hash(Msg)", hash, hash_len);
+
+ pk = crypto_public_key_import_parts(n, n_len,
+ e, e_len);
+ if (pk == NULL) {
+ printf("Failed to import public key\n");
+ ret++;
+ continue;
+ }
+
+ res = pkcs1_v15_sig_ver(pk, s, s_len, alg,
+ hash, hash_len);
+ crypto_public_key_free(pk);
+ if ((*pos == 'F' && !res) || (*pos != 'F' && res)) {
+ printf("FAIL\n");
+ ret++;
+ continue;
+ }
+
+ printf("PASS\n");
+ ok++;
+ }
+ }
+
+ fclose(f);
+
+ if (ret)
+ printf("Test case failed\n");
+ else
+ printf("%d test vectors OK\n", ok);
+
+ return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+ int i;
+
+ wpa_debug_level = 0;
+ wpa_debug_show_keys = 1;
+
+ for (i = 1; i < argc; i++) {
+ if (cavp_rsa_sig_ver(argv[i]))
+ ret++;
+ }
+
+ if (argc < 2 && cavp_rsa_sig_ver("CAVP/SigVer15_186-3.rsp"))
+ ret++;
+ if (argc < 2 && cavp_rsa_sig_ver("CAVP/SigVer15EMTest.txt"))
+ ret++;
+
+ return ret;
+}
diff --git a/contrib/wpa/tests/test-sha1.c b/contrib/wpa/tests/test-sha1.c
new file mode 100644
index 000000000000..3269d4d6097e
--- /dev/null
+++ b/contrib/wpa/tests/test-sha1.c
@@ -0,0 +1,119 @@
+/*
+ * Test program for SHA1 and MD5
+ * Copyright (c) 2003-2006, 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 "common.h"
+#include "crypto/crypto.h"
+
+
+static int cavp_shavs(const char *fname)
+{
+ FILE *f;
+ int ret = 0;
+ char buf[15000], *pos, *pos2;
+ u8 msg[6400];
+ int msg_len = 0, tmp_len;
+ u8 md[20], hash[20];
+ int ok = 0;
+
+ printf("CAVP SHAVS test vectors from %s\n", fname);
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ printf("%s does not exist - cannot validate CAVP SHAVS test vectors\n",
+ fname);
+ return 0;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ pos = os_strchr(buf, '=');
+ if (pos == NULL)
+ continue;
+ pos2 = pos - 1;
+ while (pos2 >= buf && *pos2 == ' ')
+ *pos2-- = '\0';
+ *pos++ = '\0';
+ while (*pos == ' ')
+ *pos++ = '\0';
+ pos2 = os_strchr(pos, '\r');
+ if (!pos2)
+ pos2 = os_strchr(pos, '\n');
+ if (pos2)
+ *pos2 = '\0';
+ else
+ pos2 = pos + os_strlen(pos);
+
+ if (os_strcmp(buf, "Len") == 0) {
+ msg_len = atoi(pos);
+ } else if (os_strcmp(buf, "Msg") == 0) {
+ tmp_len = os_strlen(pos);
+ if (msg_len == 0 && tmp_len == 2)
+ tmp_len = 0;
+ if (msg_len != tmp_len * 4) {
+ printf("Unexpected Msg length (msg_len=%u tmp_len=%u, Msg='%s'\n",
+ msg_len, tmp_len, pos);
+ ret++;
+ break;
+ }
+
+ if (hexstr2bin(pos, msg, msg_len / 8) < 0) {
+ printf("Invalid hex string '%s'\n", pos);
+ ret++;
+ break;
+ }
+ } else if (os_strcmp(buf, "MD") == 0) {
+ const u8 *addr[1];
+ size_t len[1];
+
+ tmp_len = os_strlen(pos);
+ if (tmp_len != 2 * 20) {
+ printf("Unexpected MD length (MD='%s'\n",
+ pos);
+ ret++;
+ break;
+ }
+
+ if (hexstr2bin(pos, md, 20) < 0) {
+ printf("Invalid hex string '%s'\n", pos);
+ ret++;
+ break;
+ }
+
+ addr[0] = msg;
+ len[0] = msg_len / 8;
+ if (sha1_vector(1, addr, len, hash) < 0 ||
+ os_memcmp(hash, md, 20) != 0)
+ ret++;
+ else
+ ok++;
+ }
+ }
+
+ fclose(f);
+
+ if (ret)
+ printf("Test case failed\n");
+ else
+ printf("%d test vectors OK\n", ok);
+
+ return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int ret = 0;
+
+ if (cavp_shavs("CAVP/SHA1ShortMsg.rsp"))
+ ret++;
+ if (cavp_shavs("CAVP/SHA1LongMsg.rsp"))
+ ret++;
+
+ return ret;
+}
diff --git a/contrib/wpa/tests/test-sha256.c b/contrib/wpa/tests/test-sha256.c
new file mode 100644
index 000000000000..741351aa0f7f
--- /dev/null
+++ b/contrib/wpa/tests/test-sha256.c
@@ -0,0 +1,119 @@
+/*
+ * Test program for SHA256
+ * Copyright (c) 2006, 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 "common.h"
+#include "crypto/crypto.h"
+
+
+static int cavp_shavs(const char *fname)
+{
+ FILE *f;
+ int ret = 0;
+ char buf[15000], *pos, *pos2;
+ u8 msg[6400];
+ int msg_len = 0, tmp_len;
+ u8 md[32], hash[32];
+ int ok = 0;
+
+ printf("CAVP SHAVS test vectors from %s\n", fname);
+
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ printf("%s does not exist - cannot validate CAVP SHAVS test vectors\n",
+ fname);
+ return 0;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ pos = os_strchr(buf, '=');
+ if (pos == NULL)
+ continue;
+ pos2 = pos - 1;
+ while (pos2 >= buf && *pos2 == ' ')
+ *pos2-- = '\0';
+ *pos++ = '\0';
+ while (*pos == ' ')
+ *pos++ = '\0';
+ pos2 = os_strchr(pos, '\r');
+ if (!pos2)
+ pos2 = os_strchr(pos, '\n');
+ if (pos2)
+ *pos2 = '\0';
+ else
+ pos2 = pos + os_strlen(pos);
+
+ if (os_strcmp(buf, "Len") == 0) {
+ msg_len = atoi(pos);
+ } else if (os_strcmp(buf, "Msg") == 0) {
+ tmp_len = os_strlen(pos);
+ if (msg_len == 0 && tmp_len == 2)
+ tmp_len = 0;
+ if (msg_len != tmp_len * 4) {
+ printf("Unexpected Msg length (msg_len=%u tmp_len=%u, Msg='%s'\n",
+ msg_len, tmp_len, pos);
+ ret++;
+ break;
+ }
+
+ if (hexstr2bin(pos, msg, msg_len / 8) < 0) {
+ printf("Invalid hex string '%s'\n", pos);
+ ret++;
+ break;
+ }
+ } else if (os_strcmp(buf, "MD") == 0) {
+ const u8 *addr[1];
+ size_t len[1];
+
+ tmp_len = os_strlen(pos);
+ if (tmp_len != 2 * 32) {
+ printf("Unexpected MD length (MD='%s'\n",
+ pos);
+ ret++;
+ break;
+ }
+
+ if (hexstr2bin(pos, md, 32) < 0) {
+ printf("Invalid hex string '%s'\n", pos);
+ ret++;
+ break;
+ }
+
+ addr[0] = msg;
+ len[0] = msg_len / 8;
+ if (sha256_vector(1, addr, len, hash) < 0 ||
+ os_memcmp(hash, md, 32) != 0)
+ ret++;
+ else
+ ok++;
+ }
+ }
+
+ fclose(f);
+
+ if (ret)
+ printf("Test case failed\n");
+ else
+ printf("%d test vectors OK\n", ok);
+
+ return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int errors = 0;
+
+ if (cavp_shavs("CAVP/SHA256ShortMsg.rsp"))
+ errors++;
+ if (cavp_shavs("CAVP/SHA256LongMsg.rsp"))
+ errors++;
+
+ return errors;
+}
diff --git a/contrib/wpa/tests/test-x509v3.c b/contrib/wpa/tests/test-x509v3.c
new file mode 100644
index 000000000000..06cd6eaab3e4
--- /dev/null
+++ b/contrib/wpa/tests/test-x509v3.c
@@ -0,0 +1,62 @@
+/*
+ * Testing tool for X.509v3 routines
+ * Copyright (c) 2006-2007, 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 "common.h"
+#include "tls/asn1.h"
+#include "tls/x509v3.h"
+
+
+int main(int argc, char *argv[])
+{
+ char *buf;
+ size_t len;
+ struct x509_certificate *certs = NULL, *last = NULL, *cert;
+ int i, reason;
+
+ wpa_debug_level = 0;
+ wpa_debug_show_keys = 1;
+
+ if (argc < 3 || strcmp(argv[1], "-v") != 0) {
+ printf("usage: test_x509v3 -v <cert1.der> <cert2.der> ..\n");
+ return -1;
+ }
+
+ for (i = 2; i < argc; i++) {
+ printf("Reading: %s\n", argv[i]);
+ buf = os_readfile(argv[i], &len);
+ if (buf == NULL) {
+ printf("Failed to read '%s'\n", argv[i]);
+ return -1;
+ }
+
+ cert = x509_certificate_parse((u8 *) buf, len);
+ if (cert == NULL) {
+ printf("Failed to parse X.509 certificate\n");
+ return -1;
+ }
+
+ free(buf);
+
+ if (certs == NULL)
+ certs = cert;
+ else
+ last->next = cert;
+ last = cert;
+ }
+
+ printf("\n\nValidating certificate chain\n");
+ if (x509_certificate_chain_validate(last, certs, &reason, 0) < 0) {
+ printf("\nCertificate chain validation failed: %d\n", reason);
+ return -1;
+ }
+ printf("\nCertificate chain is valid\n");
+
+ return 0;
+}
diff --git a/contrib/wpa/tests/test_x509v3_nist.sh b/contrib/wpa/tests/test_x509v3_nist.sh
new file mode 100755
index 000000000000..d3f94bb3a7b2
--- /dev/null
+++ b/contrib/wpa/tests/test_x509v3_nist.sh
@@ -0,0 +1,144 @@
+#!/bin/bash
+
+# X.509 Path Validation Test Suite, Version 1.07
+# http://csrc.nist.gov/pki/testing/x509paths_old.html
+# http://csrc.nist.gov/pki/testing/x509tests.tgz
+
+if [ -z "$1" ]; then
+ echo "usage: $0 <path to X509tests directory>"
+ exit 1
+fi
+
+TESTS=$1
+
+if [ ! -d $TESTS ]; then
+ echo "Not a directory: $TESTS"
+ exit 1
+fi
+
+X509TEST="./test-x509v3 -v"
+TMPOUT=test_x509v3_nist.out
+
+# TODO: add support for validating CRLs
+
+END="End Certificate "
+ROOT="Trust Anchor "
+ICA="Intermediate Certificate "
+
+SUCCESS=""
+FAILURE=""
+
+function run_test
+{
+ NUM=$1
+ RES=$2
+ shift 2
+ $X509TEST "$@" > $TMPOUT.$NUM
+ VALRES=$?
+ OK=0
+ if [ $RES -eq 0 ]; then
+ # expecting success
+ if [ $VALRES -eq 0 ]; then
+ OK=1
+ else
+ echo "test$NUM failed - expected validation success"
+ OK=0
+ fi
+ else
+ # expecting failure
+ if [ $VALRES -eq 0 ]; then
+ echo "test$NUM failed - expected validation failure"
+ OK=0
+ else
+ REASON=`grep "Certificate chain validation failed: " $TMPOUT.$NUM`
+ if [ $? -eq 0 ]; then
+ REASONNUM=`echo "$REASON" | colrm 1 37`
+ if [ $REASONNUM -eq $RES ]; then
+ OK=1
+ else
+ echo "test$NUM failed - expected validation result $RES; result was $REASONNUM"
+ OK=0
+ fi
+ else
+ echo "test$NUM failed - expected validation failure; other type of error detected"
+ OK=0
+ fi
+ fi
+ fi
+ if [ $OK -eq 1 ]; then
+ rm $TMPOUT.$NUM
+ SUCCESS="$SUCCESS $NUM"
+ else
+ FAILURE="$FAILURE $NUM"
+ fi
+}
+
+P=$TESTS/test
+
+run_test 1 0 "${P}1/${END}CP.01.01.crt" "${P}1/${ROOT}CP.01.01.crt"
+run_test 2 1 "${P}2/${END}CP.01.02.crt" "${P}2/${ICA}CP.01.02.crt" "${P}2/${ROOT}CP.01.01.crt"
+run_test 3 1 "${P}3/${END}CP.01.03.crt" "${P}3/${ICA}CP.01.03.crt" "${P}3/${ROOT}CP.01.01.crt"
+run_test 4 0 "${P}4/${END}CP.02.01.crt" "${P}4/${ICA}2 CP.02.01.crt" "${P}4/${ICA}1 CP.02.01.crt" "${P}4/${ROOT}CP.01.01.crt"
+run_test 5 4 "${P}5/${END}CP.02.02.crt" "${P}5/${ICA}CP.02.02.crt" "${P}5/${ROOT}CP.01.01.crt"
+run_test 6 4 "${P}6/${END}CP.02.03.crt" "${P}6/${ICA}CP.02.03.crt" "${P}6/${ROOT}CP.01.01.crt"
+run_test 7 0 "${P}7/${END}CP.02.04.crt" "${P}7/${ICA}CP.02.04.crt" "${P}7/${ROOT}CP.01.01.crt"
+run_test 8 4 "${P}8/${END}CP.02.05.crt" "${P}8/${ICA}CP.02.05.crt" "${P}8/${ROOT}CP.01.01.crt"
+run_test 9 4 "${P}9/${END}CP.03.01.crt" "${P}9/${ICA}CP.03.01.crt" "${P}9/${ROOT}CP.01.01.crt"
+run_test 10 4 "${P}10/${END}CP.03.02.crt" "${P}10/${ICA}CP.03.02.crt" "${P}10/${ROOT}CP.01.01.crt"
+run_test 11 4 "${P}11/${END}CP.03.03.crt" "${P}11/${ICA}CP.03.03.crt" "${P}11/${ROOT}CP.01.01.crt"
+run_test 12 0 "${P}12/${END}CP.03.04.crt" "${P}12/${ICA}CP.03.04.crt" "${P}12/${ROOT}CP.01.01.crt"
+run_test 13 5 "${P}13/${END}CP.04.01.crt" "${P}13/${ICA}CP.04.01.crt" "${P}13/${ROOT}CP.01.01.crt"
+run_test 14 5 "${P}14/${END}CP.04.02.crt" "${P}14/${ICA}CP.04.02.crt" "${P}14/${ROOT}CP.01.01.crt"
+run_test 15 0 "${P}15/${END}CP.04.03.crt" "${P}15/${ICA}CP.04.03.crt" "${P}15/${ROOT}CP.01.01.crt"
+run_test 16 0 "${P}16/${END}CP.04.04.crt" "${P}16/${ICA}CP.04.04.crt" "${P}16/${ROOT}CP.01.01.crt"
+run_test 17 0 "${P}17/${END}CP.04.05.crt" "${P}17/${ICA}CP.04.05.crt" "${P}17/${ROOT}CP.01.01.crt"
+run_test 18 0 "${P}18/${END}CP.04.06.crt" "${P}18/${ICA}CP.04.06.crt" "${P}18/${ROOT}CP.01.01.crt"
+run_test 19 1 "${P}19/${END}CP.05.01.crt" "${P}19/${ICA}CP.05.01.crt" "${P}19/${ROOT}CP.01.01.crt"
+run_test 20 3 "${P}20/${END}CP.06.01.crt" "${P}20/${ICA}CP.06.01.crt" "${P}20/${ROOT}CP.01.01.crt"
+run_test 21 3 "${P}21/${END}CP.06.02.crt" "${P}21/${ICA}CP.06.02.crt" "${P}21/${ROOT}CP.01.01.crt"
+run_test 22 1 "${P}22/${END}IC.01.01.crt" "${P}22/${ICA}IC.01.01.crt" "${P}22/${ROOT}CP.01.01.crt"
+run_test 23 1 "${P}23/${END}IC.02.01.crt" "${P}23/${ICA}IC.02.01.crt" "${P}23/${ROOT}CP.01.01.crt"
+run_test 24 0 "${P}24/${END}IC.02.02.crt" "${P}24/${ICA}IC.02.02.crt" "${P}24/${ROOT}CP.01.01.crt"
+run_test 25 1 "${P}25/${END}IC.02.03.crt" "${P}25/${ICA}IC.02.03.crt" "${P}25/${ROOT}CP.01.01.crt"
+run_test 26 0 "${P}26/${END}IC.02.04.crt" "${P}26/${ICA}IC.02.04.crt" "${P}26/${ROOT}CP.01.01.crt"
+run_test 27 0 "${P}27/${END}IC.04.01.crt" "${P}27/${ICA}IC.04.01.crt" "${P}27/${ROOT}CP.01.01.crt"
+run_test 28 1 "${P}28/${END}IC.05.01.crt" "${P}28/${ICA}IC.05.01.crt" "${P}28/${ROOT}CP.01.01.crt"
+run_test 29 1 "${P}29/${END}IC.05.02.crt" "${P}29/${ICA}IC.05.02.crt" "${P}29/${ROOT}CP.01.01.crt"
+run_test 30 0 "${P}30/${END}IC.05.03.crt" "${P}30/${ICA}IC.05.03.crt" "${P}30/${ROOT}CP.01.01.crt"
+run_test 31 1 "${P}31/${END}IC.06.01.crt" "${P}31/${ICA}IC.06.01.crt" "${P}31/${ROOT}CP.01.01.crt"
+run_test 32 1 "${P}32/${END}IC.06.02.crt" "${P}32/${ICA}IC.06.02.crt" "${P}32/${ROOT}CP.01.01.crt"
+run_test 33 0 "${P}33/${END}IC.06.03.crt" "${P}33/${ICA}IC.06.03.crt" "${P}33/${ROOT}CP.01.01.crt"
+run_test 34 0 "${P}34/${END}PP.01.01.crt" "${P}34/${ICA}PP.01.01.crt" "${P}34/${ROOT}CP.01.01.crt"
+run_test 35 0 "${P}35/${END}PP.01.02.crt" "${P}35/${ICA}PP.01.02.crt" "${P}35/${ROOT}CP.01.01.crt"
+run_test 36 0 "${P}36/${END}PP.01.03.crt" "${P}36/${ICA}2 PP.01.03.crt" "${P}36/${ICA}1 PP.01.03.crt" "${P}36/${ROOT}CP.01.01.crt"
+run_test 37 0 "${P}37/${END}PP.01.04.crt" "${P}37/${ICA}2 PP.01.04.crt" "${P}37/${ICA}1 PP.01.04.crt" "${P}37/${ROOT}CP.01.01.crt"
+run_test 38 0 "${P}38/${END}PP.01.05.crt" "${P}38/${ICA}2 PP.01.05.crt" "${P}38/${ICA}1 PP.01.05.crt" "${P}38/${ROOT}CP.01.01.crt"
+run_test 39 0 "${P}39/${END}PP.01.06.crt" "${P}39/${ICA}3 PP.01.06.crt" "${P}39/${ICA}2 PP.01.06.crt" "${P}39/${ICA}1 PP.01.06.crt" "${P}39/${ROOT}CP.01.01.crt"
+run_test 40 0 "${P}40/${END}PP.01.07.crt" "${P}40/${ICA}3 PP.01.07.crt" "${P}40/${ICA}2 PP.01.07.crt" "${P}40/${ICA}1 PP.01.07.crt" "${P}40/${ROOT}CP.01.01.crt"
+run_test 41 0 "${P}41/${END}PP.01.08.crt" "${P}41/${ICA}3 PP.01.08.crt" "${P}41/${ICA}2 PP.01.08.crt" "${P}41/${ICA}1 PP.01.08.crt" "${P}41/${ROOT}CP.01.01.crt"
+run_test 42 0 "${P}42/${END}PP.01.09.crt" "${P}42/${ICA}4 PP.01.09.crt" "${P}42/${ICA}3 PP.01.09.crt" "${P}42/${ICA}2 PP.01.09.crt" "${P}42/${ICA}1 PP.01.09.crt" "${P}42/${ROOT}CP.01.01.crt"
+run_test 43 0 "${P}43/${END}PP.06.01.crt" "${P}43/${ICA}4 PP.06.01.crt" "${P}43/${ICA}3 PP.06.01.crt" "${P}43/${ICA}2 PP.06.01.crt" "${P}43/${ICA}1 PP.06.01.crt" "${P}43/${ROOT}CP.01.01.crt"
+run_test 44 0 "${P}44/${END}PP.06.02.crt" "${P}44/${ICA}4 PP.06.02.crt" "${P}44/${ICA}3 PP.06.02.crt" "${P}44/${ICA}2 PP.06.02.crt" "${P}44/${ICA}1 PP.06.02.crt" "${P}44/${ROOT}CP.01.01.crt"
+run_test 45 0 "${P}45/${END}PP.06.03.crt" "${P}45/${ICA}4 PP.06.03.crt" "${P}45/${ICA}3 PP.06.03.crt" "${P}45/${ICA}2 PP.06.03.crt" "${P}45/${ICA}1 PP.06.03.crt" "${P}45/${ROOT}CP.01.01.crt"
+run_test 46 0 "${P}46/${END}PP.06.04.crt" "${P}46/${ICA}4 PP.06.04.crt" "${P}46/${ICA}3 PP.06.04.crt" "${P}46/${ICA}2 PP.06.04.crt" "${P}46/${ICA}1 PP.06.04.crt" "${P}46/${ROOT}CP.01.01.crt"
+run_test 47 0 "${P}47/${END}PP.06.05.crt" "${P}47/${ICA}4 PP.06.05.crt" "${P}47/${ICA}3 PP.06.05.crt" "${P}47/${ICA}2 PP.06.05.crt" "${P}47/${ICA}1 PP.06.05.crt" "${P}47/${ROOT}CP.01.01.crt"
+run_test 48 0 "${P}48/${END}PP.08.01.crt" "${P}48/${ICA}PP.08.01.crt" "${P}48/${ROOT}CP.01.01.crt"
+run_test 49 0 "${P}49/${END}PP.08.02.crt" "${P}49/${ICA}PP.08.02.crt" "${P}49/${ROOT}CP.01.01.crt"
+run_test 50 0 "${P}50/${END}PP.08.03.crt" "${P}50/${ICA}PP.08.03.crt" "${P}50/${ROOT}CP.01.01.crt"
+run_test 51 0 "${P}51/${END}PP.08.04.crt" "${P}51/${ICA}PP.08.04.crt" "${P}51/${ROOT}CP.01.01.crt"
+run_test 52 0 "${P}52/${END}PP.08.05.crt" "${P}52/${ICA}PP.08.05.crt" "${P}52/${ROOT}CP.01.01.crt"
+run_test 53 0 "${P}53/${END}PP.08.06.crt" "${P}53/${ICA}PP.08.06.crt" "${P}53/${ROOT}CP.01.01.crt"
+run_test 54 1 "${P}54/${END}PL.01.01.crt" "${P}54/${ICA}2 PL.01.01.crt" "${P}54/${ICA}1 PL.01.01.crt" "${P}54/${ROOT}CP.01.01.crt"
+run_test 55 1 "${P}55/${END}PL.01.02.crt" "${P}55/${ICA}2 PL.01.02.crt" "${P}55/${ICA}1 PL.01.02.crt" "${P}55/${ROOT}CP.01.01.crt"
+run_test 56 0 "${P}56/${END}PL.01.03.crt" "${P}56/${ICA}PL.01.03.crt" "${P}56/${ROOT}CP.01.01.crt"
+run_test 57 0 "${P}57/${END}PL.01.04.crt" "${P}57/${ICA}PL.01.04.crt" "${P}57/${ROOT}CP.01.01.crt"
+run_test 58 1 "${P}58/${END}PL.01.05.crt" "${P}58/${ICA}3 PL.01.05.crt" "${P}58/${ICA}2 PL.01.05.crt" "${P}58/${ICA}1 PL.01.05.crt" "${P}58/${ROOT}CP.01.01.crt"
+run_test 59 1 "${P}59/${END}PL.01.06.crt" "${P}59/${ICA}3 PL.01.06.crt" "${P}59/${ICA}2 PL.01.06.crt" "${P}59/${ICA}1 PL.01.06.crt" "${P}59/${ROOT}CP.01.01.crt"
+run_test 60 1 "${P}60/${END}PL.01.07.crt" "${P}60/${ICA}4 PL.01.07.crt" "${P}60/${ICA}3 PL.01.07.crt" "${P}60/${ICA}2 PL.01.07.crt" "${P}60/${ICA}1 PL.01.07.crt" "${P}60/${ROOT}CP.01.01.crt"
+run_test 61 1 "${P}61/${END}PL.01.08.crt" "${P}61/${ICA}4 PL.01.08.crt" "${P}61/${ICA}3 PL.01.08.crt" "${P}61/${ICA}2 PL.01.08.crt" "${P}61/${ICA}1 PL.01.08.crt" "${P}61/${ROOT}CP.01.01.crt"
+run_test 62 0 "${P}62/${END}PL.01.09.crt" "${P}62/${ICA}4 PL.01.09.crt" "${P}62/${ICA}3 PL.01.09.crt" "${P}62/${ICA}2 PL.01.09.crt" "${P}62/${ICA}1 PL.01.09.crt" "${P}62/${ROOT}CP.01.01.crt"
+run_test 63 0 "${P}63/${END}PL.01.10.crt" "${P}63/${ICA}4 PL.01.10.crt" "${P}63/${ICA}3 PL.01.10.crt" "${P}63/${ICA}2 PL.01.10.crt" "${P}63/${ICA}1 PL.01.10.crt" "${P}63/${ROOT}CP.01.01.crt"
+
+
+echo "Successful tests:$SUCCESS"
+echo "Failed tests:$FAILURE"
diff --git a/contrib/wpa/tests/test_x509v3_nist2.sh b/contrib/wpa/tests/test_x509v3_nist2.sh
new file mode 100755
index 000000000000..ec34a8b37a2a
--- /dev/null
+++ b/contrib/wpa/tests/test_x509v3_nist2.sh
@@ -0,0 +1,177 @@
+#!/bin/bash
+
+# Public Key Interoperability Test Suite (PKITS)
+# http://csrc.nist.gov/pki/testing/x509paths.html
+# http://csrc.nist.gov/groups/ST/crypto_apps_infra/documents/PKITS_data.zip
+
+if [ -z "$1" ]; then
+ echo "usage: $0 <path to root test directory>"
+ exit 1
+fi
+
+TESTS=$1
+
+if [ ! -d $TESTS ]; then
+ echo "Not a directory: $TESTS"
+ exit 1
+fi
+
+X509TEST="$PWD/test-x509v3 -v"
+TMPOUT="$PWD/test_x509v3_nist2.out"
+
+# TODO: add support for validating CRLs
+
+SUCCESS=""
+FAILURE=""
+
+function run_test
+{
+ NUM=$1
+ RES=$2
+ shift 2
+ $X509TEST "$@" TrustAnchorRootCertificate.crt > $TMPOUT.$NUM
+ VALRES=$?
+ OK=0
+ if [ $RES -eq 0 ]; then
+ # expecting success
+ if [ $VALRES -eq 0 ]; then
+ OK=1
+ else
+ echo "$NUM failed - expected validation success"
+ OK=0
+ fi
+ else
+ # expecting failure
+ if [ $VALRES -eq 0 ]; then
+ echo "$NUM failed - expected validation failure"
+ OK=0
+ else
+ REASON=`grep "Certificate chain validation failed: " $TMPOUT.$NUM`
+ if [ $? -eq 0 ]; then
+ REASONNUM=`echo "$REASON" | colrm 1 37`
+ if [ $REASONNUM -eq $RES ]; then
+ OK=1
+ else
+ echo "$NUM failed - expected validation result $RES; result was $REASONNUM"
+ OK=0
+ fi
+ else
+ if [ $RES -eq -1 ]; then
+ if grep -q "Failed to parse X.509 certificate" $TMPOUT.$NUM; then
+ OK=1
+ else
+ echo "$NUM failed - expected parsing failure; other type of error detected"
+ OK=0
+ fi
+ else
+ echo "$NUM failed - expected validation failure; other type of error detected"
+ OK=0
+ fi
+ fi
+ fi
+ fi
+ if [ $OK -eq 1 ]; then
+ rm $TMPOUT.$NUM
+ SUCCESS="$SUCCESS $NUM"
+ else
+ FAILURE="$FAILURE $NUM"
+ fi
+}
+
+pushd $TESTS/certs
+
+run_test 4.1.1 0 ValidCertificatePathTest1EE.crt GoodCACert.crt
+run_test 4.1.2 1 InvalidCASignatureTest2EE.crt BadSignedCACert.crt
+run_test 4.1.3 1 InvalidEESignatureTest3EE.crt GoodCACert.crt
+
+run_test 4.2.1 4 InvalidCAnotBeforeDateTest1EE.crt BadnotBeforeDateCACert.crt
+run_test 4.2.2 4 InvalidEEnotBeforeDateTest2EE.crt GoodCACert.crt
+run_test 4.2.3 0 Validpre2000UTCnotBeforeDateTest3EE.crt GoodCACert.crt
+run_test 4.2.4 0 ValidGeneralizedTimenotBeforeDateTest4EE.crt GoodCACert.crt
+run_test 4.2.5 4 InvalidCAnotAfterDateTest5EE.crt BadnotAfterDateCACert.crt
+run_test 4.2.6 4 InvalidEEnotAfterDateTest6EE.crt GoodCACert.crt
+run_test 4.2.7 4 Invalidpre2000UTCEEnotAfterDateTest7EE.crt GoodCACert.crt
+run_test 4.2.8 0 ValidGeneralizedTimenotAfterDateTest8EE.crt GoodCACert.crt
+
+run_test 4.3.1 5 InvalidNameChainingTest1EE.crt GoodCACert.crt
+run_test 4.3.2 5 InvalidNameChainingOrderTest2EE.crt NameOrderingCACert.crt
+run_test 4.3.3 0 ValidNameChainingWhitespaceTest3EE.crt GoodCACert.crt
+run_test 4.3.4 0 ValidNameChainingWhitespaceTest4EE.crt GoodCACert.crt
+run_test 4.3.5 0 ValidNameChainingCapitalizationTest5EE.crt GoodCACert.crt
+run_test 4.3.6 0 ValidNameUIDsTest6EE.crt UIDCACert.crt
+run_test 4.3.7 0 ValidRFC3280MandatoryAttributeTypesTest7EE.crt RFC3280MandatoryAttributeTypesCACert.crt
+run_test 4.3.8 0 ValidRFC3280OptionalAttributeTypesTest8EE.crt RFC3280OptionalAttributeTypesCACert.crt
+run_test 4.3.9 0 ValidUTF8StringEncodedNamesTest9EE.crt UTF8StringEncodedNamesCACert.crt
+run_test 4.3.10 0 ValidRolloverfromPrintableStringtoUTF8StringTest10EE.crt RolloverfromPrintableStringtoUTF8StringCACert.crt
+run_test 4.3.11 0 ValidUTF8StringCaseInsensitiveMatchTest11EE.crt UTF8StringCaseInsensitiveMatchCACert.crt
+
+run_test 4.4.1 1 InvalidMissingCRLTest1EE.crt NoCRLCACert.crt
+# skip rest of 4.4.x tests since CRLs are not yet supported
+
+run_test 4.5.1 0 ValidBasicSelfIssuedOldWithNewTest1EE.crt BasicSelfIssuedNewKeyOldWithNewCACert.crt BasicSelfIssuedNewKeyCACert.crt
+run_test 4.5.2 3 InvalidBasicSelfIssuedOldWithNewTest2EE.crt BasicSelfIssuedNewKeyOldWithNewCACert.crt BasicSelfIssuedNewKeyCACert.crt
+run_test 4.5.3 0 ValidBasicSelfIssuedNewWithOldTest3EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt
+run_test 4.5.4 0 ValidBasicSelfIssuedNewWithOldTest4EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt
+run_test 4.5.5 3 InvalidBasicSelfIssuedNewWithOldTest5EE.crt BasicSelfIssuedOldKeyNewWithOldCACert.crt BasicSelfIssuedOldKeyCACert.crt
+run_test 4.5.6 0 ValidBasicSelfIssuedCRLSigningKeyTest6EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt
+run_test 4.5.7 3 InvalidBasicSelfIssuedCRLSigningKeyTest7EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt
+run_test 4.5.8 1 InvalidBasicSelfIssuedCRLSigningKeyTest8EE.crt BasicSelfIssuedCRLSigningKeyCRLCert.crt BasicSelfIssuedCRLSigningKeyCACert.crt
+
+run_test 4.6.1 1 InvalidMissingbasicConstraintsTest1EE.crt MissingbasicConstraintsCACert.crt
+run_test 4.6.2 1 InvalidcAFalseTest2EE.crt basicConstraintsCriticalcAFalseCACert.crt
+run_test 4.6.3 1 InvalidcAFalseTest3EE.crt basicConstraintsNotCriticalcAFalseCACert.crt
+run_test 4.6.4 0 ValidbasicConstraintsNotCriticalTest4EE.crt basicConstraintsNotCriticalCACert.crt
+run_test 4.6.5 1 InvalidpathLenConstraintTest5EE.crt pathLenConstraint0subCACert.crt pathLenConstraint0CACert.crt
+run_test 4.6.6 1 InvalidpathLenConstraintTest6EE.crt pathLenConstraint0subCACert.crt pathLenConstraint0CACert.crt
+run_test 4.6.7 0 ValidpathLenConstraintTest7EE.crt pathLenConstraint0CACert.crt
+run_test 4.6.8 0 ValidpathLenConstraintTest8EE.crt pathLenConstraint0CACert.crt
+run_test 4.6.9 1 InvalidpathLenConstraintTest9EE.crt pathLenConstraint6subsubCA00Cert.crt pathLenConstraint6subCA0Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.10 1 InvalidpathLenConstraintTest10EE.crt pathLenConstraint6subsubCA00Cert.crt pathLenConstraint6subCA0Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.11 1 InvalidpathLenConstraintTest11EE.crt pathLenConstraint6subsubsubCA11XCert.crt pathLenConstraint6subsubCA11Cert.crt pathLenConstraint6subCA1Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.12 1 InvalidpathLenConstraintTest12EE.crt pathLenConstraint6subsubsubCA11XCert.crt pathLenConstraint6subsubCA11Cert.crt pathLenConstraint6subCA1Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.13 0 ValidpathLenConstraintTest13EE.crt pathLenConstraint6subsubsubCA41XCert.crt pathLenConstraint6subsubCA41Cert.crt pathLenConstraint6subCA4Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.14 0 ValidpathLenConstraintTest14EE.crt pathLenConstraint6subsubsubCA41XCert.crt pathLenConstraint6subsubCA41Cert.crt pathLenConstraint6subCA4Cert.crt pathLenConstraint6CACert.crt
+run_test 4.6.15 0 ValidSelfIssuedpathLenConstraintTest15EE.crt pathLenConstraint0SelfIssuedCACert.crt pathLenConstraint0CACert.crt
+run_test 4.6.16 1 InvalidSelfIssuedpathLenConstraintTest16EE.crt pathLenConstraint0subCA2Cert.crt pathLenConstraint0SelfIssuedCACert.crt pathLenConstraint0CACert.crt
+run_test 4.6.17 0 ValidSelfIssuedpathLenConstraintTest17EE.crt pathLenConstraint1SelfIssuedsubCACert.crt pathLenConstraint1subCACert.crt pathLenConstraint1SelfIssuedCACert.crt pathLenConstraint1CACert.crt
+
+run_test 4.7.1 1 InvalidkeyUsageCriticalkeyCertSignFalseTest1EE.crt keyUsageCriticalkeyCertSignFalseCACert.crt
+run_test 4.7.2 1 InvalidkeyUsageNotCriticalkeyCertSignFalseTest2EE.crt keyUsageNotCriticalkeyCertSignFalseCACert.crt
+run_test 4.7.3 0 ValidkeyUsageNotCriticalTest3EE.crt keyUsageNotCriticalCACert.crt
+run_test 4.7.4 1 InvalidkeyUsageCriticalcRLSignFalseTest4EE.crt keyUsageCriticalcRLSignFalseCACert.crt
+run_test 4.7.5 1 InvalidkeyUsageNotCriticalcRLSignFalseTest5EE.crt keyUsageNotCriticalcRLSignFalseCACert.crt
+
+run_test 4.8.1 0 ValidCertificatePathTest1EE.crt GoodCACert.crt
+run_test 4.8.2 0 AllCertificatesNoPoliciesTest2EE.crt NoPoliciesCACert.crt
+run_test 4.8.3 0 DifferentPoliciesTest3EE.crt PoliciesP2subCACert.crt GoodCACert.crt
+run_test 4.8.4 0 DifferentPoliciesTest4EE.crt GoodsubCACert.crt GoodCACert.crt
+run_test 4.8.5 0 DifferentPoliciesTest5EE.crt PoliciesP2subCA2Cert.crt GoodCACert.crt
+run_test 4.8.6 0 OverlappingPoliciesTest6EE.crt PoliciesP1234subsubCAP123P12Cert.crt PoliciesP1234subCAP123Cert.crt PoliciesP1234CACert.crt
+run_test 4.8.7 0 DifferentPoliciesTest7EE.crt PoliciesP123subsubCAP12P1Cert.crt PoliciesP123subCAP12Cert.crt PoliciesP123CACert.crt
+run_test 4.8.8 0 DifferentPoliciesTest8EE.crt PoliciesP12subsubCAP1P2Cert.crt PoliciesP12subCAP1Cert.crt PoliciesP12CACert.crt
+run_test 4.8.9 0 DifferentPoliciesTest9EE.crt PoliciesP123subsubsubCAP12P2P1Cert.crt PoliciesP123subsubCAP12P2Cert.crt PoliciesP123subCAP12Cert.crt PoliciesP123CACert.crt
+run_test 4.8.10 0 AllCertificatesSamePoliciesTest10EE.crt PoliciesP12CACert.crt
+run_test 4.8.11 0 AllCertificatesanyPolicyTest11EE.crt anyPolicyCACert.crt
+run_test 4.8.12 0 DifferentPoliciesTest12EE.crt PoliciesP3CACert.crt
+run_test 4.8.13 0 AllCertificatesSamePoliciesTest13EE.crt PoliciesP123CACert.crt
+run_test 4.8.14 0 AnyPolicyTest14EE.crt anyPolicyCACert.crt
+run_test 4.8.15 0 UserNoticeQualifierTest15EE.crt
+run_test 4.8.16 0 UserNoticeQualifierTest16EE.crt GoodCACert.crt
+run_test 4.8.17 0 UserNoticeQualifierTest17EE.crt GoodCACert.crt
+run_test 4.8.18 0 UserNoticeQualifierTest18EE.crt PoliciesP12CACert.crt
+run_test 4.8.19 0 UserNoticeQualifierTest19EE.crt TrustAnchorRootCertificate.crt
+run_test 4.8.20 0 CPSPointerQualifierTest20EE.crt GoodCACert.crt
+
+run_test 4.16.1 0 ValidUnknownNotCriticalCertificateExtensionTest1EE.crt
+run_test 4.16.2 -1 InvalidUnknownCriticalCertificateExtensionTest2EE.crt
+
+if false; then
+# DSA tests
+run_test 4.1.4 0 ValidDSASignaturesTest4EE.crt DSACACert.crt
+fi
+
+popd
+
+
+echo "Successful tests:$SUCCESS"
+echo "Failed tests:$FAILURE"
diff --git a/contrib/wpa/wlantest/.gitignore b/contrib/wpa/wlantest/.gitignore
new file mode 100644
index 000000000000..7ffabe601bb8
--- /dev/null
+++ b/contrib/wpa/wlantest/.gitignore
@@ -0,0 +1,4 @@
+libwlantest.a
+test_vectors
+wlantest
+wlantest_cli
diff --git a/contrib/wpa/wlantest/Makefile b/contrib/wpa/wlantest/Makefile
new file mode 100644
index 000000000000..0045020d4130
--- /dev/null
+++ b/contrib/wpa/wlantest/Makefile
@@ -0,0 +1,87 @@
+ALL=wlantest wlantest_cli test_vectors
+
+include ../src/build.rules
+
+UNAME := $(shell uname -s)
+
+CFLAGS += -I.
+CFLAGS += -I../src
+CFLAGS += -I../src/utils
+
+ifneq ($(UNAME),Darwin)
+# glibc < 2.17 needs -lrt for clock_gettime()
+LIBS += -lrt
+endif
+
+OWN_LIBS += ../src/utils/libutils.a
+OWN_LIBS += ../src/crypto/libcrypto.a
+
+CFLAGS += -DCONFIG_OCV
+CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DCONFIG_DEBUG_FILE
+CFLAGS += -DCONFIG_FILS
+CFLAGS += -DCONFIG_SAE
+CFLAGS += -DCONFIG_OWE
+CFLAGS += -DCONFIG_DPP
+CFLAGS += -DCONFIG_SHA384
+CFLAGS += -DCONFIG_PASN
+
+OBJS += ../src/common/ieee802_11_common.o
+OBJS += ../src/common/wpa_common.o
+OBJS += ../src/radius/radius.o
+OBJS += ../src/rsn_supp/wpa_ie.o
+
+OBJS += wlantest.o
+OBJS += readpcap.o
+OBJS += writepcap.o
+OBJS += monitor.o
+OBJS += process.o
+OBJS += wired.o
+OBJS += rx_mgmt.o
+OBJS += rx_data.o
+OBJS += rx_eapol.o
+OBJS += rx_ip.o
+OBJS += rx_tdls.o
+OBJS += bss.o
+OBJS += sta.o
+OBJS += ccmp.o
+OBJS += tkip.o
+OBJS += ctrl.o
+OBJS += inject.o
+OBJS += wep.o
+OBJS += bip.o
+OBJS += gcmp.o
+
+LIBS += -lpcap
+
+TOBJS += test_vectors.o
+TOBJS += ccmp.o
+TOBJS += tkip.o
+TOBJS += wep.o
+TOBJS += bip.o
+TOBJS += gcmp.o
+
+
+OBJS_cli = wlantest_cli.o
+
+_OBJS_VAR := OBJS
+include ../src/objs.mk
+_OBJS_VAR := TOBJS
+include ../src/objs.mk
+_OBJS_VAR := OBJS_cli
+include ../src/objs.mk
+_OBJS_VAR := OWN_LIBS
+include ../src/objs.mk
+
+wlantest: $(OBJS) $(OWN_LIBS)
+ $(LDO) $(LDFLAGS) -o wlantest $(OBJS) $(OWN_LIBS) $(LIBS)
+
+wlantest_cli: $(OBJS_cli) $(OWN_LIBS)
+ $(LDO) $(LDFLAGS) -o wlantest_cli $(OBJS_cli) $(OWN_LIBS) $(LIBS)
+
+test_vectors: $(TOBJS) $(OWN_LIBS)
+ $(LDO) $(LDFLAGS) -o test_vectors $(TOBJS) $(OWN_LIBS) $(LIBS)
+
+clean: common-clean
+ rm -f core *~
diff --git a/contrib/wpa/wlantest/bip.c b/contrib/wpa/wlantest/bip.c
new file mode 100644
index 000000000000..c73a15c98aab
--- /dev/null
+++ b/contrib/wpa/wlantest/bip.c
@@ -0,0 +1,133 @@
+/*
+ * BIP
+ * Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi>
+ *
+ * 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/ieee802_11_defs.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+u8 * bip_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+ u8 *ipn, int keyid, size_t *prot_len)
+{
+ u8 *prot, *pos, *buf;
+ u8 mic[16];
+ u16 fc;
+ struct ieee80211_hdr *hdr;
+ size_t plen;
+
+ plen = len + (igtk_len == 32 ? 26 : 18);
+ prot = os_malloc(plen);
+ if (prot == NULL)
+ return NULL;
+ os_memcpy(prot, frame, len);
+ pos = prot + len;
+ *pos++ = WLAN_EID_MMIE;
+ *pos++ = igtk_len == 32 ? 24 : 16;
+ WPA_PUT_LE16(pos, keyid);
+ pos += 2;
+ os_memcpy(pos, ipn, 6);
+ pos += 6;
+ os_memset(pos, 0, igtk_len == 32 ? 16 : 8); /* MIC */
+
+ buf = os_malloc(plen + 20 - 24);
+ if (buf == NULL) {
+ os_free(prot);
+ return NULL;
+ }
+
+ /* BIP AAD: FC(masked) A1 A2 A3 */
+ hdr = (struct ieee80211_hdr *) frame;
+ fc = le_to_host16(hdr->frame_control);
+ fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+ WPA_PUT_LE16(buf, fc);
+ os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
+ os_memcpy(buf + 20, prot + 24, plen - 24);
+ wpa_hexdump(MSG_MSGDUMP, "BIP: AAD|Body(masked)", buf, plen + 20 - 24);
+ /* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */
+ if (omac1_aes_128(igtk, buf, plen + 20 - 24, mic) < 0) {
+ os_free(prot);
+ os_free(buf);
+ return NULL;
+ }
+ os_free(buf);
+
+ os_memcpy(pos, mic, igtk_len == 32 ? 16 : 8);
+ wpa_hexdump(MSG_DEBUG, "BIP MMIE MIC", pos, igtk_len == 32 ? 16 : 8);
+
+ *prot_len = plen;
+ return prot;
+}
+
+
+u8 * bip_gmac_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+ u8 *ipn, int keyid, size_t *prot_len)
+{
+ u8 *prot, *pos, *buf;
+ u16 fc;
+ struct ieee80211_hdr *hdr;
+ size_t plen;
+ u8 nonce[12], *npos;
+
+ plen = len + 26;
+ prot = os_malloc(plen);
+ if (prot == NULL)
+ return NULL;
+ os_memcpy(prot, frame, len);
+ pos = prot + len;
+ *pos++ = WLAN_EID_MMIE;
+ *pos++ = 24;
+ WPA_PUT_LE16(pos, keyid);
+ pos += 2;
+ os_memcpy(pos, ipn, 6);
+ pos += 6;
+ os_memset(pos, 0, 16); /* MIC */
+
+ buf = os_malloc(plen + 20 - 24);
+ if (buf == NULL) {
+ os_free(prot);
+ return NULL;
+ }
+
+ /* BIP AAD: FC(masked) A1 A2 A3 */
+ hdr = (struct ieee80211_hdr *) frame;
+ fc = le_to_host16(hdr->frame_control);
+ fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+ WPA_PUT_LE16(buf, fc);
+ os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
+ os_memcpy(buf + 20, prot + 24, plen - 24);
+ wpa_hexdump(MSG_MSGDUMP, "BIP-GMAC: AAD|Body(masked)",
+ buf, plen + 20 - 24);
+
+ /* Nonce: A2 | IPN */
+ os_memcpy(nonce, hdr->addr2, ETH_ALEN);
+ npos = nonce + ETH_ALEN;
+ *npos++ = ipn[5];
+ *npos++ = ipn[4];
+ *npos++ = ipn[3];
+ *npos++ = ipn[2];
+ *npos++ = ipn[1];
+ *npos++ = ipn[0];
+ wpa_hexdump(MSG_EXCESSIVE, "BIP-GMAC: Nonce", nonce, sizeof(nonce));
+
+ /* MIC = AES-GMAC(AAD || Frame Body(masked)) */
+ if (aes_gmac(igtk, igtk_len, nonce, sizeof(nonce),
+ buf, plen + 20 - 24, pos) < 0) {
+ os_free(prot);
+ os_free(buf);
+ return NULL;
+ }
+ os_free(buf);
+
+ wpa_hexdump(MSG_DEBUG, "BIP-GMAC MMIE MIC", pos, 16);
+
+ *prot_len = plen;
+ return prot;
+}
diff --git a/contrib/wpa/wlantest/bss.c b/contrib/wpa/wlantest/bss.c
new file mode 100644
index 000000000000..1834aecba199
--- /dev/null
+++ b/contrib/wpa/wlantest/bss.c
@@ -0,0 +1,373 @@
+/*
+ * BSS list
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * 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/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "crypto/sha1.h"
+#include "wlantest.h"
+
+
+struct wlantest_bss * bss_find(struct wlantest *wt, const u8 *bssid)
+{
+ struct wlantest_bss *bss;
+
+ dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
+ if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0)
+ return bss;
+ }
+
+ return NULL;
+}
+
+
+struct wlantest_bss * bss_get(struct wlantest *wt, const u8 *bssid)
+{
+ struct wlantest_bss *bss;
+
+ if (bssid[0] & 0x01)
+ return NULL; /* Skip group addressed frames */
+
+ bss = bss_find(wt, bssid);
+ if (bss)
+ return bss;
+
+ bss = os_zalloc(sizeof(*bss));
+ if (bss == NULL)
+ return NULL;
+ dl_list_init(&bss->sta);
+ dl_list_init(&bss->pmk);
+ dl_list_init(&bss->tdls);
+ os_memcpy(bss->bssid, bssid, ETH_ALEN);
+ dl_list_add(&wt->bss, &bss->list);
+ wpa_printf(MSG_DEBUG, "Discovered new BSS - " MACSTR,
+ MAC2STR(bss->bssid));
+ return bss;
+}
+
+
+void pmk_deinit(struct wlantest_pmk *pmk)
+{
+ dl_list_del(&pmk->list);
+ os_free(pmk);
+}
+
+
+void tdls_deinit(struct wlantest_tdls *tdls)
+{
+ dl_list_del(&tdls->list);
+ os_free(tdls);
+}
+
+
+void bss_deinit(struct wlantest_bss *bss)
+{
+ struct wlantest_sta *sta, *n;
+ struct wlantest_pmk *pmk, *np;
+ struct wlantest_tdls *tdls, *nt;
+ dl_list_for_each_safe(sta, n, &bss->sta, struct wlantest_sta, list)
+ sta_deinit(sta);
+ dl_list_for_each_safe(pmk, np, &bss->pmk, struct wlantest_pmk, list)
+ pmk_deinit(pmk);
+ dl_list_for_each_safe(tdls, nt, &bss->tdls, struct wlantest_tdls, list)
+ tdls_deinit(tdls);
+ dl_list_del(&bss->list);
+ os_free(bss);
+}
+
+
+int bss_add_pmk_from_passphrase(struct wlantest_bss *bss,
+ const char *passphrase)
+{
+ struct wlantest_pmk *pmk;
+
+ pmk = os_zalloc(sizeof(*pmk));
+ if (pmk == NULL)
+ return -1;
+ if (pbkdf2_sha1(passphrase, bss->ssid, bss->ssid_len, 4096,
+ pmk->pmk, PMK_LEN) < 0) {
+ os_free(pmk);
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "Add possible PMK for BSSID " MACSTR
+ " based on passphrase '%s'",
+ MAC2STR(bss->bssid), passphrase);
+ wpa_hexdump(MSG_DEBUG, "Possible PMK", pmk->pmk, PMK_LEN);
+ dl_list_add(&bss->pmk, &pmk->list);
+
+ return 0;
+}
+
+
+static void bss_add_pmk(struct wlantest *wt, struct wlantest_bss *bss)
+{
+ struct wlantest_passphrase *p;
+
+ dl_list_for_each(p, &wt->passphrase, struct wlantest_passphrase, list)
+ {
+ if (!is_zero_ether_addr(p->bssid) &&
+ os_memcmp(p->bssid, bss->bssid, ETH_ALEN) != 0)
+ continue;
+ if (p->ssid_len &&
+ (p->ssid_len != bss->ssid_len ||
+ os_memcmp(p->ssid, bss->ssid, p->ssid_len) != 0))
+ continue;
+
+ if (bss_add_pmk_from_passphrase(bss, p->passphrase) < 0)
+ break;
+ }
+}
+
+
+void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
+ struct ieee802_11_elems *elems, int beacon)
+{
+ struct wpa_ie_data data;
+ int update = 0;
+
+ if (bss->capab_info != bss->prev_capab_info)
+ update = 1;
+
+ if (beacon && (!elems->ssid || elems->ssid_len > 32)) {
+ wpa_printf(MSG_INFO,
+ "Invalid or missing SSID in a %s frame for " MACSTR,
+ beacon == 1 ? "Beacon" : "Probe Response",
+ MAC2STR(bss->bssid));
+ bss->parse_error_reported = 1;
+ return;
+ }
+
+ if (beacon &&
+ (bss->ssid_len != elems->ssid_len ||
+ os_memcmp(bss->ssid, elems->ssid, bss->ssid_len) != 0)) {
+ wpa_printf(MSG_DEBUG, "Store SSID '%s' for BSSID " MACSTR,
+ wpa_ssid_txt(elems->ssid, elems->ssid_len),
+ MAC2STR(bss->bssid));
+ os_memcpy(bss->ssid, elems->ssid, elems->ssid_len);
+ bss->ssid_len = elems->ssid_len;
+ bss_add_pmk(wt, bss);
+ }
+
+ if (elems->osen == NULL) {
+ if (bss->osenie[0]) {
+ add_note(wt, MSG_INFO, "BSS " MACSTR
+ " - OSEN IE removed", MAC2STR(bss->bssid));
+ bss->rsnie[0] = 0;
+ update = 1;
+ }
+ } else {
+ if (bss->osenie[0] == 0 ||
+ os_memcmp(bss->osenie, elems->osen - 2,
+ elems->osen_len + 2) != 0) {
+ wpa_printf(MSG_INFO, "BSS " MACSTR " - OSEN IE "
+ "stored", MAC2STR(bss->bssid));
+ wpa_hexdump(MSG_DEBUG, "OSEN IE", elems->osen - 2,
+ elems->osen_len + 2);
+ update = 1;
+ }
+ os_memcpy(bss->osenie, elems->osen - 2,
+ elems->osen_len + 2);
+ }
+
+ /* S1G does not include RSNE in beacon, so only clear it from
+ * Probe Response frames. Note this assumes short beacons were dropped
+ * due to missing SSID above.
+ */
+ if (!elems->rsn_ie && (!elems->s1g_capab || beacon != 1)) {
+ if (bss->rsnie[0]) {
+ add_note(wt, MSG_INFO, "BSS " MACSTR
+ " - RSN IE removed", MAC2STR(bss->bssid));
+ bss->rsnie[0] = 0;
+ update = 1;
+ }
+ } else if (elems->rsn_ie) {
+ if (bss->rsnie[0] == 0 ||
+ os_memcmp(bss->rsnie, elems->rsn_ie - 2,
+ elems->rsn_ie_len + 2) != 0) {
+ wpa_printf(MSG_INFO, "BSS " MACSTR " - RSN IE "
+ "stored", MAC2STR(bss->bssid));
+ wpa_hexdump(MSG_DEBUG, "RSN IE", elems->rsn_ie - 2,
+ elems->rsn_ie_len + 2);
+ update = 1;
+ }
+ os_memcpy(bss->rsnie, elems->rsn_ie - 2,
+ elems->rsn_ie_len + 2);
+ }
+
+ if (elems->wpa_ie == NULL) {
+ if (bss->wpaie[0]) {
+ add_note(wt, MSG_INFO, "BSS " MACSTR
+ " - WPA IE removed", MAC2STR(bss->bssid));
+ bss->wpaie[0] = 0;
+ update = 1;
+ }
+ } else {
+ if (bss->wpaie[0] == 0 ||
+ os_memcmp(bss->wpaie, elems->wpa_ie - 2,
+ elems->wpa_ie_len + 2) != 0) {
+ wpa_printf(MSG_INFO, "BSS " MACSTR " - WPA IE "
+ "stored", MAC2STR(bss->bssid));
+ wpa_hexdump(MSG_DEBUG, "WPA IE", elems->wpa_ie - 2,
+ elems->wpa_ie_len + 2);
+ update = 1;
+ }
+ os_memcpy(bss->wpaie, elems->wpa_ie - 2,
+ elems->wpa_ie_len + 2);
+ }
+
+ if (elems->mdie)
+ os_memcpy(bss->mdid, elems->mdie, 2);
+
+ bss->mesh = elems->mesh_id != NULL;
+
+ if (!update)
+ return;
+
+ if (beacon == 1)
+ bss->beacon_seen = 1;
+ else if (beacon == 2)
+ bss->proberesp_seen = 1;
+ bss->ies_set = 1;
+ bss->prev_capab_info = bss->capab_info;
+ bss->proto = 0;
+ bss->pairwise_cipher = 0;
+ bss->group_cipher = 0;
+ bss->key_mgmt = 0;
+ bss->rsn_capab = 0;
+ bss->mgmt_group_cipher = 0;
+
+ if (bss->wpaie[0]) {
+ if (wpa_parse_wpa_ie_wpa(bss->wpaie, 2 + bss->wpaie[1], &data)
+ < 0) {
+ add_note(wt, MSG_INFO, "Failed to parse WPA IE from "
+ MACSTR, MAC2STR(bss->bssid));
+ } else {
+ bss->proto |= data.proto;
+ bss->pairwise_cipher |= data.pairwise_cipher;
+ bss->group_cipher |= data.group_cipher;
+ bss->key_mgmt |= data.key_mgmt;
+ bss->rsn_capab = data.capabilities;
+ bss->mgmt_group_cipher |= data.mgmt_group_cipher;
+ }
+ }
+
+ if (bss->rsnie[0]) {
+ if (wpa_parse_wpa_ie_rsn(bss->rsnie, 2 + bss->rsnie[1], &data)
+ < 0) {
+ add_note(wt, MSG_INFO, "Failed to parse RSN IE from "
+ MACSTR, MAC2STR(bss->bssid));
+ } else {
+ bss->proto |= data.proto;
+ bss->pairwise_cipher |= data.pairwise_cipher;
+ bss->group_cipher |= data.group_cipher;
+ bss->key_mgmt |= data.key_mgmt;
+ bss->rsn_capab = data.capabilities;
+ bss->mgmt_group_cipher |= data.mgmt_group_cipher;
+ }
+ }
+
+ if (bss->osenie[0]) {
+ bss->proto |= WPA_PROTO_OSEN;
+ bss->pairwise_cipher |= WPA_CIPHER_CCMP;
+ bss->group_cipher |= WPA_CIPHER_CCMP;
+ bss->key_mgmt |= WPA_KEY_MGMT_OSEN;
+ }
+
+ if (!(bss->proto & WPA_PROTO_RSN) ||
+ !(bss->rsn_capab & WPA_CAPABILITY_MFPC))
+ bss->mgmt_group_cipher = 0;
+
+ if (!bss->wpaie[0] && !bss->rsnie[0] && !bss->osenie[0] &&
+ (bss->capab_info & WLAN_CAPABILITY_PRIVACY))
+ bss->group_cipher = WPA_CIPHER_WEP40;
+
+ wpa_printf(MSG_INFO, "BSS " MACSTR
+ " proto=%s%s%s%s"
+ "pairwise=%s%s%s%s%s%s%s"
+ "group=%s%s%s%s%s%s%s%s%s"
+ "mgmt_group_cipher=%s%s%s%s%s"
+ "key_mgmt=%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
+ "rsn_capab=%s%s%s%s%s%s%s%s%s%s",
+ MAC2STR(bss->bssid),
+ bss->proto == 0 ? "OPEN " : "",
+ bss->proto & WPA_PROTO_WPA ? "WPA " : "",
+ bss->proto & WPA_PROTO_RSN ? "WPA2 " : "",
+ bss->proto & WPA_PROTO_OSEN ? "OSEN " : "",
+ bss->pairwise_cipher == 0 ? "N/A " : "",
+ bss->pairwise_cipher & WPA_CIPHER_NONE ? "NONE " : "",
+ bss->pairwise_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
+ bss->pairwise_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+ bss->pairwise_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " :
+ "",
+ bss->pairwise_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+ bss->pairwise_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " :
+ "",
+ bss->group_cipher == 0 ? "N/A " : "",
+ bss->group_cipher & WPA_CIPHER_NONE ? "NONE " : "",
+ bss->group_cipher & WPA_CIPHER_WEP40 ? "WEP40 " : "",
+ bss->group_cipher & WPA_CIPHER_WEP104 ? "WEP104 " : "",
+ bss->group_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
+ bss->group_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+ bss->group_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " : "",
+ bss->group_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+ bss->group_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " : "",
+ bss->mgmt_group_cipher == 0 ? "N/A " : "",
+ bss->mgmt_group_cipher & WPA_CIPHER_AES_128_CMAC ?
+ "BIP " : "",
+ bss->mgmt_group_cipher & WPA_CIPHER_BIP_GMAC_128 ?
+ "BIP-GMAC-128 " : "",
+ bss->mgmt_group_cipher & WPA_CIPHER_BIP_GMAC_256 ?
+ "BIP-GMAC-256 " : "",
+ bss->mgmt_group_cipher & WPA_CIPHER_BIP_CMAC_256 ?
+ "BIP-CMAC-256 " : "",
+ bss->key_mgmt == 0 ? "N/A " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X ? "EAP " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_PSK ? "PSK " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_WPA_NONE ? "WPA-NONE " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X ? "FT-EAP " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_FT_PSK ? "FT-PSK " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256 ?
+ "EAP-SHA256 " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_PSK_SHA256 ?
+ "PSK-SHA256 " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_OWE ? "OWE " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_PASN ? "PASN " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_OSEN ? "OSEN " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_DPP ? "DPP " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B ?
+ "EAP-SUITE-B " : "",
+ bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ?
+ "EAP-SUITE-B-192 " : "",
+ bss->rsn_capab & WPA_CAPABILITY_PREAUTH ? "PREAUTH " : "",
+ bss->rsn_capab & WPA_CAPABILITY_NO_PAIRWISE ?
+ "NO_PAIRWISE " : "",
+ bss->rsn_capab & WPA_CAPABILITY_MFPR ? "MFPR " : "",
+ bss->rsn_capab & WPA_CAPABILITY_MFPC ? "MFPC " : "",
+ bss->rsn_capab & WPA_CAPABILITY_PEERKEY_ENABLED ?
+ "PEERKEY " : "",
+ bss->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_CAPABLE ?
+ "SPP-A-MSDU-CAPAB " : "",
+ bss->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_REQUIRED ?
+ "SPP-A-MSDU-REQUIRED " : "",
+ bss->rsn_capab & WPA_CAPABILITY_PBAC ? "PBAC " : "",
+ bss->rsn_capab & WPA_CAPABILITY_OCVC ? "OCVC " : "",
+ bss->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST ?
+ "ExtKeyID " : "");
+}
+
+
+void bss_flush(struct wlantest *wt)
+{
+ struct wlantest_bss *bss, *n;
+ dl_list_for_each_safe(bss, n, &wt->bss, struct wlantest_bss, list)
+ bss_deinit(bss);
+}
diff --git a/contrib/wpa/wlantest/ccmp.c b/contrib/wpa/wlantest/ccmp.c
new file mode 100644
index 000000000000..2a1ad83c9ee2
--- /dev/null
+++ b/contrib/wpa/wlantest/ccmp.c
@@ -0,0 +1,367 @@
+/*
+ * CTR with CBC-MAC Protocol (CCMP)
+ * Copyright (c) 2010-2012, Jouni Malinen <j@w1.fi>
+ *
+ * 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/ieee802_11_defs.h"
+#include "crypto/aes.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static void ccmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data,
+ u8 *aad, size_t *aad_len, u8 *nonce)
+{
+ u16 fc, stype, seq;
+ int qos = 0, addr4 = 0;
+ u8 *pos;
+
+ nonce[0] = 0;
+
+ fc = le_to_host16(hdr->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+ if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+ (WLAN_FC_TODS | WLAN_FC_FROMDS))
+ addr4 = 1;
+
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+ fc &= ~0x0070; /* Mask subtype bits */
+ if (stype & 0x08) {
+ const u8 *qc;
+ qos = 1;
+ fc &= ~WLAN_FC_ORDER;
+ qc = (const u8 *) (hdr + 1);
+ if (addr4)
+ qc += ETH_ALEN;
+ nonce[0] = qc[0] & 0x0f;
+ }
+ } else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+ nonce[0] |= 0x10; /* Management */
+
+ fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+ fc |= WLAN_FC_ISWEP;
+ WPA_PUT_LE16(aad, fc);
+ pos = aad + 2;
+ os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN);
+ pos += 3 * ETH_ALEN;
+ seq = le_to_host16(hdr->seq_ctrl);
+ seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */
+ WPA_PUT_LE16(pos, seq);
+ pos += 2;
+
+ os_memcpy(pos, hdr + 1, addr4 * ETH_ALEN + qos * 2);
+ pos += addr4 * ETH_ALEN;
+ if (qos) {
+ pos[0] &= ~0x70;
+ if (1 /* FIX: either device has SPP A-MSDU Capab = 0 */)
+ pos[0] &= ~0x80;
+ pos++;
+ *pos++ = 0x00;
+ }
+
+ *aad_len = pos - aad;
+
+ os_memcpy(nonce + 1, hdr->addr2, ETH_ALEN);
+ nonce[7] = data[7]; /* PN5 */
+ nonce[8] = data[6]; /* PN4 */
+ nonce[9] = data[5]; /* PN3 */
+ nonce[10] = data[4]; /* PN2 */
+ nonce[11] = data[1]; /* PN1 */
+ nonce[12] = data[0]; /* PN0 */
+}
+
+
+static void ccmp_aad_nonce_pv1(const u8 *hdr, const u8 *a1, const u8 *a2,
+ const u8 *a3, const u8 *pn,
+ u8 *aad, size_t *aad_len, u8 *nonce)
+{
+ u16 fc, type;
+ u8 *pos;
+
+ nonce[0] = BIT(5); /* PV1 */
+ /* TODO: Priority for QMF; 0 is used for Data frames */
+
+ fc = WPA_GET_LE16(hdr);
+ type = (fc & (BIT(2) | BIT(3) | BIT(4))) >> 2;
+
+ if (type == 1)
+ nonce[0] |= 0x10; /* Management */
+
+ fc &= ~(BIT(10) | BIT(11) | BIT(13) | BIT(14) | BIT(15));
+ fc |= BIT(12);
+ WPA_PUT_LE16(aad, fc);
+ pos = aad + 2;
+ if (type == 0 || type == 3) {
+ const u8 *sc;
+
+ os_memcpy(pos, a1, ETH_ALEN);
+ pos += ETH_ALEN;
+ os_memcpy(pos, a2, ETH_ALEN);
+ pos += ETH_ALEN;
+
+ if (type == 0) {
+ /* Either A1 or A2 contains SID */
+ sc = hdr + 2 + 2 + ETH_ALEN;
+ } else {
+ /* Both A1 and A2 contain full addresses */
+ sc = hdr + 2 + 2 * ETH_ALEN;
+ }
+ /* SC with Sequence Number subfield (bits 4-15 of the Sequence
+ * Control field) masked to 0. */
+ *pos++ = *sc & 0x0f;
+ *pos++ = 0;
+
+ if (a3) {
+ os_memcpy(pos, a3, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+ }
+
+ *aad_len = pos - aad;
+
+ os_memcpy(nonce + 1, a2, ETH_ALEN);
+ nonce[7] = pn[5]; /* PN5 */
+ nonce[8] = pn[4]; /* PN4 */
+ nonce[9] = pn[3]; /* PN3 */
+ nonce[10] = pn[2]; /* PN2 */
+ nonce[11] = pn[1]; /* PN1 */
+ nonce[12] = pn[0]; /* PN0 */
+}
+
+
+u8 * ccmp_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+ u8 aad[30], nonce[13];
+ size_t aad_len;
+ size_t mlen;
+ u8 *plain;
+
+ if (data_len < 8 + 8)
+ return NULL;
+
+ plain = os_malloc(data_len + AES_BLOCK_SIZE);
+ if (plain == NULL)
+ return NULL;
+
+ mlen = data_len - 8 - 8;
+
+ os_memset(aad, 0, sizeof(aad));
+ ccmp_aad_nonce(hdr, data, aad, &aad_len, nonce);
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len);
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13);
+
+ if (aes_ccm_ad(tk, 16, nonce, 8, data + 8, mlen, aad, aad_len,
+ data + 8 + mlen, plain) < 0) {
+ u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+ wpa_printf(MSG_INFO, "Invalid CCMP MIC in frame: A1=" MACSTR
+ " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3),
+ WLAN_GET_SEQ_SEQ(seq_ctrl),
+ WLAN_GET_SEQ_FRAG(seq_ctrl));
+ os_free(plain);
+ return NULL;
+ }
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP decrypted", plain, mlen);
+
+ *decrypted_len = mlen;
+ return plain;
+}
+
+
+void ccmp_get_pn(u8 *pn, const u8 *data)
+{
+ pn[0] = data[7]; /* PN5 */
+ pn[1] = data[6]; /* PN4 */
+ pn[2] = data[5]; /* PN3 */
+ pn[3] = data[4]; /* PN2 */
+ pn[4] = data[1]; /* PN1 */
+ pn[5] = data[0]; /* PN0 */
+}
+
+
+u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+ u8 *pn, int keyid, size_t *encrypted_len)
+{
+ u8 aad[30], nonce[13];
+ size_t aad_len, plen;
+ u8 *crypt, *pos;
+ struct ieee80211_hdr *hdr;
+
+ if (len < hdrlen || hdrlen < 24)
+ return NULL;
+ plen = len - hdrlen;
+
+ crypt = os_malloc(hdrlen + 8 + plen + 8 + AES_BLOCK_SIZE);
+ if (crypt == NULL)
+ return NULL;
+
+ os_memcpy(crypt, frame, hdrlen);
+ hdr = (struct ieee80211_hdr *) crypt;
+ hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+ pos = crypt + hdrlen;
+ *pos++ = pn[5]; /* PN0 */
+ *pos++ = pn[4]; /* PN1 */
+ *pos++ = 0x00; /* Rsvd */
+ *pos++ = 0x20 | (keyid << 6);
+ *pos++ = pn[3]; /* PN2 */
+ *pos++ = pn[2]; /* PN3 */
+ *pos++ = pn[1]; /* PN4 */
+ *pos++ = pn[0]; /* PN5 */
+
+ os_memset(aad, 0, sizeof(aad));
+ ccmp_aad_nonce(hdr, crypt + hdrlen, aad, &aad_len, nonce);
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len);
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, 13);
+
+ if (aes_ccm_ae(tk, 16, nonce, 8, frame + hdrlen, plen, aad, aad_len,
+ pos, pos + plen) < 0) {
+ os_free(crypt);
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP encrypted", crypt + hdrlen + 8, plen);
+
+ *encrypted_len = hdrlen + 8 + plen + 8;
+
+ return crypt;
+}
+
+
+u8 * ccmp_encrypt_pv1(const u8 *tk, const u8 *a1, const u8 *a2, const u8 *a3,
+ const u8 *frame, size_t len,
+ size_t hdrlen, const u8 *pn, int keyid,
+ size_t *encrypted_len)
+{
+ u8 aad[24], nonce[13];
+ size_t aad_len, plen;
+ u8 *crypt, *pos;
+ struct ieee80211_hdr *hdr;
+
+ if (len < hdrlen || hdrlen < 12)
+ return NULL;
+ plen = len - hdrlen;
+
+ crypt = os_malloc(hdrlen + plen + 8 + AES_BLOCK_SIZE);
+ if (crypt == NULL)
+ return NULL;
+
+ os_memcpy(crypt, frame, hdrlen);
+ hdr = (struct ieee80211_hdr *) crypt;
+ hdr->frame_control |= host_to_le16(BIT(12)); /* Protected Frame */
+ pos = crypt + hdrlen;
+
+ os_memset(aad, 0, sizeof(aad));
+ ccmp_aad_nonce_pv1(crypt, a1, a2, a3, pn, aad, &aad_len, nonce);
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP AAD", aad, aad_len);
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP nonce", nonce, sizeof(nonce));
+
+ if (aes_ccm_ae(tk, 16, nonce, 8, frame + hdrlen, plen, aad, aad_len,
+ pos, pos + plen) < 0) {
+ os_free(crypt);
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP encrypted", crypt + hdrlen, plen);
+
+ *encrypted_len = hdrlen + plen + 8;
+
+ return crypt;
+}
+
+
+u8 * ccmp_256_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+ u8 aad[30], nonce[13];
+ size_t aad_len;
+ size_t mlen;
+ u8 *plain;
+
+ if (data_len < 8 + 16)
+ return NULL;
+
+ plain = os_malloc(data_len + AES_BLOCK_SIZE);
+ if (plain == NULL)
+ return NULL;
+
+ mlen = data_len - 8 - 16;
+
+ os_memset(aad, 0, sizeof(aad));
+ ccmp_aad_nonce(hdr, data, aad, &aad_len, nonce);
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 AAD", aad, aad_len);
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 nonce", nonce, 13);
+
+ if (aes_ccm_ad(tk, 32, nonce, 16, data + 8, mlen, aad, aad_len,
+ data + 8 + mlen, plain) < 0) {
+ u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+ wpa_printf(MSG_INFO, "Invalid CCMP-256 MIC in frame: A1=" MACSTR
+ " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3),
+ WLAN_GET_SEQ_SEQ(seq_ctrl),
+ WLAN_GET_SEQ_FRAG(seq_ctrl));
+ os_free(plain);
+ return NULL;
+ }
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 decrypted", plain, mlen);
+
+ *decrypted_len = mlen;
+ return plain;
+}
+
+
+u8 * ccmp_256_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen,
+ u8 *qos, u8 *pn, int keyid, size_t *encrypted_len)
+{
+ u8 aad[30], nonce[13];
+ size_t aad_len, plen;
+ u8 *crypt, *pos;
+ struct ieee80211_hdr *hdr;
+
+ if (len < hdrlen || hdrlen < 24)
+ return NULL;
+ plen = len - hdrlen;
+
+ crypt = os_malloc(hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE);
+ if (crypt == NULL)
+ return NULL;
+
+ os_memcpy(crypt, frame, hdrlen);
+ hdr = (struct ieee80211_hdr *) crypt;
+ hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+ pos = crypt + hdrlen;
+ *pos++ = pn[5]; /* PN0 */
+ *pos++ = pn[4]; /* PN1 */
+ *pos++ = 0x00; /* Rsvd */
+ *pos++ = 0x20 | (keyid << 6);
+ *pos++ = pn[3]; /* PN2 */
+ *pos++ = pn[2]; /* PN3 */
+ *pos++ = pn[1]; /* PN4 */
+ *pos++ = pn[0]; /* PN5 */
+
+ os_memset(aad, 0, sizeof(aad));
+ ccmp_aad_nonce(hdr, crypt + hdrlen, aad, &aad_len, nonce);
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 AAD", aad, aad_len);
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 nonce", nonce, 13);
+
+ if (aes_ccm_ae(tk, 32, nonce, 16, frame + hdrlen, plen, aad, aad_len,
+ pos, pos + plen) < 0) {
+ os_free(crypt);
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_EXCESSIVE, "CCMP-256 encrypted", crypt + hdrlen + 8,
+ plen);
+
+ *encrypted_len = hdrlen + 8 + plen + 16;
+
+ return crypt;
+}
diff --git a/contrib/wpa/wlantest/ctrl.c b/contrib/wpa/wlantest/ctrl.c
new file mode 100644
index 000000000000..587a0d3e1dfa
--- /dev/null
+++ b/contrib/wpa/wlantest/ctrl.c
@@ -0,0 +1,1471 @@
+/*
+ * wlantest control interface
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "common/version.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+#include "wlantest_ctrl.h"
+
+
+static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
+ size_t *len)
+{
+ u8 *pos = buf;
+
+ while (pos + 8 <= buf + buflen) {
+ enum wlantest_ctrl_attr a;
+ size_t alen;
+ a = WPA_GET_BE32(pos);
+ pos += 4;
+ alen = WPA_GET_BE32(pos);
+ pos += 4;
+ if (pos + alen > buf + buflen) {
+ wpa_printf(MSG_DEBUG, "Invalid control message "
+ "attribute");
+ return NULL;
+ }
+ if (a == attr) {
+ *len = alen;
+ return pos;
+ }
+ pos += alen;
+ }
+
+ return NULL;
+}
+
+
+static u8 * attr_get_macaddr(u8 *buf, size_t buflen,
+ enum wlantest_ctrl_attr attr)
+{
+ u8 *addr;
+ size_t addr_len;
+ addr = attr_get(buf, buflen, attr, &addr_len);
+ if (addr && addr_len != ETH_ALEN)
+ addr = NULL;
+ return addr;
+}
+
+
+static int attr_get_int(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr)
+{
+ u8 *pos;
+ size_t len;
+ pos = attr_get(buf, buflen, attr, &len);
+ if (pos == NULL || len != 4)
+ return -1;
+ return WPA_GET_BE32(pos);
+}
+
+
+static u8 * attr_add_str(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+ const char *str)
+{
+ size_t len = os_strlen(str);
+
+ if (pos == NULL || end - pos < 8 + len)
+ return NULL;
+ WPA_PUT_BE32(pos, attr);
+ pos += 4;
+ WPA_PUT_BE32(pos, len);
+ pos += 4;
+ os_memcpy(pos, str, len);
+ pos += len;
+ return pos;
+}
+
+
+static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+ u32 val)
+{
+ if (pos == NULL || end - pos < 12)
+ return NULL;
+ WPA_PUT_BE32(pos, attr);
+ pos += 4;
+ WPA_PUT_BE32(pos, 4);
+ pos += 4;
+ WPA_PUT_BE32(pos, val);
+ pos += 4;
+ return pos;
+}
+
+
+static void ctrl_disconnect(struct wlantest *wt, int sock)
+{
+ int i;
+ wpa_printf(MSG_DEBUG, "Disconnect control interface connection %d",
+ sock);
+ for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+ if (wt->ctrl_socks[i] == sock) {
+ close(wt->ctrl_socks[i]);
+ eloop_unregister_read_sock(wt->ctrl_socks[i]);
+ wt->ctrl_socks[i] = -1;
+ break;
+ }
+ }
+}
+
+
+static void ctrl_send(struct wlantest *wt, int sock, const u8 *buf,
+ size_t len)
+{
+ if (send(sock, buf, len, 0) < 0) {
+ wpa_printf(MSG_INFO, "send(ctrl): %s", strerror(errno));
+ ctrl_disconnect(wt, sock);
+ }
+}
+
+
+static void ctrl_send_simple(struct wlantest *wt, int sock,
+ enum wlantest_ctrl_cmd cmd)
+{
+ u8 buf[4];
+ WPA_PUT_BE32(buf, cmd);
+ ctrl_send(wt, sock, buf, sizeof(buf));
+}
+
+
+static struct wlantest_bss * ctrl_get_bss(struct wlantest *wt, int sock,
+ u8 *cmd, size_t clen)
+{
+ struct wlantest_bss *bss;
+ u8 *pos;
+ size_t len;
+
+ pos = attr_get(cmd, clen, WLANTEST_ATTR_BSSID, &len);
+ if (pos == NULL || len != ETH_ALEN) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return NULL;
+ }
+
+ bss = bss_find(wt, pos);
+ if (bss == NULL) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return NULL;
+ }
+
+ return bss;
+}
+
+
+static struct wlantest_sta * ctrl_get_sta(struct wlantest *wt, int sock,
+ u8 *cmd, size_t clen,
+ struct wlantest_bss *bss)
+{
+ struct wlantest_sta *sta;
+ u8 *pos;
+ size_t len;
+
+ if (bss == NULL)
+ return NULL;
+
+ pos = attr_get(cmd, clen, WLANTEST_ATTR_STA_ADDR, &len);
+ if (pos == NULL || len != ETH_ALEN) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return NULL;
+ }
+
+ sta = sta_find(bss, pos);
+ if (sta == NULL) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return NULL;
+ }
+
+ return sta;
+}
+
+
+static struct wlantest_sta * ctrl_get_sta2(struct wlantest *wt, int sock,
+ u8 *cmd, size_t clen,
+ struct wlantest_bss *bss)
+{
+ struct wlantest_sta *sta;
+ u8 *pos;
+ size_t len;
+
+ if (bss == NULL)
+ return NULL;
+
+ pos = attr_get(cmd, clen, WLANTEST_ATTR_STA2_ADDR, &len);
+ if (pos == NULL || len != ETH_ALEN) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return NULL;
+ }
+
+ sta = sta_find(bss, pos);
+ if (sta == NULL) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return NULL;
+ }
+
+ return sta;
+}
+
+
+static void ctrl_list_bss(struct wlantest *wt, int sock)
+{
+ u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len;
+ struct wlantest_bss *bss;
+
+ pos = buf;
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+ pos += 4;
+ WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+ pos += 4;
+ len = pos; /* to be filled */
+ pos += 4;
+
+ dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
+ if (pos + ETH_ALEN > buf + WLANTEST_CTRL_MAX_RESP_LEN)
+ break;
+ os_memcpy(pos, bss->bssid, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+
+ WPA_PUT_BE32(len, pos - len - 4);
+ ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_list_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+ u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos, *len;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ if (bss == NULL)
+ return;
+
+ pos = buf;
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+ pos += 4;
+ WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
+ pos += 4;
+ len = pos; /* to be filled */
+ pos += 4;
+
+ dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+ if (pos + ETH_ALEN > buf + WLANTEST_CTRL_MAX_RESP_LEN)
+ break;
+ os_memcpy(pos, sta->addr, ETH_ALEN);
+ pos += ETH_ALEN;
+ }
+
+ WPA_PUT_BE32(len, pos - len - 4);
+ ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_flush(struct wlantest *wt, int sock)
+{
+ wpa_printf(MSG_DEBUG, "Drop all collected BSS data");
+ bss_flush(wt);
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_clear_sta_counters(struct wlantest *wt, int sock, u8 *cmd,
+ size_t clen)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+ if (sta == NULL) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+
+ os_memset(sta->counters, 0, sizeof(sta->counters));
+ os_memset(sta->tx_tid, 0, sizeof(sta->tx_tid));
+ os_memset(sta->rx_tid, 0, sizeof(sta->rx_tid));
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_clear_bss_counters(struct wlantest *wt, int sock, u8 *cmd,
+ size_t clen)
+{
+ struct wlantest_bss *bss;
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ if (bss == NULL) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+
+ os_memset(bss->counters, 0, sizeof(bss->counters));
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_clear_tdls_counters(struct wlantest *wt, int sock, u8 *cmd,
+ size_t clen)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ struct wlantest_sta *sta2;
+ struct wlantest_tdls *tdls;
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+ sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss);
+ if (sta == NULL || sta2 == NULL) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+
+ dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+ if ((tdls->init == sta && tdls->resp == sta2) ||
+ (tdls->init == sta2 && tdls->resp == sta))
+ os_memset(tdls->counters, 0, sizeof(tdls->counters));
+ }
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_get_sta_counter(struct wlantest *wt, int sock, u8 *cmd,
+ size_t clen)
+{
+ u8 *addr;
+ size_t addr_len;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ u32 counter;
+ u8 buf[4 + 12], *end, *pos;
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+ if (sta == NULL)
+ return;
+
+ addr = attr_get(cmd, clen, WLANTEST_ATTR_STA_COUNTER, &addr_len);
+ if (addr == NULL || addr_len != 4) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+ counter = WPA_GET_BE32(addr);
+ if (counter >= NUM_WLANTEST_STA_COUNTER) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+ pos += 4;
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+ sta->counters[counter]);
+ ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_get_bss_counter(struct wlantest *wt, int sock, u8 *cmd,
+ size_t clen)
+{
+ u8 *addr;
+ size_t addr_len;
+ struct wlantest_bss *bss;
+ u32 counter;
+ u8 buf[4 + 12], *end, *pos;
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ if (bss == NULL)
+ return;
+
+ addr = attr_get(cmd, clen, WLANTEST_ATTR_BSS_COUNTER, &addr_len);
+ if (addr == NULL || addr_len != 4) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+ counter = WPA_GET_BE32(addr);
+ if (counter >= NUM_WLANTEST_BSS_COUNTER) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+ pos += 4;
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+ bss->counters[counter]);
+ ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_get_tdls_counter(struct wlantest *wt, int sock, u8 *cmd,
+ size_t clen)
+{
+ u8 *addr;
+ size_t addr_len;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ struct wlantest_sta *sta2;
+ struct wlantest_tdls *tdls;
+ u32 counter;
+ u8 buf[4 + 12], *end, *pos;
+ int found = 0;
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+ sta2 = ctrl_get_sta2(wt, sock, cmd, clen, bss);
+ if (sta == NULL || sta2 == NULL) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+
+ addr = attr_get(cmd, clen, WLANTEST_ATTR_TDLS_COUNTER, &addr_len);
+ if (addr == NULL || addr_len != 4) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+ counter = WPA_GET_BE32(addr);
+ if (counter >= NUM_WLANTEST_TDLS_COUNTER) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+ if (tdls->init == sta && tdls->resp == sta2) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+ pos += 4;
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+ tdls->counters[counter]);
+ ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void build_mgmt_hdr(struct ieee80211_mgmt *mgmt,
+ struct wlantest_bss *bss, struct wlantest_sta *sta,
+ int sender_ap, int stype)
+{
+ os_memset(mgmt, 0, 24);
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
+ if (sender_ap) {
+ if (sta)
+ os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+ else
+ os_memset(mgmt->da, 0xff, ETH_ALEN);
+ os_memcpy(mgmt->sa, bss->bssid, ETH_ALEN);
+ } else {
+ os_memcpy(mgmt->da, bss->bssid, ETH_ALEN);
+ os_memcpy(mgmt->sa, sta->addr, ETH_ALEN);
+ }
+ os_memcpy(mgmt->bssid, bss->bssid, ETH_ALEN);
+}
+
+
+static int ctrl_inject_auth(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, int sender_ap,
+ enum wlantest_inject_protection prot)
+{
+ struct ieee80211_mgmt mgmt;
+
+ if (prot != WLANTEST_INJECT_NORMAL &&
+ prot != WLANTEST_INJECT_UNPROTECTED)
+ return -1; /* Authentication frame is never protected */
+ if (sta == NULL)
+ return -1; /* No broadcast Authentication frames */
+
+ if (sender_ap)
+ wpa_printf(MSG_INFO, "INJECT: Auth " MACSTR " -> " MACSTR,
+ MAC2STR(bss->bssid), MAC2STR(sta->addr));
+ else
+ wpa_printf(MSG_INFO, "INJECT: Auth " MACSTR " -> " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_AUTH);
+
+ mgmt.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN);
+ mgmt.u.auth.auth_transaction = host_to_le16(1);
+ mgmt.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+
+ return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 6,
+ WLANTEST_INJECT_UNPROTECTED);
+}
+
+
+static int ctrl_inject_assocreq(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, int sender_ap,
+ enum wlantest_inject_protection prot)
+{
+ u8 *buf;
+ struct ieee80211_mgmt *mgmt;
+ int ret;
+
+ if (prot != WLANTEST_INJECT_NORMAL &&
+ prot != WLANTEST_INJECT_UNPROTECTED)
+ return -1; /* Association Request frame is never protected */
+ if (sta == NULL)
+ return -1; /* No broadcast Association Request frames */
+ if (sender_ap)
+ return -1; /* No Association Request frame sent by AP */
+ if (sta->assocreq_ies == NULL) {
+ wpa_printf(MSG_INFO, "INJECT: No previous (Re)Association "
+ "Request available for " MACSTR,
+ MAC2STR(sta->addr));
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "INJECT: AssocReq " MACSTR " -> " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ buf = os_malloc(sizeof(*mgmt) + sta->assocreq_ies_len);
+ if (buf == NULL)
+ return -1;
+ mgmt = (struct ieee80211_mgmt *) buf;
+
+ build_mgmt_hdr(mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_ASSOC_REQ);
+
+ mgmt->u.assoc_req.capab_info = host_to_le16(sta->assocreq_capab_info);
+ mgmt->u.assoc_req.listen_interval =
+ host_to_le16(sta->assocreq_listen_int);
+ os_memcpy(mgmt->u.assoc_req.variable, sta->assocreq_ies,
+ sta->assocreq_ies_len);
+
+ ret = wlantest_inject(wt, bss, sta, buf,
+ 24 + 4 + sta->assocreq_ies_len,
+ WLANTEST_INJECT_UNPROTECTED);
+ os_free(buf);
+ return ret;
+}
+
+
+static int ctrl_inject_reassocreq(struct wlantest *wt,
+ struct wlantest_bss *bss,
+ struct wlantest_sta *sta, int sender_ap,
+ enum wlantest_inject_protection prot)
+{
+ u8 *buf;
+ struct ieee80211_mgmt *mgmt;
+ int ret;
+
+ if (prot != WLANTEST_INJECT_NORMAL &&
+ prot != WLANTEST_INJECT_UNPROTECTED)
+ return -1; /* Reassociation Request frame is never protected */
+ if (sta == NULL)
+ return -1; /* No broadcast Reassociation Request frames */
+ if (sender_ap)
+ return -1; /* No Reassociation Request frame sent by AP */
+ if (sta->assocreq_ies == NULL) {
+ wpa_printf(MSG_INFO, "INJECT: No previous (Re)Association "
+ "Request available for " MACSTR,
+ MAC2STR(sta->addr));
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "INJECT: ReassocReq " MACSTR " -> " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ buf = os_malloc(sizeof(*mgmt) + sta->assocreq_ies_len);
+ if (buf == NULL)
+ return -1;
+ mgmt = (struct ieee80211_mgmt *) buf;
+
+ build_mgmt_hdr(mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_REASSOC_REQ);
+
+ mgmt->u.reassoc_req.capab_info =
+ host_to_le16(sta->assocreq_capab_info);
+ mgmt->u.reassoc_req.listen_interval =
+ host_to_le16(sta->assocreq_listen_int);
+ os_memcpy(mgmt->u.reassoc_req.current_ap, bss->bssid, ETH_ALEN);
+ os_memcpy(mgmt->u.reassoc_req.variable, sta->assocreq_ies,
+ sta->assocreq_ies_len);
+
+ ret = wlantest_inject(wt, bss, sta, buf,
+ 24 + 10 + sta->assocreq_ies_len,
+ WLANTEST_INJECT_UNPROTECTED);
+ os_free(buf);
+ return ret;
+}
+
+
+static int ctrl_inject_deauth(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, int sender_ap,
+ enum wlantest_inject_protection prot)
+{
+ struct ieee80211_mgmt mgmt;
+
+ if (sender_ap) {
+ if (sta)
+ wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> "
+ MACSTR,
+ MAC2STR(bss->bssid), MAC2STR(sta->addr));
+ else
+ wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR
+ " -> broadcast", MAC2STR(bss->bssid));
+ } else
+ wpa_printf(MSG_INFO, "INJECT: Deauth " MACSTR " -> " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_DEAUTH);
+
+ mgmt.u.deauth.reason_code = host_to_le16(WLAN_REASON_UNSPECIFIED);
+
+ return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 2, prot);
+}
+
+
+static int ctrl_inject_disassoc(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, int sender_ap,
+ enum wlantest_inject_protection prot)
+{
+ struct ieee80211_mgmt mgmt;
+
+ if (sender_ap) {
+ if (sta)
+ wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> "
+ MACSTR,
+ MAC2STR(bss->bssid), MAC2STR(sta->addr));
+ else
+ wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR
+ " -> broadcast", MAC2STR(bss->bssid));
+ } else
+ wpa_printf(MSG_INFO, "INJECT: Disassoc " MACSTR " -> " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_DISASSOC);
+
+ mgmt.u.disassoc.reason_code = host_to_le16(WLAN_REASON_UNSPECIFIED);
+
+ return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 2, prot);
+}
+
+
+static int ctrl_inject_saqueryreq(struct wlantest *wt,
+ struct wlantest_bss *bss,
+ struct wlantest_sta *sta, int sender_ap,
+ enum wlantest_inject_protection prot)
+{
+ struct ieee80211_mgmt mgmt;
+
+ if (sta == NULL)
+ return -1; /* No broadcast SA Query frames */
+
+ if (sender_ap)
+ wpa_printf(MSG_INFO, "INJECT: SA Query Request " MACSTR " -> "
+ MACSTR, MAC2STR(bss->bssid), MAC2STR(sta->addr));
+ else
+ wpa_printf(MSG_INFO, "INJECT: SA Query Request " MACSTR " -> "
+ MACSTR, MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ build_mgmt_hdr(&mgmt, bss, sta, sender_ap, WLAN_FC_STYPE_ACTION);
+
+ mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
+ mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+ mgmt.u.action.u.sa_query_req.trans_id[0] = 0x12;
+ mgmt.u.action.u.sa_query_req.trans_id[1] = 0x34;
+ os_memcpy(sender_ap ? sta->ap_sa_query_tr : sta->sta_sa_query_tr,
+ mgmt.u.action.u.sa_query_req.trans_id,
+ WLAN_SA_QUERY_TR_ID_LEN);
+ return wlantest_inject(wt, bss, sta, (u8 *) &mgmt, 24 + 4, prot);
+}
+
+
+static void ctrl_inject(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+ u8 *bssid, *sta_addr;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ int frame, sender_ap, prot;
+ int ret = 0;
+
+ bssid = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_BSSID);
+ sta_addr = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_STA_ADDR);
+ frame = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_FRAME);
+ sender_ap = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_SENDER_AP);
+ if (sender_ap < 0)
+ sender_ap = 0;
+ prot = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_PROTECTION);
+ if (bssid == NULL || sta_addr == NULL || frame < 0 || prot < 0) {
+ wpa_printf(MSG_INFO, "Invalid inject command parameters");
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ bss = bss_find(wt, bssid);
+ if (bss == NULL) {
+ wpa_printf(MSG_INFO, "BSS not found for inject command");
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+
+ if (is_broadcast_ether_addr(sta_addr)) {
+ if (!sender_ap) {
+ wpa_printf(MSG_INFO, "Invalid broadcast inject "
+ "command without sender_ap set");
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ } sta = NULL;
+ } else {
+ sta = sta_find(bss, sta_addr);
+ if (sta == NULL) {
+ wpa_printf(MSG_INFO, "Station not found for inject "
+ "command");
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+ }
+
+ switch (frame) {
+ case WLANTEST_FRAME_AUTH:
+ ret = ctrl_inject_auth(wt, bss, sta, sender_ap, prot);
+ break;
+ case WLANTEST_FRAME_ASSOCREQ:
+ ret = ctrl_inject_assocreq(wt, bss, sta, sender_ap, prot);
+ break;
+ case WLANTEST_FRAME_REASSOCREQ:
+ ret = ctrl_inject_reassocreq(wt, bss, sta, sender_ap, prot);
+ break;
+ case WLANTEST_FRAME_DEAUTH:
+ ret = ctrl_inject_deauth(wt, bss, sta, sender_ap, prot);
+ break;
+ case WLANTEST_FRAME_DISASSOC:
+ ret = ctrl_inject_disassoc(wt, bss, sta, sender_ap, prot);
+ break;
+ case WLANTEST_FRAME_SAQUERYREQ:
+ ret = ctrl_inject_saqueryreq(wt, bss, sta, sender_ap, prot);
+ break;
+ default:
+ wpa_printf(MSG_INFO, "Unsupported inject command frame %d",
+ frame);
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ if (ret)
+ wpa_printf(MSG_INFO, "Failed to inject frame");
+ else
+ wpa_printf(MSG_INFO, "Frame injected successfully");
+ ctrl_send_simple(wt, sock, ret == 0 ? WLANTEST_CTRL_SUCCESS :
+ WLANTEST_CTRL_FAILURE);
+}
+
+
+static void ctrl_version(struct wlantest *wt, int sock)
+{
+ u8 buf[WLANTEST_CTRL_MAX_RESP_LEN], *pos;
+
+ pos = buf;
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+ pos += 4;
+ pos = attr_add_str(pos, buf + sizeof(buf), WLANTEST_ATTR_VERSION,
+ VERSION_STR);
+ ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_add_passphrase(struct wlantest *wt, int sock, u8 *cmd,
+ size_t clen)
+{
+ u8 *passphrase;
+ size_t len;
+ struct wlantest_passphrase *p, *pa;
+ u8 *bssid;
+
+ passphrase = attr_get(cmd, clen, WLANTEST_ATTR_PASSPHRASE, &len);
+ if (passphrase == NULL) {
+ u8 *wepkey;
+ char *key;
+ enum wlantest_ctrl_cmd res;
+
+ wepkey = attr_get(cmd, clen, WLANTEST_ATTR_WEPKEY, &len);
+ if (wepkey == NULL) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+ key = os_zalloc(len + 1);
+ if (key == NULL) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+ os_memcpy(key, wepkey, len);
+ if (add_wep(wt, key) < 0)
+ res = WLANTEST_CTRL_FAILURE;
+ else
+ res = WLANTEST_CTRL_SUCCESS;
+ os_free(key);
+ ctrl_send_simple(wt, sock, res);
+ return;
+ }
+
+ if (len < 8 || len > 63) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+ os_memcpy(p->passphrase, passphrase, len);
+ wpa_printf(MSG_INFO, "Add passphrase '%s'", p->passphrase);
+
+ bssid = attr_get_macaddr(cmd, clen, WLANTEST_ATTR_BSSID);
+ if (bssid) {
+ os_memcpy(p->bssid, bssid, ETH_ALEN);
+ wpa_printf(MSG_INFO, "Limit passphrase for BSSID " MACSTR,
+ MAC2STR(p->bssid));
+ }
+
+ dl_list_for_each(pa, &wt->passphrase, struct wlantest_passphrase, list)
+ {
+ if (os_strcmp(p->passphrase, pa->passphrase) == 0 &&
+ os_memcmp(p->bssid, pa->bssid, ETH_ALEN) == 0) {
+ wpa_printf(MSG_INFO, "Passphrase was already known");
+ os_free(p);
+ p = NULL;
+ break;
+ }
+ }
+
+ if (p) {
+ struct wlantest_bss *bss;
+ dl_list_add(&wt->passphrase, &p->list);
+ dl_list_for_each(bss, &wt->bss, struct wlantest_bss, list) {
+ if (bssid &&
+ os_memcmp(p->bssid, bss->bssid, ETH_ALEN) != 0)
+ continue;
+ bss_add_pmk_from_passphrase(bss, p->passphrase);
+ }
+ }
+
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void info_print_proto(char *buf, size_t len, int proto)
+{
+ char *pos, *end;
+
+ if (proto == 0) {
+ os_snprintf(buf, len, "OPEN");
+ return;
+ }
+
+ pos = buf;
+ end = buf + len;
+
+ if (proto & WPA_PROTO_WPA)
+ pos += os_snprintf(pos, end - pos, "%sWPA",
+ pos == buf ? "" : " ");
+ if (proto & WPA_PROTO_RSN)
+ pos += os_snprintf(pos, end - pos, "%sWPA2",
+ pos == buf ? "" : " ");
+}
+
+
+static void info_print_cipher(char *buf, size_t len, int cipher)
+{
+ char *pos, *end;
+
+ if (cipher == 0) {
+ os_snprintf(buf, len, "N/A");
+ return;
+ }
+
+ pos = buf;
+ end = buf + len;
+
+ if (cipher & WPA_CIPHER_NONE)
+ pos += os_snprintf(pos, end - pos, "%sNONE",
+ pos == buf ? "" : " ");
+ if (cipher & WPA_CIPHER_WEP40)
+ pos += os_snprintf(pos, end - pos, "%sWEP40",
+ pos == buf ? "" : " ");
+ if (cipher & WPA_CIPHER_WEP104)
+ pos += os_snprintf(pos, end - pos, "%sWEP104",
+ pos == buf ? "" : " ");
+ if (cipher & WPA_CIPHER_TKIP)
+ pos += os_snprintf(pos, end - pos, "%sTKIP",
+ pos == buf ? "" : " ");
+ if (cipher & WPA_CIPHER_CCMP)
+ pos += os_snprintf(pos, end - pos, "%sCCMP",
+ pos == buf ? "" : " ");
+ if (cipher & WPA_CIPHER_AES_128_CMAC)
+ pos += os_snprintf(pos, end - pos, "%sBIP",
+ pos == buf ? "" : " ");
+ if (cipher & WPA_CIPHER_BIP_GMAC_128)
+ pos += os_snprintf(pos, end - pos, "%sBIP-GMAC-128",
+ pos == buf ? "" : " ");
+ if (cipher & WPA_CIPHER_BIP_GMAC_256)
+ pos += os_snprintf(pos, end - pos, "%sBIP-GMAC-256",
+ pos == buf ? "" : " ");
+ if (cipher & WPA_CIPHER_BIP_CMAC_256)
+ pos += os_snprintf(pos, end - pos, "%sBIP-CMAC-256",
+ pos == buf ? "" : " ");
+}
+
+
+static void info_print_key_mgmt(char *buf, size_t len, int key_mgmt)
+{
+ char *pos, *end;
+
+ if (key_mgmt == 0) {
+ os_snprintf(buf, len, "N/A");
+ return;
+ }
+
+ pos = buf;
+ end = buf + len;
+
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X)
+ pos += os_snprintf(pos, end - pos, "%sEAP",
+ pos == buf ? "" : " ");
+ if (key_mgmt & WPA_KEY_MGMT_PSK)
+ pos += os_snprintf(pos, end - pos, "%sPSK",
+ pos == buf ? "" : " ");
+ if (key_mgmt & WPA_KEY_MGMT_WPA_NONE)
+ pos += os_snprintf(pos, end - pos, "%sWPA-NONE",
+ pos == buf ? "" : " ");
+ if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
+ pos += os_snprintf(pos, end - pos, "%sFT-EAP",
+ pos == buf ? "" : " ");
+ if (key_mgmt & WPA_KEY_MGMT_FT_PSK)
+ pos += os_snprintf(pos, end - pos, "%sFT-PSK",
+ pos == buf ? "" : " ");
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
+ pos += os_snprintf(pos, end - pos, "%sEAP-SHA256",
+ pos == buf ? "" : " ");
+ if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
+ pos += os_snprintf(pos, end - pos, "%sPSK-SHA256",
+ pos == buf ? "" : " ");
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+ pos += os_snprintf(pos, end - pos, "%sEAP-SUITE-B",
+ pos == buf ? "" : " ");
+ if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+ pos += os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192",
+ pos == buf ? "" : " ");
+}
+
+
+static void info_print_rsn_capab(char *buf, size_t len, int capab)
+{
+ char *pos, *end;
+
+ pos = buf;
+ end = buf + len;
+
+ if (capab & WPA_CAPABILITY_PREAUTH)
+ pos += os_snprintf(pos, end - pos, "%sPREAUTH",
+ pos == buf ? "" : " ");
+ if (capab & WPA_CAPABILITY_NO_PAIRWISE)
+ pos += os_snprintf(pos, end - pos, "%sNO_PAIRWISE",
+ pos == buf ? "" : " ");
+ if (capab & WPA_CAPABILITY_MFPR)
+ pos += os_snprintf(pos, end - pos, "%sMFPR",
+ pos == buf ? "" : " ");
+ if (capab & WPA_CAPABILITY_MFPC)
+ pos += os_snprintf(pos, end - pos, "%sMFPC",
+ pos == buf ? "" : " ");
+ if (capab & WPA_CAPABILITY_PEERKEY_ENABLED)
+ pos += os_snprintf(pos, end - pos, "%sPEERKEY",
+ pos == buf ? "" : " ");
+ if (capab & WPA_CAPABILITY_OCVC)
+ pos += os_snprintf(pos, end - pos, "%sOCVC",
+ pos == buf ? "" : " ");
+}
+
+
+static void info_print_state(char *buf, size_t len, int state)
+{
+ switch (state) {
+ case STATE1:
+ os_strlcpy(buf, "NOT-AUTH", len);
+ break;
+ case STATE2:
+ os_strlcpy(buf, "AUTH", len);
+ break;
+ case STATE3:
+ os_strlcpy(buf, "AUTH+ASSOC", len);
+ break;
+ }
+}
+
+
+static void info_print_gtk(char *buf, size_t len, struct wlantest_sta *sta)
+{
+ size_t pos;
+
+ pos = os_snprintf(buf, len, "IDX=%d,GTK=", sta->gtk_idx);
+ wpa_snprintf_hex(buf + pos, len - pos, sta->gtk, sta->gtk_len);
+}
+
+
+static void ctrl_info_sta(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+ u8 *addr;
+ size_t addr_len;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ enum wlantest_sta_info info;
+ u8 buf[4 + 108], *end, *pos;
+ char resp[100];
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+ if (sta == NULL)
+ return;
+
+ addr = attr_get(cmd, clen, WLANTEST_ATTR_STA_INFO, &addr_len);
+ if (addr == NULL || addr_len != 4) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+ info = WPA_GET_BE32(addr);
+
+ resp[0] = '\0';
+ switch (info) {
+ case WLANTEST_STA_INFO_PROTO:
+ info_print_proto(resp, sizeof(resp), sta->proto);
+ break;
+ case WLANTEST_STA_INFO_PAIRWISE:
+ info_print_cipher(resp, sizeof(resp), sta->pairwise_cipher);
+ break;
+ case WLANTEST_STA_INFO_KEY_MGMT:
+ info_print_key_mgmt(resp, sizeof(resp), sta->key_mgmt);
+ break;
+ case WLANTEST_STA_INFO_RSN_CAPAB:
+ info_print_rsn_capab(resp, sizeof(resp), sta->rsn_capab);
+ break;
+ case WLANTEST_STA_INFO_STATE:
+ info_print_state(resp, sizeof(resp), sta->state);
+ break;
+ case WLANTEST_STA_INFO_GTK:
+ info_print_gtk(resp, sizeof(resp), sta);
+ break;
+ default:
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+ pos += 4;
+ pos = attr_add_str(pos, end, WLANTEST_ATTR_INFO, resp);
+ ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_info_bss(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+ u8 *addr;
+ size_t addr_len;
+ struct wlantest_bss *bss;
+ enum wlantest_bss_info info;
+ u8 buf[4 + 108], *end, *pos;
+ char resp[100];
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ if (bss == NULL)
+ return;
+
+ addr = attr_get(cmd, clen, WLANTEST_ATTR_BSS_INFO, &addr_len);
+ if (addr == NULL || addr_len != 4) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+ info = WPA_GET_BE32(addr);
+
+ resp[0] = '\0';
+ switch (info) {
+ case WLANTEST_BSS_INFO_PROTO:
+ info_print_proto(resp, sizeof(resp), bss->proto);
+ break;
+ case WLANTEST_BSS_INFO_PAIRWISE:
+ info_print_cipher(resp, sizeof(resp), bss->pairwise_cipher);
+ break;
+ case WLANTEST_BSS_INFO_GROUP:
+ info_print_cipher(resp, sizeof(resp), bss->group_cipher);
+ break;
+ case WLANTEST_BSS_INFO_GROUP_MGMT:
+ info_print_cipher(resp, sizeof(resp), bss->mgmt_group_cipher);
+ break;
+ case WLANTEST_BSS_INFO_KEY_MGMT:
+ info_print_key_mgmt(resp, sizeof(resp), bss->key_mgmt);
+ break;
+ case WLANTEST_BSS_INFO_RSN_CAPAB:
+ info_print_rsn_capab(resp, sizeof(resp), bss->rsn_capab);
+ break;
+ default:
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+ pos += 4;
+ pos = attr_add_str(pos, end, WLANTEST_ATTR_INFO, resp);
+ ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_send_(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ u8 *bssid, *sta_addr;
+ int prot;
+ u8 *frame;
+ size_t frame_len;
+ int ret = 0;
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+
+ frame = attr_get(cmd, clen, WLANTEST_ATTR_FRAME, &frame_len);
+ prot = attr_get_int(cmd, clen, WLANTEST_ATTR_INJECT_PROTECTION);
+ if (frame == NULL || frame_len < 24 || prot < 0) {
+ wpa_printf(MSG_INFO, "Invalid send command parameters");
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ hdr = (struct ieee80211_hdr *) frame;
+ fc = le_to_host16(hdr->frame_control);
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case WLAN_FC_TYPE_MGMT:
+ bssid = hdr->addr3;
+ if (os_memcmp(hdr->addr2, hdr->addr3, ETH_ALEN) == 0)
+ sta_addr = hdr->addr1;
+ else
+ sta_addr = hdr->addr2;
+ break;
+ case WLAN_FC_TYPE_DATA:
+ switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+ case 0:
+ bssid = hdr->addr3;
+ sta_addr = hdr->addr2;
+ break;
+ case WLAN_FC_TODS:
+ bssid = hdr->addr1;
+ sta_addr = hdr->addr2;
+ break;
+ case WLAN_FC_FROMDS:
+ bssid = hdr->addr2;
+ sta_addr = hdr->addr1;
+ break;
+ default:
+ wpa_printf(MSG_INFO, "Unsupported inject frame");
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+ break;
+ default:
+ wpa_printf(MSG_INFO, "Unsupported inject frame");
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+
+ bss = bss_find(wt, bssid);
+ if (bss == NULL && prot != WLANTEST_INJECT_UNPROTECTED) {
+ wpa_printf(MSG_INFO, "Unknown BSSID");
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+
+ if (bss)
+ sta = sta_find(bss, sta_addr);
+ else
+ sta = NULL;
+ if (sta == NULL && prot != WLANTEST_INJECT_UNPROTECTED) {
+ wpa_printf(MSG_INFO, "Unknown STA address");
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_FAILURE);
+ return;
+ }
+
+ ret = wlantest_inject(wt, bss, sta, frame, frame_len, prot);
+
+ if (ret)
+ wpa_printf(MSG_INFO, "Failed to inject frame");
+ else
+ wpa_printf(MSG_INFO, "Frame injected successfully");
+ ctrl_send_simple(wt, sock, ret == 0 ? WLANTEST_CTRL_SUCCESS :
+ WLANTEST_CTRL_FAILURE);
+}
+
+
+static void ctrl_relog(struct wlantest *wt, int sock)
+{
+ int res = wlantest_relog(wt);
+ ctrl_send_simple(wt, sock, res ? WLANTEST_CTRL_FAILURE :
+ WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_get_tx_tid(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+ u8 *addr;
+ size_t addr_len;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ u32 counter;
+ u8 buf[4 + 12], *end, *pos;
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+ if (sta == NULL)
+ return;
+
+ addr = attr_get(cmd, clen, WLANTEST_ATTR_TID, &addr_len);
+ if (addr == NULL || addr_len != 4) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+ counter = WPA_GET_BE32(addr);
+ if (counter >= 16 + 1) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+ pos += 4;
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+ sta->tx_tid[counter]);
+ ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_get_rx_tid(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+ u8 *addr;
+ size_t addr_len;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ u32 counter;
+ u8 buf[4 + 12], *end, *pos;
+
+ bss = ctrl_get_bss(wt, sock, cmd, clen);
+ sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+ if (sta == NULL)
+ return;
+
+ addr = attr_get(cmd, clen, WLANTEST_ATTR_TID, &addr_len);
+ if (addr == NULL || addr_len != 4) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+ counter = WPA_GET_BE32(addr);
+ if (counter >= 16 + 1) {
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+ return;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+ pos += 4;
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+ sta->rx_tid[counter]);
+ ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wlantest *wt = eloop_ctx;
+ u8 buf[WLANTEST_CTRL_MAX_CMD_LEN];
+ int len;
+ enum wlantest_ctrl_cmd cmd;
+
+ wpa_printf(MSG_EXCESSIVE, "New control interface message from %d",
+ sock);
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ wpa_printf(MSG_INFO, "recv(ctrl): %s", strerror(errno));
+ ctrl_disconnect(wt, sock);
+ return;
+ }
+ if (len == 0) {
+ ctrl_disconnect(wt, sock);
+ return;
+ }
+
+ if (len < 4) {
+ wpa_printf(MSG_INFO, "Too short control interface command "
+ "from %d", sock);
+ ctrl_disconnect(wt, sock);
+ return;
+ }
+ cmd = WPA_GET_BE32(buf);
+ wpa_printf(MSG_EXCESSIVE, "Control interface command %d from %d",
+ cmd, sock);
+
+ switch (cmd) {
+ case WLANTEST_CTRL_PING:
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+ break;
+ case WLANTEST_CTRL_TERMINATE:
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
+ eloop_terminate();
+ break;
+ case WLANTEST_CTRL_LIST_BSS:
+ ctrl_list_bss(wt, sock);
+ break;
+ case WLANTEST_CTRL_LIST_STA:
+ ctrl_list_sta(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_FLUSH:
+ ctrl_flush(wt, sock);
+ break;
+ case WLANTEST_CTRL_CLEAR_STA_COUNTERS:
+ ctrl_clear_sta_counters(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_CLEAR_BSS_COUNTERS:
+ ctrl_clear_bss_counters(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_CLEAR_TDLS_COUNTERS:
+ ctrl_clear_tdls_counters(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_GET_STA_COUNTER:
+ ctrl_get_sta_counter(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_GET_BSS_COUNTER:
+ ctrl_get_bss_counter(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_GET_TDLS_COUNTER:
+ ctrl_get_tdls_counter(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_INJECT:
+ ctrl_inject(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_VERSION:
+ ctrl_version(wt, sock);
+ break;
+ case WLANTEST_CTRL_ADD_PASSPHRASE:
+ ctrl_add_passphrase(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_INFO_STA:
+ ctrl_info_sta(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_INFO_BSS:
+ ctrl_info_bss(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_SEND:
+ ctrl_send_(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_RELOG:
+ ctrl_relog(wt, sock);
+ break;
+ case WLANTEST_CTRL_GET_TX_TID:
+ ctrl_get_tx_tid(wt, sock, buf + 4, len - 4);
+ break;
+ case WLANTEST_CTRL_GET_RX_TID:
+ ctrl_get_rx_tid(wt, sock, buf + 4, len - 4);
+ break;
+ default:
+ ctrl_send_simple(wt, sock, WLANTEST_CTRL_UNKNOWN_CMD);
+ break;
+ }
+}
+
+
+static void ctrl_connect(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wlantest *wt = eloop_ctx;
+ int conn, i;
+
+ conn = accept(sock, NULL, NULL);
+ if (conn < 0) {
+ wpa_printf(MSG_INFO, "accept(ctrl): %s", strerror(errno));
+ return;
+ }
+ wpa_printf(MSG_MSGDUMP, "New control interface connection %d", conn);
+
+ for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+ if (wt->ctrl_socks[i] < 0)
+ break;
+ }
+
+ if (i == MAX_CTRL_CONNECTIONS) {
+ wpa_printf(MSG_INFO, "No room for new control connection");
+ close(conn);
+ return;
+ }
+
+ wt->ctrl_socks[i] = conn;
+ eloop_register_read_sock(conn, ctrl_read, wt, NULL);
+}
+
+
+int ctrl_init(struct wlantest *wt)
+{
+ struct sockaddr_un addr;
+
+ wt->ctrl_sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ if (wt->ctrl_sock < 0) {
+ wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
+ sizeof(addr.sun_path) - 1);
+ if (bind(wt->ctrl_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
+ close(wt->ctrl_sock);
+ wt->ctrl_sock = -1;
+ return -1;
+ }
+
+ if (listen(wt->ctrl_sock, 5) < 0) {
+ wpa_printf(MSG_ERROR, "listen: %s", strerror(errno));
+ close(wt->ctrl_sock);
+ wt->ctrl_sock = -1;
+ return -1;
+ }
+
+ if (eloop_register_read_sock(wt->ctrl_sock, ctrl_connect, wt, NULL)) {
+ close(wt->ctrl_sock);
+ wt->ctrl_sock = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void ctrl_deinit(struct wlantest *wt)
+{
+ int i;
+
+ if (wt->ctrl_sock < 0)
+ return;
+
+ for (i = 0; i < MAX_CTRL_CONNECTIONS; i++) {
+ if (wt->ctrl_socks[i] >= 0) {
+ close(wt->ctrl_socks[i]);
+ eloop_unregister_read_sock(wt->ctrl_socks[i]);
+ wt->ctrl_socks[i] = -1;
+ }
+ }
+
+ eloop_unregister_read_sock(wt->ctrl_sock);
+ close(wt->ctrl_sock);
+ wt->ctrl_sock = -1;
+}
diff --git a/contrib/wpa/wlantest/gcmp.c b/contrib/wpa/wlantest/gcmp.c
new file mode 100644
index 000000000000..d92f4edae6e7
--- /dev/null
+++ b/contrib/wpa/wlantest/gcmp.c
@@ -0,0 +1,160 @@
+/*
+ * GCM with GMAC Protocol (GCMP)
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * 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/ieee802_11_defs.h"
+#include "crypto/aes.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static void gcmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data,
+ u8 *aad, size_t *aad_len, u8 *nonce)
+{
+ u16 fc, stype, seq;
+ int qos = 0, addr4 = 0;
+ u8 *pos;
+
+ fc = le_to_host16(hdr->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+ if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+ (WLAN_FC_TODS | WLAN_FC_FROMDS))
+ addr4 = 1;
+
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+ fc &= ~0x0070; /* Mask subtype bits */
+ if (stype & 0x08) {
+ const u8 *qc;
+ qos = 1;
+ fc &= ~WLAN_FC_ORDER;
+ qc = (const u8 *) (hdr + 1);
+ if (addr4)
+ qc += ETH_ALEN;
+ }
+ }
+
+ fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+ WPA_PUT_LE16(aad, fc);
+ pos = aad + 2;
+ os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN);
+ pos += 3 * ETH_ALEN;
+ seq = le_to_host16(hdr->seq_ctrl);
+ seq &= ~0xfff0; /* Mask Seq#; do not modify Frag# */
+ WPA_PUT_LE16(pos, seq);
+ pos += 2;
+
+ os_memcpy(pos, hdr + 1, addr4 * ETH_ALEN + qos * 2);
+ pos += addr4 * ETH_ALEN;
+ if (qos) {
+ pos[0] &= ~0x70;
+ if (1 /* FIX: either device has SPP A-MSDU Capab = 0 */)
+ pos[0] &= ~0x80;
+ pos++;
+ *pos++ = 0x00;
+ }
+
+ *aad_len = pos - aad;
+
+ os_memcpy(nonce, hdr->addr2, ETH_ALEN);
+ nonce[6] = data[7]; /* PN5 */
+ nonce[7] = data[6]; /* PN4 */
+ nonce[8] = data[5]; /* PN3 */
+ nonce[9] = data[4]; /* PN2 */
+ nonce[10] = data[1]; /* PN1 */
+ nonce[11] = data[0]; /* PN0 */
+}
+
+
+u8 * gcmp_decrypt(const u8 *tk, size_t tk_len, const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+ u8 aad[30], nonce[12], *plain;
+ size_t aad_len, mlen;
+ const u8 *m;
+
+ if (data_len < 8 + 16)
+ return NULL;
+
+ plain = os_malloc(data_len + AES_BLOCK_SIZE);
+ if (plain == NULL)
+ return NULL;
+
+ m = data + 8;
+ mlen = data_len - 8 - 16;
+
+ os_memset(aad, 0, sizeof(aad));
+ gcmp_aad_nonce(hdr, data, aad, &aad_len, nonce);
+ wpa_hexdump(MSG_EXCESSIVE, "GCMP AAD", aad, aad_len);
+ wpa_hexdump(MSG_EXCESSIVE, "GCMP nonce", nonce, sizeof(nonce));
+
+ if (aes_gcm_ad(tk, tk_len, nonce, sizeof(nonce), m, mlen, aad, aad_len,
+ m + mlen, plain) < 0) {
+ u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+ wpa_printf(MSG_INFO, "Invalid GCMP frame: A1=" MACSTR
+ " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3),
+ WLAN_GET_SEQ_SEQ(seq_ctrl),
+ WLAN_GET_SEQ_FRAG(seq_ctrl));
+ os_free(plain);
+ return NULL;
+ }
+
+ *decrypted_len = mlen;
+ return plain;
+}
+
+
+u8 * gcmp_encrypt(const u8 *tk, size_t tk_len, const u8 *frame, size_t len,
+ size_t hdrlen, const u8 *qos,
+ const u8 *pn, int keyid, size_t *encrypted_len)
+{
+ u8 aad[30], nonce[12], *crypt, *pos;
+ size_t aad_len, plen;
+ struct ieee80211_hdr *hdr;
+
+ if (len < hdrlen || hdrlen < 24)
+ return NULL;
+ plen = len - hdrlen;
+
+ crypt = os_malloc(hdrlen + 8 + plen + 16 + AES_BLOCK_SIZE);
+ if (crypt == NULL)
+ return NULL;
+
+ os_memcpy(crypt, frame, hdrlen);
+ hdr = (struct ieee80211_hdr *) crypt;
+ pos = crypt + hdrlen;
+ *pos++ = pn[5]; /* PN0 */
+ *pos++ = pn[4]; /* PN1 */
+ *pos++ = 0x00; /* Rsvd */
+ *pos++ = 0x20 | (keyid << 6);
+ *pos++ = pn[3]; /* PN2 */
+ *pos++ = pn[2]; /* PN3 */
+ *pos++ = pn[1]; /* PN4 */
+ *pos++ = pn[0]; /* PN5 */
+
+ os_memset(aad, 0, sizeof(aad));
+ gcmp_aad_nonce(hdr, crypt + hdrlen, aad, &aad_len, nonce);
+ wpa_hexdump(MSG_EXCESSIVE, "GCMP AAD", aad, aad_len);
+ wpa_hexdump(MSG_EXCESSIVE, "GCMP nonce", nonce, sizeof(nonce));
+
+ if (aes_gcm_ae(tk, tk_len, nonce, sizeof(nonce), frame + hdrlen, plen,
+ aad, aad_len, pos, pos + plen) < 0) {
+ os_free(crypt);
+ return NULL;
+ }
+
+ wpa_hexdump(MSG_EXCESSIVE, "GCMP MIC", pos + plen, 16);
+ wpa_hexdump(MSG_EXCESSIVE, "GCMP encrypted", pos, plen);
+
+ *encrypted_len = hdrlen + 8 + plen + 16;
+
+ return crypt;
+}
diff --git a/contrib/wpa/wlantest/inject.c b/contrib/wpa/wlantest/inject.c
new file mode 100644
index 000000000000..399f1a3c0707
--- /dev/null
+++ b/contrib/wpa/wlantest/inject.c
@@ -0,0 +1,341 @@
+/*
+ * wlantest frame injection
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
+ *
+ * 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/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static int inject_frame(int s, const void *data, size_t len)
+{
+#define IEEE80211_RADIOTAP_F_FRAG 0x08
+ unsigned char rtap_hdr[] = {
+ 0x00, 0x00, /* radiotap version */
+ 0x0e, 0x00, /* radiotap length */
+ 0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+ IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
+ 0x00, /* padding */
+ 0x00, 0x00, /* RX and TX flags to indicate that */
+ 0x00, 0x00, /* this is the injected frame directly */
+ };
+ struct iovec iov[2] = {
+ {
+ .iov_base = &rtap_hdr,
+ .iov_len = sizeof(rtap_hdr),
+ },
+ {
+ .iov_base = (void *) data,
+ .iov_len = len,
+ }
+ };
+ struct msghdr msg = {
+ .msg_name = NULL,
+ .msg_namelen = 0,
+ .msg_iov = iov,
+ .msg_iovlen = 2,
+ .msg_control = NULL,
+ .msg_controllen = 0,
+ .msg_flags = 0,
+ };
+ int ret;
+
+ ret = sendmsg(s, &msg, 0);
+ if (ret < 0)
+ wpa_printf(MSG_ERROR, "sendmsg: %s", strerror(errno));
+ return ret;
+}
+
+
+static int is_robust_mgmt(u8 *frame, size_t len)
+{
+ struct ieee80211_mgmt *mgmt;
+ u16 fc, stype;
+ if (len < 24)
+ return 0;
+ mgmt = (struct ieee80211_mgmt *) frame;
+ fc = le_to_host16(mgmt->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
+ return 0;
+ stype = WLAN_FC_GET_STYPE(fc);
+ if (stype == WLAN_FC_STYPE_DEAUTH || stype == WLAN_FC_STYPE_DISASSOC)
+ return 1;
+ if (stype == WLAN_FC_STYPE_ACTION ||
+ stype == WLAN_FC_STYPE_ACTION_NO_ACK) {
+ if (len < 25)
+ return 0;
+ if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+ return 1;
+ }
+ return 0;
+}
+
+
+static int wlantest_inject_bip(struct wlantest *wt, struct wlantest_bss *bss,
+ u8 *frame, size_t len, int incorrect_key)
+{
+ u8 *prot;
+ u8 dummy[32];
+ int ret;
+ size_t plen;
+
+ if (!bss->igtk_len[bss->igtk_idx])
+ return -1;
+
+ os_memset(dummy, 0x11, sizeof(dummy));
+ inc_byte_array(bss->ipn[bss->igtk_idx], 6);
+
+ prot = bip_protect(incorrect_key ? dummy : bss->igtk[bss->igtk_idx],
+ bss->igtk_len[bss->igtk_idx],
+ frame, len, bss->ipn[bss->igtk_idx],
+ bss->igtk_idx, &plen);
+ if (prot == NULL)
+ return -1;
+
+
+ ret = inject_frame(wt->monitor_sock, prot, plen);
+ os_free(prot);
+
+ return (ret < 0) ? -1 : 0;
+}
+
+
+static int wlantest_inject_prot_bc(struct wlantest *wt,
+ struct wlantest_bss *bss,
+ u8 *frame, size_t len, int incorrect_key)
+{
+ u8 *crypt;
+ size_t crypt_len;
+ int ret;
+ u8 dummy[64];
+ u8 *pn;
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+ int hdrlen;
+
+ hdr = (struct ieee80211_hdr *) frame;
+ hdrlen = 24;
+ fc = le_to_host16(hdr->frame_control);
+
+ if (!bss->gtk_len[bss->gtk_idx])
+ return -1;
+
+ if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+ (WLAN_FC_TODS | WLAN_FC_FROMDS))
+ hdrlen += ETH_ALEN;
+ pn = bss->rsc[bss->gtk_idx];
+ inc_byte_array(pn, 6);
+
+ os_memset(dummy, 0x11, sizeof(dummy));
+ if (bss->group_cipher == WPA_CIPHER_TKIP)
+ crypt = tkip_encrypt(incorrect_key ? dummy :
+ bss->gtk[bss->gtk_idx],
+ frame, len, hdrlen, NULL, pn,
+ bss->gtk_idx, &crypt_len);
+ else
+ crypt = ccmp_encrypt(incorrect_key ? dummy :
+ bss->gtk[bss->gtk_idx],
+ frame, len, hdrlen, NULL, pn,
+ bss->gtk_idx, &crypt_len);
+
+ if (crypt == NULL)
+ return -1;
+
+ ret = inject_frame(wt->monitor_sock, crypt, crypt_len);
+ os_free(crypt);
+
+ return (ret < 0) ? -1 : 0;
+}
+
+
+static int wlantest_inject_prot(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, u8 *frame,
+ size_t len, int incorrect_key)
+{
+ u8 *crypt;
+ size_t crypt_len;
+ int ret;
+ u8 dummy[64];
+ u8 *pn;
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+ int tid = 0;
+ u8 *qos = NULL;
+ int hdrlen;
+ struct wlantest_tdls *tdls = NULL;
+ const u8 *tk = NULL;
+
+ hdr = (struct ieee80211_hdr *) frame;
+ hdrlen = 24;
+ fc = le_to_host16(hdr->frame_control);
+
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+ (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == 0) {
+ struct wlantest_sta *sta2;
+ bss = bss_get(wt, hdr->addr3);
+ if (bss == NULL) {
+ wpa_printf(MSG_DEBUG, "No BSS found for TDLS "
+ "injection");
+ return -1;
+ }
+ sta = sta_find(bss, hdr->addr2);
+ sta2 = sta_find(bss, hdr->addr1);
+ if (sta == NULL || sta2 == NULL) {
+ wpa_printf(MSG_DEBUG, "No stations found for TDLS "
+ "injection");
+ return -1;
+ }
+ dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list)
+ {
+ if ((tdls->init == sta && tdls->resp == sta2) ||
+ (tdls->init == sta2 && tdls->resp == sta)) {
+ if (!tdls->link_up)
+ wpa_printf(MSG_DEBUG, "TDLS: Link not "
+ "up, but injecting Data "
+ "frame on direct link");
+ tk = tdls->tpk.tk;
+ break;
+ }
+ }
+ }
+
+ if (tk == NULL && sta == NULL) {
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+ return wlantest_inject_bip(wt, bss, frame, len,
+ incorrect_key);
+ return wlantest_inject_prot_bc(wt, bss, frame, len,
+ incorrect_key);
+ }
+
+ if (tk == NULL && !sta->ptk_set) {
+ wpa_printf(MSG_DEBUG, "No key known for injection");
+ return -1;
+ }
+
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+ tid = 16;
+ else if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA) {
+ if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+ (WLAN_FC_TODS | WLAN_FC_FROMDS))
+ hdrlen += ETH_ALEN;
+ if (WLAN_FC_GET_STYPE(fc) & 0x08) {
+ qos = frame + hdrlen;
+ hdrlen += 2;
+ tid = qos[0] & 0x0f;
+ }
+ }
+ if (tk) {
+ if (os_memcmp(hdr->addr2, tdls->init->addr, ETH_ALEN) == 0)
+ pn = tdls->rsc_init[tid];
+ else
+ pn = tdls->rsc_resp[tid];
+ } else if (os_memcmp(hdr->addr2, bss->bssid, ETH_ALEN) == 0)
+ pn = sta->rsc_fromds[tid];
+ else
+ pn = sta->rsc_tods[tid];
+ inc_byte_array(pn, 6);
+
+ os_memset(dummy, 0x11, sizeof(dummy));
+ if (tk)
+ crypt = ccmp_encrypt(incorrect_key ? dummy : tk,
+ frame, len, hdrlen, qos, pn, 0,
+ &crypt_len);
+ else if (sta->pairwise_cipher == WPA_CIPHER_TKIP)
+ crypt = tkip_encrypt(incorrect_key ? dummy : sta->ptk.tk,
+ frame, len, hdrlen, qos, pn, 0,
+ &crypt_len);
+ else
+ crypt = ccmp_encrypt(incorrect_key ? dummy : sta->ptk.tk,
+ frame, len, hdrlen, qos, pn, 0,
+ &crypt_len);
+
+ if (crypt == NULL) {
+ wpa_printf(MSG_DEBUG, "Frame encryption failed");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "Inject frame (encrypted)", crypt, crypt_len);
+ ret = inject_frame(wt->monitor_sock, crypt, crypt_len);
+ os_free(crypt);
+ wpa_printf(MSG_DEBUG, "inject_frame for protected frame: %d", ret);
+
+ return (ret < 0) ? -1 : 0;
+}
+
+
+int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, u8 *frame, size_t len,
+ enum wlantest_inject_protection prot)
+{
+ int ret;
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+ int protectable, protect = 0;
+
+ wpa_hexdump(MSG_DEBUG, "Inject frame", frame, len);
+ if (wt->monitor_sock < 0) {
+ wpa_printf(MSG_INFO, "Cannot inject frames when monitor "
+ "interface is not in use");
+ return -1;
+ }
+
+ if (prot != WLANTEST_INJECT_UNPROTECTED && bss == NULL) {
+ wpa_printf(MSG_INFO, "No BSS information to inject "
+ "protected frames");
+ return -1;
+ }
+
+ hdr = (struct ieee80211_hdr *) frame;
+ fc = le_to_host16(hdr->frame_control);
+ protectable = WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA ||
+ is_robust_mgmt(frame, len);
+
+ if ((prot == WLANTEST_INJECT_PROTECTED ||
+ prot == WLANTEST_INJECT_INCORRECT_KEY) && bss) {
+ if (!sta &&
+ ((WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ !bss->igtk_len[bss->igtk_idx]) ||
+ (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+ !bss->gtk_len[bss->gtk_idx]))) {
+ wpa_printf(MSG_INFO, "No GTK/IGTK known for "
+ MACSTR " to protect the injected "
+ "frame", MAC2STR(bss->bssid));
+ return -1;
+ }
+ if (sta && !sta->ptk_set) {
+ wpa_printf(MSG_INFO, "No PTK known for the STA " MACSTR
+ " to encrypt the injected frame",
+ MAC2STR(sta->addr));
+ return -1;
+ }
+ protect = 1;
+ } else if (protectable && prot != WLANTEST_INJECT_UNPROTECTED && bss) {
+ if (sta && sta->ptk_set)
+ protect = 1;
+ else if (!sta) {
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+ bss->gtk_len[bss->gtk_idx])
+ protect = 1;
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+ bss->igtk_len[bss->igtk_idx])
+ protect = 1;
+ }
+ }
+
+ if (protect && bss)
+ return wlantest_inject_prot(
+ wt, bss, sta, frame, len,
+ prot == WLANTEST_INJECT_INCORRECT_KEY);
+
+ ret = inject_frame(wt->monitor_sock, frame, len);
+ wpa_printf(MSG_DEBUG, "inject_frame for unprotected frame: %d", ret);
+ return (ret < 0) ? -1 : 0;
+}
diff --git a/contrib/wpa/wlantest/monitor.c b/contrib/wpa/wlantest/monitor.c
new file mode 100644
index 000000000000..f28708689ed9
--- /dev/null
+++ b/contrib/wpa/wlantest/monitor.c
@@ -0,0 +1,172 @@
+/*
+ * Linux packet socket monitor
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#ifndef __APPLE__
+#include <net/if.h>
+#include <netpacket/packet.h>
+#endif /* __APPLE__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "wlantest.h"
+
+
+#ifdef __APPLE__
+
+int monitor_init(struct wlantest *wt, const char *ifname)
+{
+ return -1;
+}
+
+
+int monitor_init_wired(struct wlantest *wt, const char *ifname)
+{
+ return -1;
+}
+
+
+void monitor_deinit(struct wlantest *wt)
+{
+}
+
+#else /* __APPLE__ */
+
+static void monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wlantest *wt = eloop_ctx;
+ u8 buf[3000];
+ int len;
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ wpa_printf(MSG_INFO, "recv(PACKET): %s", strerror(errno));
+ return;
+ }
+
+ clear_notes(wt);
+ os_free(wt->decrypted);
+ wt->decrypted = NULL;
+ write_pcap_captured(wt, buf, len);
+ wlantest_process(wt, buf, len);
+ write_pcapng_captured(wt, buf, len);
+}
+
+
+static void monitor_read_wired(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct wlantest *wt = eloop_ctx;
+ u8 buf[3000];
+ int len;
+
+ len = recv(sock, buf, sizeof(buf), 0);
+ if (len < 0) {
+ wpa_printf(MSG_INFO, "recv(PACKET): %s", strerror(errno));
+ return;
+ }
+
+ wlantest_process_wired(wt, buf, len);
+}
+
+
+int monitor_init(struct wlantest *wt, const char *ifname)
+{
+ struct sockaddr_ll ll;
+
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_ifindex = if_nametoindex(ifname);
+ if (ll.sll_ifindex == 0) {
+ wpa_printf(MSG_ERROR, "Monitor interface '%s' does not exist",
+ ifname);
+ return -1;
+ }
+
+ wt->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (wt->monitor_sock < 0) {
+ wpa_printf(MSG_ERROR, "socket(PF_PACKET,SOCK_RAW): %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (bind(wt->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+ wpa_printf(MSG_ERROR, "bind(PACKET): %s", strerror(errno));
+ close(wt->monitor_sock);
+ wt->monitor_sock = -1;
+ return -1;
+ }
+
+ if (eloop_register_read_sock(wt->monitor_sock, monitor_read, wt, NULL))
+ {
+ wpa_printf(MSG_ERROR, "Could not register monitor read "
+ "socket");
+ close(wt->monitor_sock);
+ wt->monitor_sock = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int monitor_init_wired(struct wlantest *wt, const char *ifname)
+{
+ struct sockaddr_ll ll;
+
+ os_memset(&ll, 0, sizeof(ll));
+ ll.sll_family = AF_PACKET;
+ ll.sll_ifindex = if_nametoindex(ifname);
+ if (ll.sll_ifindex == 0) {
+ wpa_printf(MSG_ERROR, "Monitor interface '%s' does not exist",
+ ifname);
+ return -1;
+ }
+
+ wt->monitor_wired = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+ if (wt->monitor_wired < 0) {
+ wpa_printf(MSG_ERROR, "socket(PF_PACKET,SOCK_RAW): %s",
+ strerror(errno));
+ return -1;
+ }
+
+ if (bind(wt->monitor_wired, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+ wpa_printf(MSG_ERROR, "bind(PACKET): %s", strerror(errno));
+ close(wt->monitor_wired);
+ wt->monitor_wired = -1;
+ return -1;
+ }
+
+ if (eloop_register_read_sock(wt->monitor_wired, monitor_read_wired,
+ wt, NULL)) {
+ wpa_printf(MSG_ERROR, "Could not register monitor read "
+ "socket");
+ close(wt->monitor_wired);
+ wt->monitor_wired = -1;
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void monitor_deinit(struct wlantest *wt)
+{
+ if (wt->monitor_sock >= 0) {
+ eloop_unregister_read_sock(wt->monitor_sock);
+ close(wt->monitor_sock);
+ wt->monitor_sock = -1;
+ }
+
+ if (wt->monitor_wired >= 0) {
+ eloop_unregister_read_sock(wt->monitor_wired);
+ close(wt->monitor_wired);
+ wt->monitor_wired = -1;
+ }
+}
+
+#endif /* __APPLE__ */
diff --git a/contrib/wpa/wlantest/process.c b/contrib/wpa/wlantest/process.c
new file mode 100644
index 000000000000..4d174bada947
--- /dev/null
+++ b/contrib/wpa/wlantest/process.c
@@ -0,0 +1,409 @@
+/*
+ * Received frame processing
+ * Copyright (c) 2010-2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/crc32.h"
+#include "utils/radiotap.h"
+#include "utils/radiotap_iter.h"
+#include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
+#include "wlantest.h"
+
+
+static struct wlantest_sta * rx_get_sta(struct wlantest *wt,
+ const struct ieee80211_hdr *hdr,
+ size_t len, int *to_ap)
+{
+ u16 fc;
+ const u8 *sta_addr, *bssid;
+ struct wlantest_bss *bss;
+
+ *to_ap = 0;
+ if (hdr->addr1[0] & 0x01)
+ return NULL; /* Ignore group addressed frames */
+
+ fc = le_to_host16(hdr->frame_control);
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case WLAN_FC_TYPE_MGMT:
+ if (len < 24)
+ return NULL;
+ bssid = hdr->addr3;
+ if (os_memcmp(bssid, hdr->addr2, ETH_ALEN) == 0) {
+ sta_addr = hdr->addr1;
+ *to_ap = 0;
+ } else {
+ if (os_memcmp(bssid, hdr->addr1, ETH_ALEN) != 0)
+ return NULL; /* Unsupported STA-to-STA frame */
+ sta_addr = hdr->addr2;
+ *to_ap = 1;
+ }
+ break;
+ case WLAN_FC_TYPE_DATA:
+ if (len < 24)
+ return NULL;
+ switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+ case 0:
+ return NULL; /* IBSS not supported */
+ case WLAN_FC_FROMDS:
+ sta_addr = hdr->addr1;
+ bssid = hdr->addr2;
+ *to_ap = 0;
+ break;
+ case WLAN_FC_TODS:
+ sta_addr = hdr->addr2;
+ bssid = hdr->addr1;
+ *to_ap = 1;
+ break;
+ case WLAN_FC_TODS | WLAN_FC_FROMDS:
+ return NULL; /* WDS not supported */
+ default:
+ return NULL;
+ }
+ break;
+ case WLAN_FC_TYPE_CTRL:
+ if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PSPOLL &&
+ len >= 16) {
+ sta_addr = hdr->addr2;
+ bssid = hdr->addr1;
+ *to_ap = 1;
+ } else
+ return NULL;
+ break;
+ default:
+ return NULL;
+ }
+
+ bss = bss_find(wt, bssid);
+ if (bss == NULL)
+ return NULL;
+ return sta_find(bss, sta_addr);
+}
+
+
+static void rx_update_ps(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+ size_t len, struct wlantest_sta *sta, int to_ap)
+{
+ u16 fc, type, stype;
+
+ if (sta == NULL)
+ return;
+
+ fc = le_to_host16(hdr->frame_control);
+ type = WLAN_FC_GET_TYPE(fc);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ if (!to_ap) {
+ if (sta->pwrmgt && !sta->pspoll) {
+ u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+ add_note(wt, MSG_DEBUG, "AP " MACSTR " sent a frame "
+ "(%u:%u) to a sleeping STA " MACSTR
+ " (seq=%u)",
+ MAC2STR(sta->bss->bssid),
+ type, stype, MAC2STR(sta->addr),
+ WLAN_GET_SEQ_SEQ(seq_ctrl));
+ } else
+ sta->pspoll = 0;
+ return;
+ }
+
+ sta->pspoll = 0;
+
+ if (type == WLAN_FC_TYPE_DATA || type == WLAN_FC_TYPE_MGMT ||
+ (type == WLAN_FC_TYPE_CTRL && stype == WLAN_FC_STYPE_PSPOLL)) {
+ /*
+ * In theory, the PS state changes only at the end of the frame
+ * exchange that is ACKed by the AP. However, most cases are
+ * handled with this simpler implementation that does not
+ * maintain state through the frame exchange.
+ */
+ if (sta->pwrmgt && !(fc & WLAN_FC_PWRMGT)) {
+ add_note(wt, MSG_DEBUG, "STA " MACSTR " woke up from "
+ "sleep", MAC2STR(sta->addr));
+ sta->pwrmgt = 0;
+ } else if (!sta->pwrmgt && (fc & WLAN_FC_PWRMGT)) {
+ add_note(wt, MSG_DEBUG, "STA " MACSTR " went to sleep",
+ MAC2STR(sta->addr));
+ sta->pwrmgt = 1;
+ }
+ }
+
+ if (type == WLAN_FC_TYPE_CTRL && stype == WLAN_FC_STYPE_PSPOLL)
+ sta->pspoll = 1;
+}
+
+
+static int rx_duplicate(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+ size_t len, struct wlantest_sta *sta, int to_ap)
+{
+ u16 fc;
+ int tid = 16;
+ le16 *seq_ctrl;
+
+ if (sta == NULL)
+ return 0;
+
+ fc = le_to_host16(hdr->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+ (WLAN_FC_GET_STYPE(fc) & 0x08) && len >= 26) {
+ const u8 *qos = ((const u8 *) hdr) + 24;
+ tid = qos[0] & 0x0f;
+ }
+
+ if (to_ap)
+ seq_ctrl = &sta->seq_ctrl_to_ap[tid];
+ else
+ seq_ctrl = &sta->seq_ctrl_to_sta[tid];
+
+ if ((fc & WLAN_FC_RETRY) && hdr->seq_ctrl == *seq_ctrl &&
+ !sta->allow_duplicate) {
+ u16 s = le_to_host16(hdr->seq_ctrl);
+ add_note(wt, MSG_MSGDUMP, "Ignore duplicated frame (seq=%u "
+ "frag=%u A1=" MACSTR " A2=" MACSTR ")",
+ WLAN_GET_SEQ_SEQ(s), WLAN_GET_SEQ_FRAG(s),
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2));
+ return 1;
+ }
+
+ *seq_ctrl = hdr->seq_ctrl;
+ sta->allow_duplicate = 0;
+
+ return 0;
+}
+
+
+static void rx_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr)
+{
+ struct ieee80211_hdr *last = (struct ieee80211_hdr *) wt->last_hdr;
+ u16 fc;
+
+ if (wt->last_len < 24 || (last->addr1[0] & 0x01) ||
+ os_memcmp(hdr->addr1, last->addr2, ETH_ALEN) != 0) {
+ add_note(wt, MSG_MSGDUMP, "Unknown Ack frame (previous frame "
+ "not seen)");
+ return;
+ }
+
+ /* Ack to the previous frame */
+ fc = le_to_host16(last->frame_control);
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT)
+ rx_mgmt_ack(wt, last);
+}
+
+
+static void rx_frame(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_hdr *hdr;
+ u16 fc;
+ struct wlantest_sta *sta;
+ int to_ap;
+
+ wpa_hexdump(MSG_EXCESSIVE, "RX frame", data, len);
+ if (len < 2)
+ return;
+
+ hdr = (const struct ieee80211_hdr *) data;
+ fc = le_to_host16(hdr->frame_control);
+ if (fc & WLAN_FC_PVER) {
+ wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected pver=%d",
+ fc & WLAN_FC_PVER);
+ return;
+ }
+
+ sta = rx_get_sta(wt, hdr, len, &to_ap);
+
+ switch (WLAN_FC_GET_TYPE(fc)) {
+ case WLAN_FC_TYPE_MGMT:
+ if (len < 24)
+ break;
+ if (rx_duplicate(wt, hdr, len, sta, to_ap))
+ break;
+ rx_update_ps(wt, hdr, len, sta, to_ap);
+ rx_mgmt(wt, data, len);
+ break;
+ case WLAN_FC_TYPE_CTRL:
+ if (len < 10)
+ break;
+ wt->rx_ctrl++;
+ rx_update_ps(wt, hdr, len, sta, to_ap);
+ if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACK)
+ rx_ack(wt, hdr);
+ break;
+ case WLAN_FC_TYPE_DATA:
+ if (len < 24)
+ break;
+ if (rx_duplicate(wt, hdr, len, sta, to_ap))
+ break;
+ rx_update_ps(wt, hdr, len, sta, to_ap);
+ rx_data(wt, data, len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Drop RX frame with unexpected type %d",
+ WLAN_FC_GET_TYPE(fc));
+ break;
+ }
+
+ os_memcpy(wt->last_hdr, data, len > sizeof(wt->last_hdr) ?
+ sizeof(wt->last_hdr) : len);
+ wt->last_len = len;
+}
+
+
+static void tx_status(struct wlantest *wt, const u8 *data, size_t len, int ack)
+{
+ wpa_printf(MSG_DEBUG, "TX status: ack=%d", ack);
+ wpa_hexdump(MSG_EXCESSIVE, "TX status frame", data, len);
+}
+
+
+static int check_fcs(const u8 *frame, size_t frame_len, const u8 *fcs)
+{
+ if (WPA_GET_LE32(fcs) != crc32(frame, frame_len))
+ return -1;
+ return 0;
+}
+
+
+void wlantest_process(struct wlantest *wt, const u8 *data, size_t len)
+{
+ struct ieee80211_radiotap_iterator iter;
+ int ret;
+ int rxflags = 0, txflags = 0, failed = 0, fcs = 0;
+ const u8 *frame, *fcspos;
+ size_t frame_len;
+
+ if (wt->ethernet)
+ return;
+
+ wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
+
+ if (ieee80211_radiotap_iterator_init(&iter, (void *) data, len, NULL)) {
+ add_note(wt, MSG_INFO, "Invalid radiotap frame");
+ return;
+ }
+
+ for (;;) {
+ ret = ieee80211_radiotap_iterator_next(&iter);
+ wpa_printf(MSG_EXCESSIVE, "radiotap iter: %d "
+ "this_arg_index=%d", ret, iter.this_arg_index);
+ if (ret == -ENOENT)
+ break;
+ if (ret) {
+ add_note(wt, MSG_INFO, "Invalid radiotap header: %d",
+ ret);
+ return;
+ }
+ switch (iter.this_arg_index) {
+ case IEEE80211_RADIOTAP_FLAGS:
+ if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+ fcs = 1;
+ break;
+ case IEEE80211_RADIOTAP_RX_FLAGS:
+ rxflags = 1;
+ break;
+ case IEEE80211_RADIOTAP_TX_FLAGS:
+ txflags = 1;
+ failed = le_to_host16((*(u16 *) iter.this_arg)) &
+ IEEE80211_RADIOTAP_F_TX_FAIL;
+ break;
+ case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+ if (WPA_GET_BE24(iter.this_arg) == OUI_QCA &&
+ iter.this_arg[3] == QCA_RADIOTAP_VID_WLANTEST) {
+ add_note(wt, MSG_DEBUG,
+ "Skip frame inserted by wlantest");
+ return;
+ }
+ }
+ }
+
+ frame = data + iter._max_length;
+ frame_len = len - iter._max_length;
+
+ if (fcs && frame_len >= 4) {
+ frame_len -= 4;
+ fcspos = frame + frame_len;
+ if (check_fcs(frame, frame_len, fcspos) < 0) {
+ add_note(wt, MSG_EXCESSIVE, "Drop RX frame with "
+ "invalid FCS");
+ wt->fcs_error++;
+ return;
+ }
+ }
+
+ if (rxflags && txflags)
+ return;
+ if (!txflags)
+ rx_frame(wt, frame, frame_len);
+ else {
+ add_note(wt, MSG_EXCESSIVE, "TX status - process as RX of "
+ "local frame");
+ tx_status(wt, frame, frame_len, !failed);
+ /* Process as RX frame to support local monitor interface */
+ rx_frame(wt, frame, frame_len);
+ }
+}
+
+
+void wlantest_process_prism(struct wlantest *wt, const u8 *data, size_t len)
+{
+ int fcs = 0;
+ const u8 *frame, *fcspos;
+ size_t frame_len;
+ u32 hdrlen;
+
+ wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
+
+ if (len < 8)
+ return;
+ hdrlen = WPA_GET_LE32(data + 4);
+
+ if (len < hdrlen) {
+ wpa_printf(MSG_INFO, "Too short frame to include prism "
+ "header");
+ return;
+ }
+
+ frame = data + hdrlen;
+ frame_len = len - hdrlen;
+ fcs = 1;
+
+ if (fcs && frame_len >= 4) {
+ frame_len -= 4;
+ fcspos = frame + frame_len;
+ if (check_fcs(frame, frame_len, fcspos) < 0) {
+ add_note(wt, MSG_EXCESSIVE, "Drop RX frame with "
+ "invalid FCS");
+ wt->fcs_error++;
+ return;
+ }
+ }
+
+ rx_frame(wt, frame, frame_len);
+}
+
+
+void wlantest_process_80211(struct wlantest *wt, const u8 *data, size_t len)
+{
+ wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
+
+ if (wt->assume_fcs && len >= 4) {
+ const u8 *fcspos;
+
+ len -= 4;
+ fcspos = data + len;
+ if (check_fcs(data, len, fcspos) < 0) {
+ add_note(wt, MSG_EXCESSIVE, "Drop RX frame with "
+ "invalid FCS");
+ wt->fcs_error++;
+ return;
+ }
+ }
+
+ rx_frame(wt, data, len);
+}
diff --git a/contrib/wpa/wlantest/readpcap.c b/contrib/wpa/wlantest/readpcap.c
new file mode 100644
index 000000000000..1e7e66260c30
--- /dev/null
+++ b/contrib/wpa/wlantest/readpcap.c
@@ -0,0 +1,190 @@
+/*
+ * PCAP capture file reader
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <pcap.h>
+
+#include "utils/common.h"
+#include "wlantest.h"
+
+
+static void write_pcap_with_radiotap(struct wlantest *wt,
+ const u8 *data, size_t data_len)
+{
+ struct pcap_pkthdr h;
+ u8 rtap[] = {
+ 0x00 /* rev */,
+ 0x00 /* pad */,
+ 0x0a, 0x00, /* header len */
+ 0x02, 0x00, 0x00, 0x00, /* present flags */
+ 0x00, /* flags */
+ 0x00 /* pad */
+ };
+ u8 *buf;
+ size_t len;
+
+ if (wt->assume_fcs)
+ rtap[8] |= 0x10;
+
+ os_memset(&h, 0, sizeof(h));
+ h.ts = wt->write_pcap_time;
+ len = sizeof(rtap) + data_len;
+ buf = os_malloc(len);
+ if (buf == NULL)
+ return;
+ os_memcpy(buf, rtap, sizeof(rtap));
+ os_memcpy(buf + sizeof(rtap), data, data_len);
+ h.caplen = len;
+ h.len = len;
+ pcap_dump(wt->write_pcap_dumper, &h, buf);
+ os_free(buf);
+}
+
+
+int read_cap_file(struct wlantest *wt, const char *fname)
+{
+ char errbuf[PCAP_ERRBUF_SIZE];
+ pcap_t *pcap;
+ unsigned int count = 0;
+ struct pcap_pkthdr *hdr;
+ const u_char *data;
+ int res;
+ int dlt;
+
+ pcap = pcap_open_offline(fname, errbuf);
+ if (pcap == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to read pcap file '%s': %s",
+ fname, errbuf);
+ return -1;
+ }
+ dlt = pcap_datalink(pcap);
+ if (dlt != DLT_IEEE802_11_RADIO && dlt != DLT_PRISM_HEADER &&
+ dlt != DLT_IEEE802_11) {
+ wpa_printf(MSG_ERROR, "Unsupported pcap datalink type: %d",
+ dlt);
+ pcap_close(pcap);
+ return -1;
+ }
+ wpa_printf(MSG_DEBUG, "pcap datalink type: %d", dlt);
+
+ for (;;) {
+ clear_notes(wt);
+ os_free(wt->decrypted);
+ wt->decrypted = NULL;
+
+ res = pcap_next_ex(pcap, &hdr, &data);
+ if (res == -2)
+ break; /* No more packets */
+ if (res == -1) {
+ wpa_printf(MSG_INFO, "pcap_next_ex failure: %s",
+ pcap_geterr(pcap));
+ break;
+ }
+ if (res != 1) {
+ wpa_printf(MSG_INFO, "Unexpected pcap_next_ex return "
+ "value %d", res);
+ break;
+ }
+
+ /* Packet was read without problems */
+ wt->frame_num++;
+ wpa_printf(MSG_EXCESSIVE, "pcap hdr: ts=%d.%06d "
+ "len=%u/%u",
+ (int) hdr->ts.tv_sec, (int) hdr->ts.tv_usec,
+ hdr->caplen, hdr->len);
+ if (wt->write_pcap_dumper) {
+ wt->write_pcap_time = hdr->ts;
+ if (dlt == DLT_IEEE802_11)
+ write_pcap_with_radiotap(wt, data, hdr->caplen);
+ else
+ pcap_dump(wt->write_pcap_dumper, hdr, data);
+ if (wt->pcap_no_buffer)
+ pcap_dump_flush(wt->write_pcap_dumper);
+ }
+ if (hdr->caplen < hdr->len) {
+ add_note(wt, MSG_DEBUG, "pcap: Dropped incomplete "
+ "frame (%u/%u captured)",
+ hdr->caplen, hdr->len);
+ write_pcapng_write_read(wt, dlt, hdr, data);
+ continue;
+ }
+ count++;
+ switch (dlt) {
+ case DLT_IEEE802_11_RADIO:
+ wlantest_process(wt, data, hdr->caplen);
+ break;
+ case DLT_PRISM_HEADER:
+ wlantest_process_prism(wt, data, hdr->caplen);
+ break;
+ case DLT_IEEE802_11:
+ wlantest_process_80211(wt, data, hdr->caplen);
+ break;
+ }
+ write_pcapng_write_read(wt, dlt, hdr, data);
+ }
+
+ pcap_close(pcap);
+
+ wpa_printf(MSG_DEBUG, "Read %s: %u packets", fname, count);
+
+ return 0;
+}
+
+
+int read_wired_cap_file(struct wlantest *wt, const char *fname)
+{
+ char errbuf[PCAP_ERRBUF_SIZE];
+ pcap_t *pcap;
+ unsigned int count = 0;
+ struct pcap_pkthdr *hdr;
+ const u_char *data;
+ int res;
+
+ pcap = pcap_open_offline(fname, errbuf);
+ if (pcap == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to read pcap file '%s': %s",
+ fname, errbuf);
+ return -1;
+ }
+
+ for (;;) {
+ res = pcap_next_ex(pcap, &hdr, &data);
+ if (res == -2)
+ break; /* No more packets */
+ if (res == -1) {
+ wpa_printf(MSG_INFO, "pcap_next_ex failure: %s",
+ pcap_geterr(pcap));
+ break;
+ }
+ if (res != 1) {
+ wpa_printf(MSG_INFO, "Unexpected pcap_next_ex return "
+ "value %d", res);
+ break;
+ }
+
+ /* Packet was read without problems */
+ wpa_printf(MSG_EXCESSIVE, "pcap hdr: ts=%d.%06d "
+ "len=%u/%u",
+ (int) hdr->ts.tv_sec, (int) hdr->ts.tv_usec,
+ hdr->caplen, hdr->len);
+ if (hdr->caplen < hdr->len) {
+ wpa_printf(MSG_DEBUG, "pcap: Dropped incomplete frame "
+ "(%u/%u captured)",
+ hdr->caplen, hdr->len);
+ continue;
+ }
+ count++;
+ wlantest_process_wired(wt, data, hdr->caplen);
+ }
+
+ pcap_close(pcap);
+
+ wpa_printf(MSG_DEBUG, "Read %s: %u packets", fname, count);
+
+ return 0;
+}
diff --git a/contrib/wpa/wlantest/rx_data.c b/contrib/wpa/wlantest/rx_data.c
new file mode 100644
index 000000000000..8cb2d37187eb
--- /dev/null
+++ b/contrib/wpa/wlantest/rx_data.c
@@ -0,0 +1,904 @@
+/*
+ * Received Data frame processing
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
+ *
+ * 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/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+static const char * data_stype(u16 stype)
+{
+ switch (stype) {
+ case WLAN_FC_STYPE_DATA:
+ return "DATA";
+ case WLAN_FC_STYPE_DATA_CFACK:
+ return "DATA-CFACK";
+ case WLAN_FC_STYPE_DATA_CFPOLL:
+ return "DATA-CFPOLL";
+ case WLAN_FC_STYPE_DATA_CFACKPOLL:
+ return "DATA-CFACKPOLL";
+ case WLAN_FC_STYPE_NULLFUNC:
+ return "NULLFUNC";
+ case WLAN_FC_STYPE_CFACK:
+ return "CFACK";
+ case WLAN_FC_STYPE_CFPOLL:
+ return "CFPOLL";
+ case WLAN_FC_STYPE_CFACKPOLL:
+ return "CFACKPOLL";
+ case WLAN_FC_STYPE_QOS_DATA:
+ return "QOSDATA";
+ case WLAN_FC_STYPE_QOS_DATA_CFACK:
+ return "QOSDATA-CFACK";
+ case WLAN_FC_STYPE_QOS_DATA_CFPOLL:
+ return "QOSDATA-CFPOLL";
+ case WLAN_FC_STYPE_QOS_DATA_CFACKPOLL:
+ return "QOSDATA-CFACKPOLL";
+ case WLAN_FC_STYPE_QOS_NULL:
+ return "QOS-NULL";
+ case WLAN_FC_STYPE_QOS_CFPOLL:
+ return "QOS-CFPOLL";
+ case WLAN_FC_STYPE_QOS_CFACKPOLL:
+ return "QOS-CFACKPOLL";
+ }
+ return "??";
+}
+
+
+static void rx_data_eth(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst, const u8 *src,
+ u16 ethertype, const u8 *data, size_t len, int prot,
+ const u8 *peer_addr);
+
+static void rx_data_vlan(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst, const u8 *src,
+ const u8 *data, size_t len, int prot,
+ const u8 *peer_addr)
+{
+ u16 tag;
+
+ if (len < 4)
+ return;
+ tag = WPA_GET_BE16(data);
+ wpa_printf(MSG_MSGDUMP, "VLAN tag: Priority=%u ID=%u",
+ tag >> 12, tag & 0x0ffff);
+ /* ignore VLAN information and process the original frame */
+ rx_data_eth(wt, bssid, sta_addr, dst, src, WPA_GET_BE16(data + 2),
+ data + 4, len - 4, prot, peer_addr);
+}
+
+
+static void rx_data_eth(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst, const u8 *src,
+ u16 ethertype, const u8 *data, size_t len, int prot,
+ const u8 *peer_addr)
+{
+ switch (ethertype) {
+ case ETH_P_PAE:
+ rx_data_eapol(wt, bssid, sta_addr, dst, src, data, len, prot);
+ break;
+ case ETH_P_IP:
+ rx_data_ip(wt, bssid, sta_addr, dst, src, data, len,
+ peer_addr);
+ break;
+ case 0x890d:
+ rx_data_80211_encap(wt, bssid, sta_addr, dst, src, data, len);
+ break;
+ case ETH_P_8021Q:
+ rx_data_vlan(wt, bssid, sta_addr, dst, src, data, len, prot,
+ peer_addr);
+ break;
+ }
+}
+
+
+static void rx_data_process(struct wlantest *wt, struct wlantest_bss *bss,
+ const u8 *bssid,
+ const u8 *sta_addr,
+ const u8 *dst, const u8 *src,
+ const u8 *data, size_t len, int prot,
+ const u8 *peer_addr, const u8 *qos)
+{
+ if (len == 0)
+ return;
+
+ if (bss && bss->mesh && qos && !(qos[0] & BIT(7)) &&
+ (qos[1] & BIT(0))) {
+ u8 addr_ext_mode;
+ size_t mesh_control_len = 6;
+
+ /* Skip Mesh Control field if this is not an A-MSDU */
+ if (len < mesh_control_len) {
+ wpa_printf(MSG_DEBUG,
+ "Not enough room for Mesh Control field");
+ return;
+ }
+
+ addr_ext_mode = data[0] & 0x03;
+ if (addr_ext_mode == 3) {
+ wpa_printf(MSG_DEBUG,
+ "Reserved Mesh Control :: Address Extension Mode");
+ return;
+ }
+
+ mesh_control_len += addr_ext_mode * ETH_ALEN;
+ if (len < mesh_control_len) {
+ wpa_printf(MSG_DEBUG,
+ "Not enough room for Mesh Address Extension");
+ return;
+ }
+
+ len -= mesh_control_len;
+ data += mesh_control_len;
+ }
+
+ if (len >= 8 && os_memcmp(data, "\xaa\xaa\x03\x00\x00\x00", 6) == 0) {
+ rx_data_eth(wt, bssid, sta_addr, dst, src,
+ WPA_GET_BE16(data + 6), data + 8, len - 8, prot,
+ peer_addr);
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "Unrecognized LLC", data, len > 8 ? 8 : len);
+}
+
+
+static u8 * try_ptk(int pairwise_cipher, struct wpa_ptk *ptk,
+ const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+ u8 *decrypted;
+ unsigned int tk_len = ptk->tk_len;
+
+ decrypted = NULL;
+ if ((pairwise_cipher == WPA_CIPHER_CCMP ||
+ pairwise_cipher == 0) && tk_len == 16) {
+ decrypted = ccmp_decrypt(ptk->tk, hdr, data,
+ data_len, decrypted_len);
+ } else if ((pairwise_cipher == WPA_CIPHER_CCMP_256 ||
+ pairwise_cipher == 0) && tk_len == 32) {
+ decrypted = ccmp_256_decrypt(ptk->tk, hdr, data,
+ data_len, decrypted_len);
+ } else if ((pairwise_cipher == WPA_CIPHER_GCMP ||
+ pairwise_cipher == WPA_CIPHER_GCMP_256 ||
+ pairwise_cipher == 0) &&
+ (tk_len == 16 || tk_len == 32)) {
+ decrypted = gcmp_decrypt(ptk->tk, tk_len, hdr,
+ data, data_len, decrypted_len);
+ } else if ((pairwise_cipher == WPA_CIPHER_TKIP ||
+ pairwise_cipher == 0) && tk_len == 32) {
+ decrypted = tkip_decrypt(ptk->tk, hdr, data, data_len,
+ decrypted_len);
+ }
+
+ return decrypted;
+}
+
+
+static u8 * try_all_ptk(struct wlantest *wt, int pairwise_cipher,
+ const struct ieee80211_hdr *hdr, int keyid,
+ const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+ struct wlantest_ptk *ptk;
+ u8 *decrypted;
+ int prev_level = wpa_debug_level;
+
+ wpa_debug_level = MSG_WARNING;
+ dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
+ decrypted = try_ptk(pairwise_cipher, &ptk->ptk, hdr,
+ data, data_len, decrypted_len);
+ if (decrypted) {
+ wpa_debug_level = prev_level;
+ add_note(wt, MSG_DEBUG,
+ "Found PTK match from list of all known PTKs");
+ write_decrypted_note(wt, decrypted, ptk->ptk.tk,
+ ptk->ptk.tk_len, keyid);
+ return decrypted;
+ }
+ }
+ wpa_debug_level = prev_level;
+
+ return NULL;
+}
+
+
+static void check_plaintext_prot(struct wlantest *wt,
+ const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t len)
+{
+ if (len < 8 + 3 || data[8] != 0xaa || data[9] != 0xaa ||
+ data[10] != 0x03)
+ return;
+
+ add_note(wt, MSG_DEBUG,
+ "Plaintext payload in protected frame");
+ wpa_printf(MSG_INFO, "Plaintext payload in protected frame #%u: A2="
+ MACSTR " seq=%u",
+ wt->frame_num, MAC2STR(hdr->addr2),
+ WLAN_GET_SEQ_SEQ(le_to_host16(hdr->seq_ctrl)));
+}
+
+
+static void rx_data_bss_prot_group(struct wlantest *wt,
+ const struct ieee80211_hdr *hdr,
+ size_t hdrlen,
+ const u8 *qos, const u8 *dst, const u8 *src,
+ const u8 *data, size_t len)
+{
+ struct wlantest_bss *bss;
+ int keyid;
+ u8 *decrypted = NULL;
+ size_t dlen;
+ u8 pn[6];
+ int replay = 0;
+
+ bss = bss_get(wt, hdr->addr2);
+ if (bss == NULL)
+ return;
+ if (len < 4) {
+ add_note(wt, MSG_INFO, "Too short group addressed data frame");
+ return;
+ }
+
+ if (bss->group_cipher & (WPA_CIPHER_TKIP | WPA_CIPHER_CCMP) &&
+ !(data[3] & 0x20)) {
+ add_note(wt, MSG_INFO, "Expected TKIP/CCMP frame from "
+ MACSTR " did not have ExtIV bit set to 1",
+ MAC2STR(bss->bssid));
+ return;
+ }
+
+ if (bss->group_cipher == WPA_CIPHER_TKIP) {
+ if (data[3] & 0x1f) {
+ add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+ " used non-zero reserved bit",
+ MAC2STR(bss->bssid));
+ }
+ if (data[1] != ((data[0] | 0x20) & 0x7f)) {
+ add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+ " used incorrect WEPSeed[1] (was 0x%x, "
+ "expected 0x%x)",
+ MAC2STR(bss->bssid), data[1],
+ (data[0] | 0x20) & 0x7f);
+ }
+ } else if (bss->group_cipher == WPA_CIPHER_CCMP) {
+ if (data[2] != 0 || (data[3] & 0x1f) != 0) {
+ add_note(wt, MSG_INFO, "CCMP frame from " MACSTR
+ " used non-zero reserved bit",
+ MAC2STR(bss->bssid));
+ }
+ }
+
+ check_plaintext_prot(wt, hdr, data, len);
+ keyid = data[3] >> 6;
+ if (bss->gtk_len[keyid] == 0 &&
+ (bss->group_cipher != WPA_CIPHER_WEP40 ||
+ dl_list_empty(&wt->wep))) {
+ decrypted = try_all_ptk(wt, bss->group_cipher, hdr, keyid,
+ data, len, &dlen);
+ if (decrypted)
+ goto process;
+ add_note(wt, MSG_MSGDUMP,
+ "No GTK known to decrypt the frame (A2=" MACSTR
+ " KeyID=%d)",
+ MAC2STR(hdr->addr2), keyid);
+ return;
+ }
+
+ if (bss->group_cipher == WPA_CIPHER_TKIP)
+ tkip_get_pn(pn, data);
+ else if (bss->group_cipher == WPA_CIPHER_WEP40)
+ goto skip_replay_det;
+ else
+ ccmp_get_pn(pn, data);
+ if (os_memcmp(pn, bss->rsc[keyid], 6) <= 0) {
+ u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+ char pn_hex[6 * 2 + 1], rsc_hex[6 * 2 + 1];
+
+ wpa_snprintf_hex(pn_hex, sizeof(pn_hex), pn, 6);
+ wpa_snprintf_hex(rsc_hex, sizeof(rsc_hex), bss->rsc[keyid], 6);
+ add_note(wt, MSG_INFO, "replay detected: A1=" MACSTR
+ " A2=" MACSTR " A3=" MACSTR
+ " seq=%u frag=%u%s keyid=%d #%u %s<=%s",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3),
+ WLAN_GET_SEQ_SEQ(seq_ctrl),
+ WLAN_GET_SEQ_FRAG(seq_ctrl),
+ (le_to_host16(hdr->frame_control) & WLAN_FC_RETRY) ?
+ " Retry" : "",
+ keyid, wt->frame_num, pn_hex, rsc_hex);
+ replay = 1;
+ }
+
+skip_replay_det:
+ if (bss->group_cipher == WPA_CIPHER_TKIP)
+ decrypted = tkip_decrypt(bss->gtk[keyid], hdr, data, len,
+ &dlen);
+ else if (bss->group_cipher == WPA_CIPHER_WEP40)
+ decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
+ else if (bss->group_cipher == WPA_CIPHER_CCMP)
+ decrypted = ccmp_decrypt(bss->gtk[keyid], hdr, data, len,
+ &dlen);
+ else if (bss->group_cipher == WPA_CIPHER_CCMP_256)
+ decrypted = ccmp_256_decrypt(bss->gtk[keyid], hdr, data, len,
+ &dlen);
+ else if (bss->group_cipher == WPA_CIPHER_GCMP ||
+ bss->group_cipher == WPA_CIPHER_GCMP_256)
+ decrypted = gcmp_decrypt(bss->gtk[keyid], bss->gtk_len[keyid],
+ hdr, data, len, &dlen);
+
+ if (decrypted) {
+ char gtk[65];
+
+ wpa_snprintf_hex(gtk, sizeof(gtk), bss->gtk[keyid],
+ bss->gtk_len[keyid]);
+ add_note(wt, MSG_EXCESSIVE, "GTK[%d] %s", keyid, gtk);
+ process:
+ rx_data_process(wt, bss, bss->bssid, NULL, dst, src, decrypted,
+ dlen, 1, NULL, qos);
+ if (!replay)
+ os_memcpy(bss->rsc[keyid], pn, 6);
+ write_pcap_decrypted(wt, (const u8 *) hdr, hdrlen,
+ decrypted, dlen);
+ } else {
+ wpa_printf(MSG_DEBUG, "Failed to decrypt frame (group) #%u A2="
+ MACSTR " seq=%u",
+ wt->frame_num, MAC2STR(hdr->addr2),
+ WLAN_GET_SEQ_SEQ(le_to_host16(hdr->seq_ctrl)));
+ add_note(wt, MSG_DEBUG, "Failed to decrypt frame (group)");
+ }
+ os_free(decrypted);
+}
+
+
+static u8 * try_ptk_decrypt(struct wlantest *wt, struct wlantest_sta *sta,
+ const struct ieee80211_hdr *hdr, int keyid,
+ const u8 *data, size_t len,
+ const u8 *tk, size_t tk_len, size_t *dlen)
+{
+ u8 *decrypted = NULL;
+
+ if (sta->pairwise_cipher == WPA_CIPHER_CCMP_256)
+ decrypted = ccmp_256_decrypt(tk, hdr, data, len, dlen);
+ else if (sta->pairwise_cipher == WPA_CIPHER_GCMP ||
+ sta->pairwise_cipher == WPA_CIPHER_GCMP_256)
+ decrypted = gcmp_decrypt(tk, tk_len, hdr, data, len, dlen);
+ else
+ decrypted = ccmp_decrypt(tk, hdr, data, len, dlen);
+ write_decrypted_note(wt, decrypted, tk, tk_len, keyid);
+
+ return decrypted;
+}
+
+
+static void rx_data_bss_prot(struct wlantest *wt,
+ const struct ieee80211_hdr *hdr, size_t hdrlen,
+ const u8 *qos, const u8 *dst, const u8 *src,
+ const u8 *data, size_t len)
+{
+ struct wlantest_bss *bss, *bss2;
+ struct wlantest_sta *sta, *sta2;
+ int keyid;
+ u16 fc = le_to_host16(hdr->frame_control);
+ u8 *decrypted = NULL;
+ size_t dlen;
+ int tid;
+ u8 pn[6], *rsc = NULL;
+ struct wlantest_tdls *tdls = NULL, *found;
+ const u8 *tk = NULL;
+ int ptk_iter_done = 0;
+ int try_ptk_iter = 0;
+ int replay = 0;
+ int only_zero_tk = 0;
+ u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+
+ if (hdr->addr1[0] & 0x01) {
+ rx_data_bss_prot_group(wt, hdr, hdrlen, qos, dst, src,
+ data, len);
+ return;
+ }
+
+ if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+ (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+ bss = bss_find(wt, hdr->addr1);
+ if (bss) {
+ sta = sta_find(bss, hdr->addr2);
+ if (sta) {
+ sta->counters[
+ WLANTEST_STA_COUNTER_PROT_DATA_TX]++;
+ }
+ if (!sta || !sta->ptk_set) {
+ bss2 = bss_find(wt, hdr->addr2);
+ if (bss2) {
+ sta2 = sta_find(bss2, hdr->addr1);
+ if (sta2 && (!sta || sta2->ptk_set)) {
+ bss = bss2;
+ sta = sta2;
+ }
+ }
+ }
+ } else {
+ bss = bss_find(wt, hdr->addr2);
+ if (!bss)
+ return;
+ sta = sta_find(bss, hdr->addr1);
+ }
+ } else if (fc & WLAN_FC_TODS) {
+ bss = bss_get(wt, hdr->addr1);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, hdr->addr2);
+ if (sta)
+ sta->counters[WLANTEST_STA_COUNTER_PROT_DATA_TX]++;
+ } else if (fc & WLAN_FC_FROMDS) {
+ bss = bss_get(wt, hdr->addr2);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, hdr->addr1);
+ } else {
+ bss = bss_get(wt, hdr->addr3);
+ if (bss == NULL)
+ return;
+ sta = sta_find(bss, hdr->addr2);
+ sta2 = sta_find(bss, hdr->addr1);
+ if (sta == NULL || sta2 == NULL)
+ return;
+ found = NULL;
+ dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list)
+ {
+ if ((tdls->init == sta && tdls->resp == sta2) ||
+ (tdls->init == sta2 && tdls->resp == sta)) {
+ found = tdls;
+ if (tdls->link_up)
+ break;
+ }
+ }
+ if (found) {
+ if (!found->link_up)
+ add_note(wt, MSG_DEBUG,
+ "TDLS: Link not up, but Data "
+ "frame seen");
+ tk = found->tpk.tk;
+ tdls = found;
+ }
+ }
+ check_plaintext_prot(wt, hdr, data, len);
+ if ((sta == NULL ||
+ (!sta->ptk_set && sta->pairwise_cipher != WPA_CIPHER_WEP40)) &&
+ tk == NULL) {
+ add_note(wt, MSG_MSGDUMP, "No PTK known to decrypt the frame");
+ if (dl_list_empty(&wt->ptk)) {
+ if (len >= 4 && sta) {
+ keyid = data[3] >> 6;
+ only_zero_tk = 1;
+ goto check_zero_tk;
+ }
+ return;
+ }
+
+ try_ptk_iter = 1;
+ }
+
+ if (len < 4) {
+ add_note(wt, MSG_INFO, "Too short encrypted data frame");
+ return;
+ }
+
+ if (sta == NULL)
+ return;
+ if (sta->pairwise_cipher & (WPA_CIPHER_TKIP | WPA_CIPHER_CCMP) &&
+ !(data[3] & 0x20)) {
+ add_note(wt, MSG_INFO, "Expected TKIP/CCMP frame from "
+ MACSTR " did not have ExtIV bit set to 1",
+ MAC2STR(src));
+ return;
+ }
+
+ if (tk == NULL && sta->pairwise_cipher == WPA_CIPHER_TKIP) {
+ if (data[3] & 0x1f) {
+ add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+ " used non-zero reserved bit",
+ MAC2STR(hdr->addr2));
+ }
+ if (data[1] != ((data[0] | 0x20) & 0x7f)) {
+ add_note(wt, MSG_INFO, "TKIP frame from " MACSTR
+ " used incorrect WEPSeed[1] (was 0x%x, "
+ "expected 0x%x)",
+ MAC2STR(hdr->addr2), data[1],
+ (data[0] | 0x20) & 0x7f);
+ }
+ } else if (tk || sta->pairwise_cipher == WPA_CIPHER_CCMP) {
+ if (data[2] != 0 || (data[3] & 0x1f) != 0) {
+ add_note(wt, MSG_INFO, "CCMP frame from " MACSTR
+ " used non-zero reserved bit",
+ MAC2STR(hdr->addr2));
+ }
+ }
+
+ keyid = data[3] >> 6;
+ if (keyid != 0 &&
+ (!(sta->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) ||
+ !(bss->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST) ||
+ keyid != 1)) {
+ add_note(wt, MSG_INFO,
+ "Unexpected KeyID %d in individually addressed Data frame from "
+ MACSTR,
+ keyid, MAC2STR(hdr->addr2));
+ }
+
+ if (qos) {
+ tid = qos[0] & 0x0f;
+ if (fc & WLAN_FC_TODS)
+ sta->tx_tid[tid]++;
+ else
+ sta->rx_tid[tid]++;
+ } else {
+ tid = 0;
+ if (fc & WLAN_FC_TODS)
+ sta->tx_tid[16]++;
+ else
+ sta->rx_tid[16]++;
+ }
+ if (tk) {
+ if (os_memcmp(hdr->addr2, tdls->init->addr, ETH_ALEN) == 0)
+ rsc = tdls->rsc_init[tid];
+ else
+ rsc = tdls->rsc_resp[tid];
+ } else if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+ (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+ if (os_memcmp(sta->addr, hdr->addr2, ETH_ALEN) == 0)
+ rsc = sta->rsc_tods[tid];
+ else
+ rsc = sta->rsc_fromds[tid];
+ } else if (fc & WLAN_FC_TODS)
+ rsc = sta->rsc_tods[tid];
+ else
+ rsc = sta->rsc_fromds[tid];
+
+
+ if (tk == NULL && sta->pairwise_cipher == WPA_CIPHER_TKIP)
+ tkip_get_pn(pn, data);
+ else if (sta->pairwise_cipher == WPA_CIPHER_WEP40)
+ goto skip_replay_det;
+ else
+ ccmp_get_pn(pn, data);
+ if (os_memcmp(pn, rsc, 6) <= 0) {
+ char pn_hex[6 * 2 + 1], rsc_hex[6 * 2 + 1];
+
+ wpa_snprintf_hex(pn_hex, sizeof(pn_hex), pn, 6);
+ wpa_snprintf_hex(rsc_hex, sizeof(rsc_hex), rsc, 6);
+ add_note(wt, MSG_INFO, "replay detected: A1=" MACSTR
+ " A2=" MACSTR " A3=" MACSTR
+ " seq=%u frag=%u%s keyid=%d tid=%d #%u %s<=%s",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3),
+ WLAN_GET_SEQ_SEQ(seq_ctrl),
+ WLAN_GET_SEQ_FRAG(seq_ctrl),
+ (le_to_host16(hdr->frame_control) & WLAN_FC_RETRY) ?
+ " Retry" : "",
+ keyid, tid, wt->frame_num, pn_hex, rsc_hex);
+ replay = 1;
+ }
+
+skip_replay_det:
+ if (tk) {
+ if (sta->pairwise_cipher == WPA_CIPHER_CCMP_256) {
+ decrypted = ccmp_256_decrypt(tk, hdr, data, len, &dlen);
+ write_decrypted_note(wt, decrypted, tk, 32, keyid);
+ } else if (sta->pairwise_cipher == WPA_CIPHER_GCMP ||
+ sta->pairwise_cipher == WPA_CIPHER_GCMP_256) {
+ decrypted = gcmp_decrypt(tk, sta->ptk.tk_len, hdr, data,
+ len, &dlen);
+ write_decrypted_note(wt, decrypted, tk, sta->ptk.tk_len,
+ keyid);
+ } else {
+ decrypted = ccmp_decrypt(tk, hdr, data, len, &dlen);
+ write_decrypted_note(wt, decrypted, tk, 16, keyid);
+ }
+ } else if (sta->pairwise_cipher == WPA_CIPHER_TKIP) {
+ decrypted = tkip_decrypt(sta->ptk.tk, hdr, data, len, &dlen);
+ write_decrypted_note(wt, decrypted, sta->ptk.tk, 32, keyid);
+ } else if (sta->pairwise_cipher == WPA_CIPHER_WEP40) {
+ decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
+ } else if (sta->ptk_set) {
+ decrypted = try_ptk_decrypt(wt, sta, hdr, keyid, data, len,
+ sta->ptk.tk, sta->ptk.tk_len,
+ &dlen);
+ } else {
+ decrypted = try_all_ptk(wt, sta->pairwise_cipher, hdr, keyid,
+ data, len, &dlen);
+ ptk_iter_done = 1;
+ }
+ if (!decrypted && !ptk_iter_done) {
+ decrypted = try_all_ptk(wt, sta->pairwise_cipher, hdr, keyid,
+ data, len, &dlen);
+ if (decrypted) {
+ add_note(wt, MSG_DEBUG, "Current PTK did not work, but found a match from all known PTKs");
+ }
+ }
+check_zero_tk:
+ if (!decrypted) {
+ struct wpa_ptk zero_ptk;
+ int old_debug_level = wpa_debug_level;
+
+ os_memset(&zero_ptk, 0, sizeof(zero_ptk));
+ zero_ptk.tk_len = wpa_cipher_key_len(sta->pairwise_cipher);
+ wpa_debug_level = MSG_ERROR;
+ decrypted = try_ptk(sta->pairwise_cipher, &zero_ptk, hdr,
+ data, len, &dlen);
+ wpa_debug_level = old_debug_level;
+ if (decrypted) {
+ add_note(wt, MSG_DEBUG,
+ "Frame was encrypted with zero TK");
+ wpa_printf(MSG_INFO, "Zero TK used in frame #%u: A2="
+ MACSTR " seq=%u",
+ wt->frame_num, MAC2STR(hdr->addr2),
+ WLAN_GET_SEQ_SEQ(
+ le_to_host16(hdr->seq_ctrl)));
+ write_decrypted_note(wt, decrypted, zero_ptk.tk,
+ zero_ptk.tk_len, keyid);
+ }
+ }
+ if (decrypted) {
+ u16 fc = le_to_host16(hdr->frame_control);
+ const u8 *peer_addr = NULL;
+ if (!(fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)))
+ peer_addr = hdr->addr1;
+ if (!replay && rsc)
+ os_memcpy(rsc, pn, 6);
+ rx_data_process(wt, bss, bss->bssid, sta->addr, dst, src,
+ decrypted, dlen, 1, peer_addr, qos);
+ write_pcap_decrypted(wt, (const u8 *) hdr, hdrlen,
+ decrypted, dlen);
+ } else if (sta->tptk_set) {
+ /* Check whether TPTK has a matching TK that could be used to
+ * decrypt the frame. That could happen if EAPOL-Key msg 4/4
+ * was missing in the capture and this was PTK rekeying. */
+ decrypted = try_ptk_decrypt(wt, sta, hdr, keyid, data, len,
+ sta->tptk.tk, sta->tptk.tk_len,
+ &dlen);
+ if (decrypted) {
+ add_note(wt, MSG_DEBUG,
+ "Update PTK (rekeying; no valid EAPOL-Key msg 4/4 seen)");
+ os_memcpy(&sta->ptk, &sta->tptk, sizeof(sta->ptk));
+ sta->ptk_set = 1;
+ sta->tptk_set = 0;
+ os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+ os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+ }
+ } else {
+ if (!try_ptk_iter && !only_zero_tk) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to decrypt frame #%u A2=" MACSTR
+ " seq=%u",
+ wt->frame_num, MAC2STR(hdr->addr2),
+ WLAN_GET_SEQ_SEQ(seq_ctrl));
+ add_note(wt, MSG_DEBUG, "Failed to decrypt frame");
+ }
+
+ /* Assume the frame was corrupted and there was no FCS to check.
+ * Allow retry of this particular frame to be processed so that
+ * it could end up getting decrypted if it was received without
+ * corruption. */
+ sta->allow_duplicate = 1;
+ }
+ os_free(decrypted);
+}
+
+
+static void rx_data_bss(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+ size_t hdrlen, const u8 *qos, const u8 *dst,
+ const u8 *src, const u8 *data, size_t len)
+{
+ u16 fc = le_to_host16(hdr->frame_control);
+ int prot = !!(fc & WLAN_FC_ISWEP);
+
+ if (qos) {
+ u8 ack = (qos[0] & 0x60) >> 5;
+ wpa_printf(MSG_MSGDUMP, "BSS DATA: " MACSTR " -> " MACSTR
+ " len=%u%s tid=%u%s%s",
+ MAC2STR(src), MAC2STR(dst), (unsigned int) len,
+ prot ? " Prot" : "", qos[0] & 0x0f,
+ (qos[0] & 0x10) ? " EOSP" : "",
+ ack == 0 ? "" :
+ (ack == 1 ? " NoAck" :
+ (ack == 2 ? " NoExpAck" : " BA")));
+ } else {
+ wpa_printf(MSG_MSGDUMP, "BSS DATA: " MACSTR " -> " MACSTR
+ " len=%u%s",
+ MAC2STR(src), MAC2STR(dst), (unsigned int) len,
+ prot ? " Prot" : "");
+ }
+
+ if (prot)
+ rx_data_bss_prot(wt, hdr, hdrlen, qos, dst, src, data, len);
+ else {
+ const u8 *bssid, *sta_addr, *peer_addr;
+ struct wlantest_bss *bss;
+
+ if (fc & WLAN_FC_TODS) {
+ bssid = hdr->addr1;
+ sta_addr = hdr->addr2;
+ peer_addr = NULL;
+ } else if (fc & WLAN_FC_FROMDS) {
+ bssid = hdr->addr2;
+ sta_addr = hdr->addr1;
+ peer_addr = NULL;
+ } else {
+ bssid = hdr->addr3;
+ sta_addr = hdr->addr2;
+ peer_addr = hdr->addr1;
+ }
+
+ bss = bss_get(wt, bssid);
+ if (bss) {
+ struct wlantest_sta *sta = sta_get(bss, sta_addr);
+
+ if (sta) {
+ if (qos) {
+ int tid = qos[0] & 0x0f;
+ if (fc & WLAN_FC_TODS)
+ sta->tx_tid[tid]++;
+ else
+ sta->rx_tid[tid]++;
+ } else {
+ if (fc & WLAN_FC_TODS)
+ sta->tx_tid[16]++;
+ else
+ sta->rx_tid[16]++;
+ }
+ }
+ }
+
+ rx_data_process(wt, bss, bssid, sta_addr, dst, src, data, len,
+ 0, peer_addr, qos);
+ }
+}
+
+
+static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta1_addr,
+ const u8 *sta2_addr)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta1, *sta2;
+ struct wlantest_tdls *tdls, *found = NULL;
+
+ bss = bss_find(wt, bssid);
+ if (bss == NULL)
+ return NULL;
+ sta1 = sta_find(bss, sta1_addr);
+ if (sta1 == NULL)
+ return NULL;
+ sta2 = sta_find(bss, sta2_addr);
+ if (sta2 == NULL)
+ return NULL;
+
+ dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+ if ((tdls->init == sta1 && tdls->resp == sta2) ||
+ (tdls->init == sta2 && tdls->resp == sta1)) {
+ found = tdls;
+ if (tdls->link_up)
+ break;
+ }
+ }
+
+ return found;
+}
+
+
+static void add_direct_link(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta1_addr, const u8 *sta2_addr)
+{
+ struct wlantest_tdls *tdls;
+
+ tdls = get_tdls(wt, bssid, sta1_addr, sta2_addr);
+ if (tdls == NULL)
+ return;
+
+ if (tdls->link_up)
+ tdls->counters[WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK]++;
+ else
+ tdls->counters[WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK]++;
+}
+
+
+static void add_ap_path(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta1_addr, const u8 *sta2_addr)
+{
+ struct wlantest_tdls *tdls;
+
+ tdls = get_tdls(wt, bssid, sta1_addr, sta2_addr);
+ if (tdls == NULL)
+ return;
+
+ if (tdls->link_up)
+ tdls->counters[WLANTEST_TDLS_COUNTER_INVALID_AP_PATH]++;
+ else
+ tdls->counters[WLANTEST_TDLS_COUNTER_VALID_AP_PATH]++;
+}
+
+
+void rx_data(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_hdr *hdr;
+ u16 fc, stype;
+ size_t hdrlen;
+ const u8 *qos = NULL;
+
+ if (len < 24)
+ return;
+
+ hdr = (const struct ieee80211_hdr *) data;
+ fc = le_to_host16(hdr->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+ hdrlen = 24;
+ if ((fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) ==
+ (WLAN_FC_TODS | WLAN_FC_FROMDS))
+ hdrlen += ETH_ALEN;
+ if (stype & 0x08) {
+ qos = data + hdrlen;
+ hdrlen += 2;
+ }
+ if (len < hdrlen)
+ return;
+ wt->rx_data++;
+
+ switch (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) {
+ case 0:
+ wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s IBSS DA=" MACSTR " SA="
+ MACSTR " BSSID=" MACSTR,
+ data_stype(WLAN_FC_GET_STYPE(fc)),
+ fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+ fc & WLAN_FC_ISWEP ? " Prot" : "",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3));
+ add_direct_link(wt, hdr->addr3, hdr->addr1, hdr->addr2);
+ rx_data_bss(wt, hdr, hdrlen, qos, hdr->addr1, hdr->addr2,
+ data + hdrlen, len - hdrlen);
+ break;
+ case WLAN_FC_FROMDS:
+ wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s FromDS DA=" MACSTR
+ " BSSID=" MACSTR " SA=" MACSTR,
+ data_stype(WLAN_FC_GET_STYPE(fc)),
+ fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+ fc & WLAN_FC_ISWEP ? " Prot" : "",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3));
+ add_ap_path(wt, hdr->addr2, hdr->addr1, hdr->addr3);
+ rx_data_bss(wt, hdr, hdrlen, qos, hdr->addr1, hdr->addr3,
+ data + hdrlen, len - hdrlen);
+ break;
+ case WLAN_FC_TODS:
+ wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s ToDS BSSID=" MACSTR
+ " SA=" MACSTR " DA=" MACSTR,
+ data_stype(WLAN_FC_GET_STYPE(fc)),
+ fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+ fc & WLAN_FC_ISWEP ? " Prot" : "",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3));
+ add_ap_path(wt, hdr->addr1, hdr->addr3, hdr->addr2);
+ rx_data_bss(wt, hdr, hdrlen, qos, hdr->addr3, hdr->addr2,
+ data + hdrlen, len - hdrlen);
+ break;
+ case WLAN_FC_TODS | WLAN_FC_FROMDS:
+ wpa_printf(MSG_EXCESSIVE, "DATA %s%s%s WDS RA=" MACSTR " TA="
+ MACSTR " DA=" MACSTR " SA=" MACSTR,
+ data_stype(WLAN_FC_GET_STYPE(fc)),
+ fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+ fc & WLAN_FC_ISWEP ? " Prot" : "",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3),
+ MAC2STR((const u8 *) (hdr + 1)));
+ rx_data_bss(wt, hdr, hdrlen, qos, hdr->addr1, hdr->addr2,
+ data + hdrlen, len - hdrlen);
+ break;
+ }
+}
diff --git a/contrib/wpa/wlantest/rx_eapol.c b/contrib/wpa/wlantest/rx_eapol.c
new file mode 100644
index 000000000000..d5e10debf3e0
--- /dev/null
+++ b/contrib/wpa/wlantest/rx_eapol.c
@@ -0,0 +1,1317 @@
+/*
+ * Received Data frame processing for EAPOL messages
+ * Copyright (c) 2010-2020, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "common/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/eapol_common.h"
+#include "common/wpa_common.h"
+#include "rsn_supp/wpa_ie.h"
+#include "wlantest.h"
+
+
+static int is_zero(const u8 *buf, size_t len)
+{
+ size_t i;
+ for (i = 0; i < len; i++) {
+ if (buf[i])
+ return 0;
+ }
+ return 1;
+}
+
+
+static int check_mic(const u8 *kck, size_t kck_len, int akmp, int ver,
+ const u8 *data, size_t len)
+{
+ u8 *buf;
+ int ret = -1;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ u8 rx_mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ size_t mic_len = wpa_mic_len(akmp, PMK_LEN);
+
+ buf = os_memdup(data, len);
+ if (buf == NULL)
+ return -1;
+ hdr = (struct ieee802_1x_hdr *) buf;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+
+ os_memcpy(rx_mic, key + 1, mic_len);
+ os_memset(key + 1, 0, mic_len);
+
+ if (wpa_eapol_key_mic(kck, kck_len, akmp, ver, buf, len,
+ (u8 *) (key + 1)) == 0 &&
+ os_memcmp(rx_mic, key + 1, mic_len) == 0)
+ ret = 0;
+
+ os_free(buf);
+
+ return ret;
+}
+
+
+static void rx_data_eapol_key_1_of_4(struct wlantest *wt, const u8 *dst,
+ const u8 *src, const u8 *data, size_t len)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ const struct ieee802_1x_hdr *eapol;
+ const struct wpa_eapol_key *hdr;
+
+ wpa_printf(MSG_DEBUG, "EAPOL-Key 1/4 " MACSTR " -> " MACSTR,
+ MAC2STR(src), MAC2STR(dst));
+ bss = bss_get(wt, src);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, dst);
+ if (sta == NULL)
+ return;
+
+ eapol = (const struct ieee802_1x_hdr *) data;
+ hdr = (const struct wpa_eapol_key *) (eapol + 1);
+ if (is_zero(hdr->key_nonce, WPA_NONCE_LEN)) {
+ add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
+ " used zero nonce", MAC2STR(src));
+ }
+ if (!is_zero(hdr->key_rsc, 8)) {
+ add_note(wt, MSG_INFO, "EAPOL-Key 1/4 from " MACSTR
+ " used non-zero Key RSC", MAC2STR(src));
+ }
+ os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN);
+}
+
+
+static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, u16 ver,
+ const u8 *data, size_t len,
+ struct wlantest_pmk *pmk)
+{
+ struct wpa_ptk ptk;
+
+ if (wpa_key_mgmt_ft(sta->key_mgmt)) {
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ int use_sha384 = wpa_key_mgmt_sha384(sta->key_mgmt);
+
+ if (wpa_derive_pmk_r0(pmk->pmk, pmk->pmk_len,
+ bss->ssid, bss->ssid_len, bss->mdid,
+ bss->r0kh_id, bss->r0kh_id_len,
+ sta->addr, sta->pmk_r0, sta->pmk_r0_name,
+ use_sha384) < 0)
+ return -1;
+ sta->pmk_r0_len = use_sha384 ? PMK_LEN_SUITE_B_192 : PMK_LEN;
+ if (wpa_derive_pmk_r1(sta->pmk_r0, sta->pmk_r0_len,
+ sta->pmk_r0_name,
+ bss->r1kh_id, sta->addr,
+ sta->pmk_r1, sta->pmk_r1_name) < 0)
+ return -1;
+ sta->pmk_r1_len = sta->pmk_r0_len;
+ if (wpa_pmk_r1_to_ptk(sta->pmk_r1, sta->pmk_r1_len,
+ sta->snonce, sta->anonce, sta->addr,
+ bss->bssid, sta->pmk_r1_name,
+ &ptk, ptk_name, sta->key_mgmt,
+ sta->pairwise_cipher, 0) < 0 ||
+ check_mic(ptk.kck, ptk.kck_len, sta->key_mgmt, ver, data,
+ len) < 0)
+ return -1;
+ } else if (wpa_pmk_to_ptk(pmk->pmk, PMK_LEN,
+ "Pairwise key expansion",
+ bss->bssid, sta->addr, sta->anonce,
+ sta->snonce, &ptk, sta->key_mgmt,
+ sta->pairwise_cipher, NULL, 0, 0) < 0 ||
+ check_mic(ptk.kck, ptk.kck_len, sta->key_mgmt, ver, data,
+ len) < 0) {
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO, "Derived PTK for STA " MACSTR " BSSID " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ sta->counters[WLANTEST_STA_COUNTER_PTK_LEARNED]++;
+ if (sta->ptk_set) {
+ /*
+ * Rekeying - use new PTK for EAPOL-Key frames, but continue
+ * using the old PTK for frame decryption.
+ */
+ add_note(wt, MSG_DEBUG, "Derived PTK during rekeying");
+ os_memcpy(&sta->tptk, &ptk, sizeof(ptk));
+ wpa_hexdump(MSG_DEBUG, "TPTK:KCK",
+ sta->tptk.kck, sta->tptk.kck_len);
+ wpa_hexdump(MSG_DEBUG, "TPTK:KEK",
+ sta->tptk.kek, sta->tptk.kek_len);
+ wpa_hexdump(MSG_DEBUG, "TPTK:TK",
+ sta->tptk.tk, sta->tptk.tk_len);
+ sta->tptk_set = 1;
+ return 0;
+ }
+ add_note(wt, MSG_DEBUG, "Derived new PTK");
+ os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
+ wpa_hexdump(MSG_DEBUG, "PTK:KCK", sta->ptk.kck, sta->ptk.kck_len);
+ wpa_hexdump(MSG_DEBUG, "PTK:KEK", sta->ptk.kek, sta->ptk.kek_len);
+ wpa_hexdump(MSG_DEBUG, "PTK:TK", sta->ptk.tk, sta->ptk.tk_len);
+ sta->ptk_set = 1;
+ os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+ os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+ return 0;
+}
+
+
+static void derive_ptk(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, u16 ver,
+ const u8 *data, size_t len)
+{
+ struct wlantest_pmk *pmk;
+
+ wpa_printf(MSG_DEBUG, "Trying to derive PTK for " MACSTR " (ver %u)",
+ MAC2STR(sta->addr), ver);
+ dl_list_for_each(pmk, &bss->pmk, struct wlantest_pmk, list) {
+ wpa_printf(MSG_DEBUG, "Try per-BSS PMK");
+ if (try_pmk(wt, bss, sta, ver, data, len, pmk) == 0)
+ return;
+ }
+
+ dl_list_for_each(pmk, &wt->pmk, struct wlantest_pmk, list) {
+ wpa_printf(MSG_DEBUG, "Try global PMK");
+ if (try_pmk(wt, bss, sta, ver, data, len, pmk) == 0)
+ return;
+ }
+
+ if (!sta->ptk_set) {
+ struct wlantest_ptk *ptk;
+ int prev_level = wpa_debug_level;
+
+ wpa_debug_level = MSG_WARNING;
+ dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
+ if (check_mic(ptk->ptk.kck, ptk->ptk.kck_len,
+ sta->key_mgmt, ver, data, len) < 0)
+ continue;
+ wpa_printf(MSG_INFO, "Pre-set PTK matches for STA "
+ MACSTR " BSSID " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ add_note(wt, MSG_DEBUG, "Using pre-set PTK");
+ ptk->ptk_len = 32 +
+ wpa_cipher_key_len(sta->pairwise_cipher);
+ os_memcpy(&sta->ptk, &ptk->ptk, sizeof(ptk->ptk));
+ wpa_hexdump(MSG_DEBUG, "PTK:KCK",
+ sta->ptk.kck, sta->ptk.kck_len);
+ wpa_hexdump(MSG_DEBUG, "PTK:KEK",
+ sta->ptk.kek, sta->ptk.kek_len);
+ wpa_hexdump(MSG_DEBUG, "PTK:TK",
+ sta->ptk.tk, sta->ptk.tk_len);
+ sta->ptk_set = 1;
+ os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+ os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+ }
+ wpa_debug_level = prev_level;
+ }
+
+ add_note(wt, MSG_DEBUG, "No matching PMK found to derive PTK");
+}
+
+
+static void elems_from_eapol_ie(struct ieee802_11_elems *elems,
+ struct wpa_eapol_ie_parse *ie)
+{
+ os_memset(elems, 0, sizeof(*elems));
+ if (ie->wpa_ie) {
+ elems->wpa_ie = ie->wpa_ie + 2;
+ elems->wpa_ie_len = ie->wpa_ie_len - 2;
+ }
+ if (ie->rsn_ie) {
+ elems->rsn_ie = ie->rsn_ie + 2;
+ elems->rsn_ie_len = ie->rsn_ie_len - 2;
+ }
+ if (ie->osen) {
+ elems->osen = ie->osen + 2;
+ elems->osen_len = ie->osen_len - 2;
+ }
+}
+
+
+static void rx_data_eapol_key_2_of_4(struct wlantest *wt, const u8 *dst,
+ const u8 *src, const u8 *data, size_t len)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ const struct ieee802_1x_hdr *eapol;
+ const struct wpa_eapol_key *hdr;
+ const u8 *key_data, *kck, *mic;
+ size_t kck_len, mic_len;
+ u16 key_info, key_data_len;
+ struct wpa_eapol_ie_parse ie;
+
+ wpa_printf(MSG_DEBUG, "EAPOL-Key 2/4 " MACSTR " -> " MACSTR,
+ MAC2STR(src), MAC2STR(dst));
+ bss = bss_get(wt, dst);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, src);
+ if (sta == NULL)
+ return;
+
+ eapol = (const struct ieee802_1x_hdr *) data;
+ hdr = (const struct wpa_eapol_key *) (eapol + 1);
+ mic_len = wpa_mic_len(sta->key_mgmt, PMK_LEN);
+ mic = (const u8 *) (hdr + 1);
+ if (is_zero(hdr->key_nonce, WPA_NONCE_LEN)) {
+ add_note(wt, MSG_INFO, "EAPOL-Key 2/4 from " MACSTR
+ " used zero nonce", MAC2STR(src));
+ }
+ if (!is_zero(hdr->key_rsc, 8)) {
+ add_note(wt, MSG_INFO, "EAPOL-Key 2/4 from " MACSTR
+ " used non-zero Key RSC", MAC2STR(src));
+ }
+ os_memcpy(sta->snonce, hdr->key_nonce, WPA_NONCE_LEN);
+ key_info = WPA_GET_BE16(hdr->key_info);
+ key_data = mic + mic_len + 2;
+ key_data_len = WPA_GET_BE16(mic + mic_len);
+
+ if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0) {
+ add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
+ return;
+ }
+
+ if (!sta->assocreq_seen) {
+ struct ieee802_11_elems elems;
+
+ elems_from_eapol_ie(&elems, &ie);
+ wpa_printf(MSG_DEBUG,
+ "Update STA data based on IEs in EAPOL-Key 2/4");
+ sta_update_assoc(sta, &elems);
+ }
+
+ derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK, data, len);
+
+ if (!sta->ptk_set && !sta->tptk_set) {
+ add_note(wt, MSG_DEBUG,
+ "No PTK known to process EAPOL-Key 2/4");
+ return;
+ }
+
+ kck = sta->ptk.kck;
+ kck_len = sta->ptk.kck_len;
+ if (sta->tptk_set) {
+ add_note(wt, MSG_DEBUG,
+ "Use TPTK for validation EAPOL-Key MIC");
+ kck = sta->tptk.kck;
+ kck_len = sta->tptk.kck_len;
+ }
+ if (check_mic(kck, kck_len, sta->key_mgmt,
+ key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+ add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/4 MIC");
+ return;
+ }
+ add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/4");
+
+ if (ie.wpa_ie) {
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - WPA IE",
+ ie.wpa_ie, ie.wpa_ie_len);
+ if (os_memcmp(ie.wpa_ie, sta->rsnie, ie.wpa_ie_len) != 0) {
+ add_note(wt, MSG_INFO,
+ "Mismatch in WPA IE between EAPOL-Key 2/4 "
+ "and (Re)Association Request from " MACSTR,
+ MAC2STR(sta->addr));
+ wpa_hexdump(MSG_INFO, "WPA IE in EAPOL-Key",
+ ie.wpa_ie, ie.wpa_ie_len);
+ wpa_hexdump(MSG_INFO, "WPA IE in (Re)Association "
+ "Request",
+ sta->rsnie,
+ sta->rsnie[0] ? 2 + sta->rsnie[1] : 0);
+ }
+ }
+
+ if (ie.rsn_ie) {
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - RSN IE",
+ ie.rsn_ie, ie.rsn_ie_len);
+ if (os_memcmp(ie.rsn_ie, sta->rsnie, ie.rsn_ie_len) != 0) {
+ add_note(wt, MSG_INFO,
+ "Mismatch in RSN IE between EAPOL-Key 2/4 "
+ "and (Re)Association Request from " MACSTR,
+ MAC2STR(sta->addr));
+ wpa_hexdump(MSG_INFO, "RSN IE in EAPOL-Key",
+ ie.rsn_ie, ie.rsn_ie_len);
+ wpa_hexdump(MSG_INFO, "RSN IE in (Re)Association "
+ "Request",
+ sta->rsnie,
+ sta->rsnie[0] ? 2 + sta->rsnie[1] : 0);
+ }
+ }
+}
+
+
+static u8 * decrypt_eapol_key_data_rc4(struct wlantest *wt, const u8 *kek,
+ const struct wpa_eapol_key *hdr,
+ const u8 *keydata, u16 keydatalen,
+ size_t *len)
+{
+ u8 ek[32], *buf;
+
+ buf = os_memdup(keydata, keydatalen);
+ if (buf == NULL)
+ return NULL;
+
+ os_memcpy(ek, hdr->key_iv, 16);
+ os_memcpy(ek + 16, kek, 16);
+ if (rc4_skip(ek, 32, 256, buf, keydatalen)) {
+ add_note(wt, MSG_INFO, "RC4 failed");
+ os_free(buf);
+ return NULL;
+ }
+
+ *len = keydatalen;
+ return buf;
+}
+
+
+static u8 * decrypt_eapol_key_data_aes(struct wlantest *wt, const u8 *kek,
+ const struct wpa_eapol_key *hdr,
+ const u8 *keydata, u16 keydatalen,
+ size_t *len)
+{
+ u8 *buf;
+
+ if (keydatalen % 8) {
+ add_note(wt, MSG_INFO, "Unsupported AES-WRAP len %d",
+ keydatalen);
+ return NULL;
+ }
+ keydatalen -= 8; /* AES-WRAP adds 8 bytes */
+ buf = os_malloc(keydatalen);
+ if (buf == NULL)
+ return NULL;
+ if (aes_unwrap(kek, 16, keydatalen / 8, keydata, buf)) {
+ os_free(buf);
+ add_note(wt, MSG_INFO,
+ "AES unwrap failed - could not decrypt EAPOL-Key "
+ "key data");
+ return NULL;
+ }
+
+ *len = keydatalen;
+ return buf;
+}
+
+
+static u8 * decrypt_eapol_key_data(struct wlantest *wt, int akmp, const u8 *kek,
+ size_t kek_len, u16 ver,
+ const struct wpa_eapol_key *hdr,
+ size_t *len)
+{
+ size_t mic_len;
+ u16 keydatalen;
+ const u8 *mic, *keydata;
+
+ if (kek_len != 16)
+ return NULL;
+
+ mic = (const u8 *) (hdr + 1);
+ mic_len = wpa_mic_len(akmp, PMK_LEN);
+ keydata = mic + mic_len + 2;
+ keydatalen = WPA_GET_BE16(mic + mic_len);
+
+ switch (ver) {
+ case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
+ return decrypt_eapol_key_data_rc4(wt, kek, hdr, keydata,
+ keydatalen, len);
+ case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
+ case WPA_KEY_INFO_TYPE_AES_128_CMAC:
+ return decrypt_eapol_key_data_aes(wt, kek, hdr, keydata,
+ keydatalen, len);
+ case WPA_KEY_INFO_TYPE_AKM_DEFINED:
+ /* For now, assume this is OSEN */
+ return decrypt_eapol_key_data_aes(wt, kek, hdr, keydata,
+ keydatalen, len);
+ default:
+ add_note(wt, MSG_INFO,
+ "Unsupported EAPOL-Key Key Descriptor Version %u",
+ ver);
+ return NULL;
+ }
+}
+
+
+static void learn_kde_keys(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta,
+ const u8 *buf, size_t len, const u8 *rsc)
+{
+ struct wpa_eapol_ie_parse ie;
+
+ if (wpa_supplicant_parse_ies(buf, len, &ie) < 0) {
+ add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
+ return;
+ }
+
+ if (ie.wpa_ie) {
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - WPA IE",
+ ie.wpa_ie, ie.wpa_ie_len);
+ }
+
+ if (ie.rsn_ie) {
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - RSN IE",
+ ie.rsn_ie, ie.rsn_ie_len);
+ }
+
+ if (ie.key_id)
+ add_note(wt, MSG_DEBUG, "KeyID %u", ie.key_id[0]);
+
+ if (ie.gtk) {
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - GTK KDE",
+ ie.gtk, ie.gtk_len);
+ if (ie.gtk_len >= 2 && ie.gtk_len <= 2 + 32) {
+ int id;
+ id = ie.gtk[0] & 0x03;
+ add_note(wt, MSG_DEBUG, "GTK KeyID=%u tx=%u",
+ id, !!(ie.gtk[0] & 0x04));
+ if ((ie.gtk[0] & 0xf8) || ie.gtk[1]) {
+ add_note(wt, MSG_INFO,
+ "GTK KDE: Reserved field set: "
+ "%02x %02x", ie.gtk[0], ie.gtk[1]);
+ }
+ wpa_hexdump(MSG_DEBUG, "GTK", ie.gtk + 2,
+ ie.gtk_len - 2);
+ bss->gtk_len[id] = ie.gtk_len - 2;
+ sta->gtk_len = ie.gtk_len - 2;
+ os_memcpy(bss->gtk[id], ie.gtk + 2, ie.gtk_len - 2);
+ os_memcpy(sta->gtk, ie.gtk + 2, ie.gtk_len - 2);
+ bss->rsc[id][0] = rsc[5];
+ bss->rsc[id][1] = rsc[4];
+ bss->rsc[id][2] = rsc[3];
+ bss->rsc[id][3] = rsc[2];
+ bss->rsc[id][4] = rsc[1];
+ bss->rsc[id][5] = rsc[0];
+ bss->gtk_idx = id;
+ sta->gtk_idx = id;
+ wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[id], 6);
+ } else {
+ add_note(wt, MSG_INFO, "Invalid GTK KDE length %u",
+ (unsigned) ie.gtk_len);
+ }
+ }
+
+ if (ie.igtk) {
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - IGTK KDE",
+ ie.igtk, ie.igtk_len);
+ if (ie.igtk_len == 24) {
+ u16 id;
+ id = WPA_GET_LE16(ie.igtk);
+ if (id > 5) {
+ add_note(wt, MSG_INFO,
+ "Unexpected IGTK KeyID %u", id);
+ } else {
+ const u8 *ipn;
+ add_note(wt, MSG_DEBUG, "IGTK KeyID %u", id);
+ wpa_hexdump(MSG_DEBUG, "IPN", ie.igtk + 2, 6);
+ wpa_hexdump(MSG_DEBUG, "IGTK", ie.igtk + 8,
+ 16);
+ os_memcpy(bss->igtk[id], ie.igtk + 8, 16);
+ bss->igtk_len[id] = 16;
+ ipn = ie.igtk + 2;
+ bss->ipn[id][0] = ipn[5];
+ bss->ipn[id][1] = ipn[4];
+ bss->ipn[id][2] = ipn[3];
+ bss->ipn[id][3] = ipn[2];
+ bss->ipn[id][4] = ipn[1];
+ bss->ipn[id][5] = ipn[0];
+ bss->igtk_idx = id;
+ }
+ } else if (ie.igtk_len == 40) {
+ u16 id;
+ id = WPA_GET_LE16(ie.igtk);
+ if (id > 5) {
+ add_note(wt, MSG_INFO,
+ "Unexpected IGTK KeyID %u", id);
+ } else {
+ const u8 *ipn;
+ add_note(wt, MSG_DEBUG, "IGTK KeyID %u", id);
+ wpa_hexdump(MSG_DEBUG, "IPN", ie.igtk + 2, 6);
+ wpa_hexdump(MSG_DEBUG, "IGTK", ie.igtk + 8,
+ 32);
+ os_memcpy(bss->igtk[id], ie.igtk + 8, 32);
+ bss->igtk_len[id] = 32;
+ ipn = ie.igtk + 2;
+ bss->ipn[id][0] = ipn[5];
+ bss->ipn[id][1] = ipn[4];
+ bss->ipn[id][2] = ipn[3];
+ bss->ipn[id][3] = ipn[2];
+ bss->ipn[id][4] = ipn[1];
+ bss->ipn[id][5] = ipn[0];
+ bss->igtk_idx = id;
+ }
+ } else {
+ add_note(wt, MSG_INFO, "Invalid IGTK KDE length %u",
+ (unsigned) ie.igtk_len);
+ }
+ }
+
+ if (ie.bigtk) {
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data - BIGTK KDE",
+ ie.bigtk, ie.bigtk_len);
+ if (ie.bigtk_len == 24) {
+ u16 id;
+
+ id = WPA_GET_LE16(ie.bigtk);
+ if (id < 6 || id > 7) {
+ add_note(wt, MSG_INFO,
+ "Unexpected BIGTK KeyID %u", id);
+ } else {
+ const u8 *ipn;
+
+ add_note(wt, MSG_DEBUG, "BIGTK KeyID %u", id);
+ wpa_hexdump(MSG_DEBUG, "BIPN", ie.bigtk + 2, 6);
+ wpa_hexdump(MSG_DEBUG, "BIGTK", ie.bigtk + 8,
+ 16);
+ os_memcpy(bss->igtk[id], ie.bigtk + 8, 16);
+ bss->igtk_len[id] = 16;
+ ipn = ie.bigtk + 2;
+ bss->ipn[id][0] = ipn[5];
+ bss->ipn[id][1] = ipn[4];
+ bss->ipn[id][2] = ipn[3];
+ bss->ipn[id][3] = ipn[2];
+ bss->ipn[id][4] = ipn[1];
+ bss->ipn[id][5] = ipn[0];
+ bss->bigtk_idx = id;
+ }
+ } else if (ie.bigtk_len == 40) {
+ u16 id;
+
+ id = WPA_GET_LE16(ie.bigtk);
+ if (id < 6 || id > 7) {
+ add_note(wt, MSG_INFO,
+ "Unexpected BIGTK KeyID %u", id);
+ } else {
+ const u8 *ipn;
+
+ add_note(wt, MSG_DEBUG, "BIGTK KeyID %u", id);
+ wpa_hexdump(MSG_DEBUG, "BIPN", ie.bigtk + 2, 6);
+ wpa_hexdump(MSG_DEBUG, "BIGTK", ie.bigtk + 8,
+ 32);
+ os_memcpy(bss->igtk[id], ie.bigtk + 8, 32);
+ bss->igtk_len[id] = 32;
+ ipn = ie.bigtk + 2;
+ bss->ipn[id][0] = ipn[5];
+ bss->ipn[id][1] = ipn[4];
+ bss->ipn[id][2] = ipn[3];
+ bss->ipn[id][3] = ipn[2];
+ bss->ipn[id][4] = ipn[1];
+ bss->ipn[id][5] = ipn[0];
+ bss->bigtk_idx = id;
+ }
+ } else {
+ add_note(wt, MSG_INFO, "Invalid BIGTK KDE length %u",
+ (unsigned) ie.bigtk_len);
+ }
+ }
+}
+
+
+static void rx_data_eapol_key_3_of_4(struct wlantest *wt, const u8 *dst,
+ const u8 *src, const u8 *data, size_t len)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ const struct ieee802_1x_hdr *eapol;
+ const struct wpa_eapol_key *hdr;
+ const u8 *key_data, *kck, *kek, *mic;
+ size_t kck_len, kek_len, mic_len;
+ int recalc = 0;
+ u16 key_info, ver;
+ u8 *decrypted_buf = NULL;
+ const u8 *decrypted;
+ size_t decrypted_len = 0;
+ struct wpa_eapol_ie_parse ie;
+ struct wpa_ie_data rsn;
+
+ wpa_printf(MSG_DEBUG, "EAPOL-Key 3/4 " MACSTR " -> " MACSTR,
+ MAC2STR(src), MAC2STR(dst));
+ bss = bss_get(wt, src);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, dst);
+ if (sta == NULL)
+ return;
+ mic_len = wpa_mic_len(sta->key_mgmt, PMK_LEN);
+
+ eapol = (const struct ieee802_1x_hdr *) data;
+ hdr = (const struct wpa_eapol_key *) (eapol + 1);
+ mic = (const u8 *) (hdr + 1);
+ key_info = WPA_GET_BE16(hdr->key_info);
+
+ if (os_memcmp(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN) != 0) {
+ add_note(wt, MSG_INFO,
+ "EAPOL-Key ANonce mismatch between 1/4 and 3/4");
+ recalc = 1;
+ }
+ os_memcpy(sta->anonce, hdr->key_nonce, WPA_NONCE_LEN);
+ if (recalc) {
+ derive_ptk(wt, bss, sta, key_info & WPA_KEY_INFO_TYPE_MASK,
+ data, len);
+ }
+
+ if (!sta->ptk_set && !sta->tptk_set) {
+ add_note(wt, MSG_DEBUG,
+ "No PTK known to process EAPOL-Key 3/4");
+ return;
+ }
+
+ kek = sta->ptk.kek;
+ kek_len = sta->ptk.kek_len;
+ kck = sta->ptk.kck;
+ kck_len = sta->ptk.kck_len;
+ if (sta->tptk_set) {
+ add_note(wt, MSG_DEBUG,
+ "Use TPTK for validation EAPOL-Key MIC");
+ kck = sta->tptk.kck;
+ kck_len = sta->tptk.kck_len;
+ kek = sta->tptk.kek;
+ kek_len = sta->tptk.kek_len;
+ }
+ if (check_mic(kck, kck_len, sta->key_mgmt,
+ key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+ add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 3/4 MIC");
+ return;
+ }
+ add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 3/4");
+
+ key_data = mic + mic_len + 2;
+ if (!(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ if (sta->proto & WPA_PROTO_RSN)
+ add_note(wt, MSG_INFO,
+ "EAPOL-Key 3/4 without EncrKeyData bit");
+ decrypted = key_data;
+ decrypted_len = WPA_GET_BE16(mic + mic_len);
+ } else {
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ decrypted_buf = decrypt_eapol_key_data(wt, sta->key_mgmt,
+ kek, kek_len, ver,
+ hdr, &decrypted_len);
+ if (decrypted_buf == NULL) {
+ add_note(wt, MSG_INFO,
+ "Failed to decrypt EAPOL-Key Key Data");
+ return;
+ }
+ decrypted = decrypted_buf;
+ wpa_hexdump(MSG_DEBUG, "Decrypted EAPOL-Key Key Data",
+ decrypted, decrypted_len);
+ }
+ if ((wt->write_pcap_dumper || wt->pcapng) && decrypted != key_data) {
+ /* Fill in a dummy Data frame header */
+ u8 buf[24 + 8 + sizeof(*eapol) + sizeof(*hdr) + 64];
+ struct ieee80211_hdr *h;
+ struct wpa_eapol_key *k;
+ const u8 *p;
+ u8 *pos;
+ size_t plain_len;
+
+ plain_len = decrypted_len;
+ p = decrypted;
+ while (p + 1 < decrypted + decrypted_len) {
+ if (p[0] == 0xdd && p[1] == 0x00) {
+ /* Remove padding */
+ plain_len = p - decrypted;
+ p = NULL;
+ break;
+ }
+ p += 2 + p[1];
+ }
+ if (p && p > decrypted && p + 1 == decrypted + decrypted_len &&
+ *p == 0xdd) {
+ /* Remove padding */
+ plain_len = p - decrypted;
+ }
+
+ os_memset(buf, 0, sizeof(buf));
+ h = (struct ieee80211_hdr *) buf;
+ h->frame_control = host_to_le16(0x0208);
+ os_memcpy(h->addr1, dst, ETH_ALEN);
+ os_memcpy(h->addr2, src, ETH_ALEN);
+ os_memcpy(h->addr3, src, ETH_ALEN);
+ pos = (u8 *) (h + 1);
+ os_memcpy(pos, "\xaa\xaa\x03\x00\x00\x00\x88\x8e", 8);
+ pos += 8;
+ os_memcpy(pos, eapol, sizeof(*eapol));
+ pos += sizeof(*eapol);
+ os_memcpy(pos, hdr, sizeof(*hdr) + mic_len);
+ k = (struct wpa_eapol_key *) pos;
+ pos += sizeof(struct wpa_eapol_key) + mic_len;
+ WPA_PUT_BE16(k->key_info,
+ key_info & ~WPA_KEY_INFO_ENCR_KEY_DATA);
+ WPA_PUT_BE16(pos, plain_len);
+ write_pcap_decrypted(wt, buf, 24 + 8 + sizeof(*eapol) +
+ sizeof(*hdr) + mic_len + 2,
+ decrypted, plain_len);
+ }
+
+ if (wpa_supplicant_parse_ies(decrypted, decrypted_len, &ie) < 0) {
+ add_note(wt, MSG_INFO, "Failed to parse EAPOL-Key Key Data");
+ os_free(decrypted_buf);
+ return;
+ }
+
+ if (!bss->ies_set) {
+ struct ieee802_11_elems elems;
+
+ elems_from_eapol_ie(&elems, &ie);
+ wpa_printf(MSG_DEBUG,
+ "Update BSS data based on IEs in EAPOL-Key 3/4");
+ bss_update(wt, bss, &elems, 0);
+ }
+
+ if ((ie.wpa_ie &&
+ os_memcmp(ie.wpa_ie, bss->wpaie, ie.wpa_ie_len) != 0) ||
+ (ie.wpa_ie == NULL && bss->wpaie[0])) {
+ add_note(wt, MSG_INFO,
+ "Mismatch in WPA IE between EAPOL-Key 3/4 and "
+ "Beacon/Probe Response from " MACSTR,
+ MAC2STR(bss->bssid));
+ wpa_hexdump(MSG_INFO, "WPA IE in EAPOL-Key",
+ ie.wpa_ie, ie.wpa_ie_len);
+ wpa_hexdump(MSG_INFO, "WPA IE in Beacon/Probe "
+ "Response",
+ bss->wpaie,
+ bss->wpaie[0] ? 2 + bss->wpaie[1] : 0);
+ }
+
+ if ((ie.rsn_ie &&
+ wpa_compare_rsn_ie(wpa_key_mgmt_ft(sta->key_mgmt),
+ ie.rsn_ie, ie.rsn_ie_len,
+ bss->rsnie, 2 + bss->rsnie[1])) ||
+ (ie.rsn_ie == NULL && bss->rsnie[0])) {
+ add_note(wt, MSG_INFO, "Mismatch in RSN IE between EAPOL-Key "
+ "3/4 and Beacon/Probe Response from " MACSTR,
+ MAC2STR(bss->bssid));
+ wpa_hexdump(MSG_INFO, "RSN IE in EAPOL-Key",
+ ie.rsn_ie, ie.rsn_ie_len);
+ wpa_hexdump(MSG_INFO, "RSN IE in Beacon/Probe Response",
+ bss->rsnie,
+ bss->rsnie[0] ? 2 + bss->rsnie[1] : 0);
+ }
+
+ if (wpa_key_mgmt_ft(sta->key_mgmt) &&
+ (wpa_parse_wpa_ie_rsn(ie.rsn_ie, ie.rsn_ie_len, &rsn) < 0 ||
+ rsn.num_pmkid != 1 || !rsn.pmkid ||
+ os_memcmp_const(rsn.pmkid, sta->pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0))
+ add_note(wt, MSG_INFO,
+ "FT: No matching PMKR1Name in FT 4-way handshake message 3/4");
+
+ /* TODO: validate MDE and FTE match */
+
+ learn_kde_keys(wt, bss, sta, decrypted, decrypted_len, hdr->key_rsc);
+ os_free(decrypted_buf);
+}
+
+
+static void rx_data_eapol_key_4_of_4(struct wlantest *wt, const u8 *dst,
+ const u8 *src, const u8 *data, size_t len)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ const struct ieee802_1x_hdr *eapol;
+ const struct wpa_eapol_key *hdr;
+ u16 key_info;
+ const u8 *kck;
+ size_t kck_len;
+
+ wpa_printf(MSG_DEBUG, "EAPOL-Key 4/4 " MACSTR " -> " MACSTR,
+ MAC2STR(src), MAC2STR(dst));
+ bss = bss_get(wt, dst);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, src);
+ if (sta == NULL)
+ return;
+
+ eapol = (const struct ieee802_1x_hdr *) data;
+ hdr = (const struct wpa_eapol_key *) (eapol + 1);
+ if (!is_zero(hdr->key_rsc, 8)) {
+ add_note(wt, MSG_INFO, "EAPOL-Key 4/4 from " MACSTR " used "
+ "non-zero Key RSC", MAC2STR(src));
+ }
+ key_info = WPA_GET_BE16(hdr->key_info);
+
+ if (!sta->ptk_set && !sta->tptk_set) {
+ add_note(wt, MSG_DEBUG,
+ "No PTK known to process EAPOL-Key 4/4");
+ return;
+ }
+
+ kck = sta->ptk.kck;
+ kck_len = sta->ptk.kck_len;
+ if (sta->tptk_set) {
+ add_note(wt, MSG_DEBUG,
+ "Use TPTK for validation EAPOL-Key MIC");
+ kck = sta->tptk.kck;
+ kck_len = sta->tptk.kck_len;
+ }
+ if (check_mic(kck, kck_len, sta->key_mgmt,
+ key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+ add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 4/4 MIC");
+ return;
+ }
+ add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 4/4");
+ if (sta->tptk_set) {
+ add_note(wt, MSG_DEBUG, "Update PTK (rekeying)");
+ os_memcpy(&sta->ptk, &sta->tptk, sizeof(sta->ptk));
+ sta->ptk_set = 1;
+ sta->tptk_set = 0;
+ os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+ os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+ }
+}
+
+
+static void rx_data_eapol_key_1_of_2(struct wlantest *wt, const u8 *dst,
+ const u8 *src, const u8 *data, size_t len)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ const struct ieee802_1x_hdr *eapol;
+ const struct wpa_eapol_key *hdr;
+ u16 key_info, ver;
+ u8 *decrypted;
+ size_t decrypted_len = 0;
+ size_t mic_len;
+
+ wpa_printf(MSG_DEBUG, "EAPOL-Key 1/2 " MACSTR " -> " MACSTR,
+ MAC2STR(src), MAC2STR(dst));
+ bss = bss_get(wt, src);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, dst);
+ if (sta == NULL)
+ return;
+ mic_len = wpa_mic_len(sta->key_mgmt, PMK_LEN);
+
+ eapol = (const struct ieee802_1x_hdr *) data;
+ hdr = (const struct wpa_eapol_key *) (eapol + 1);
+ key_info = WPA_GET_BE16(hdr->key_info);
+
+ if (!sta->ptk_set) {
+ add_note(wt, MSG_DEBUG,
+ "No PTK known to process EAPOL-Key 1/2");
+ return;
+ }
+
+ if (sta->ptk_set &&
+ check_mic(sta->ptk.kck, sta->ptk.kck_len, sta->key_mgmt,
+ key_info & WPA_KEY_INFO_TYPE_MASK,
+ data, len) < 0) {
+ add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 1/2 MIC");
+ return;
+ }
+ add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 1/2");
+
+ if (sta->proto & WPA_PROTO_RSN &&
+ !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
+ add_note(wt, MSG_INFO, "EAPOL-Key 1/2 without EncrKeyData bit");
+ return;
+ }
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ decrypted = decrypt_eapol_key_data(wt, sta->key_mgmt,
+ sta->ptk.kek, sta->ptk.kek_len,
+ ver, hdr, &decrypted_len);
+ if (decrypted == NULL) {
+ add_note(wt, MSG_INFO, "Failed to decrypt EAPOL-Key Key Data");
+ return;
+ }
+ wpa_hexdump(MSG_DEBUG, "Decrypted EAPOL-Key Key Data",
+ decrypted, decrypted_len);
+ if (wt->write_pcap_dumper || wt->pcapng) {
+ /* Fill in a dummy Data frame header */
+ u8 buf[24 + 8 + sizeof(*eapol) + sizeof(*hdr) + 64];
+ struct ieee80211_hdr *h;
+ struct wpa_eapol_key *k;
+ u8 *pos;
+ size_t plain_len;
+
+ plain_len = decrypted_len;
+ pos = decrypted;
+ while (pos + 1 < decrypted + decrypted_len) {
+ if (pos[0] == 0xdd && pos[1] == 0x00) {
+ /* Remove padding */
+ plain_len = pos - decrypted;
+ break;
+ }
+ pos += 2 + pos[1];
+ }
+
+ os_memset(buf, 0, sizeof(buf));
+ h = (struct ieee80211_hdr *) buf;
+ h->frame_control = host_to_le16(0x0208);
+ os_memcpy(h->addr1, dst, ETH_ALEN);
+ os_memcpy(h->addr2, src, ETH_ALEN);
+ os_memcpy(h->addr3, src, ETH_ALEN);
+ pos = (u8 *) (h + 1);
+ os_memcpy(pos, "\xaa\xaa\x03\x00\x00\x00\x88\x8e", 8);
+ pos += 8;
+ os_memcpy(pos, eapol, sizeof(*eapol));
+ pos += sizeof(*eapol);
+ os_memcpy(pos, hdr, sizeof(*hdr) + mic_len);
+ k = (struct wpa_eapol_key *) pos;
+ pos += sizeof(struct wpa_eapol_key) + mic_len;
+ WPA_PUT_BE16(k->key_info,
+ key_info & ~WPA_KEY_INFO_ENCR_KEY_DATA);
+ WPA_PUT_BE16(pos, plain_len);
+ write_pcap_decrypted(wt, buf, 24 + 8 + sizeof(*eapol) +
+ sizeof(*hdr) + mic_len + 2,
+ decrypted, plain_len);
+ }
+ if (sta->proto & WPA_PROTO_RSN)
+ learn_kde_keys(wt, bss, sta, decrypted, decrypted_len,
+ hdr->key_rsc);
+ else {
+ int klen = bss->group_cipher == WPA_CIPHER_TKIP ? 32 : 16;
+ if (decrypted_len == klen) {
+ const u8 *rsc = hdr->key_rsc;
+ int id;
+ id = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT;
+ add_note(wt, MSG_DEBUG, "GTK key index %d", id);
+ wpa_hexdump(MSG_DEBUG, "GTK", decrypted,
+ decrypted_len);
+ bss->gtk_len[id] = decrypted_len;
+ os_memcpy(bss->gtk[id], decrypted, decrypted_len);
+ bss->rsc[id][0] = rsc[5];
+ bss->rsc[id][1] = rsc[4];
+ bss->rsc[id][2] = rsc[3];
+ bss->rsc[id][3] = rsc[2];
+ bss->rsc[id][4] = rsc[1];
+ bss->rsc[id][5] = rsc[0];
+ wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[id], 6);
+ } else {
+ add_note(wt, MSG_INFO, "Unexpected WPA Key Data length "
+ "in Group Key msg 1/2 from " MACSTR,
+ MAC2STR(src));
+ }
+ }
+ os_free(decrypted);
+}
+
+
+static void rx_data_eapol_key_2_of_2(struct wlantest *wt, const u8 *dst,
+ const u8 *src, const u8 *data, size_t len)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ const struct ieee802_1x_hdr *eapol;
+ const struct wpa_eapol_key *hdr;
+ u16 key_info;
+
+ wpa_printf(MSG_DEBUG, "EAPOL-Key 2/2 " MACSTR " -> " MACSTR,
+ MAC2STR(src), MAC2STR(dst));
+ bss = bss_get(wt, dst);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, src);
+ if (sta == NULL)
+ return;
+
+ eapol = (const struct ieee802_1x_hdr *) data;
+ hdr = (const struct wpa_eapol_key *) (eapol + 1);
+ if (!is_zero(hdr->key_rsc, 8)) {
+ add_note(wt, MSG_INFO, "EAPOL-Key 2/2 from " MACSTR " used "
+ "non-zero Key RSC", MAC2STR(src));
+ }
+ key_info = WPA_GET_BE16(hdr->key_info);
+
+ if (!sta->ptk_set) {
+ add_note(wt, MSG_DEBUG,
+ "No PTK known to process EAPOL-Key 2/2");
+ return;
+ }
+
+ if (sta->ptk_set &&
+ check_mic(sta->ptk.kck, sta->ptk.kck_len, sta->key_mgmt,
+ key_info & WPA_KEY_INFO_TYPE_MASK,
+ data, len) < 0) {
+ add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/2 MIC");
+ return;
+ }
+ add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/2");
+}
+
+
+static void rx_data_eapol_key(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst,
+ const u8 *src, const u8 *data, size_t len,
+ int prot)
+{
+ const struct ieee802_1x_hdr *eapol;
+ const struct wpa_eapol_key *hdr;
+ const u8 *key_data;
+ u16 key_info, key_length, ver, key_data_length;
+ size_t mic_len = 16;
+ const u8 *mic;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+
+ bss = bss_get(wt, bssid);
+ if (bss) {
+ if (sta_addr)
+ sta = sta_get(bss, sta_addr);
+ else
+ sta = NULL;
+ if (sta)
+ mic_len = wpa_mic_len(sta->key_mgmt, PMK_LEN);
+ }
+
+ eapol = (const struct ieee802_1x_hdr *) data;
+ hdr = (const struct wpa_eapol_key *) (eapol + 1);
+
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key",
+ (const u8 *) hdr, len - sizeof(*eapol));
+ if (len < sizeof(*hdr) + mic_len + 2) {
+ add_note(wt, MSG_INFO, "Too short EAPOL-Key frame from " MACSTR,
+ MAC2STR(src));
+ return;
+ }
+ mic = (const u8 *) (hdr + 1);
+
+ if (hdr->type == EAPOL_KEY_TYPE_RC4) {
+ /* TODO: EAPOL-Key RC4 for WEP */
+ wpa_printf(MSG_INFO, "EAPOL-Key Descriptor Type RC4 from "
+ MACSTR, MAC2STR(src));
+ return;
+ }
+
+ if (hdr->type != EAPOL_KEY_TYPE_RSN &&
+ hdr->type != EAPOL_KEY_TYPE_WPA) {
+ wpa_printf(MSG_INFO, "Unsupported EAPOL-Key Descriptor Type "
+ "%u from " MACSTR, hdr->type, MAC2STR(src));
+ return;
+ }
+
+ key_info = WPA_GET_BE16(hdr->key_info);
+ key_length = WPA_GET_BE16(hdr->key_length);
+ key_data_length = WPA_GET_BE16(mic + mic_len);
+ key_data = mic + mic_len + 2;
+ if (key_data + key_data_length > data + len) {
+ add_note(wt, MSG_INFO, "Truncated EAPOL-Key from " MACSTR,
+ MAC2STR(src));
+ return;
+ }
+ if (key_data + key_data_length < data + len) {
+ wpa_hexdump(MSG_DEBUG, "Extra data after EAPOL-Key Key Data "
+ "field", key_data + key_data_length,
+ data + len - key_data - key_data_length);
+ }
+
+
+ ver = key_info & WPA_KEY_INFO_TYPE_MASK;
+ wpa_printf(MSG_DEBUG, "EAPOL-Key ver=%u %c idx=%u%s%s%s%s%s%s%s%s "
+ "datalen=%u",
+ ver, key_info & WPA_KEY_INFO_KEY_TYPE ? 'P' : 'G',
+ (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
+ WPA_KEY_INFO_KEY_INDEX_SHIFT,
+ (key_info & WPA_KEY_INFO_INSTALL) ? " Install" : "",
+ (key_info & WPA_KEY_INFO_ACK) ? " ACK" : "",
+ (key_info & WPA_KEY_INFO_MIC) ? " MIC" : "",
+ (key_info & WPA_KEY_INFO_SECURE) ? " Secure" : "",
+ (key_info & WPA_KEY_INFO_ERROR) ? " Error" : "",
+ (key_info & WPA_KEY_INFO_REQUEST) ? " Request" : "",
+ (key_info & WPA_KEY_INFO_ENCR_KEY_DATA) ? " Encr" : "",
+ (key_info & WPA_KEY_INFO_SMK_MESSAGE) ? " SMK" : "",
+ key_data_length);
+
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+ ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+ ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+ ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+ wpa_printf(MSG_INFO, "Unsupported EAPOL-Key Key Descriptor "
+ "Version %u from " MACSTR, ver, MAC2STR(src));
+ return;
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Replay Counter",
+ hdr->replay_counter, WPA_REPLAY_COUNTER_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Nonce",
+ hdr->key_nonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key IV",
+ hdr->key_iv, 16);
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key RSC",
+ hdr->key_rsc, WPA_KEY_RSC_LEN);
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key MIC",
+ mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key Key Data",
+ key_data, key_data_length);
+
+ if (hdr->type == EAPOL_KEY_TYPE_RSN &&
+ (key_info & (WPA_KEY_INFO_KEY_INDEX_MASK | BIT(14) | BIT(15))) !=
+ 0) {
+ wpa_printf(MSG_INFO, "RSN EAPOL-Key with non-zero reserved "
+ "Key Info bits 0x%x from " MACSTR,
+ key_info, MAC2STR(src));
+ }
+
+ if (hdr->type == EAPOL_KEY_TYPE_WPA &&
+ (key_info & (WPA_KEY_INFO_ENCR_KEY_DATA |
+ WPA_KEY_INFO_SMK_MESSAGE |BIT(14) | BIT(15))) != 0) {
+ wpa_printf(MSG_INFO, "WPA EAPOL-Key with non-zero reserved "
+ "Key Info bits 0x%x from " MACSTR,
+ key_info, MAC2STR(src));
+ }
+
+ if (key_length > 32) {
+ wpa_printf(MSG_INFO, "EAPOL-Key with invalid Key Length %d "
+ "from " MACSTR, key_length, MAC2STR(src));
+ }
+
+ if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
+ !is_zero(hdr->key_iv, 16)) {
+ wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key IV "
+ "(reserved with ver=%d) field from " MACSTR,
+ ver, MAC2STR(src));
+ wpa_hexdump(MSG_INFO, "EAPOL-Key Key IV (reserved)",
+ hdr->key_iv, 16);
+ }
+
+ if (!is_zero(hdr->key_id, 8)) {
+ wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key ID "
+ "(reserved) field from " MACSTR, MAC2STR(src));
+ wpa_hexdump(MSG_INFO, "EAPOL-Key Key ID (reserved)",
+ hdr->key_id, 8);
+ }
+
+ if (hdr->key_rsc[6] || hdr->key_rsc[7]) {
+ wpa_printf(MSG_INFO, "EAPOL-Key with non-zero Key RSC octets "
+ "(last two are unused)" MACSTR, MAC2STR(src));
+ }
+
+ if (key_info & (WPA_KEY_INFO_ERROR | WPA_KEY_INFO_REQUEST))
+ return;
+
+ if (key_info & WPA_KEY_INFO_SMK_MESSAGE)
+ return;
+
+ if (key_info & WPA_KEY_INFO_KEY_TYPE) {
+ /* 4-Way Handshake */
+ switch (key_info & (WPA_KEY_INFO_SECURE |
+ WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_ACK |
+ WPA_KEY_INFO_INSTALL)) {
+ case WPA_KEY_INFO_ACK:
+ rx_data_eapol_key_1_of_4(wt, dst, src, data, len);
+ break;
+ case WPA_KEY_INFO_MIC:
+ if (key_data_length == 0)
+ rx_data_eapol_key_4_of_4(wt, dst, src, data,
+ len);
+ else
+ rx_data_eapol_key_2_of_4(wt, dst, src, data,
+ len);
+ break;
+ case WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK |
+ WPA_KEY_INFO_INSTALL:
+ /* WPA does not include Secure bit in 3/4 */
+ rx_data_eapol_key_3_of_4(wt, dst, src, data, len);
+ break;
+ case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL:
+ case WPA_KEY_INFO_SECURE |
+ WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL:
+ rx_data_eapol_key_3_of_4(wt, dst, src, data, len);
+ break;
+ case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC:
+ case WPA_KEY_INFO_SECURE:
+ if (key_data_length == 0)
+ rx_data_eapol_key_4_of_4(wt, dst, src, data,
+ len);
+ else
+ rx_data_eapol_key_2_of_4(wt, dst, src, data,
+ len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Unsupported EAPOL-Key frame");
+ break;
+ }
+ } else {
+ /* Group Key Handshake */
+ switch (key_info & (WPA_KEY_INFO_SECURE |
+ WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_ACK)) {
+ case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
+ WPA_KEY_INFO_ACK:
+ case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_ACK:
+ rx_data_eapol_key_1_of_2(wt, dst, src, data, len);
+ break;
+ case WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC:
+ case WPA_KEY_INFO_SECURE:
+ rx_data_eapol_key_2_of_2(wt, dst, src, data, len);
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "Unsupported EAPOL-Key frame");
+ break;
+ }
+ }
+}
+
+
+void rx_data_eapol(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr,
+ const u8 *dst, const u8 *src,
+ const u8 *data, size_t len, int prot)
+{
+ const struct ieee802_1x_hdr *hdr;
+ u16 length;
+ const u8 *p;
+
+ wpa_hexdump(MSG_EXCESSIVE, "EAPOL", data, len);
+ if (len < sizeof(*hdr)) {
+ wpa_printf(MSG_INFO, "Too short EAPOL frame from " MACSTR,
+ MAC2STR(src));
+ return;
+ }
+
+ hdr = (const struct ieee802_1x_hdr *) data;
+ length = be_to_host16(hdr->length);
+ wpa_printf(MSG_DEBUG, "RX EAPOL: " MACSTR " -> " MACSTR "%s ver=%u "
+ "type=%u len=%u",
+ MAC2STR(src), MAC2STR(dst), prot ? " Prot" : "",
+ hdr->version, hdr->type, length);
+ if (hdr->version < 1 || hdr->version > 3) {
+ wpa_printf(MSG_INFO, "Unexpected EAPOL version %u from "
+ MACSTR, hdr->version, MAC2STR(src));
+ }
+ if (sizeof(*hdr) + length > len) {
+ wpa_printf(MSG_INFO, "Truncated EAPOL frame from " MACSTR,
+ MAC2STR(src));
+ return;
+ }
+
+ if (sizeof(*hdr) + length < len) {
+ wpa_printf(MSG_INFO, "EAPOL frame with %d extra bytes",
+ (int) (len - sizeof(*hdr) - length));
+ }
+ p = (const u8 *) (hdr + 1);
+
+ switch (hdr->type) {
+ case IEEE802_1X_TYPE_EAP_PACKET:
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL - EAP packet", p, length);
+ break;
+ case IEEE802_1X_TYPE_EAPOL_START:
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Start", p, length);
+ break;
+ case IEEE802_1X_TYPE_EAPOL_LOGOFF:
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL-Logoff", p, length);
+ break;
+ case IEEE802_1X_TYPE_EAPOL_KEY:
+ rx_data_eapol_key(wt, bssid, sta_addr, dst, src, data,
+ sizeof(*hdr) + length, prot);
+ break;
+ case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT:
+ wpa_hexdump(MSG_MSGDUMP, "EAPOL - Encapsulated ASF alert",
+ p, length);
+ break;
+ default:
+ wpa_hexdump(MSG_MSGDUMP, "Unknown EAPOL payload", p, length);
+ break;
+ }
+}
diff --git a/contrib/wpa/wlantest/rx_ip.c b/contrib/wpa/wlantest/rx_ip.c
new file mode 100644
index 000000000000..b0fdd2068c96
--- /dev/null
+++ b/contrib/wpa/wlantest/rx_ip.c
@@ -0,0 +1,184 @@
+/*
+ * Received Data frame processing for IPv4 packets
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include "utils/common.h"
+#include "wlantest.h"
+
+
+#ifndef __APPLE__
+
+static void ping_update(struct wlantest *wt, struct wlantest_sta *sta, int req,
+ u32 src, u32 dst, u16 id, u16 seq)
+{
+ if (req) {
+ sta->icmp_echo_req_src = src;
+ sta->icmp_echo_req_dst = dst;
+ sta->icmp_echo_req_id = id;
+ sta->icmp_echo_req_seq = seq;
+ return;
+ }
+
+ if (sta->icmp_echo_req_src == dst &&
+ sta->icmp_echo_req_dst == src &&
+ sta->icmp_echo_req_id == id &&
+ sta->icmp_echo_req_seq == seq) {
+ sta->counters[WLANTEST_STA_COUNTER_PING_OK]++;
+ if (sta->counters[WLANTEST_STA_COUNTER_ASSOCREQ_TX] == 0 &&
+ sta->counters[WLANTEST_STA_COUNTER_REASSOCREQ_TX] == 0)
+ sta->counters[
+ WLANTEST_STA_COUNTER_PING_OK_FIRST_ASSOC]++;
+ add_note(wt, MSG_DEBUG, "ICMP echo (ping) match for STA "
+ MACSTR, MAC2STR(sta->addr));
+ }
+}
+
+
+static void rx_data_icmp(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, u32 dst, u32 src,
+ const u8 *data, size_t len, const u8 *peer_addr)
+{
+ struct in_addr addr;
+ char buf[20];
+ const struct icmphdr *hdr;
+ u16 id, seq;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+
+ hdr = (const struct icmphdr *) data;
+ if (len < 4)
+ return;
+
+ /* TODO: check hdr->checksum */
+
+ if (hdr->type != ICMP_ECHOREPLY && hdr->type != ICMP_ECHO)
+ return;
+ if (len < 8)
+ return;
+
+ id = ntohs(hdr->un.echo.id);
+ seq = ntohs(hdr->un.echo.sequence);
+
+ addr.s_addr = dst;
+ snprintf(buf, sizeof(buf), "%s", inet_ntoa(addr));
+ addr.s_addr = src;
+ add_note(wt, MSG_DEBUG, "ICMP echo %s %s -> %s id=%04x seq=%u len=%u%s",
+ hdr->type == ICMP_ECHO ? "request" : "response",
+ inet_ntoa(addr), buf, id, seq, (unsigned) len - 8,
+ peer_addr ? " [DL]" : "");
+
+ bss = bss_find(wt, bssid);
+ if (bss == NULL) {
+ add_note(wt, MSG_INFO, "No BSS " MACSTR
+ " known for ICMP packet", MAC2STR(bssid));
+ return;
+ }
+
+ if (sta_addr == NULL)
+ return; /* FromDS broadcast ping */
+
+ sta = sta_find(bss, sta_addr);
+ if (sta == NULL) {
+ add_note(wt, MSG_INFO, "No STA " MACSTR
+ " known for ICMP packet", MAC2STR(sta_addr));
+ return;
+ }
+
+ ping_update(wt, sta, hdr->type == ICMP_ECHO, src, dst, id, seq);
+ if (peer_addr && (sta = sta_find(bss, peer_addr)))
+ ping_update(wt, sta, hdr->type == ICMP_ECHO, src, dst, id, seq);
+}
+
+#endif /* __APPLE__ */
+
+
+static int hwsim_test_packet(const u8 *data, size_t len)
+{
+ size_t i;
+
+ if (len != 1500 - 14)
+ return 0;
+
+ for (i = 0; i < len; i++) {
+ if (data[i] != (i & 0xff))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+void rx_data_ip(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr,
+ const u8 *dst, const u8 *src, const u8 *data, size_t len,
+ const u8 *peer_addr)
+{
+ struct ip ip;
+ const u8 *payload;
+ size_t plen;
+ uint16_t frag_off, ip_len;
+
+ if (len < sizeof(ip))
+ return;
+ os_memcpy(&ip, data, sizeof(ip));
+
+ if (ip.ip_v != 4) {
+ if (hwsim_test_packet(data, len)) {
+ add_note(wt, MSG_INFO, "hwsim_test package");
+ return;
+ }
+ add_note(wt, MSG_DEBUG, "Unexpected IP protocol version %u in "
+ "IPv4 packet (bssid=" MACSTR " str=" MACSTR
+ " dst=" MACSTR ")", ip.ip_v, MAC2STR(bssid),
+ MAC2STR(src), MAC2STR(dst));
+ return;
+ }
+ if (ip.ip_hl * 4 < sizeof(ip)) {
+ add_note(wt, MSG_DEBUG, "Unexpected IP header length %u in "
+ "IPv4 packet (bssid=" MACSTR " str=" MACSTR
+ " dst=" MACSTR ")", ip.ip_hl, MAC2STR(bssid),
+ MAC2STR(src), MAC2STR(dst));
+ return;
+ }
+ if (ip.ip_hl * 4 > len) {
+ add_note(wt, MSG_DEBUG, "Truncated IP header (ihl=%u len=%u) "
+ "in IPv4 packet (bssid=" MACSTR " str=" MACSTR
+ " dst=" MACSTR ")", ip.ip_hl, (unsigned) len,
+ MAC2STR(bssid), MAC2STR(src), MAC2STR(dst));
+ return;
+ }
+
+ /* TODO: check header checksum in ip.ip_sum */
+
+ frag_off = be_to_host16(ip.ip_off);
+ if (frag_off & 0x1fff) {
+ wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet "
+ "supported");
+ return;
+ }
+
+ ip_len = be_to_host16(ip.ip_len);
+ if (ip_len > len)
+ return;
+ if (ip_len < len)
+ len = ip_len;
+
+ payload = data + 4 * ip.ip_hl;
+ plen = len - 4 * ip.ip_hl;
+
+ switch (ip.ip_p) {
+#ifndef __APPLE__
+ case IPPROTO_ICMP:
+ rx_data_icmp(wt, bssid, sta_addr, ip.ip_dst.s_addr,
+ ip.ip_src.s_addr, payload, plen, peer_addr);
+ break;
+#endif /* __APPLE__ */
+ }
+}
diff --git a/contrib/wpa/wlantest/rx_mgmt.c b/contrib/wpa/wlantest/rx_mgmt.c
new file mode 100644
index 000000000000..f7690e07f910
--- /dev/null
+++ b/contrib/wpa/wlantest/rx_mgmt.c
@@ -0,0 +1,2642 @@
+/*
+ * Received Management frame processing
+ * Copyright (c) 2010-2020, Jouni Malinen <j@w1.fi>
+ *
+ * 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/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/wpa_common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "crypto/aes_wrap.h"
+#include "wlantest.h"
+
+
+static int check_mmie_mic(unsigned int mgmt_group_cipher,
+ const u8 *igtk, size_t igtk_len,
+ const u8 *data, size_t len);
+
+
+static const char * mgmt_stype(u16 stype)
+{
+ switch (stype) {
+ case WLAN_FC_STYPE_ASSOC_REQ:
+ return "ASSOC-REQ";
+ case WLAN_FC_STYPE_ASSOC_RESP:
+ return "ASSOC-RESP";
+ case WLAN_FC_STYPE_REASSOC_REQ:
+ return "REASSOC-REQ";
+ case WLAN_FC_STYPE_REASSOC_RESP:
+ return "REASSOC-RESP";
+ case WLAN_FC_STYPE_PROBE_REQ:
+ return "PROBE-REQ";
+ case WLAN_FC_STYPE_PROBE_RESP:
+ return "PROBE-RESP";
+ case WLAN_FC_STYPE_BEACON:
+ return "BEACON";
+ case WLAN_FC_STYPE_ATIM:
+ return "ATIM";
+ case WLAN_FC_STYPE_DISASSOC:
+ return "DISASSOC";
+ case WLAN_FC_STYPE_AUTH:
+ return "AUTH";
+ case WLAN_FC_STYPE_DEAUTH:
+ return "DEAUTH";
+ case WLAN_FC_STYPE_ACTION:
+ return "ACTION";
+ case WLAN_FC_STYPE_ACTION_NO_ACK:
+ return "ACTION-NO-ACK";
+ }
+ return "??";
+}
+
+
+static void rx_mgmt_beacon(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct ieee802_11_elems elems;
+ size_t offset;
+ const u8 *mme;
+ size_t mic_len;
+ u16 keyid;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ offset = mgmt->u.beacon.variable - data;
+ if (len < offset)
+ return;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ if (bss->proberesp_seen)
+ return; /* do not override with Beacon data */
+ bss->capab_info = le_to_host16(mgmt->u.beacon.capab_info);
+ if (ieee802_11_parse_elems(mgmt->u.beacon.variable, len - offset,
+ &elems, 0) == ParseFailed) {
+ if (bss->parse_error_reported)
+ return;
+ add_note(wt, MSG_INFO, "Invalid IEs in a Beacon frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ bss->parse_error_reported = 1;
+ return;
+ }
+
+ bss_update(wt, bss, &elems, 1);
+
+ mme = get_ie(mgmt->u.beacon.variable, len - offset, WLAN_EID_MMIE);
+ if (!mme) {
+ if (bss->bigtk_idx) {
+ add_note(wt, MSG_INFO,
+ "Unexpected unprotected Beacon frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ bss->counters[WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE]++;
+ }
+ return;
+ }
+
+ mic_len = bss->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16;
+ if (len < 24 + 10 + mic_len ||
+ data[len - (10 + mic_len)] != WLAN_EID_MMIE ||
+ data[len - (10 + mic_len - 1)] != 8 + mic_len) {
+ add_note(wt, MSG_INFO, "Invalid MME in a Beacon frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ mme += 2;
+ keyid = WPA_GET_LE16(mme);
+ if (keyid < 6 || keyid > 7) {
+ add_note(wt, MSG_INFO, "Unexpected MME KeyID %u from " MACSTR,
+ keyid, MAC2STR(mgmt->sa));
+ bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Beacon frame MME KeyID %u", keyid);
+ wpa_hexdump(MSG_MSGDUMP, "MME IPN", mme + 2, 6);
+ wpa_hexdump(MSG_MSGDUMP, "MME MIC", mme + 8, mic_len);
+
+ if (!bss->igtk_len[keyid]) {
+ add_note(wt, MSG_DEBUG, "No BIGTK known to validate BIP frame");
+ return;
+ }
+
+ if (os_memcmp(mme + 2, bss->ipn[keyid], 6) <= 0) {
+ add_note(wt, MSG_INFO, "BIP replay detected: SA=" MACSTR,
+ MAC2STR(mgmt->sa));
+ wpa_hexdump(MSG_INFO, "RX IPN", mme + 2, 6);
+ wpa_hexdump(MSG_INFO, "Last RX IPN", bss->ipn[keyid], 6);
+ }
+
+ if (check_mmie_mic(bss->mgmt_group_cipher, bss->igtk[keyid],
+ bss->igtk_len[keyid], data, len) < 0) {
+ add_note(wt, MSG_INFO, "Invalid MME MIC in a Beacon frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
+ return;
+ }
+
+ add_note(wt, MSG_DEBUG, "Valid MME MIC in Beacon frame");
+ os_memcpy(bss->ipn[keyid], mme + 2, 6);
+}
+
+
+static void rx_mgmt_probe_resp(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct ieee802_11_elems elems;
+ size_t offset;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ offset = mgmt->u.probe_resp.variable - data;
+ if (len < offset)
+ return;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+
+ bss->counters[WLANTEST_BSS_COUNTER_PROBE_RESPONSE]++;
+ bss->capab_info = le_to_host16(mgmt->u.probe_resp.capab_info);
+ if (ieee802_11_parse_elems(mgmt->u.probe_resp.variable, len - offset,
+ &elems, 0) == ParseFailed) {
+ if (bss->parse_error_reported)
+ return;
+ add_note(wt, MSG_INFO, "Invalid IEs in a Probe Response frame "
+ "from " MACSTR, MAC2STR(mgmt->sa));
+ bss->parse_error_reported = 1;
+ return;
+ }
+
+ bss_update(wt, bss, &elems, 2);
+}
+
+
+static void process_fils_auth(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct ieee802_11_elems elems;
+ u16 trans;
+ struct wpa_ie_data data;
+
+ if (sta->auth_alg != WLAN_AUTH_FILS_SK ||
+ len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
+ return;
+
+ trans = le_to_host16(mgmt->u.auth.auth_transaction);
+
+ if (ieee802_11_parse_elems(mgmt->u.auth.variable,
+ len - IEEE80211_HDRLEN -
+ sizeof(mgmt->u.auth), &elems, 0) ==
+ ParseFailed)
+ return;
+
+ if (trans == 1) {
+ if (!elems.rsn_ie) {
+ add_note(wt, MSG_INFO,
+ "FILS Authentication frame missing RSNE");
+ return;
+ }
+ if (wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2,
+ elems.rsn_ie_len + 2, &data) < 0) {
+ add_note(wt, MSG_INFO,
+ "Invalid RSNE in FILS Authentication frame");
+ return;
+ }
+ sta->key_mgmt = data.key_mgmt;
+ sta->pairwise_cipher = data.pairwise_cipher;
+ }
+
+ if (!elems.fils_nonce) {
+ add_note(wt, MSG_INFO,
+ "FILS Authentication frame missing nonce");
+ return;
+ }
+
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+ os_memcpy(sta->anonce, elems.fils_nonce, FILS_NONCE_LEN);
+ else
+ os_memcpy(sta->snonce, elems.fils_nonce, FILS_NONCE_LEN);
+}
+
+
+static void process_ft_auth(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ u16 trans;
+ struct wpa_ft_ies parse;
+ struct wpa_ptk ptk;
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+ struct wlantest_bss *old_bss;
+ struct wlantest_sta *old_sta = NULL;
+
+ if (sta->auth_alg != WLAN_AUTH_FT ||
+ len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
+ return;
+
+ trans = le_to_host16(mgmt->u.auth.auth_transaction);
+
+ if (wpa_ft_parse_ies(mgmt->u.auth.variable,
+ len - IEEE80211_HDRLEN - sizeof(mgmt->u.auth),
+ &parse, -1)) {
+ add_note(wt, MSG_INFO,
+ "Could not parse FT Authentication Response frame");
+ return;
+ }
+
+ if (trans == 1) {
+ sta->key_mgmt = parse.key_mgmt;
+ sta->pairwise_cipher = parse.pairwise_cipher;
+ return;
+ }
+
+ if (trans != 2)
+ return;
+
+ /* TODO: Should find the latest updated PMK-R0 value here instead
+ * copying the one from the first found matching old STA entry. */
+ dl_list_for_each(old_bss, &wt->bss, struct wlantest_bss, list) {
+ if (old_bss == bss)
+ continue;
+ old_sta = sta_find(old_bss, sta->addr);
+ if (old_sta)
+ break;
+ }
+ if (!old_sta)
+ return;
+
+ os_memcpy(sta->pmk_r0, old_sta->pmk_r0, old_sta->pmk_r0_len);
+ sta->pmk_r0_len = old_sta->pmk_r0_len;
+ os_memcpy(sta->pmk_r0_name, old_sta->pmk_r0_name,
+ sizeof(sta->pmk_r0_name));
+
+ if (parse.r1kh_id)
+ os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+
+ if (wpa_derive_pmk_r1(sta->pmk_r0, sta->pmk_r0_len, sta->pmk_r0_name,
+ bss->r1kh_id, sta->addr, sta->pmk_r1,
+ sta->pmk_r1_name) < 0)
+ return;
+ sta->pmk_r1_len = sta->pmk_r0_len;
+
+ if (!parse.fte_anonce || !parse.fte_snonce ||
+ wpa_pmk_r1_to_ptk(sta->pmk_r1, sta->pmk_r1_len, parse.fte_snonce,
+ parse.fte_anonce, sta->addr, bss->bssid,
+ sta->pmk_r1_name, &ptk, ptk_name, sta->key_mgmt,
+ sta->pairwise_cipher, 0) < 0)
+ return;
+
+ add_note(wt, MSG_DEBUG, "Derived new PTK");
+ os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
+ sta->ptk_set = 1;
+ os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+ os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+}
+
+
+static void rx_mgmt_auth(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ u16 alg, trans, status;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+ sta = sta_get(bss, mgmt->da);
+ else
+ sta = sta_get(bss, mgmt->sa);
+ if (sta == NULL)
+ return;
+
+ if (len < 24 + 6) {
+ add_note(wt, MSG_INFO, "Too short Authentication frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ alg = le_to_host16(mgmt->u.auth.auth_alg);
+ sta->auth_alg = alg;
+ trans = le_to_host16(mgmt->u.auth.auth_transaction);
+ status = le_to_host16(mgmt->u.auth.status_code);
+
+ wpa_printf(MSG_DEBUG, "AUTH " MACSTR " -> " MACSTR
+ " (alg=%u trans=%u status=%u)",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), alg, trans, status);
+
+ if (alg == 0 && trans == 2 && status == 0) {
+ if (sta->state == STATE1) {
+ add_note(wt, MSG_DEBUG, "STA " MACSTR
+ " moved to State 2 with " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ sta->state = STATE2;
+ }
+ }
+
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+ sta->counters[WLANTEST_STA_COUNTER_AUTH_RX]++;
+ else
+ sta->counters[WLANTEST_STA_COUNTER_AUTH_TX]++;
+
+ process_fils_auth(wt, bss, sta, mgmt, len);
+ process_ft_auth(wt, bss, sta, mgmt, len);
+}
+
+
+static void deauth_all_stas(struct wlantest *wt, struct wlantest_bss *bss)
+{
+ struct wlantest_sta *sta;
+ dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+ if (sta->state == STATE1)
+ continue;
+ add_note(wt, MSG_DEBUG, "STA " MACSTR
+ " moved to State 1 with " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ sta->state = STATE1;
+ }
+}
+
+
+static void tdls_link_down(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta)
+{
+ struct wlantest_tdls *tdls;
+ dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+ if ((tdls->init == sta || tdls->resp == sta) && tdls->link_up)
+ {
+ add_note(wt, MSG_DEBUG, "TDLS: Set link down based on "
+ "STA deauth/disassoc");
+ tdls->link_up = 0;
+ }
+ }
+}
+
+
+static void rx_mgmt_deauth(struct wlantest *wt, const u8 *data, size_t len,
+ int valid)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ u16 fc, reason;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+ sta = sta_get(bss, mgmt->da);
+ else
+ sta = sta_get(bss, mgmt->sa);
+
+ if (len < 24 + 2) {
+ add_note(wt, MSG_INFO, "Too short Deauthentication frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ reason = le_to_host16(mgmt->u.deauth.reason_code);
+ wpa_printf(MSG_DEBUG, "DEAUTH " MACSTR " -> " MACSTR
+ " (reason=%u) (valid=%d)",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+ reason, valid);
+ wpa_hexdump(MSG_MSGDUMP, "DEAUTH payload", data + 24, len - 24);
+
+ if (sta == NULL) {
+ if (valid && mgmt->da[0] == 0xff)
+ deauth_all_stas(wt, bss);
+ return;
+ }
+
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+ sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DEAUTH_RX :
+ WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX]++;
+ if (sta->pwrmgt && !sta->pspoll)
+ sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP]++;
+ else
+ sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE]++;
+
+ fc = le_to_host16(mgmt->frame_control);
+ if (!(fc & WLAN_FC_ISWEP) && reason == 6)
+ sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_RC6]++;
+ else if (!(fc & WLAN_FC_ISWEP) && reason == 7)
+ sta->counters[WLANTEST_STA_COUNTER_DEAUTH_RX_RC7]++;
+ } else
+ sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DEAUTH_TX :
+ WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX]++;
+
+ if (!valid) {
+ add_note(wt, MSG_INFO, "Do not change STA " MACSTR " State "
+ "since Disassociation frame was not protected "
+ "correctly", MAC2STR(sta->addr));
+ return;
+ }
+
+ if (sta->state != STATE1) {
+ add_note(wt, MSG_DEBUG, "STA " MACSTR
+ " moved to State 1 with " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ sta->state = STATE1;
+ }
+ tdls_link_down(wt, bss, sta);
+}
+
+
+static const u8 * get_fils_session(const u8 *ies, size_t ies_len)
+{
+ const u8 *ie, *end;
+
+ ie = ies;
+ end = ((const u8 *) ie) + ies_len;
+ while (ie + 1 < end) {
+ if (ie + 2 + ie[1] > end)
+ break;
+ if (ie[0] == WLAN_EID_EXTENSION &&
+ ie[1] >= 1 + FILS_SESSION_LEN &&
+ ie[2] == WLAN_EID_EXT_FILS_SESSION)
+ return ie;
+ ie += 2 + ie[1];
+ }
+ return NULL;
+}
+
+
+static int try_rmsk(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, struct wlantest_pmk *pmk,
+ const u8 *frame_start, const u8 *frame_ad,
+ const u8 *frame_ad_end, const u8 *encr_end)
+{
+ size_t pmk_len = 0;
+ u8 pmk_buf[PMK_LEN_MAX];
+ struct wpa_ptk ptk;
+ u8 ick[FILS_ICK_MAX_LEN];
+ size_t ick_len;
+ const u8 *aad[5];
+ size_t aad_len[5];
+ u8 buf[2000];
+
+ if (fils_rmsk_to_pmk(sta->key_mgmt, pmk->pmk, pmk->pmk_len,
+ sta->snonce, sta->anonce, NULL, 0,
+ pmk_buf, &pmk_len) < 0)
+ return -1;
+
+ if (fils_pmk_to_ptk(pmk_buf, pmk_len, sta->addr, bss->bssid,
+ sta->snonce, sta->anonce, NULL, 0,
+ &ptk, ick, &ick_len,
+ sta->key_mgmt, sta->pairwise_cipher,
+ NULL, NULL, 0) < 0)
+ return -1;
+
+ /* Check AES-SIV decryption with the derived key */
+
+ /* AES-SIV AAD vectors */
+
+ /* The STA's MAC address */
+ aad[0] = sta->addr;
+ aad_len[0] = ETH_ALEN;
+ /* The AP's BSSID */
+ aad[1] = bss->bssid;
+ aad_len[1] = ETH_ALEN;
+ /* The STA's nonce */
+ aad[2] = sta->snonce;
+ aad_len[2] = FILS_NONCE_LEN;
+ /* The AP's nonce */
+ aad[3] = sta->anonce;
+ aad_len[3] = FILS_NONCE_LEN;
+ /*
+ * The (Re)Association Request frame from the Capability Information
+ * field to the FILS Session element (both inclusive).
+ */
+ aad[4] = frame_ad;
+ aad_len[4] = frame_ad_end - frame_ad;
+
+ if (encr_end - frame_ad_end < AES_BLOCK_SIZE ||
+ encr_end - frame_ad_end > sizeof(buf))
+ return -1;
+ if (aes_siv_decrypt(ptk.kek, ptk.kek_len,
+ frame_ad_end, encr_end - frame_ad_end,
+ 5, aad, aad_len, buf) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Derived PTK did not match AES-SIV data");
+ return -1;
+ }
+
+ add_note(wt, MSG_DEBUG, "Derived FILS PTK");
+ os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
+ sta->ptk_set = 1;
+ sta->counters[WLANTEST_STA_COUNTER_PTK_LEARNED]++;
+ wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Request elements",
+ buf, encr_end - frame_ad_end - AES_BLOCK_SIZE);
+
+ if (wt->write_pcap_dumper || wt->pcapng) {
+ write_pcap_decrypted(wt, frame_start,
+ frame_ad_end - frame_start,
+ buf,
+ encr_end - frame_ad_end - AES_BLOCK_SIZE);
+ }
+
+ return 0;
+}
+
+
+static void derive_fils_keys(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, const u8 *frame_start,
+ const u8 *frame_ad, const u8 *frame_ad_end,
+ const u8 *encr_end)
+{
+ struct wlantest_pmk *pmk;
+
+ wpa_printf(MSG_DEBUG, "Trying to derive PTK for " MACSTR
+ " from FILS rMSK", MAC2STR(sta->addr));
+
+ dl_list_for_each(pmk, &bss->pmk, struct wlantest_pmk,
+ list) {
+ wpa_printf(MSG_DEBUG, "Try per-BSS PMK");
+ if (try_rmsk(wt, bss, sta, pmk, frame_start, frame_ad,
+ frame_ad_end, encr_end) == 0)
+ return;
+ }
+
+ dl_list_for_each(pmk, &wt->pmk, struct wlantest_pmk, list) {
+ wpa_printf(MSG_DEBUG, "Try global PMK");
+ if (try_rmsk(wt, bss, sta, pmk, frame_start, frame_ad,
+ frame_ad_end, encr_end) == 0)
+ return;
+ }
+}
+
+
+static void rx_mgmt_assoc_req(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ struct ieee802_11_elems elems;
+ const u8 *ie;
+ size_t ie_len;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, mgmt->sa);
+ if (sta == NULL)
+ return;
+
+ if (len < 24 + 4) {
+ add_note(wt, MSG_INFO, "Too short Association Request frame "
+ "from " MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "ASSOCREQ " MACSTR " -> " MACSTR
+ " (capab=0x%x listen_int=%u)",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+ le_to_host16(mgmt->u.assoc_req.capab_info),
+ le_to_host16(mgmt->u.assoc_req.listen_interval));
+
+ sta->counters[WLANTEST_STA_COUNTER_ASSOCREQ_TX]++;
+
+ ie = mgmt->u.assoc_req.variable;
+ ie_len = len - (mgmt->u.assoc_req.variable - data);
+
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK) {
+ const u8 *session, *frame_ad, *frame_ad_end, *encr_end;
+
+ session = get_fils_session(ie, ie_len);
+ if (session) {
+ frame_ad = (const u8 *) &mgmt->u.assoc_req.capab_info;
+ frame_ad_end = session + 2 + session[1];
+ encr_end = data + len;
+ derive_fils_keys(wt, bss, sta, data, frame_ad,
+ frame_ad_end, encr_end);
+ ie_len = session - ie;
+ }
+ }
+
+ if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+ add_note(wt, MSG_INFO, "Invalid IEs in Association Request "
+ "frame from " MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ sta->assocreq_capab_info = le_to_host16(mgmt->u.assoc_req.capab_info);
+ sta->assocreq_listen_int =
+ le_to_host16(mgmt->u.assoc_req.listen_interval);
+ os_free(sta->assocreq_ies);
+ sta->assocreq_ies_len = len - (mgmt->u.assoc_req.variable - data);
+ sta->assocreq_ies = os_malloc(sta->assocreq_ies_len);
+ if (sta->assocreq_ies)
+ os_memcpy(sta->assocreq_ies, mgmt->u.assoc_req.variable,
+ sta->assocreq_ies_len);
+
+ sta->assocreq_seen = 1;
+ sta_update_assoc(sta, &elems);
+}
+
+
+static void decrypt_fils_assoc_resp(struct wlantest *wt,
+ struct wlantest_bss *bss,
+ struct wlantest_sta *sta,
+ const u8 *frame_start, const u8 *frame_ad,
+ const u8 *frame_ad_end, const u8 *encr_end)
+{
+ const u8 *aad[5];
+ size_t aad_len[5];
+ u8 buf[2000];
+
+ if (!sta->ptk_set)
+ return;
+
+ /* Check AES-SIV decryption with the derived key */
+
+ /* AES-SIV AAD vectors */
+
+ /* The AP's BSSID */
+ aad[0] = bss->bssid;
+ aad_len[0] = ETH_ALEN;
+ /* The STA's MAC address */
+ aad[1] = sta->addr;
+ aad_len[1] = ETH_ALEN;
+ /* The AP's nonce */
+ aad[2] = sta->anonce;
+ aad_len[2] = FILS_NONCE_LEN;
+ /* The STA's nonce */
+ aad[3] = sta->snonce;
+ aad_len[3] = FILS_NONCE_LEN;
+ /*
+ * The (Re)Association Response frame from the Capability Information
+ * field to the FILS Session element (both inclusive).
+ */
+ aad[4] = frame_ad;
+ aad_len[4] = frame_ad_end - frame_ad;
+
+ if (encr_end - frame_ad_end < AES_BLOCK_SIZE ||
+ encr_end - frame_ad_end > sizeof(buf))
+ return;
+ if (aes_siv_decrypt(sta->ptk.kek, sta->ptk.kek_len,
+ frame_ad_end, encr_end - frame_ad_end,
+ 5, aad, aad_len, buf) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS: Derived PTK did not match AES-SIV data");
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FILS: Decrypted Association Response elements",
+ buf, encr_end - frame_ad_end - AES_BLOCK_SIZE);
+
+ if (wt->write_pcap_dumper || wt->pcapng) {
+ write_pcap_decrypted(wt, frame_start,
+ frame_ad_end - frame_start,
+ buf,
+ encr_end - frame_ad_end - AES_BLOCK_SIZE);
+ }
+}
+
+
+static void rx_mgmt_assoc_resp(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ u16 capab, status, aid;
+ const u8 *ies;
+ size_t ies_len;
+ struct wpa_ft_ies parse;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, mgmt->da);
+ if (sta == NULL)
+ return;
+
+ if (len < 24 + 6) {
+ add_note(wt, MSG_INFO, "Too short Association Response frame "
+ "from " MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ ies = mgmt->u.assoc_resp.variable;
+ ies_len = len - (mgmt->u.assoc_resp.variable - data);
+
+ capab = le_to_host16(mgmt->u.assoc_resp.capab_info);
+ status = le_to_host16(mgmt->u.assoc_resp.status_code);
+ aid = le_to_host16(mgmt->u.assoc_resp.aid);
+
+ wpa_printf(MSG_DEBUG, "ASSOCRESP " MACSTR " -> " MACSTR
+ " (capab=0x%x status=%u aid=%u)",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), capab, status,
+ aid & 0x3fff);
+
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK) {
+ const u8 *session, *frame_ad, *frame_ad_end, *encr_end;
+
+ session = get_fils_session(ies, ies_len);
+ if (session) {
+ frame_ad = (const u8 *) &mgmt->u.assoc_resp.capab_info;
+ frame_ad_end = session + 2 + session[1];
+ encr_end = data + len;
+ decrypt_fils_assoc_resp(wt, bss, sta, data, frame_ad,
+ frame_ad_end, encr_end);
+ ies_len = session - ies;
+ }
+ }
+
+ if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+ struct ieee802_11_elems elems;
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) ==
+ ParseFailed) {
+ add_note(wt, MSG_INFO, "Failed to parse IEs in "
+ "AssocResp from " MACSTR,
+ MAC2STR(mgmt->sa));
+ } else if (elems.timeout_int == NULL ||
+ elems.timeout_int[0] !=
+ WLAN_TIMEOUT_ASSOC_COMEBACK) {
+ add_note(wt, MSG_INFO, "No valid Timeout Interval IE "
+ "with Assoc Comeback time in AssocResp "
+ "(status=30) from " MACSTR,
+ MAC2STR(mgmt->sa));
+ } else {
+ sta->counters[
+ WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK]++;
+ }
+ }
+
+ if (status)
+ return;
+
+ if ((aid & 0xc000) != 0xc000) {
+ add_note(wt, MSG_DEBUG, "Two MSBs of the AID were not set to 1 "
+ "in Association Response from " MACSTR,
+ MAC2STR(mgmt->sa));
+ }
+ sta->aid = aid & 0xc000;
+
+ if (sta->state < STATE2) {
+ add_note(wt, MSG_DEBUG,
+ "STA " MACSTR " was not in State 2 when "
+ "getting associated", MAC2STR(sta->addr));
+ }
+
+ if (sta->state < STATE3) {
+ add_note(wt, MSG_DEBUG, "STA " MACSTR
+ " moved to State 3 with " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ sta->state = STATE3;
+ }
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, 0) == 0) {
+ if (parse.r0kh_id) {
+ os_memcpy(bss->r0kh_id, parse.r0kh_id,
+ parse.r0kh_id_len);
+ bss->r0kh_id_len = parse.r0kh_id_len;
+ }
+ if (parse.r1kh_id)
+ os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+ }
+}
+
+
+static void rx_mgmt_reassoc_req(struct wlantest *wt, const u8 *data,
+ size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ struct ieee802_11_elems elems;
+ const u8 *ie;
+ size_t ie_len;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, mgmt->sa);
+ if (sta == NULL)
+ return;
+
+ if (len < 24 + 4 + ETH_ALEN) {
+ add_note(wt, MSG_INFO, "Too short Reassociation Request frame "
+ "from " MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "REASSOCREQ " MACSTR " -> " MACSTR
+ " (capab=0x%x listen_int=%u current_ap=" MACSTR ")",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+ le_to_host16(mgmt->u.reassoc_req.capab_info),
+ le_to_host16(mgmt->u.reassoc_req.listen_interval),
+ MAC2STR(mgmt->u.reassoc_req.current_ap));
+
+ sta->counters[WLANTEST_STA_COUNTER_REASSOCREQ_TX]++;
+
+ ie = mgmt->u.reassoc_req.variable;
+ ie_len = len - (mgmt->u.reassoc_req.variable - data);
+
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK) {
+ const u8 *session, *frame_ad, *frame_ad_end, *encr_end;
+
+ session = get_fils_session(ie, ie_len);
+ if (session) {
+ frame_ad = (const u8 *) &mgmt->u.reassoc_req.capab_info;
+ frame_ad_end = session + 2 + session[1];
+ encr_end = data + len;
+ derive_fils_keys(wt, bss, sta, data, frame_ad,
+ frame_ad_end, encr_end);
+ ie_len = session - ie;
+ }
+ }
+
+ if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+ add_note(wt, MSG_INFO, "Invalid IEs in Reassociation Request "
+ "frame from " MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ sta->assocreq_capab_info =
+ le_to_host16(mgmt->u.reassoc_req.capab_info);
+ sta->assocreq_listen_int =
+ le_to_host16(mgmt->u.reassoc_req.listen_interval);
+ os_free(sta->assocreq_ies);
+ sta->assocreq_ies_len = len - (mgmt->u.reassoc_req.variable - data);
+ sta->assocreq_ies = os_malloc(sta->assocreq_ies_len);
+ if (sta->assocreq_ies)
+ os_memcpy(sta->assocreq_ies, mgmt->u.reassoc_req.variable,
+ sta->assocreq_ies_len);
+
+ sta->assocreq_seen = 1;
+ sta_update_assoc(sta, &elems);
+
+ if (elems.ftie) {
+ struct wpa_ft_ies parse;
+ int use_sha384;
+ struct rsn_mdie *mde;
+ const u8 *anonce, *snonce, *fte_mic;
+ u8 fte_elem_count;
+ unsigned int count;
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ size_t mic_len = 16;
+ const u8 *kck;
+ size_t kck_len;
+
+ use_sha384 = wpa_key_mgmt_sha384(sta->key_mgmt);
+
+ if (wpa_ft_parse_ies(ie, ie_len, &parse, use_sha384) < 0) {
+ add_note(wt, MSG_INFO, "FT: Failed to parse FT IEs");
+ return;
+ }
+
+ if (!parse.rsn) {
+ add_note(wt, MSG_INFO, "FT: No RSNE in Reassoc Req");
+ return;
+ }
+
+ if (!parse.rsn_pmkid) {
+ add_note(wt, MSG_INFO, "FT: No PMKID in RSNE");
+ return;
+ }
+
+ if (os_memcmp_const(parse.rsn_pmkid, sta->pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0) {
+ add_note(wt, MSG_INFO,
+ "FT: PMKID in Reassoc Req did not match PMKR1Name");
+ wpa_hexdump(MSG_DEBUG,
+ "FT: Received RSNE[PMKR1Name]",
+ parse.rsn_pmkid, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG,
+ "FT: Previously derived PMKR1Name",
+ sta->pmk_r1_name, WPA_PMK_NAME_LEN);
+ return;
+ }
+
+ mde = (struct rsn_mdie *) parse.mdie;
+ if (!mde || parse.mdie_len < sizeof(*mde) ||
+ os_memcmp(mde->mobility_domain, bss->mdid,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ add_note(wt, MSG_INFO, "FT: Invalid MDE");
+ }
+
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *fte;
+
+ fte = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (!fte || parse.ftie_len < sizeof(*fte)) {
+ add_note(wt, MSG_INFO, "FT: Invalid FTE");
+ return;
+ }
+
+ anonce = fte->anonce;
+ snonce = fte->snonce;
+ fte_elem_count = fte->mic_control[1];
+ fte_mic = fte->mic;
+ } else {
+ struct rsn_ftie *fte;
+
+ fte = (struct rsn_ftie *) parse.ftie;
+ if (!fte || parse.ftie_len < sizeof(*fte)) {
+ add_note(wt, MSG_INFO, "FT: Invalid FTIE");
+ return;
+ }
+
+ anonce = fte->anonce;
+ snonce = fte->snonce;
+ fte_elem_count = fte->mic_control[1];
+ fte_mic = fte->mic;
+ }
+
+ if (os_memcmp(snonce, sta->snonce, WPA_NONCE_LEN) != 0) {
+ add_note(wt, MSG_INFO, "FT: SNonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+ snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+ sta->snonce, WPA_NONCE_LEN);
+ return;
+ }
+
+ if (os_memcmp(anonce, sta->anonce, WPA_NONCE_LEN) != 0) {
+ add_note(wt, MSG_INFO, "FT: ANonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+ anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+ sta->anonce, WPA_NONCE_LEN);
+ return;
+ }
+
+ if (!parse.r0kh_id) {
+ add_note(wt, MSG_INFO, "FT: No R0KH-ID subelem in FTE");
+ return;
+ }
+ os_memcpy(bss->r0kh_id, parse.r0kh_id, parse.r0kh_id_len);
+ bss->r0kh_id_len = parse.r0kh_id_len;
+
+ if (!parse.r1kh_id) {
+ add_note(wt, MSG_INFO, "FT: No R1KH-ID subelem in FTE");
+ return;
+ }
+
+ os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+
+ if (!parse.rsn_pmkid ||
+ os_memcmp_const(parse.rsn_pmkid, sta->pmk_r1_name,
+ WPA_PMK_NAME_LEN)) {
+ add_note(wt, MSG_INFO,
+ "FT: No matching PMKR1Name (PMKID) in RSNE (pmkid=%d)",
+ !!parse.rsn_pmkid);
+ return;
+ }
+
+ count = 3;
+ if (parse.ric)
+ count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+ if (parse.rsnxe)
+ count++;
+ if (fte_elem_count != count) {
+ add_note(wt, MSG_INFO,
+ "FT: Unexpected IE count in MIC Control: received %u expected %u",
+ fte_elem_count, count);
+ return;
+ }
+
+ if (wpa_key_mgmt_fils(sta->key_mgmt)) {
+ kck = sta->ptk.kck2;
+ kck_len = sta->ptk.kck2_len;
+ } else {
+ kck = sta->ptk.kck;
+ kck_len = sta->ptk.kck_len;
+ }
+ if (wpa_ft_mic(kck, kck_len, sta->addr, bss->bssid, 5,
+ parse.mdie - 2, parse.mdie_len + 2,
+ parse.ftie - 2, parse.ftie_len + 2,
+ parse.rsn - 2, parse.rsn_len + 2,
+ parse.ric, parse.ric_len,
+ parse.rsnxe ? parse.rsnxe - 2 : NULL,
+ parse.rsnxe ? parse.rsnxe_len + 2 : 0,
+ mic) < 0) {
+ add_note(wt, MSG_INFO, "FT: Failed to calculate MIC");
+ return;
+ }
+
+ if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
+ add_note(wt, MSG_INFO, "FT: Invalid MIC in FTE");
+ wpa_printf(MSG_DEBUG,
+ "FT: addr=" MACSTR " auth_addr=" MACSTR,
+ MAC2STR(sta->addr),
+ MAC2STR(bss->bssid));
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+ fte_mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC",
+ mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: MDE",
+ parse.mdie - 2, parse.mdie_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: FTE",
+ parse.ftie - 2, parse.ftie_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
+ parse.rsn - 2, parse.rsn_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE",
+ parse.rsnxe ? parse.rsnxe - 2 : NULL,
+ parse.rsnxe ? parse.rsnxe_len + 2 : 0);
+ return;
+ }
+
+ add_note(wt, MSG_INFO, "FT: Valid FTE MIC");
+ }
+}
+
+
+static void process_gtk_subelem(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta,
+ const u8 *kek, size_t kek_len,
+ const u8 *gtk_elem,
+ size_t gtk_elem_len)
+{
+ u8 gtk[32];
+ int keyidx;
+ enum wpa_alg alg;
+ size_t gtk_len, keylen;
+ const u8 *rsc;
+
+ if (!gtk_elem) {
+ add_note(wt, MSG_INFO, "FT: No GTK included in FTE");
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: Received GTK in Reassoc Resp",
+ gtk_elem, gtk_elem_len);
+
+ if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 ||
+ gtk_elem_len - 19 > sizeof(gtk)) {
+ add_note(wt, MSG_INFO, "FT: Invalid GTK sub-elem length %zu",
+ gtk_elem_len);
+ return;
+ }
+ gtk_len = gtk_elem_len - 19;
+ if (aes_unwrap(kek, kek_len, gtk_len / 8, gtk_elem + 11, gtk)) {
+ add_note(wt, MSG_INFO,
+ "FT: AES unwrap failed - could not decrypt GTK");
+ return;
+ }
+
+ keylen = wpa_cipher_key_len(bss->group_cipher);
+ alg = wpa_cipher_to_alg(bss->group_cipher);
+ if (alg == WPA_ALG_NONE) {
+ add_note(wt, MSG_INFO, "FT: Unsupported Group Cipher %d",
+ bss->group_cipher);
+ return;
+ }
+
+ if (gtk_len < keylen) {
+ add_note(wt, MSG_INFO, "FT: Too short GTK in FTE");
+ return;
+ }
+
+ /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */
+
+ keyidx = WPA_GET_LE16(gtk_elem) & 0x03;
+
+ if (gtk_elem[2] != keylen) {
+ add_note(wt, MSG_INFO,
+ "FT: GTK length mismatch: received %u negotiated %zu",
+ gtk_elem[2], keylen);
+ return;
+ }
+
+ add_note(wt, MSG_DEBUG, "GTK KeyID=%u", keyidx);
+ wpa_hexdump(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen);
+ if (bss->group_cipher == WPA_CIPHER_TKIP) {
+ /* Swap Tx/Rx keys for Michael MIC */
+ u8 tmp[8];
+
+ os_memcpy(tmp, gtk + 16, 8);
+ os_memcpy(gtk + 16, gtk + 24, 8);
+ os_memcpy(gtk + 24, tmp, 8);
+ }
+
+ bss->gtk_len[keyidx] = gtk_len;
+ sta->gtk_len = gtk_len;
+ os_memcpy(bss->gtk[keyidx], gtk, gtk_len);
+ os_memcpy(sta->gtk, gtk, gtk_len);
+ rsc = gtk_elem + 2;
+ bss->rsc[keyidx][0] = rsc[5];
+ bss->rsc[keyidx][1] = rsc[4];
+ bss->rsc[keyidx][2] = rsc[3];
+ bss->rsc[keyidx][3] = rsc[2];
+ bss->rsc[keyidx][4] = rsc[1];
+ bss->rsc[keyidx][5] = rsc[0];
+ bss->gtk_idx = keyidx;
+ sta->gtk_idx = keyidx;
+ wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[keyidx], 6);
+}
+
+
+static void process_igtk_subelem(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta,
+ const u8 *kek, size_t kek_len,
+ const u8 *igtk_elem, size_t igtk_elem_len)
+{
+ u8 igtk[WPA_IGTK_MAX_LEN];
+ size_t igtk_len;
+ u16 keyidx;
+ const u8 *ipn;
+
+ if (bss->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC &&
+ bss->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 &&
+ bss->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 &&
+ bss->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256)
+ return;
+
+ if (!igtk_elem) {
+ add_note(wt, MSG_INFO, "FT: No IGTK included in FTE");
+ return;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "FT: Received IGTK in Reassoc Resp",
+ igtk_elem, igtk_elem_len);
+
+ igtk_len = wpa_cipher_key_len(bss->mgmt_group_cipher);
+ if (igtk_elem_len != 2 + 6 + 1 + igtk_len + 8) {
+ add_note(wt, MSG_INFO, "FT: Invalid IGTK sub-elem length %zu",
+ igtk_elem_len);
+ return;
+ }
+ if (igtk_elem[8] != igtk_len) {
+ add_note(wt, MSG_INFO,
+ "FT: Invalid IGTK sub-elem Key Length %d",
+ igtk_elem[8]);
+ return;
+ }
+
+ if (aes_unwrap(kek, kek_len, igtk_len / 8, igtk_elem + 9, igtk)) {
+ add_note(wt, MSG_INFO,
+ "FT: AES unwrap failed - could not decrypt IGTK");
+ return;
+ }
+
+ /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
+
+ keyidx = WPA_GET_LE16(igtk_elem);
+
+ wpa_hexdump(MSG_DEBUG, "FT: IGTK from Reassoc Resp", igtk, igtk_len);
+
+ if (keyidx < 4 || keyidx > 5) {
+ add_note(wt, MSG_INFO, "Unexpected IGTK KeyID %u", keyidx);
+ return;
+ }
+
+ add_note(wt, MSG_DEBUG, "IGTK KeyID %u", keyidx);
+ wpa_hexdump(MSG_DEBUG, "IPN", igtk_elem + 2, 6);
+ wpa_hexdump(MSG_DEBUG, "IGTK", igtk, igtk_len);
+ os_memcpy(bss->igtk[keyidx], igtk, igtk_len);
+ bss->igtk_len[keyidx] = igtk_len;
+ ipn = igtk_elem + 2;
+ bss->ipn[keyidx][0] = ipn[5];
+ bss->ipn[keyidx][1] = ipn[4];
+ bss->ipn[keyidx][2] = ipn[3];
+ bss->ipn[keyidx][3] = ipn[2];
+ bss->ipn[keyidx][4] = ipn[1];
+ bss->ipn[keyidx][5] = ipn[0];
+ bss->igtk_idx = keyidx;
+}
+
+
+static void process_bigtk_subelem(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta,
+ const u8 *kek, size_t kek_len,
+ const u8 *bigtk_elem, size_t bigtk_elem_len)
+{
+ u8 bigtk[WPA_BIGTK_MAX_LEN];
+ size_t bigtk_len;
+ u16 keyidx;
+ const u8 *ipn;
+
+ if (!bigtk_elem ||
+ (bss->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC &&
+ bss->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_128 &&
+ bss->mgmt_group_cipher != WPA_CIPHER_BIP_GMAC_256 &&
+ bss->mgmt_group_cipher != WPA_CIPHER_BIP_CMAC_256))
+ return;
+
+ wpa_hexdump_key(MSG_DEBUG, "FT: Received BIGTK in Reassoc Resp",
+ bigtk_elem, bigtk_elem_len);
+
+ bigtk_len = wpa_cipher_key_len(bss->mgmt_group_cipher);
+ if (bigtk_elem_len != 2 + 6 + 1 + bigtk_len + 8) {
+ add_note(wt, MSG_INFO,
+ "FT: Invalid BIGTK sub-elem length %zu",
+ bigtk_elem_len);
+ return;
+ }
+ if (bigtk_elem[8] != bigtk_len) {
+ add_note(wt, MSG_INFO,
+ "FT: Invalid BIGTK sub-elem Key Length %d",
+ bigtk_elem[8]);
+ return;
+ }
+
+ if (aes_unwrap(kek, kek_len, bigtk_len / 8, bigtk_elem + 9, bigtk)) {
+ add_note(wt, MSG_INFO,
+ "FT: AES unwrap failed - could not decrypt BIGTK");
+ return;
+ }
+
+ /* KeyID[2] | IPN[6] | Key Length[1] | Key[16+8] */
+
+ keyidx = WPA_GET_LE16(bigtk_elem);
+
+ wpa_hexdump(MSG_DEBUG, "FT: BIGTK from Reassoc Resp", bigtk, bigtk_len);
+
+ if (keyidx < 6 || keyidx > 7) {
+ add_note(wt, MSG_INFO, "Unexpected BIGTK KeyID %u", keyidx);
+ return;
+ }
+
+ add_note(wt, MSG_DEBUG, "BIGTK KeyID %u", keyidx);
+ wpa_hexdump(MSG_DEBUG, "BIPN", bigtk_elem + 2, 6);
+ wpa_hexdump(MSG_DEBUG, "BIGTK", bigtk, bigtk_len);
+ os_memcpy(bss->igtk[keyidx], bigtk, bigtk_len);
+ bss->igtk_len[keyidx] = bigtk_len;
+ ipn = bigtk_elem + 2;
+ bss->ipn[keyidx][0] = ipn[5];
+ bss->ipn[keyidx][1] = ipn[4];
+ bss->ipn[keyidx][2] = ipn[3];
+ bss->ipn[keyidx][3] = ipn[2];
+ bss->ipn[keyidx][4] = ipn[1];
+ bss->ipn[keyidx][5] = ipn[0];
+ bss->bigtk_idx = keyidx;
+}
+
+
+static void rx_mgmt_reassoc_resp(struct wlantest *wt, const u8 *data,
+ size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ u16 capab, status, aid;
+ const u8 *ies;
+ size_t ies_len;
+ struct ieee802_11_elems elems;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ sta = sta_get(bss, mgmt->da);
+ if (sta == NULL)
+ return;
+
+ if (len < 24 + 6) {
+ add_note(wt, MSG_INFO, "Too short Reassociation Response frame "
+ "from " MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ ies = mgmt->u.reassoc_resp.variable;
+ ies_len = len - (mgmt->u.reassoc_resp.variable - data);
+
+ capab = le_to_host16(mgmt->u.reassoc_resp.capab_info);
+ status = le_to_host16(mgmt->u.reassoc_resp.status_code);
+ aid = le_to_host16(mgmt->u.reassoc_resp.aid);
+
+ wpa_printf(MSG_DEBUG, "REASSOCRESP " MACSTR " -> " MACSTR
+ " (capab=0x%x status=%u aid=%u)",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), capab, status,
+ aid & 0x3fff);
+
+ if (sta->auth_alg == WLAN_AUTH_FILS_SK) {
+ const u8 *session, *frame_ad, *frame_ad_end, *encr_end;
+
+ session = get_fils_session(ies, ies_len);
+ if (session) {
+ frame_ad = (const u8 *)
+ &mgmt->u.reassoc_resp.capab_info;
+ frame_ad_end = session + 2 + session[1];
+ encr_end = data + len;
+ decrypt_fils_assoc_resp(wt, bss, sta, data, frame_ad,
+ frame_ad_end, encr_end);
+ ies_len = session - ies;
+ }
+ }
+
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed) {
+ add_note(wt, MSG_INFO,
+ "Failed to parse IEs in ReassocResp from " MACSTR,
+ MAC2STR(mgmt->sa));
+ }
+
+ if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+ if (!elems.timeout_int ||
+ elems.timeout_int[0] != WLAN_TIMEOUT_ASSOC_COMEBACK) {
+ add_note(wt, MSG_INFO, "No valid Timeout Interval IE "
+ "with Assoc Comeback time in ReassocResp "
+ "(status=30) from " MACSTR,
+ MAC2STR(mgmt->sa));
+ } else {
+ sta->counters[
+ WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK]++;
+ }
+ }
+
+ if (status)
+ return;
+
+ if ((aid & 0xc000) != 0xc000) {
+ add_note(wt, MSG_DEBUG, "Two MSBs of the AID were not set to 1 "
+ "in Reassociation Response from " MACSTR,
+ MAC2STR(mgmt->sa));
+ }
+ sta->aid = aid & 0xc000;
+
+ if (sta->state < STATE2 && !sta->ft_over_ds) {
+ add_note(wt, MSG_DEBUG,
+ "STA " MACSTR " was not in State 2 when "
+ "getting associated", MAC2STR(sta->addr));
+ }
+
+ if (sta->state < STATE3) {
+ add_note(wt, MSG_DEBUG, "STA " MACSTR
+ " moved to State 3 with " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ sta->state = STATE3;
+ }
+
+ if (elems.ftie) {
+ struct wpa_ft_ies parse;
+ int use_sha384;
+ struct rsn_mdie *mde;
+ const u8 *anonce, *snonce, *fte_mic;
+ u8 fte_elem_count;
+ unsigned int count;
+ u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+ size_t mic_len = 16;
+ const u8 *kck, *kek;
+ size_t kck_len, kek_len;
+
+ use_sha384 = wpa_key_mgmt_sha384(sta->key_mgmt);
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, use_sha384) < 0) {
+ add_note(wt, MSG_INFO, "FT: Failed to parse FT IEs");
+ return;
+ }
+
+ if (!parse.rsn) {
+ add_note(wt, MSG_INFO, "FT: No RSNE in Reassoc Resp");
+ return;
+ }
+
+ if (!parse.rsn_pmkid) {
+ add_note(wt, MSG_INFO, "FT: No PMKID in RSNE");
+ return;
+ }
+
+ if (os_memcmp_const(parse.rsn_pmkid, sta->pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0) {
+ add_note(wt, MSG_INFO,
+ "FT: PMKID in Reassoc Resp did not match PMKR1Name");
+ wpa_hexdump(MSG_DEBUG,
+ "FT: Received RSNE[PMKR1Name]",
+ parse.rsn_pmkid, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG,
+ "FT: Previously derived PMKR1Name",
+ sta->pmk_r1_name, WPA_PMK_NAME_LEN);
+ return;
+ }
+
+ mde = (struct rsn_mdie *) parse.mdie;
+ if (!mde || parse.mdie_len < sizeof(*mde) ||
+ os_memcmp(mde->mobility_domain, bss->mdid,
+ MOBILITY_DOMAIN_ID_LEN) != 0) {
+ add_note(wt, MSG_INFO, "FT: Invalid MDE");
+ }
+
+ if (use_sha384) {
+ struct rsn_ftie_sha384 *fte;
+
+ fte = (struct rsn_ftie_sha384 *) parse.ftie;
+ if (!fte || parse.ftie_len < sizeof(*fte)) {
+ add_note(wt, MSG_INFO, "FT: Invalid FTE");
+ return;
+ }
+
+ anonce = fte->anonce;
+ snonce = fte->snonce;
+ fte_elem_count = fte->mic_control[1];
+ fte_mic = fte->mic;
+ } else {
+ struct rsn_ftie *fte;
+
+ fte = (struct rsn_ftie *) parse.ftie;
+ if (!fte || parse.ftie_len < sizeof(*fte)) {
+ add_note(wt, MSG_INFO, "FT: Invalid FTIE");
+ return;
+ }
+
+ anonce = fte->anonce;
+ snonce = fte->snonce;
+ fte_elem_count = fte->mic_control[1];
+ fte_mic = fte->mic;
+ }
+
+ if (os_memcmp(snonce, sta->snonce, WPA_NONCE_LEN) != 0) {
+ add_note(wt, MSG_INFO, "FT: SNonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received SNonce",
+ snonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce",
+ sta->snonce, WPA_NONCE_LEN);
+ return;
+ }
+
+ if (os_memcmp(anonce, sta->anonce, WPA_NONCE_LEN) != 0) {
+ add_note(wt, MSG_INFO, "FT: ANonce mismatch in FTIE");
+ wpa_hexdump(MSG_DEBUG, "FT: Received ANonce",
+ anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce",
+ sta->anonce, WPA_NONCE_LEN);
+ return;
+ }
+
+ if (!parse.r0kh_id) {
+ add_note(wt, MSG_INFO, "FT: No R0KH-ID subelem in FTE");
+ return;
+ }
+
+ if (parse.r0kh_id_len != bss->r0kh_id_len ||
+ os_memcmp_const(parse.r0kh_id, bss->r0kh_id,
+ parse.r0kh_id_len) != 0) {
+ add_note(wt, MSG_INFO,
+ "FT: R0KH-ID in FTE did not match the current R0KH-ID");
+ wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
+ parse.r0kh_id, parse.r0kh_id_len);
+ wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID",
+ bss->r0kh_id, bss->r0kh_id_len);
+ os_memcpy(bss->r0kh_id, parse.r0kh_id,
+ parse.r0kh_id_len);
+ bss->r0kh_id_len = parse.r0kh_id_len;
+ }
+
+ if (!parse.r1kh_id) {
+ add_note(wt, MSG_INFO, "FT: No R1KH-ID subelem in FTE");
+ return;
+ }
+
+ if (os_memcmp_const(parse.r1kh_id, bss->r1kh_id,
+ FT_R1KH_ID_LEN) != 0) {
+ add_note(wt, MSG_INFO,
+ "FT: Unknown R1KH-ID used in ReassocResp");
+ os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+ }
+
+ count = 3;
+ if (parse.ric)
+ count += ieee802_11_ie_count(parse.ric, parse.ric_len);
+ if (parse.rsnxe)
+ count++;
+ if (fte_elem_count != count) {
+ add_note(wt, MSG_INFO,
+ "FT: Unexpected IE count in MIC Control: received %u expected %u",
+ fte_elem_count, count);
+ return;
+ }
+
+ if (wpa_key_mgmt_fils(sta->key_mgmt)) {
+ kck = sta->ptk.kck2;
+ kck_len = sta->ptk.kck2_len;
+ kek = sta->ptk.kek2;
+ kek_len = sta->ptk.kek2_len;
+ } else {
+ kck = sta->ptk.kck;
+ kck_len = sta->ptk.kck_len;
+ kek = sta->ptk.kek;
+ kek_len = sta->ptk.kek_len;
+ }
+ if (wpa_ft_mic(kck, kck_len, sta->addr, bss->bssid, 6,
+ parse.mdie - 2, parse.mdie_len + 2,
+ parse.ftie - 2, parse.ftie_len + 2,
+ parse.rsn - 2, parse.rsn_len + 2,
+ parse.ric, parse.ric_len,
+ parse.rsnxe ? parse.rsnxe - 2 : NULL,
+ parse.rsnxe ? parse.rsnxe_len + 2 : 0,
+ mic) < 0) {
+ add_note(wt, MSG_INFO, "FT: Failed to calculate MIC");
+ return;
+ }
+
+ if (os_memcmp_const(mic, fte_mic, mic_len) != 0) {
+ add_note(wt, MSG_INFO, "FT: Invalid MIC in FTE");
+ wpa_printf(MSG_DEBUG,
+ "FT: addr=" MACSTR " auth_addr=" MACSTR,
+ MAC2STR(sta->addr),
+ MAC2STR(bss->bssid));
+ wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+ fte_mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC",
+ mic, mic_len);
+ wpa_hexdump(MSG_MSGDUMP, "FT: MDE",
+ parse.mdie - 2, parse.mdie_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: FTE",
+ parse.ftie - 2, parse.ftie_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: RSN",
+ parse.rsn - 2, parse.rsn_len + 2);
+ wpa_hexdump(MSG_MSGDUMP, "FT: RSNXE",
+ parse.rsnxe ? parse.rsnxe - 2 : NULL,
+ parse.rsnxe ? parse.rsnxe_len + 2 : 0);
+ return;
+ }
+
+ add_note(wt, MSG_INFO, "FT: Valid FTE MIC");
+
+ if (wpa_compare_rsn_ie(wpa_key_mgmt_ft(sta->key_mgmt),
+ bss->rsnie, 2 + bss->rsnie[1],
+ parse.rsn - 2, parse.rsn_len + 2)) {
+ add_note(wt, MSG_INFO,
+ "FT: RSNE mismatch between Beacon/ProbeResp and FT protocol Reassociation Response frame");
+ wpa_hexdump(MSG_INFO, "RSNE in Beacon/ProbeResp",
+ &bss->rsnie[2], bss->rsnie[1]);
+ wpa_hexdump(MSG_INFO,
+ "RSNE in FT protocol Reassociation Response frame",
+ parse.rsn ? parse.rsn - 2 : NULL,
+ parse.rsn ? parse.rsn_len + 2 : 0);
+ }
+
+ process_gtk_subelem(wt, bss, sta, kek, kek_len,
+ parse.gtk, parse.gtk_len);
+ process_igtk_subelem(wt, bss, sta, kek, kek_len,
+ parse.igtk, parse.igtk_len);
+ process_bigtk_subelem(wt, bss, sta, kek, kek_len,
+ parse.bigtk, parse.bigtk_len);
+ }
+}
+
+
+static void disassoc_all_stas(struct wlantest *wt, struct wlantest_bss *bss)
+{
+ struct wlantest_sta *sta;
+ dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+ if (sta->state <= STATE2)
+ continue;
+ add_note(wt, MSG_DEBUG, "STA " MACSTR
+ " moved to State 2 with " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ sta->state = STATE2;
+ }
+}
+
+
+static void rx_mgmt_disassoc(struct wlantest *wt, const u8 *data, size_t len,
+ int valid)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ u16 fc, reason;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+ sta = sta_get(bss, mgmt->da);
+ else
+ sta = sta_get(bss, mgmt->sa);
+
+ if (len < 24 + 2) {
+ add_note(wt, MSG_INFO, "Too short Disassociation frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ reason = le_to_host16(mgmt->u.disassoc.reason_code);
+ wpa_printf(MSG_DEBUG, "DISASSOC " MACSTR " -> " MACSTR
+ " (reason=%u) (valid=%d)",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
+ reason, valid);
+ wpa_hexdump(MSG_MSGDUMP, "DISASSOC payload", data + 24, len - 24);
+
+ if (sta == NULL) {
+ if (valid && mgmt->da[0] == 0xff)
+ disassoc_all_stas(wt, bss);
+ return;
+ }
+
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+ sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DISASSOC_RX :
+ WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX]++;
+ if (sta->pwrmgt && !sta->pspoll)
+ sta->counters[
+ WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP]++;
+ else
+ sta->counters[
+ WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE]++;
+
+ fc = le_to_host16(mgmt->frame_control);
+ if (!(fc & WLAN_FC_ISWEP) && reason == 6)
+ sta->counters[WLANTEST_STA_COUNTER_DISASSOC_RX_RC6]++;
+ else if (!(fc & WLAN_FC_ISWEP) && reason == 7)
+ sta->counters[WLANTEST_STA_COUNTER_DISASSOC_RX_RC7]++;
+ } else
+ sta->counters[valid ? WLANTEST_STA_COUNTER_VALID_DISASSOC_TX :
+ WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX]++;
+
+ if (!valid) {
+ add_note(wt, MSG_INFO, "Do not change STA " MACSTR " State "
+ "since Disassociation frame was not protected "
+ "correctly", MAC2STR(sta->addr));
+ return;
+ }
+
+ if (sta->state < STATE2) {
+ add_note(wt, MSG_DEBUG,
+ "STA " MACSTR " was not in State 2 or 3 "
+ "when getting disassociated", MAC2STR(sta->addr));
+ }
+
+ if (sta->state > STATE2) {
+ add_note(wt, MSG_DEBUG, "STA " MACSTR
+ " moved to State 2 with " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ sta->state = STATE2;
+ }
+ tdls_link_down(wt, bss, sta);
+}
+
+
+static void rx_mgmt_action_ft_request(struct wlantest *wt,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ const u8 *ies;
+ size_t ies_len;
+ struct wpa_ft_ies parse;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+
+ if (len < 24 + 2 + 2 * ETH_ALEN) {
+ add_note(wt, MSG_INFO, "Too short FT Request frame");
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT Request: STA Address: " MACSTR
+ " Target AP Address: " MACSTR,
+ MAC2STR(mgmt->u.action.u.ft_action_req.sta_addr),
+ MAC2STR(mgmt->u.action.u.ft_action_req.target_ap_addr));
+ ies = mgmt->u.action.u.ft_action_req.variable;
+ ies_len = len - (24 + 2 + 2 * ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "FT Request frame body", ies, ies_len);
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
+ add_note(wt, MSG_INFO, "Could not parse FT Request frame body");
+ return;
+ }
+
+ bss = bss_get(wt, mgmt->u.action.u.ft_action_resp.target_ap_addr);
+ if (!bss) {
+ add_note(wt, MSG_INFO, "No BSS entry for Target AP");
+ return;
+ }
+
+ sta = sta_get(bss, mgmt->sa);
+ if (!sta)
+ return;
+
+ sta->ft_over_ds = true;
+ sta->key_mgmt = parse.key_mgmt;
+ sta->pairwise_cipher = parse.pairwise_cipher;
+}
+
+
+static void rx_mgmt_action_ft_response(struct wlantest *wt,
+ struct wlantest_sta *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *new_sta;
+ const u8 *ies;
+ size_t ies_len;
+ struct wpa_ft_ies parse;
+ struct wpa_ptk ptk;
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+
+ if (len < 24 + 2 + 2 * ETH_ALEN + 2) {
+ add_note(wt, MSG_INFO, "Too short FT Response frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "FT Response: STA Address: " MACSTR
+ " Target AP Address: " MACSTR " Status Code: %u",
+ MAC2STR(mgmt->u.action.u.ft_action_resp.sta_addr),
+ MAC2STR(mgmt->u.action.u.ft_action_resp.target_ap_addr),
+ le_to_host16(mgmt->u.action.u.ft_action_resp.status_code));
+ ies = mgmt->u.action.u.ft_action_req.variable;
+ ies_len = len - (24 + 2 + 2 * ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "FT Response frame body", ies, ies_len);
+
+ if (wpa_ft_parse_ies(ies, ies_len, &parse, -1)) {
+ add_note(wt, MSG_INFO,
+ "Could not parse FT Response frame body");
+ return;
+ }
+
+ bss = bss_get(wt, mgmt->u.action.u.ft_action_resp.target_ap_addr);
+ if (!bss) {
+ add_note(wt, MSG_INFO, "No BSS entry for Target AP");
+ return;
+ }
+
+ if (parse.r1kh_id)
+ os_memcpy(bss->r1kh_id, parse.r1kh_id, FT_R1KH_ID_LEN);
+
+ if (wpa_derive_pmk_r1(sta->pmk_r0, sta->pmk_r0_len, sta->pmk_r0_name,
+ bss->r1kh_id, sta->addr, sta->pmk_r1,
+ sta->pmk_r1_name) < 0)
+ return;
+ sta->pmk_r1_len = sta->pmk_r0_len;
+
+ new_sta = sta_get(bss, sta->addr);
+ if (!new_sta)
+ return;
+ os_memcpy(new_sta->pmk_r0, sta->pmk_r0, sta->pmk_r0_len);
+ new_sta->pmk_r0_len = sta->pmk_r0_len;
+ os_memcpy(new_sta->pmk_r0_name, sta->pmk_r0_name,
+ sizeof(sta->pmk_r0_name));
+ os_memcpy(new_sta->pmk_r1, sta->pmk_r1, sta->pmk_r1_len);
+ new_sta->pmk_r1_len = sta->pmk_r1_len;
+ os_memcpy(new_sta->pmk_r1_name, sta->pmk_r1_name,
+ sizeof(sta->pmk_r1_name));
+ if (!parse.fte_anonce || !parse.fte_snonce ||
+ wpa_pmk_r1_to_ptk(sta->pmk_r1, sta->pmk_r1_len, parse.fte_snonce,
+ parse.fte_anonce, new_sta->addr, bss->bssid,
+ sta->pmk_r1_name, &ptk, ptk_name,
+ new_sta->key_mgmt, new_sta->pairwise_cipher,
+ 0) < 0)
+ return;
+
+ add_note(wt, MSG_DEBUG, "Derived new PTK");
+ os_memcpy(&new_sta->ptk, &ptk, sizeof(ptk));
+ new_sta->ptk_set = 1;
+ os_memset(new_sta->rsc_tods, 0, sizeof(new_sta->rsc_tods));
+ os_memset(new_sta->rsc_fromds, 0, sizeof(new_sta->rsc_fromds));
+ os_memcpy(new_sta->snonce, parse.fte_snonce, WPA_NONCE_LEN);
+ os_memcpy(new_sta->anonce, parse.fte_anonce, WPA_NONCE_LEN);
+}
+
+
+static void rx_mgmt_action_ft(struct wlantest *wt, struct wlantest_sta *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int valid)
+{
+ if (len < 24 + 2) {
+ add_note(wt, MSG_INFO, "Too short FT Action frame from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ switch (mgmt->u.action.u.ft_action_req.action) {
+ case 1:
+ rx_mgmt_action_ft_request(wt, mgmt, len);
+ break;
+ case 2:
+ rx_mgmt_action_ft_response(wt, sta, mgmt, len);
+ break;
+ default:
+ add_note(wt, MSG_INFO, "Unsupported FT action value %u from "
+ MACSTR, mgmt->u.action.u.ft_action_req.action,
+ MAC2STR(mgmt->sa));
+ }
+}
+
+
+static void rx_mgmt_action_sa_query_req(struct wlantest *wt,
+ struct wlantest_sta *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int valid)
+{
+ const u8 *rx_id;
+ u8 *id;
+
+ rx_id = (const u8 *) mgmt->u.action.u.sa_query_req.trans_id;
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+ id = sta->ap_sa_query_tr;
+ else
+ id = sta->sta_sa_query_tr;
+ add_note(wt, MSG_INFO, "SA Query Request " MACSTR " -> " MACSTR
+ " (trans_id=%02x%02x)%s",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), rx_id[0], rx_id[1],
+ valid ? "" : " (invalid protection)");
+ os_memcpy(id, mgmt->u.action.u.sa_query_req.trans_id, 2);
+ if (os_memcmp(mgmt->sa, sta->addr, ETH_ALEN) == 0)
+ sta->counters[valid ?
+ WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX :
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX]++;
+ else
+ sta->counters[valid ?
+ WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX :
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX]++;
+}
+
+
+static void rx_mgmt_action_sa_query_resp(struct wlantest *wt,
+ struct wlantest_sta *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int valid)
+{
+ const u8 *rx_id;
+ u8 *id;
+ int match;
+
+ rx_id = (const u8 *) mgmt->u.action.u.sa_query_resp.trans_id;
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+ id = sta->sta_sa_query_tr;
+ else
+ id = sta->ap_sa_query_tr;
+ match = os_memcmp(rx_id, id, 2) == 0;
+ add_note(wt, MSG_INFO, "SA Query Response " MACSTR " -> " MACSTR
+ " (trans_id=%02x%02x; %s)%s",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), rx_id[0], rx_id[1],
+ match ? "match" : "mismatch",
+ valid ? "" : " (invalid protection)");
+ if (os_memcmp(mgmt->sa, sta->addr, ETH_ALEN) == 0)
+ sta->counters[(valid && match) ?
+ WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX :
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX]++;
+ else
+ sta->counters[(valid && match) ?
+ WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX :
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX]++;
+}
+
+
+static void rx_mgmt_action_sa_query(struct wlantest *wt,
+ struct wlantest_sta *sta,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, int valid)
+{
+ if (len < 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN) {
+ add_note(wt, MSG_INFO, "Too short SA Query frame from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ if (len > 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN) {
+ size_t elen = len - (24 + 2 + WLAN_SA_QUERY_TR_ID_LEN);
+ add_note(wt, MSG_INFO, "Unexpected %u octets of extra data at "
+ "the end of SA Query frame from " MACSTR,
+ (unsigned) elen, MAC2STR(mgmt->sa));
+ wpa_hexdump(MSG_INFO, "SA Query extra data",
+ ((const u8 *) mgmt) + len - elen, elen);
+ }
+
+ switch (mgmt->u.action.u.sa_query_req.action) {
+ case WLAN_SA_QUERY_REQUEST:
+ rx_mgmt_action_sa_query_req(wt, sta, mgmt, len, valid);
+ break;
+ case WLAN_SA_QUERY_RESPONSE:
+ rx_mgmt_action_sa_query_resp(wt, sta, mgmt, len, valid);
+ break;
+ default:
+ add_note(wt, MSG_INFO, "Unexpected SA Query action value %u "
+ "from " MACSTR,
+ mgmt->u.action.u.sa_query_req.action,
+ MAC2STR(mgmt->sa));
+ }
+}
+
+
+static void
+rx_mgmt_location_measurement_report(struct wlantest *wt,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool no_ack)
+{
+ const u8 *pos = mgmt->u.action.u.public_action.variable;
+ const u8 *end = ((const u8 *) mgmt) + len;
+
+ if (end - pos < 1) {
+ add_note(wt, MSG_INFO,
+ "Too short Location Measurement Report frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "Location Measurement Report " MACSTR " --> "
+ MACSTR " (dialog token %u)",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), *pos);
+ pos++;
+
+ if (!no_ack)
+ add_note(wt, MSG_INFO,
+ "Protected Fine Timing Measurement Report incorrectly as an Action frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+
+ wpa_hexdump(MSG_MSGDUMP, "Location Measurement Report contents",
+ pos, end - pos);
+}
+
+
+static void rx_mgmt_action_no_bss_public(struct wlantest *wt,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool no_ack)
+{
+ switch (mgmt->u.action.u.public_action.action) {
+ case WLAN_PA_LOCATION_MEASUREMENT_REPORT:
+ rx_mgmt_location_measurement_report(wt, mgmt, len, no_ack);
+ break;
+ }
+}
+
+
+static void rx_mgmt_prot_ftm_request(struct wlantest *wt,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool no_ack)
+{
+ wpa_printf(MSG_DEBUG, "Protected Fine Timing Measurement Request "
+ MACSTR " --> " MACSTR,
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+ if (no_ack)
+ add_note(wt, MSG_INFO,
+ "Protected Fine Timing Measurement Request incorrectly as an Action No Ack frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+}
+
+
+static void rx_mgmt_prot_ftm(struct wlantest *wt,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool no_ack)
+{
+ wpa_printf(MSG_DEBUG, "Protected Fine Timing Measurement "
+ MACSTR " --> " MACSTR,
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+ if (no_ack)
+ add_note(wt, MSG_INFO,
+ "Protected Fine Timing Measurement incorrectly as an Action No Ack frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+}
+
+
+static void rx_mgmt_prot_ftm_report(struct wlantest *wt,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool no_ack)
+{
+ wpa_printf(MSG_DEBUG, "Protected Fine Timing Measurement Report "
+ MACSTR " --> " MACSTR,
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+ if (!no_ack)
+ add_note(wt, MSG_INFO,
+ "Protected Fine Timing Measurement Report incorrectly as an Action frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+}
+
+
+static void
+rx_mgmt_action_no_bss_protected_ftm(struct wlantest *wt,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len, bool no_ack)
+{
+ switch (mgmt->u.action.u.public_action.action) {
+ case WLAN_PROT_FTM_REQUEST:
+ rx_mgmt_prot_ftm_request(wt, mgmt, len, no_ack);
+ break;
+ case WLAN_PROT_FTM:
+ rx_mgmt_prot_ftm(wt, mgmt, len, no_ack);
+ break;
+ case WLAN_PROT_FTM_REPORT:
+ rx_mgmt_prot_ftm_report(wt, mgmt, len, no_ack);
+ break;
+ }
+}
+
+
+static void rx_mgmt_action_no_bss(struct wlantest *wt,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ bool no_ack)
+{
+ switch (mgmt->u.action.category) {
+ case WLAN_ACTION_PUBLIC:
+ rx_mgmt_action_no_bss_public(wt, mgmt, len, no_ack);
+ break;
+ case WLAN_ACTION_PROTECTED_FTM:
+ rx_mgmt_action_no_bss_protected_ftm(wt, mgmt, len, no_ack);
+ break;
+ }
+}
+
+
+static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len,
+ int valid, bool no_ack)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ if (mgmt->da[0] & 0x01) {
+ add_note(wt, MSG_DEBUG, "Group addressed Action frame: DA="
+ MACSTR " SA=" MACSTR " BSSID=" MACSTR
+ " category=%u",
+ MAC2STR(mgmt->da), MAC2STR(mgmt->sa),
+ MAC2STR(mgmt->bssid), mgmt->u.action.category);
+ return; /* Ignore group addressed Action frames for now */
+ }
+
+ if (len < 24 + 2) {
+ add_note(wt, MSG_INFO, "Too short Action frame from " MACSTR,
+ MAC2STR(mgmt->sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "ACTION%s " MACSTR " -> " MACSTR
+ " BSSID=" MACSTR " (category=%u) (valid=%d)",
+ no_ack ? "-NO-ACK" : "",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da), MAC2STR(mgmt->bssid),
+ mgmt->u.action.category, valid);
+ wpa_hexdump(MSG_MSGDUMP, "ACTION payload", data + 24, len - 24);
+
+ if (is_broadcast_ether_addr(mgmt->bssid)) {
+ rx_mgmt_action_no_bss(wt, mgmt, len, no_ack);
+ return;
+ }
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+ sta = sta_get(bss, mgmt->da);
+ else
+ sta = sta_get(bss, mgmt->sa);
+ if (sta == NULL)
+ return;
+
+ if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+ sta->state < STATE3) {
+ add_note(wt, MSG_INFO, "Action frame sent when STA is not in "
+ "State 3 (SA=" MACSTR " DATA=" MACSTR ")",
+ MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+ }
+
+ switch (mgmt->u.action.category) {
+ case WLAN_ACTION_FT:
+ rx_mgmt_action_ft(wt, sta, mgmt, len, valid);
+ break;
+ case WLAN_ACTION_SA_QUERY:
+ rx_mgmt_action_sa_query(wt, sta, mgmt, len, valid);
+ break;
+ }
+}
+
+
+static int check_mmie_mic(unsigned int mgmt_group_cipher,
+ const u8 *igtk, size_t igtk_len,
+ const u8 *data, size_t len)
+{
+ u8 *buf;
+ u8 mic[16];
+ u16 fc;
+ const struct ieee80211_hdr *hdr;
+ int ret, mic_len;
+
+ if (!mgmt_group_cipher || igtk_len < 16)
+ return -1;
+ mic_len = mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16;
+
+ if (len < 24 || len - 24 < mic_len)
+ return -1;
+
+ buf = os_malloc(len + 20 - 24);
+ if (buf == NULL)
+ return -1;
+
+ /* BIP AAD: FC(masked) A1 A2 A3 */
+ hdr = (const struct ieee80211_hdr *) data;
+ fc = le_to_host16(hdr->frame_control);
+ fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
+ WPA_PUT_LE16(buf, fc);
+ os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
+
+ /* Frame body with MMIE MIC masked to zero */
+ os_memcpy(buf + 20, data + 24, len - 24 - mic_len);
+ os_memset(buf + 20 + len - 24 - mic_len, 0, mic_len);
+
+ if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) {
+ /* Timestamp field masked to zero */
+ os_memset(buf + 20, 0, 8);
+ }
+
+ wpa_hexdump(MSG_MSGDUMP, "BIP: AAD|Body(masked)", buf, len + 20 - 24);
+ /* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */
+ if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+ ret = omac1_aes_128(igtk, buf, len + 20 - 24, mic);
+ } else if (mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256) {
+ ret = omac1_aes_256(igtk, buf, len + 20 - 24, mic);
+ } else if (mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_128 ||
+ mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_256) {
+ u8 nonce[12], *npos;
+ const u8 *ipn;
+
+ ipn = data + len - mic_len - 6;
+
+ /* Nonce: A2 | IPN */
+ os_memcpy(nonce, hdr->addr2, ETH_ALEN);
+ npos = nonce + ETH_ALEN;
+ *npos++ = ipn[5];
+ *npos++ = ipn[4];
+ *npos++ = ipn[3];
+ *npos++ = ipn[2];
+ *npos++ = ipn[1];
+ *npos++ = ipn[0];
+
+ ret = aes_gmac(igtk, igtk_len, nonce, sizeof(nonce),
+ buf, len + 20 - 24, mic);
+ } else {
+ ret = -1;
+ }
+ if (ret < 0) {
+ os_free(buf);
+ return -1;
+ }
+
+ os_free(buf);
+
+ if (os_memcmp(data + len - mic_len, mic, mic_len) != 0)
+ return -1;
+
+ return 0;
+}
+
+
+static int check_bip(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ u16 fc, stype;
+ const u8 *mmie;
+ u16 keyid;
+ struct wlantest_bss *bss;
+ size_t mic_len;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ fc = le_to_host16(mgmt->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ if (stype == WLAN_FC_STYPE_ACTION ||
+ stype == WLAN_FC_STYPE_ACTION_NO_ACK) {
+ if (len < 24 + 1)
+ return 0;
+ if (mgmt->u.action.category == WLAN_ACTION_PUBLIC)
+ return 0; /* Not a robust management frame */
+ }
+
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return 0; /* No key known yet */
+
+ mic_len = bss->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16;
+
+ if (len < 24 + 10 + mic_len ||
+ data[len - (10 + mic_len)] != WLAN_EID_MMIE ||
+ data[len - (10 + mic_len - 1)] != 8 + mic_len) {
+ /* No MMIE */
+ if (bss->rsn_capab & WPA_CAPABILITY_MFPC) {
+ add_note(wt, MSG_INFO, "Robust group-addressed "
+ "management frame sent without BIP by "
+ MACSTR, MAC2STR(mgmt->sa));
+ bss->counters[WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE]++;
+ return -1;
+ }
+ return 0;
+ }
+
+ mmie = data + len - (8 + mic_len);
+ keyid = WPA_GET_LE16(mmie);
+ if (keyid & 0xf000) {
+ add_note(wt, MSG_INFO, "MMIE KeyID reserved bits not zero "
+ "(%04x) from " MACSTR, keyid, MAC2STR(mgmt->sa));
+ keyid &= 0x0fff;
+ }
+ if (keyid < 4 || keyid > 5) {
+ add_note(wt, MSG_INFO, "Unexpected MMIE KeyID %u from " MACSTR,
+ keyid, MAC2STR(mgmt->sa));
+ bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
+ return 0;
+ }
+ wpa_printf(MSG_DEBUG, "MMIE KeyID %u", keyid);
+ wpa_hexdump(MSG_MSGDUMP, "MMIE IPN", mmie + 2, 6);
+ wpa_hexdump(MSG_MSGDUMP, "MMIE MIC", mmie + 8, mic_len);
+
+ if (!bss->igtk_len[keyid]) {
+ add_note(wt, MSG_DEBUG, "No IGTK known to validate BIP frame");
+ return 0;
+ }
+
+ if (os_memcmp(mmie + 2, bss->ipn[keyid], 6) <= 0) {
+ add_note(wt, MSG_INFO, "BIP replay detected: SA=" MACSTR,
+ MAC2STR(mgmt->sa));
+ wpa_hexdump(MSG_INFO, "RX IPN", mmie + 2, 6);
+ wpa_hexdump(MSG_INFO, "Last RX IPN", bss->ipn[keyid], 6);
+ }
+
+ if (check_mmie_mic(bss->mgmt_group_cipher, bss->igtk[keyid],
+ bss->igtk_len[keyid], data, len) < 0) {
+ add_note(wt, MSG_INFO, "Invalid MMIE MIC in a frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
+ return -1;
+ }
+
+ add_note(wt, MSG_DEBUG, "Valid MMIE MIC");
+ os_memcpy(bss->ipn[keyid], mmie + 2, 6);
+ bss->counters[WLANTEST_BSS_COUNTER_VALID_BIP_MMIE]++;
+
+ if (stype == WLAN_FC_STYPE_DEAUTH)
+ bss->counters[WLANTEST_BSS_COUNTER_BIP_DEAUTH]++;
+ else if (stype == WLAN_FC_STYPE_DISASSOC)
+ bss->counters[WLANTEST_BSS_COUNTER_BIP_DISASSOC]++;
+
+ return 0;
+}
+
+
+static u8 * try_tk(struct wpa_ptk *ptk, const u8 *data, size_t len,
+ size_t *dlen)
+{
+ const struct ieee80211_hdr *hdr;
+ u8 *decrypted, *frame;
+
+ hdr = (const struct ieee80211_hdr *) data;
+ decrypted = ccmp_decrypt(ptk->tk, hdr, data + 24, len - 24, dlen);
+ if (!decrypted)
+ return NULL;
+
+ frame = os_malloc(24 + *dlen);
+ if (frame) {
+ os_memcpy(frame, data, 24);
+ os_memcpy(frame + 24, decrypted, *dlen);
+ *dlen += 24;
+ }
+ os_free(decrypted);
+ return frame;
+}
+
+
+static u8 * mgmt_ccmp_decrypt_tk(struct wlantest *wt, const u8 *data,
+ size_t len, size_t *dlen)
+{
+ struct wlantest_ptk *ptk;
+ u8 *decrypted;
+ int prev_level = wpa_debug_level;
+ int keyid;
+
+ keyid = data[24 + 3] >> 6;
+
+ wpa_debug_level = MSG_WARNING;
+ dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
+ decrypted = try_tk(&ptk->ptk, data, len, dlen);
+ if (decrypted) {
+ wpa_debug_level = prev_level;
+ add_note(wt, MSG_DEBUG,
+ "Found TK match from the list of all known TKs");
+ write_decrypted_note(wt, decrypted, ptk->ptk.tk,
+ ptk->ptk.tk_len, keyid);
+ return decrypted;
+ }
+ }
+ wpa_debug_level = prev_level;
+
+ return NULL;
+}
+
+
+static u8 * mgmt_ccmp_decrypt(struct wlantest *wt, const u8 *data, size_t len,
+ size_t *dlen)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+ const struct ieee80211_hdr *hdr;
+ int keyid;
+ u8 *decrypted, *frame = NULL;
+ u8 pn[6], *rsc;
+ u16 fc;
+ u8 mask;
+
+ hdr = (const struct ieee80211_hdr *) data;
+ fc = le_to_host16(hdr->frame_control);
+
+ if (len < 24 + 4)
+ return NULL;
+
+ if (!(data[24 + 3] & 0x20)) {
+ add_note(wt, MSG_INFO, "Expected CCMP frame from " MACSTR
+ " did not have ExtIV bit set to 1",
+ MAC2STR(hdr->addr2));
+ return NULL;
+ }
+
+ mask = 0x1f;
+ if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION ||
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION_NO_ACK)
+ mask &= ~0x10; /* FTM */
+ if (data[24 + 2] != 0 || (data[24 + 3] & mask) != 0) {
+ add_note(wt, MSG_INFO, "CCMP mgmt frame from " MACSTR " used "
+ "non-zero reserved bit", MAC2STR(hdr->addr2));
+ }
+
+ keyid = data[24 + 3] >> 6;
+ if (keyid != 0) {
+ add_note(wt, MSG_INFO, "Unexpected non-zero KeyID %d in "
+ "individually addressed Management frame from "
+ MACSTR, keyid, MAC2STR(hdr->addr2));
+ }
+
+ bss = bss_get(wt, hdr->addr3);
+ if (bss == NULL)
+ return mgmt_ccmp_decrypt_tk(wt, data, len, dlen);
+ if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0)
+ sta = sta_get(bss, hdr->addr2);
+ else
+ sta = sta_get(bss, hdr->addr1);
+ if (sta == NULL || !sta->ptk_set) {
+ decrypted = mgmt_ccmp_decrypt_tk(wt, data, len, dlen);
+ if (!decrypted)
+ add_note(wt, MSG_MSGDUMP,
+ "No PTK known to decrypt the frame");
+ return decrypted;
+ }
+
+ if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0)
+ rsc = sta->rsc_tods[16];
+ else
+ rsc = sta->rsc_fromds[16];
+
+ ccmp_get_pn(pn, data + 24);
+ if (os_memcmp(pn, rsc, 6) <= 0) {
+ u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
+ add_note(wt, MSG_INFO, "CCMP/TKIP replay detected: A1=" MACSTR
+ " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u%s",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3),
+ WLAN_GET_SEQ_SEQ(seq_ctrl),
+ WLAN_GET_SEQ_FRAG(seq_ctrl),
+ (le_to_host16(hdr->frame_control) & WLAN_FC_RETRY) ?
+ " Retry" : "");
+ wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
+ wpa_hexdump(MSG_INFO, "RSC", rsc, 6);
+ }
+
+ decrypted = ccmp_decrypt(sta->ptk.tk, hdr, data + 24, len - 24, dlen);
+ if (decrypted) {
+ os_memcpy(rsc, pn, 6);
+ frame = os_malloc(24 + *dlen);
+ if (frame) {
+ os_memcpy(frame, data, 24);
+ os_memcpy(frame + 24, decrypted, *dlen);
+ *dlen += 24;
+ }
+ } else {
+ /* Assume the frame was corrupted and there was no FCS to check.
+ * Allow retry of this particular frame to be processed so that
+ * it could end up getting decrypted if it was received without
+ * corruption. */
+ sta->allow_duplicate = 1;
+ }
+
+ os_free(decrypted);
+
+ return frame;
+}
+
+
+static int check_mgmt_ccmp(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_mgmt *mgmt;
+ u16 fc;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+
+ mgmt = (const struct ieee80211_mgmt *) data;
+ fc = le_to_host16(mgmt->frame_control);
+
+ if (WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION ||
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION_NO_ACK) {
+ if (len > 24 &&
+ mgmt->u.action.category == WLAN_ACTION_PUBLIC)
+ return 0; /* Not a robust management frame */
+ }
+
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return 0;
+ if (os_memcmp(mgmt->da, mgmt->bssid, ETH_ALEN) == 0)
+ sta = sta_get(bss, mgmt->sa);
+ else
+ sta = sta_get(bss, mgmt->da);
+ if (sta == NULL)
+ return 0;
+
+ if ((bss->rsn_capab & WPA_CAPABILITY_MFPC) &&
+ (sta->rsn_capab & WPA_CAPABILITY_MFPC) &&
+ (sta->state == STATE3 ||
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION ||
+ WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION_NO_ACK)) {
+ add_note(wt, MSG_INFO, "Robust individually-addressed "
+ "management frame sent without CCMP by "
+ MACSTR, MAC2STR(mgmt->sa));
+ return -1;
+ }
+
+ return 0;
+}
+
+
+void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ieee80211_hdr *hdr;
+ u16 fc, stype;
+ int valid = 1;
+ u8 *decrypted = NULL;
+ size_t dlen;
+
+ if (len < 24)
+ return;
+
+ hdr = (const struct ieee80211_hdr *) data;
+ fc = le_to_host16(hdr->frame_control);
+ wt->rx_mgmt++;
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ if ((hdr->addr1[0] & 0x01) &&
+ (stype == WLAN_FC_STYPE_DEAUTH ||
+ stype == WLAN_FC_STYPE_DISASSOC ||
+ stype == WLAN_FC_STYPE_ACTION ||
+ stype == WLAN_FC_STYPE_ACTION_NO_ACK)) {
+ if (check_bip(wt, data, len) < 0)
+ valid = 0;
+ }
+
+ wpa_printf((stype == WLAN_FC_STYPE_BEACON ||
+ stype == WLAN_FC_STYPE_PROBE_RESP ||
+ stype == WLAN_FC_STYPE_PROBE_REQ) ?
+ MSG_EXCESSIVE : MSG_MSGDUMP,
+ "MGMT %s%s%s DA=" MACSTR " SA=" MACSTR " BSSID=" MACSTR,
+ mgmt_stype(stype),
+ fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
+ fc & WLAN_FC_ISWEP ? " Prot" : "",
+ MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3));
+
+ if ((fc & WLAN_FC_ISWEP) &&
+ !(hdr->addr1[0] & 0x01) &&
+ (stype == WLAN_FC_STYPE_DEAUTH ||
+ stype == WLAN_FC_STYPE_DISASSOC ||
+ stype == WLAN_FC_STYPE_ACTION ||
+ stype == WLAN_FC_STYPE_ACTION_NO_ACK)) {
+ decrypted = mgmt_ccmp_decrypt(wt, data, len, &dlen);
+ if (decrypted) {
+ write_pcap_decrypted(wt, decrypted, dlen, NULL, 0);
+ data = decrypted;
+ len = dlen;
+ } else
+ valid = 0;
+ }
+
+ if (!(fc & WLAN_FC_ISWEP) &&
+ !(hdr->addr1[0] & 0x01) &&
+ (stype == WLAN_FC_STYPE_DEAUTH ||
+ stype == WLAN_FC_STYPE_DISASSOC ||
+ stype == WLAN_FC_STYPE_ACTION ||
+ stype == WLAN_FC_STYPE_ACTION_NO_ACK)) {
+ if (check_mgmt_ccmp(wt, data, len) < 0)
+ valid = 0;
+ }
+
+ switch (stype) {
+ case WLAN_FC_STYPE_BEACON:
+ rx_mgmt_beacon(wt, data, len);
+ break;
+ case WLAN_FC_STYPE_PROBE_RESP:
+ rx_mgmt_probe_resp(wt, data, len);
+ break;
+ case WLAN_FC_STYPE_AUTH:
+ rx_mgmt_auth(wt, data, len);
+ break;
+ case WLAN_FC_STYPE_DEAUTH:
+ rx_mgmt_deauth(wt, data, len, valid);
+ break;
+ case WLAN_FC_STYPE_ASSOC_REQ:
+ rx_mgmt_assoc_req(wt, data, len);
+ break;
+ case WLAN_FC_STYPE_ASSOC_RESP:
+ rx_mgmt_assoc_resp(wt, data, len);
+ break;
+ case WLAN_FC_STYPE_REASSOC_REQ:
+ rx_mgmt_reassoc_req(wt, data, len);
+ break;
+ case WLAN_FC_STYPE_REASSOC_RESP:
+ rx_mgmt_reassoc_resp(wt, data, len);
+ break;
+ case WLAN_FC_STYPE_DISASSOC:
+ rx_mgmt_disassoc(wt, data, len, valid);
+ break;
+ case WLAN_FC_STYPE_ACTION:
+ rx_mgmt_action(wt, data, len, valid, false);
+ break;
+ case WLAN_FC_STYPE_ACTION_NO_ACK:
+ rx_mgmt_action(wt, data, len, valid, true);
+ break;
+ }
+
+ os_free(decrypted);
+
+ wt->last_mgmt_valid = valid;
+}
+
+
+static void rx_mgmt_deauth_ack(struct wlantest *wt,
+ const struct ieee80211_hdr *hdr)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+
+ mgmt = (const struct ieee80211_mgmt *) hdr;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+ sta = sta_get(bss, mgmt->da);
+ else
+ sta = sta_get(bss, mgmt->sa);
+ if (sta == NULL)
+ return;
+
+ add_note(wt, MSG_DEBUG, "DEAUTH from " MACSTR " acknowledged by "
+ MACSTR, MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+ int c;
+ c = wt->last_mgmt_valid ?
+ WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK :
+ WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK;
+ sta->counters[c]++;
+ }
+}
+
+
+static void rx_mgmt_disassoc_ack(struct wlantest *wt,
+ const struct ieee80211_hdr *hdr)
+{
+ const struct ieee80211_mgmt *mgmt;
+ struct wlantest_bss *bss;
+ struct wlantest_sta *sta;
+
+ mgmt = (const struct ieee80211_mgmt *) hdr;
+ bss = bss_get(wt, mgmt->bssid);
+ if (bss == NULL)
+ return;
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
+ sta = sta_get(bss, mgmt->da);
+ else
+ sta = sta_get(bss, mgmt->sa);
+ if (sta == NULL)
+ return;
+
+ add_note(wt, MSG_DEBUG, "DISASSOC from " MACSTR " acknowledged by "
+ MACSTR, MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
+ if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0) {
+ int c;
+ c = wt->last_mgmt_valid ?
+ WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK :
+ WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK;
+ sta->counters[c]++;
+ }
+}
+
+
+void rx_mgmt_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr)
+{
+ u16 fc, stype;
+ fc = le_to_host16(hdr->frame_control);
+ stype = WLAN_FC_GET_STYPE(fc);
+
+ wpa_printf(MSG_MSGDUMP, "MGMT ACK: stype=%u a1=" MACSTR " a2=" MACSTR
+ " a3=" MACSTR,
+ stype, MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
+ MAC2STR(hdr->addr3));
+
+ switch (stype) {
+ case WLAN_FC_STYPE_DEAUTH:
+ rx_mgmt_deauth_ack(wt, hdr);
+ break;
+ case WLAN_FC_STYPE_DISASSOC:
+ rx_mgmt_disassoc_ack(wt, hdr);
+ break;
+ }
+}
diff --git a/contrib/wpa/wlantest/rx_tdls.c b/contrib/wpa/wlantest/rx_tdls.c
new file mode 100644
index 000000000000..0c012a931822
--- /dev/null
+++ b/contrib/wpa/wlantest/rx_tdls.c
@@ -0,0 +1,618 @@
+/*
+ * Received Data frame processing for TDLS packets
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "crypto/sha256.h"
+#include "crypto/crypto.h"
+#include "crypto/aes_wrap.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wlantest.h"
+
+
+static struct wlantest_tdls * get_tdls(struct wlantest *wt, const u8 *linkid,
+ int create_new, const u8 *bssid)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_sta *init, *resp;
+ struct wlantest_tdls *tdls;
+
+ bss = bss_find(wt, linkid);
+ if (bss == NULL && bssid) {
+ bss = bss_find(wt, bssid);
+ if (bss)
+ add_note(wt, MSG_INFO, "TDLS: Incorrect BSSID " MACSTR
+ " in LinkId?! (init=" MACSTR " resp="
+ MACSTR ")",
+ MAC2STR(linkid), MAC2STR(linkid + ETH_ALEN),
+ MAC2STR(linkid + 2 * ETH_ALEN));
+ }
+ if (bss == NULL)
+ return NULL;
+
+ init = sta_find(bss, linkid + ETH_ALEN);
+ if (init == NULL)
+ return NULL;
+
+ resp = sta_find(bss, linkid + 2 * ETH_ALEN);
+ if (resp == NULL)
+ return NULL;
+
+ dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+ if (tdls->init == init && tdls->resp == resp)
+ return tdls;
+ }
+
+ if (!create_new)
+ return NULL;
+
+ add_note(wt, MSG_DEBUG, "Add new TDLS link context: initiator " MACSTR
+ " responder " MACSTR " BSSID " MACSTR,
+ MAC2STR(linkid + ETH_ALEN),
+ MAC2STR(linkid + 2 * ETH_ALEN),
+ MAC2STR(bssid));
+
+ tdls = os_zalloc(sizeof(*tdls));
+ if (tdls == NULL)
+ return NULL;
+ tdls->init = init;
+ tdls->resp = resp;
+ dl_list_add(&bss->tdls, &tdls->list);
+ return tdls;
+}
+
+
+static int tdls_derive_tpk(struct wlantest_tdls *tdls, const u8 *bssid,
+ const u8 *ftie, u8 ftie_len)
+{
+ const struct rsn_ftie *f;
+ u8 key_input[SHA256_MAC_LEN];
+ const u8 *nonce[2];
+ size_t len[2];
+ u8 data[3 * ETH_ALEN];
+
+ if (ftie == NULL || ftie_len < sizeof(struct rsn_ftie))
+ return 0;
+
+ f = (const struct rsn_ftie *) ftie;
+ wpa_hexdump(MSG_DEBUG, "TDLS ANonce", f->anonce, WPA_NONCE_LEN);
+ wpa_hexdump(MSG_DEBUG, "TDLS SNonce", f->snonce, WPA_NONCE_LEN);
+
+ /*
+ * IEEE Std 802.11z-2010 8.5.9.1:
+ * TPK-Key-Input = SHA-256(min(SNonce, ANonce) || max(SNonce, ANonce))
+ */
+ len[0] = WPA_NONCE_LEN;
+ len[1] = WPA_NONCE_LEN;
+ if (os_memcmp(f->anonce, f->snonce, WPA_NONCE_LEN) < 0) {
+ nonce[0] = f->anonce;
+ nonce[1] = f->snonce;
+ } else {
+ nonce[0] = f->snonce;
+ nonce[1] = f->anonce;
+ }
+ sha256_vector(2, nonce, len, key_input);
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-Key-Input",
+ key_input, SHA256_MAC_LEN);
+
+ /*
+ * TPK-Key-Data = KDF-N_KEY(TPK-Key-Input, "TDLS PMK",
+ * min(MAC_I, MAC_R) || max(MAC_I, MAC_R) || BSSID || N_KEY)
+ * TODO: is N_KEY really included in KDF Context and if so, in which
+ * presentation format (little endian 16-bit?) is it used? It gets
+ * added by the KDF anyway..
+ */
+
+ if (os_memcmp(tdls->init->addr, tdls->resp->addr, ETH_ALEN) < 0) {
+ os_memcpy(data, tdls->init->addr, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, tdls->resp->addr, ETH_ALEN);
+ } else {
+ os_memcpy(data, tdls->resp->addr, ETH_ALEN);
+ os_memcpy(data + ETH_ALEN, tdls->init->addr, ETH_ALEN);
+ }
+ os_memcpy(data + 2 * ETH_ALEN, bssid, ETH_ALEN);
+ wpa_hexdump(MSG_DEBUG, "TDLS: KDF Context", data, sizeof(data));
+
+ sha256_prf(key_input, SHA256_MAC_LEN, "TDLS PMK", data, sizeof(data),
+ (u8 *) &tdls->tpk, sizeof(tdls->tpk));
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-KCK",
+ tdls->tpk.kck, sizeof(tdls->tpk.kck));
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: TPK-TK",
+ tdls->tpk.tk, sizeof(tdls->tpk.tk));
+
+ return 1;
+}
+
+
+static int tdls_verify_mic(struct wlantest *wt, struct wlantest_tdls *tdls,
+ u8 trans_seq, struct ieee802_11_elems *elems)
+{
+ u8 *buf, *pos;
+ int len;
+ u8 mic[16];
+ int ret;
+ const struct rsn_ftie *rx_ftie;
+ struct rsn_ftie *tmp_ftie;
+
+ if (elems->link_id == NULL || elems->rsn_ie == NULL ||
+ elems->timeout_int == NULL || elems->ftie == NULL ||
+ elems->ftie_len < sizeof(struct rsn_ftie))
+ return -1;
+
+ len = 2 * ETH_ALEN + 1 + 2 + 18 + 2 + elems->rsn_ie_len +
+ 2 + 5 + 2 + elems->ftie_len;
+
+ buf = os_zalloc(len);
+ if (buf == NULL)
+ return -1;
+
+ pos = buf;
+ /* 1) TDLS initiator STA MAC address */
+ os_memcpy(pos, elems->link_id + ETH_ALEN, ETH_ALEN);
+ pos += ETH_ALEN;
+ /* 2) TDLS responder STA MAC address */
+ os_memcpy(pos, elems->link_id + 2 * ETH_ALEN, ETH_ALEN);
+ pos += ETH_ALEN;
+ /* 3) Transaction Sequence number */
+ *pos++ = trans_seq;
+ /* 4) Link Identifier IE */
+ os_memcpy(pos, elems->link_id - 2, 2 + 18);
+ pos += 2 + 18;
+ /* 5) RSN IE */
+ os_memcpy(pos, elems->rsn_ie - 2, 2 + elems->rsn_ie_len);
+ pos += 2 + elems->rsn_ie_len;
+ /* 6) Timeout Interval IE */
+ os_memcpy(pos, elems->timeout_int - 2, 2 + 5);
+ pos += 2 + 5;
+ /* 7) FTIE, with the MIC field of the FTIE set to 0 */
+ os_memcpy(pos, elems->ftie - 2, 2 + elems->ftie_len);
+ pos += 2;
+ tmp_ftie = (struct rsn_ftie *) pos;
+ os_memset(tmp_ftie->mic, 0, 16);
+ pos += elems->ftie_len;
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", tdls->tpk.kck, 16);
+ ret = omac1_aes_128(tdls->tpk.kck, buf, pos - buf, mic);
+ os_free(buf);
+ if (ret)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+ rx_ftie = (const struct rsn_ftie *) elems->ftie;
+
+ if (os_memcmp(mic, rx_ftie->mic, 16) == 0) {
+ add_note(wt, MSG_DEBUG, "TDLS: Valid MIC");
+ return 0;
+ }
+ add_note(wt, MSG_DEBUG, "TDLS: Invalid MIC");
+ return -1;
+}
+
+
+static void rx_data_tdls_setup_request(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst,
+ const u8 *src,
+ const u8 *data, size_t len)
+{
+ struct ieee802_11_elems elems;
+ struct wlantest_tdls *tdls;
+ u8 linkid[3 * ETH_ALEN];
+
+ if (len < 3) {
+ add_note(wt, MSG_INFO, "Too short TDLS Setup Request " MACSTR
+ " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "TDLS Setup Request " MACSTR " -> "
+ MACSTR, MAC2STR(src), MAC2STR(dst));
+
+ if (ieee802_11_parse_elems(data + 3, len - 3, &elems, 1) ==
+ ParseFailed || elems.link_id == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+ " initiator STA " MACSTR " responder STA " MACSTR,
+ MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+ MAC2STR(elems.link_id + 2 * ETH_ALEN));
+ tdls = get_tdls(wt, elems.link_id, 1, bssid);
+ if (tdls) {
+ tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_REQ]++;
+ tdls->dialog_token = data[0];
+ if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+ const struct rsn_ftie *f;
+ f = (const struct rsn_ftie *) elems.ftie;
+ os_memcpy(tdls->inonce, f->snonce, WPA_NONCE_LEN);
+ }
+ }
+
+ /* Check whether reverse direction context exists already */
+ os_memcpy(linkid, bssid, ETH_ALEN);
+ os_memcpy(linkid + ETH_ALEN, dst, ETH_ALEN);
+ os_memcpy(linkid + 2 * ETH_ALEN, src, ETH_ALEN);
+ tdls = get_tdls(wt, linkid, 0, bssid);
+ if (tdls)
+ add_note(wt, MSG_INFO, "Reverse direction TDLS context exists");
+}
+
+
+static void rx_data_tdls_setup_response_failure(struct wlantest *wt,
+ const u8 *bssid,
+ const u8 *sta_addr,
+ u8 dialog_token, u16 status)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_tdls *tdls;
+ struct wlantest_sta *sta;
+
+ if (status == WLAN_STATUS_SUCCESS) {
+ add_note(wt, MSG_INFO, "TDLS: Invalid TDLS Setup Response from "
+ MACSTR, MAC2STR(sta_addr));
+ return;
+ }
+
+ bss = bss_find(wt, bssid);
+ if (!bss)
+ return;
+ sta = sta_find(bss, sta_addr);
+ if (!sta)
+ return;
+
+ dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+ if (tdls->resp == sta) {
+ if (dialog_token != tdls->dialog_token) {
+ add_note(wt, MSG_DEBUG, "TDLS: Dialog token "
+ "mismatch in TDLS Setup Response "
+ "(failure)");
+ break;
+ }
+ add_note(wt, MSG_DEBUG, "TDLS: Found matching TDLS "
+ "setup session based on dialog token");
+ tdls->counters[
+ WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL]++;
+ break;
+ }
+ }
+}
+
+
+static void rx_data_tdls_setup_response(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst,
+ const u8 *src,
+ const u8 *data, size_t len)
+{
+ u16 status;
+ struct ieee802_11_elems elems;
+ struct wlantest_tdls *tdls;
+
+ if (len < 3) {
+ add_note(wt, MSG_INFO, "Too short TDLS Setup Response " MACSTR
+ " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+ return;
+ }
+ status = WPA_GET_LE16(data);
+ wpa_printf(MSG_DEBUG, "TDLS Setup Response " MACSTR " -> "
+ MACSTR " (status %d)",
+ MAC2STR(src), MAC2STR(dst), status);
+ if (len < 5 && status == 0) {
+ add_note(wt, MSG_INFO, "Too short TDLS Setup Response " MACSTR
+ " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+ return;
+ }
+
+ if (len < 5 ||
+ ieee802_11_parse_elems(data + 5, len - 5, &elems, 1) ==
+ ParseFailed || elems.link_id == NULL) {
+ /* Need to match TDLS link based on Dialog Token */
+ rx_data_tdls_setup_response_failure(wt, bssid, sta_addr,
+ data[2], status);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+ " initiator STA " MACSTR " responder STA " MACSTR,
+ MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+ MAC2STR(elems.link_id + 2 * ETH_ALEN));
+
+ tdls = get_tdls(wt, elems.link_id, 1, bssid);
+ if (!tdls) {
+ add_note(wt, MSG_INFO, "No match TDLS context found");
+ return;
+ }
+ if (status)
+ tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL]++;
+ else
+ tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_RESP_OK]++;
+
+ if (status != WLAN_STATUS_SUCCESS)
+ return;
+
+ if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+ const struct rsn_ftie *f;
+ f = (const struct rsn_ftie *) elems.ftie;
+ if (os_memcmp(tdls->inonce, f->snonce, WPA_NONCE_LEN) != 0) {
+ add_note(wt, MSG_INFO, "Mismatch in TDLS initiator "
+ "nonce");
+ }
+ os_memcpy(tdls->rnonce, f->anonce, WPA_NONCE_LEN);
+ }
+
+ if (tdls_derive_tpk(tdls, bssid, elems.ftie, elems.ftie_len) < 1)
+ return;
+ if (tdls_verify_mic(wt, tdls, 2, &elems) == 0) {
+ tdls->dialog_token = data[2];
+ add_note(wt, MSG_DEBUG, "TDLS: Dialog Token for the link: %u",
+ tdls->dialog_token);
+ }
+}
+
+
+static void rx_data_tdls_setup_confirm_failure(struct wlantest *wt,
+ const u8 *bssid,
+ const u8 *src,
+ u8 dialog_token, u16 status)
+{
+ struct wlantest_bss *bss;
+ struct wlantest_tdls *tdls;
+ struct wlantest_sta *sta;
+
+ if (status == WLAN_STATUS_SUCCESS) {
+ add_note(wt, MSG_INFO, "TDLS: Invalid TDLS Setup Confirm from "
+ MACSTR, MAC2STR(src));
+ return;
+ }
+
+ bss = bss_find(wt, bssid);
+ if (!bss)
+ return;
+ sta = sta_find(bss, src);
+ if (!sta)
+ return;
+
+ dl_list_for_each(tdls, &bss->tdls, struct wlantest_tdls, list) {
+ if (tdls->init == sta) {
+ if (dialog_token != tdls->dialog_token) {
+ add_note(wt, MSG_DEBUG, "TDLS: Dialog token "
+ "mismatch in TDLS Setup Confirm "
+ "(failure)");
+ break;
+ }
+ add_note(wt, MSG_DEBUG, "TDLS: Found matching TDLS "
+ "setup session based on dialog token");
+ tdls->counters[
+ WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL]++;
+ break;
+ }
+ }
+}
+
+
+static void rx_data_tdls_setup_confirm(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst,
+ const u8 *src,
+ const u8 *data, size_t len)
+{
+ u16 status;
+ struct ieee802_11_elems elems;
+ struct wlantest_tdls *tdls;
+ u8 link_id[3 * ETH_ALEN];
+
+ if (len < 3) {
+ add_note(wt, MSG_INFO, "Too short TDLS Setup Confirm " MACSTR
+ " -> " MACSTR, MAC2STR(src), MAC2STR(dst));
+ return;
+ }
+ status = WPA_GET_LE16(data);
+ wpa_printf(MSG_DEBUG, "TDLS Setup Confirm " MACSTR " -> "
+ MACSTR " (status %d)",
+ MAC2STR(src), MAC2STR(dst), status);
+
+ if (ieee802_11_parse_elems(data + 3, len - 3, &elems, 1) ==
+ ParseFailed || elems.link_id == NULL) {
+ /* Need to match TDLS link based on Dialog Token */
+ rx_data_tdls_setup_confirm_failure(wt, bssid, src,
+ data[2], status);
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+ " initiator STA " MACSTR " responder STA " MACSTR,
+ MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+ MAC2STR(elems.link_id + 2 * ETH_ALEN));
+
+ tdls = get_tdls(wt, elems.link_id, 1, bssid);
+ if (tdls == NULL)
+ return;
+ if (status)
+ tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL]++;
+ else
+ tdls->counters[WLANTEST_TDLS_COUNTER_SETUP_CONF_OK]++;
+
+ if (status != WLAN_STATUS_SUCCESS)
+ return;
+
+ if (elems.ftie && elems.ftie_len >= sizeof(struct rsn_ftie)) {
+ const struct rsn_ftie *f;
+ f = (const struct rsn_ftie *) elems.ftie;
+ if (os_memcmp(tdls->inonce, f->snonce, WPA_NONCE_LEN) != 0) {
+ add_note(wt, MSG_INFO, "Mismatch in TDLS initiator "
+ "nonce");
+ }
+ if (os_memcmp(tdls->rnonce, f->anonce, WPA_NONCE_LEN) != 0) {
+ add_note(wt, MSG_INFO, "Mismatch in TDLS responder "
+ "nonce");
+ }
+ }
+
+ tdls->link_up = 1;
+ if (tdls_derive_tpk(tdls, bssid, elems.ftie, elems.ftie_len) < 1) {
+ if (elems.ftie == NULL)
+ goto remove_reverse;
+ return;
+ }
+ if (tdls_verify_mic(wt, tdls, 3, &elems) == 0) {
+ tdls->dialog_token = data[2];
+ add_note(wt, MSG_DEBUG, "TDLS: Link up - Dialog Token: %u",
+ tdls->dialog_token);
+ }
+
+remove_reverse:
+ /*
+ * The TDLS link itself is bidirectional, but there is explicit
+ * initiator/responder roles. Remove the other direction of the link
+ * (if it exists) to make sure that the link counters are stored for
+ * the current TDLS entery.
+ */
+ os_memcpy(link_id, elems.link_id, ETH_ALEN);
+ os_memcpy(link_id + ETH_ALEN, elems.link_id + 2 * ETH_ALEN, ETH_ALEN);
+ os_memcpy(link_id + 2 * ETH_ALEN, elems.link_id + ETH_ALEN, ETH_ALEN);
+ tdls = get_tdls(wt, link_id, 0, bssid);
+ if (tdls) {
+ add_note(wt, MSG_DEBUG, "TDLS: Remove reverse link entry");
+ tdls_deinit(tdls);
+ }
+}
+
+
+static int tdls_verify_mic_teardown(struct wlantest *wt,
+ struct wlantest_tdls *tdls, u8 trans_seq,
+ const u8 *reason_code,
+ struct ieee802_11_elems *elems)
+{
+ u8 *buf, *pos;
+ int len;
+ u8 mic[16];
+ int ret;
+ const struct rsn_ftie *rx_ftie;
+ struct rsn_ftie *tmp_ftie;
+
+ if (elems->link_id == NULL || elems->ftie == NULL ||
+ elems->ftie_len < sizeof(struct rsn_ftie))
+ return -1;
+
+ len = 2 + 18 + 2 + 1 + 1 + 2 + elems->ftie_len;
+
+ buf = os_zalloc(len);
+ if (buf == NULL)
+ return -1;
+
+ pos = buf;
+ /* 1) Link Identifier IE */
+ os_memcpy(pos, elems->link_id - 2, 2 + 18);
+ pos += 2 + 18;
+ /* 2) Reason Code */
+ os_memcpy(pos, reason_code, 2);
+ pos += 2;
+ /* 3) Dialog token */
+ *pos++ = tdls->dialog_token;
+ /* 4) Transaction Sequence number */
+ *pos++ = trans_seq;
+ /* 5) FTIE, with the MIC field of the FTIE set to 0 */
+ os_memcpy(pos, elems->ftie - 2, 2 + elems->ftie_len);
+ pos += 2;
+ tmp_ftie = (struct rsn_ftie *) pos;
+ os_memset(tmp_ftie->mic, 0, 16);
+ pos += elems->ftie_len;
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: Data for FTIE MIC", buf, pos - buf);
+ wpa_hexdump_key(MSG_DEBUG, "TDLS: KCK", tdls->tpk.kck, 16);
+ ret = omac1_aes_128(tdls->tpk.kck, buf, pos - buf, mic);
+ os_free(buf);
+ if (ret)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "TDLS: FTIE MIC", mic, 16);
+ rx_ftie = (const struct rsn_ftie *) elems->ftie;
+
+ if (os_memcmp(mic, rx_ftie->mic, 16) == 0) {
+ add_note(wt, MSG_DEBUG, "TDLS: Valid MIC");
+ return 0;
+ }
+ add_note(wt, MSG_DEBUG, "TDLS: Invalid MIC");
+ return -1;
+}
+
+
+static void rx_data_tdls_teardown(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst,
+ const u8 *src,
+ const u8 *data, size_t len)
+{
+ u16 reason;
+ struct ieee802_11_elems elems;
+ struct wlantest_tdls *tdls;
+
+ if (len < 2)
+ return;
+ reason = WPA_GET_LE16(data);
+ wpa_printf(MSG_DEBUG, "TDLS Teardown " MACSTR " -> "
+ MACSTR " (reason %d)",
+ MAC2STR(src), MAC2STR(dst), reason);
+
+ if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) ==
+ ParseFailed || elems.link_id == NULL)
+ return;
+ wpa_printf(MSG_DEBUG, "TDLS Link Identifier: BSSID " MACSTR
+ " initiator STA " MACSTR " responder STA " MACSTR,
+ MAC2STR(elems.link_id), MAC2STR(elems.link_id + ETH_ALEN),
+ MAC2STR(elems.link_id + 2 * ETH_ALEN));
+
+ tdls = get_tdls(wt, elems.link_id, 1, bssid);
+ if (tdls) {
+ if (tdls->link_up)
+ add_note(wt, MSG_DEBUG, "TDLS: Link down");
+ tdls->link_up = 0;
+ tdls->counters[WLANTEST_TDLS_COUNTER_TEARDOWN]++;
+ tdls_verify_mic_teardown(wt, tdls, 4, data, &elems);
+ }
+}
+
+
+static void rx_data_tdls(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst, const u8 *src,
+ const u8 *data, size_t len)
+{
+ /* data contains the payload of a TDLS Action frame */
+ if (len < 2 || data[0] != WLAN_ACTION_TDLS) {
+ wpa_hexdump(MSG_DEBUG, "Unrecognized encapsulated TDLS frame",
+ data, len);
+ return;
+ }
+
+ switch (data[1]) {
+ case WLAN_TDLS_SETUP_REQUEST:
+ rx_data_tdls_setup_request(wt, bssid, sta_addr, dst, src,
+ data + 2, len - 2);
+ break;
+ case WLAN_TDLS_SETUP_RESPONSE:
+ rx_data_tdls_setup_response(wt, bssid, sta_addr, dst, src,
+ data + 2, len - 2);
+ break;
+ case WLAN_TDLS_SETUP_CONFIRM:
+ rx_data_tdls_setup_confirm(wt, bssid, sta_addr, dst, src,
+ data + 2, len - 2);
+ break;
+ case WLAN_TDLS_TEARDOWN:
+ rx_data_tdls_teardown(wt, bssid, sta_addr, dst, src, data + 2,
+ len - 2);
+ break;
+ case WLAN_TDLS_DISCOVERY_REQUEST:
+ wpa_printf(MSG_DEBUG, "TDLS Discovery Request " MACSTR " -> "
+ MACSTR, MAC2STR(src), MAC2STR(dst));
+ break;
+ }
+}
+
+
+void rx_data_80211_encap(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst, const u8 *src,
+ const u8 *data, size_t len)
+{
+ wpa_hexdump(MSG_EXCESSIVE, "802.11 data encap frame", data, len);
+ if (len < 1)
+ return;
+ if (data[0] == 0x02)
+ rx_data_tdls(wt, bssid, sta_addr, dst, src, data + 1, len - 1);
+}
diff --git a/contrib/wpa/wlantest/sta.c b/contrib/wpa/wlantest/sta.c
new file mode 100644
index 000000000000..02ecb78c3322
--- /dev/null
+++ b/contrib/wpa/wlantest/sta.c
@@ -0,0 +1,232 @@
+/*
+ * STA list
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
+ *
+ * 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/defs.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "wlantest.h"
+
+
+struct wlantest_sta * sta_find(struct wlantest_bss *bss, const u8 *addr)
+{
+ struct wlantest_sta *sta;
+
+ dl_list_for_each(sta, &bss->sta, struct wlantest_sta, list) {
+ if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0)
+ return sta;
+ }
+
+ return NULL;
+}
+
+
+struct wlantest_sta * sta_get(struct wlantest_bss *bss, const u8 *addr)
+{
+ struct wlantest_sta *sta;
+
+ if (addr[0] & 0x01)
+ return NULL; /* Skip group addressed frames */
+
+ sta = sta_find(bss, addr);
+ if (sta)
+ return sta;
+
+ sta = os_zalloc(sizeof(*sta));
+ if (sta == NULL)
+ return NULL;
+ os_memset(sta->seq_ctrl_to_sta, 0xff, sizeof(sta->seq_ctrl_to_sta));
+ os_memset(sta->seq_ctrl_to_ap, 0xff, sizeof(sta->seq_ctrl_to_ap));
+ sta->bss = bss;
+ os_memcpy(sta->addr, addr, ETH_ALEN);
+ dl_list_add(&bss->sta, &sta->list);
+ wpa_printf(MSG_DEBUG, "Discovered new STA " MACSTR " in BSS " MACSTR,
+ MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ return sta;
+}
+
+
+void sta_deinit(struct wlantest_sta *sta)
+{
+ dl_list_del(&sta->list);
+ os_free(sta->assocreq_ies);
+ os_free(sta);
+}
+
+
+void sta_update_assoc(struct wlantest_sta *sta, struct ieee802_11_elems *elems)
+{
+ struct wpa_ie_data data;
+ struct wlantest_bss *bss = sta->bss;
+
+ if (elems->wpa_ie && !bss->wpaie[0] &&
+ (bss->beacon_seen || bss->proberesp_seen)) {
+ wpa_printf(MSG_INFO, "WPA IE included in Association Request "
+ "frame from " MACSTR " even though BSS does not "
+ "use WPA - ignore IE",
+ MAC2STR(sta->addr));
+ elems->wpa_ie = NULL;
+ }
+
+ if (elems->rsn_ie && !bss->rsnie[0] &&
+ (bss->beacon_seen || bss->proberesp_seen)) {
+ wpa_printf(MSG_INFO, "RSN IE included in Association Request "
+ "frame from " MACSTR " even though BSS does not "
+ "use RSN - ignore IE",
+ MAC2STR(sta->addr));
+ elems->rsn_ie = NULL;
+ }
+
+ if (elems->osen && !bss->osenie[0] &&
+ (bss->beacon_seen || bss->proberesp_seen)) {
+ wpa_printf(MSG_INFO, "OSEN IE included in Association Request "
+ "frame from " MACSTR " even though BSS does not "
+ "use OSEN - ignore IE",
+ MAC2STR(sta->addr));
+ elems->osen = NULL;
+ }
+
+ if (elems->wpa_ie && elems->rsn_ie) {
+ wpa_printf(MSG_INFO, "Both WPA IE and RSN IE included in "
+ "Association Request frame from " MACSTR,
+ MAC2STR(sta->addr));
+ }
+
+ if (elems->rsn_ie) {
+ wpa_hexdump(MSG_DEBUG, "RSN IE", elems->rsn_ie - 2,
+ elems->rsn_ie_len + 2);
+ os_memcpy(sta->rsnie, elems->rsn_ie - 2,
+ elems->rsn_ie_len + 2);
+ if (wpa_parse_wpa_ie_rsn(sta->rsnie, 2 + sta->rsnie[1], &data)
+ < 0) {
+ wpa_printf(MSG_INFO, "Failed to parse RSN IE from "
+ MACSTR, MAC2STR(sta->addr));
+ }
+ } else if (elems->wpa_ie) {
+ wpa_hexdump(MSG_DEBUG, "WPA IE", elems->wpa_ie - 2,
+ elems->wpa_ie_len + 2);
+ os_memcpy(sta->rsnie, elems->wpa_ie - 2,
+ elems->wpa_ie_len + 2);
+ if (wpa_parse_wpa_ie_wpa(sta->rsnie, 2 + sta->rsnie[1], &data)
+ < 0) {
+ wpa_printf(MSG_INFO, "Failed to parse WPA IE from "
+ MACSTR, MAC2STR(sta->addr));
+ }
+ } else if (elems->osen) {
+ wpa_hexdump(MSG_DEBUG, "OSEN IE", elems->osen - 2,
+ elems->osen_len + 2);
+ os_memcpy(sta->osenie, elems->osen - 2, elems->osen_len + 2);
+ sta->proto = WPA_PROTO_OSEN;
+ sta->pairwise_cipher = WPA_CIPHER_CCMP;
+ sta->key_mgmt = WPA_KEY_MGMT_OSEN;
+ sta->rsn_capab = 0;
+ goto skip_rsn_wpa;
+ } else {
+ sta->rsnie[0] = 0;
+ sta->proto = 0;
+ sta->pairwise_cipher = 0;
+ sta->key_mgmt = 0;
+ sta->rsn_capab = 0;
+ if (sta->assocreq_capab_info & WLAN_CAPABILITY_PRIVACY)
+ sta->pairwise_cipher = WPA_CIPHER_WEP40;
+ goto skip_rsn_wpa;
+ }
+
+ sta->proto = data.proto;
+ sta->pairwise_cipher = data.pairwise_cipher;
+ sta->key_mgmt = data.key_mgmt;
+ sta->rsn_capab = data.capabilities;
+ if (bss->proto && (sta->proto & bss->proto) == 0) {
+ wpa_printf(MSG_INFO, "Mismatch in WPA/WPA2 proto: STA "
+ MACSTR " 0x%x BSS " MACSTR " 0x%x",
+ MAC2STR(sta->addr), sta->proto,
+ MAC2STR(bss->bssid), bss->proto);
+ }
+ if (bss->pairwise_cipher &&
+ (sta->pairwise_cipher & bss->pairwise_cipher) == 0) {
+ wpa_printf(MSG_INFO, "Mismatch in pairwise cipher: STA "
+ MACSTR " 0x%x BSS " MACSTR " 0x%x",
+ MAC2STR(sta->addr), sta->pairwise_cipher,
+ MAC2STR(bss->bssid), bss->pairwise_cipher);
+ }
+ if (sta->proto && data.group_cipher != bss->group_cipher &&
+ bss->ies_set) {
+ wpa_printf(MSG_INFO, "Mismatch in group cipher: STA "
+ MACSTR " 0x%x != BSS " MACSTR " 0x%x",
+ MAC2STR(sta->addr), data.group_cipher,
+ MAC2STR(bss->bssid), bss->group_cipher);
+ }
+ if ((bss->rsn_capab & WPA_CAPABILITY_MFPR) &&
+ !(sta->rsn_capab & WPA_CAPABILITY_MFPC)) {
+ wpa_printf(MSG_INFO, "STA " MACSTR " tries to associate "
+ "without MFP to BSS " MACSTR " that advertises "
+ "MFPR", MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ }
+ if ((sta->rsn_capab & WPA_CAPABILITY_OCVC) &&
+ !(sta->rsn_capab & WPA_CAPABILITY_MFPC)) {
+ wpa_printf(MSG_INFO, "STA " MACSTR " tries to associate "
+ "without MFP to BSS " MACSTR " while supporting "
+ "OCV", MAC2STR(sta->addr), MAC2STR(bss->bssid));
+ }
+
+skip_rsn_wpa:
+ wpa_printf(MSG_INFO, "STA " MACSTR
+ " proto=%s%s%s%s"
+ "pairwise=%s%s%s%s%s%s%s"
+ "key_mgmt=%s%s%s%s%s%s%s%s%s%s%s%s%s%s"
+ "rsn_capab=%s%s%s%s%s%s%s%s%s%s",
+ MAC2STR(sta->addr),
+ sta->proto == 0 ? "OPEN " : "",
+ sta->proto & WPA_PROTO_WPA ? "WPA " : "",
+ sta->proto & WPA_PROTO_RSN ? "WPA2 " : "",
+ sta->proto & WPA_PROTO_OSEN ? "OSEN " : "",
+ sta->pairwise_cipher == 0 ? "N/A " : "",
+ sta->pairwise_cipher & WPA_CIPHER_NONE ? "NONE " : "",
+ sta->pairwise_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
+ sta->pairwise_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+ bss->pairwise_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " :
+ "",
+ bss->pairwise_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+ bss->pairwise_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " :
+ "",
+ sta->key_mgmt == 0 ? "N/A " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X ? "EAP " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_PSK ? "PSK " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_WPA_NONE ? "WPA-NONE " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X ? "FT-EAP " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_FT_PSK ? "FT-PSK " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256 ?
+ "EAP-SHA256 " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_PSK_SHA256 ?
+ "PSK-SHA256 " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_OWE ? "OWE " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_PASN ? "PASN " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_OSEN ? "OSEN " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_DPP ? "DPP " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B ?
+ "EAP-SUITE-B " : "",
+ sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ?
+ "EAP-SUITE-B-192 " : "",
+ sta->rsn_capab & WPA_CAPABILITY_PREAUTH ? "PREAUTH " : "",
+ sta->rsn_capab & WPA_CAPABILITY_NO_PAIRWISE ?
+ "NO_PAIRWISE " : "",
+ sta->rsn_capab & WPA_CAPABILITY_MFPR ? "MFPR " : "",
+ sta->rsn_capab & WPA_CAPABILITY_MFPC ? "MFPC " : "",
+ sta->rsn_capab & WPA_CAPABILITY_PEERKEY_ENABLED ?
+ "PEERKEY " : "",
+ sta->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_CAPABLE ?
+ "SPP-A-MSDU-CAPAB " : "",
+ sta->rsn_capab & WPA_CAPABILITY_SPP_A_MSDU_REQUIRED ?
+ "SPP-A-MSDU-REQUIRED " : "",
+ sta->rsn_capab & WPA_CAPABILITY_PBAC ? "PBAC " : "",
+ sta->rsn_capab & WPA_CAPABILITY_OCVC ? "OCVC " : "",
+ sta->rsn_capab & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST ?
+ "ExtKeyID " : "");
+}
diff --git a/contrib/wpa/wlantest/test_vectors.c b/contrib/wpa/wlantest/test_vectors.c
new file mode 100644
index 000000000000..ab9c0a39d37a
--- /dev/null
+++ b/contrib/wpa/wlantest/test_vectors.c
@@ -0,0 +1,937 @@
+/*
+ * test_vectors - IEEE 802.11 test vector generator
+ * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/crc32.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+static void test_vector_tkip(void)
+{
+ u8 tk[] = {
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56,
+ 0x78, 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+ 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+ 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34
+ };
+ u8 pn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+ u8 frame[] = {
+ 0x08, 0x42, 0x2c, 0x00, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x08, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0xd0, 0x02,
+ /* 0x00, 0x20, 0x01, 0x20, 0x00, 0x00, 0x00, 0x00, */
+ 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x08, 0x00,
+ 0x45, 0x00, 0x00, 0x54, 0x00, 0x00, 0x40, 0x00,
+ 0x40, 0x01, 0xa5, 0x55, 0xc0, 0xa8, 0x0a, 0x02,
+ 0xc0, 0xa8, 0x0a, 0x01, 0x08, 0x00, 0x3a, 0xb0,
+ 0x00, 0x00, 0x00, 0x00, 0xcd, 0x4c, 0x05, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
+ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+ 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
+ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+ 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,
+ 0x34, 0x35, 0x36, 0x37,
+ /* 0x68, 0x81, 0xa3, 0xf3, 0xd6, 0x48, 0xd0, 0x3c */
+ };
+ u8 *enc, *plain;
+ size_t enc_len, plain_len;
+
+ wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.6.3 TKIP test "
+ "vector\n");
+
+ wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+ wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+ wpa_hexdump(MSG_INFO, "Plaintext MPDU", frame, sizeof(frame));
+
+ enc = tkip_encrypt(tk, frame, sizeof(frame), 24, NULL, pn, 0, &enc_len);
+ if (enc == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to encrypt TKIP frame");
+ return;
+ }
+
+ wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+
+ wpa_debug_level = MSG_INFO;
+ plain = tkip_decrypt(tk, (const struct ieee80211_hdr *) enc,
+ enc + 24, enc_len - 24, &plain_len);
+ wpa_debug_level = MSG_EXCESSIVE;
+ os_free(enc);
+
+ if (plain == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to decrypt TKIP frame");
+ return;
+ }
+
+ if (plain_len != sizeof(frame) - 24 ||
+ os_memcmp(plain, frame + 24, plain_len) != 0) {
+ wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+ plain, plain_len);
+ }
+
+ os_free(plain);
+}
+
+
+static void test_vector_ccmp(void)
+{
+ u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+ 0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f };
+ u8 pn[] = { 0xB5, 0x03, 0x97, 0x76, 0xE7, 0x0C };
+ u8 frame[] = {
+ 0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+ 0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+ 0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+ 0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+ 0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+ 0x7e, 0x78, 0xa0, 0x50
+ };
+ u8 *enc, *plain;
+ size_t enc_len, plain_len;
+ u8 fcs[4];
+
+ wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.6.4 CCMP test "
+ "vector\n");
+
+ wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+ wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+ wpa_hexdump(MSG_INFO, "802.11 Header", frame, 24);
+ wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 24, sizeof(frame) - 24);
+
+ enc = ccmp_encrypt(tk, frame, sizeof(frame), 24, NULL, pn, 0, &enc_len);
+ if (enc == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+ return;
+ }
+
+ wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+ WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+ wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+ wpa_debug_level = MSG_INFO;
+ plain = ccmp_decrypt(tk, (const struct ieee80211_hdr *) enc,
+ enc + 24, enc_len - 24, &plain_len);
+ wpa_debug_level = MSG_EXCESSIVE;
+ os_free(enc);
+
+ if (plain == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to decrypt CCMP frame");
+ return;
+ }
+
+ if (plain_len != sizeof(frame) - 24 ||
+ os_memcmp(plain, frame + 24, plain_len) != 0) {
+ wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+ plain, plain_len);
+ }
+
+ os_free(plain);
+}
+
+
+static void test_vector_ccmp_pv1(void)
+{
+ u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+ 0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f };
+ u8 pn[8];
+ u8 frame1[] = {
+ 0x61, 0x00, 0xa2, 0xae, 0xa5, 0xb8, 0xfc, 0xba,
+ 0x07, 0x00, 0x80, 0x33,
+ 0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+ 0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+ 0x7e, 0x78, 0xa0, 0x50
+ };
+ u8 frame2[] = {
+ 0x61, 0x00, 0xa2, 0xae, 0xa5, 0xb8, 0xfc, 0xba,
+ 0x07, 0x20, 0x80, 0x33, 0x02, 0xd2, 0xe1, 0x28,
+ 0xa5, 0x7c,
+ 0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+ 0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+ 0x7e, 0x78, 0xa0, 0x50
+ };
+ u8 frame3[] = {
+ 0x6d, 0x00, 0xa2, 0xae, 0xa5, 0xb8, 0xfc, 0xba,
+ 0x52, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+ 0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+ 0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+ 0x7e, 0x78, 0xa0, 0x50
+ };
+ u8 *enc;
+ size_t enc_len;
+ u8 fcs[4];
+ u8 bssid[ETH_ALEN] = { 0xa2, 0xae, 0xa5, 0xb8, 0xfc, 0xba };
+ u8 da[ETH_ALEN] = { 0x02, 0xd2, 0xe1, 0x28, 0xa5, 0x7c };
+ u8 sa[ETH_ALEN] = { 0x52, 0x30, 0xf1, 0x84, 0x44, 0x08 };
+ u16 aid = 7;
+ u32 bpn = 123;
+ u16 sc = 0x3380;
+ int key_id = 0;
+ u16 fc;
+ int tid = 3;
+ u16 sid;
+
+ wpa_printf(MSG_INFO,
+ "\nIEEE P802.11ah/D10.0, J.6.4 CCMP PV1 test vectors\n");
+
+ wpa_printf(MSG_INFO, "BSSID: " MACSTR, MAC2STR(bssid));
+ wpa_printf(MSG_INFO, "DA: " MACSTR, MAC2STR(da));
+ wpa_printf(MSG_INFO, "SA: " MACSTR, MAC2STR(sa));
+ wpa_printf(MSG_INFO, "Association ID: %u", aid);
+ wpa_printf(MSG_INFO, "Base PN: %u (0x%08x)", bpn, bpn);
+ wpa_printf(MSG_INFO, "SC = 0x%04x (FragNum=%u SeqNum=%u)",
+ sc, WLAN_GET_SEQ_FRAG(sc), WLAN_GET_SEQ_SEQ(sc));
+ wpa_printf(MSG_INFO, "TID = %u", tid);
+ wpa_printf(MSG_INFO, "Key ID: %u", key_id);
+ wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+ wpa_printf(MSG_INFO, "PN = SC||BPN");
+ WPA_PUT_LE16(&pn[0], sc);
+ WPA_PUT_LE32(&pn[2], bpn);
+ wpa_hexdump(MSG_INFO, "PN (PN0..PN5)", pn, sizeof(pn));
+
+ wpa_printf(MSG_INFO,
+ "\nPV1 test vector #1:\nHeader compression used and A3 was previously stored at the receiver\n");
+ fc = WPA_GET_LE16(frame1);
+ wpa_printf(MSG_INFO,
+ "FC=0x%04x (PV=%u Type=%u PTID/Subtype=%u From_DS=%u More_Fragments=%u Power_Management=%u More_Data=%u Protected_Frame=%u End_of_SP=%u Relayed_Frame=%u Ack_Policy=%u)",
+ fc,
+ fc & WLAN_FC_PVER,
+ (fc & (BIT(2) | BIT(3) | BIT(4))) >> 2,
+ (fc & (BIT(5) | BIT(6) | BIT(7))) >> 5,
+ !!(fc & BIT(8)),
+ !!(fc & BIT(9)),
+ !!(fc & BIT(10)),
+ !!(fc & BIT(11)),
+ !!(fc & BIT(12)),
+ !!(fc & BIT(13)),
+ !!(fc & BIT(14)),
+ !!(fc & BIT(15)));
+ wpa_printf(MSG_INFO, "A1=" MACSTR, MAC2STR(&frame1[2]));
+ sid = WPA_GET_LE16(&frame1[8]);
+ wpa_printf(MSG_INFO,
+ "A2=%02x %02x (SID: AID=%u A3_Present=%u A4_Present=%u A-MSDU=%u); corresponds to 52:30:f1:84:44:08 in uncompressed header",
+ frame1[8], frame1[9],
+ sid & ~(BIT(13) | BIT(14) | BIT(15)),
+ !!(sid & BIT(13)),
+ !!(sid & BIT(14)),
+ !!(sid & BIT(15)));
+ sc = WPA_GET_LE16(&frame1[10]);
+ wpa_printf(MSG_INFO, "Sequence Control: %02x %02x (FN=%u SN=%u)",
+ frame1[10], frame1[11],
+ WLAN_GET_SEQ_FRAG(sc), WLAN_GET_SEQ_SEQ(sc));
+ wpa_printf(MSG_INFO, "A3 not present; corresponds to 02:d2:e1:28:a5:7c in uncompressed header");
+ wpa_printf(MSG_INFO, "A4 not present");
+ wpa_hexdump(MSG_INFO, "Plaintext Frame Header", frame1, 12);
+ wpa_hexdump(MSG_INFO, "Plaintext Frame Body",
+ frame1 + 12, sizeof(frame1) - 12);
+
+ enc = ccmp_encrypt_pv1(tk, &frame1[2], sa, da, frame1, sizeof(frame1),
+ 12, pn, key_id, &enc_len);
+ if (enc == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+ return;
+ }
+
+ wpa_hexdump(MSG_INFO, "Encrypted Frame Header", enc, 12);
+ wpa_hexdump(MSG_INFO, "Encrypted Frame Frame Body",
+ enc + 12, enc_len - 12);
+ WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+ wpa_hexdump(MSG_INFO, "Encrypted Frame FCS", fcs, sizeof(fcs));
+
+ wpa_printf(MSG_INFO,
+ "\nPV1 test vector #2:\nHeader compression used and A3 was not previously stored at the receiver\n");
+ fc = WPA_GET_LE16(frame2);
+ wpa_printf(MSG_INFO,
+ "FC=0x%04x (PV=%u Type=%u PTID/Subtype=%u From_DS=%u More_Fragments=%u Power_Management=%u More_Data=%u Protected_Frame=%u End_of_SP=%u Relayed_Frame=%u Ack_Policy=%u)",
+ fc,
+ fc & WLAN_FC_PVER,
+ (fc & (BIT(2) | BIT(3) | BIT(4))) >> 2,
+ (fc & (BIT(5) | BIT(6) | BIT(7))) >> 5,
+ !!(fc & BIT(8)),
+ !!(fc & BIT(9)),
+ !!(fc & BIT(10)),
+ !!(fc & BIT(11)),
+ !!(fc & BIT(12)),
+ !!(fc & BIT(13)),
+ !!(fc & BIT(14)),
+ !!(fc & BIT(15)));
+ wpa_printf(MSG_INFO, "A1=" MACSTR, MAC2STR(&frame2[2]));
+ sid = WPA_GET_LE16(&frame2[8]);
+ wpa_printf(MSG_INFO,
+ "A2=%02x %02x (SID: AID=%u A3_Present=%u A4_Present=%u A-MSDU=%u); corresponds to 52:30:f1:84:44:08 in uncompressed header",
+ frame2[8], frame2[9],
+ sid & ~(BIT(13) | BIT(14) | BIT(15)),
+ !!(sid & BIT(13)),
+ !!(sid & BIT(14)),
+ !!(sid & BIT(15)));
+ sc = WPA_GET_LE16(&frame2[10]);
+ wpa_printf(MSG_INFO, "Sequence Control: %02x %02x (FN=%u SN=%u)",
+ frame2[10], frame2[11],
+ WLAN_GET_SEQ_FRAG(sc), WLAN_GET_SEQ_SEQ(sc));
+ wpa_printf(MSG_INFO, "A3=" MACSTR, MAC2STR(&frame2[12]));
+ wpa_printf(MSG_INFO, "A4 not present");
+ wpa_hexdump(MSG_INFO, "Plaintext Frame Header", frame2, 18);
+ wpa_hexdump(MSG_INFO, "Plaintext Frame Body",
+ frame2 + 18, sizeof(frame2) - 18);
+
+ enc = ccmp_encrypt_pv1(tk, &frame2[2], sa, &frame2[12],
+ frame2, sizeof(frame2), 18, pn, key_id,
+ &enc_len);
+ if (enc == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+ return;
+ }
+
+ wpa_hexdump(MSG_INFO, "Encrypted Frame Header", enc, 18);
+ wpa_hexdump(MSG_INFO, "Encrypted Frame Frame Body",
+ enc + 18, enc_len - 18);
+ WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+ wpa_hexdump(MSG_INFO, "Encrypted Frame FCS", fcs, sizeof(fcs));
+
+ wpa_printf(MSG_INFO,
+ "\nPV1 test vector #3:\nType 3 frame from SA to DA(=BSSID) (i.e., no separate DA in this example)\n");
+ fc = WPA_GET_LE16(frame3);
+ wpa_printf(MSG_INFO,
+ "FC=0x%04x (PV=%u Type=%u PTID/Subtype=%u From_DS=%u More_Fragments=%u Power_Management=%u More_Data=%u Protected_Frame=%u End_of_SP=%u Relayed_Frame=%u Ack_Policy=%u)",
+ fc,
+ fc & WLAN_FC_PVER,
+ (fc & (BIT(2) | BIT(3) | BIT(4))) >> 2,
+ (fc & (BIT(5) | BIT(6) | BIT(7))) >> 5,
+ !!(fc & BIT(8)),
+ !!(fc & BIT(9)),
+ !!(fc & BIT(10)),
+ !!(fc & BIT(11)),
+ !!(fc & BIT(12)),
+ !!(fc & BIT(13)),
+ !!(fc & BIT(14)),
+ !!(fc & BIT(15)));
+ wpa_printf(MSG_INFO, "A1=" MACSTR, MAC2STR(&frame3[2]));
+ wpa_printf(MSG_INFO, "A2=" MACSTR, MAC2STR(&frame3[8]));
+ sc = WPA_GET_LE16(&frame3[14]);
+ wpa_printf(MSG_INFO, "Sequence Control: %02x %02x (FN=%u SN=%u)",
+ frame3[14], frame3[15],
+ WLAN_GET_SEQ_FRAG(sc), WLAN_GET_SEQ_SEQ(sc));
+ wpa_printf(MSG_INFO,
+ "A3 not present; corresponds to 02:d2:e1:28:a5:7c in uncompressed header");
+ wpa_printf(MSG_INFO, "A4 not present");
+ wpa_hexdump(MSG_INFO, "Plaintext Frame Header", frame3, 16);
+ wpa_hexdump(MSG_INFO, "Plaintext Frame Body",
+ frame3 + 16, sizeof(frame3) - 16);
+
+ enc = ccmp_encrypt_pv1(tk, &frame3[2], &frame3[8], da,
+ frame3, sizeof(frame3), 16, pn, key_id,
+ &enc_len);
+ if (enc == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+ return;
+ }
+
+ wpa_hexdump(MSG_INFO, "Encrypted Frame Header", enc, 16);
+ wpa_hexdump(MSG_INFO, "Encrypted Frame Frame Body",
+ enc + 16, enc_len - 16);
+ WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+ wpa_hexdump(MSG_INFO, "Encrypted Frame FCS", fcs, sizeof(fcs));
+
+ wpa_debug_level = MSG_INFO;
+}
+
+
+static void test_vector_bip(void)
+{
+ u8 igtk[] = {
+ 0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
+ 0xca, 0x66, 0xff, 0xc5, 0x8b, 0xde, 0xcb, 0xcf
+ };
+ u8 ipn[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ u8 frame[] = {
+ 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x02, 0x00
+ };
+ u8 *prot;
+ size_t prot_len;
+
+ wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.9.1 BIP with broadcast "
+ "Deauthentication frame\n");
+
+ wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
+ wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
+ wpa_hexdump(MSG_INFO, "Plaintext frame", frame, sizeof(frame));
+
+ prot = bip_protect(igtk, sizeof(igtk), frame, sizeof(frame),
+ ipn, 4, &prot_len);
+ if (prot == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to protect BIP frame");
+ return;
+ }
+
+ wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+ os_free(prot);
+}
+
+
+static void test_vector_ccmp_mgmt(void)
+{
+ u8 tk[] = { 0x66, 0xed, 0x21, 0x04, 0x2f, 0x9f, 0x26, 0xd7,
+ 0x11, 0x57, 0x06, 0xe4, 0x04, 0x14, 0xcf, 0x2e };
+ u8 pn[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 };
+ u8 frame[] = {
+ 0xc0, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+ 0x01, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
+ 0x02, 0x00
+ };
+ u8 *enc, *plain;
+ size_t enc_len, plain_len;
+
+ wpa_printf(MSG_INFO, "\nIEEE Std 802.11-2012, M.9.2 CCMP with unicast "
+ "Deauthentication frame\n");
+
+ wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+ wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+ wpa_hexdump(MSG_INFO, "802.11 Header", frame, 24);
+ wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 24, sizeof(frame) - 24);
+
+ enc = ccmp_encrypt(tk, frame, sizeof(frame), 24, NULL, pn, 0, &enc_len);
+ if (enc == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+ return;
+ }
+
+ wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+
+ wpa_debug_level = MSG_INFO;
+ plain = ccmp_decrypt(tk, (const struct ieee80211_hdr *) enc,
+ enc + 24, enc_len - 24, &plain_len);
+ wpa_debug_level = MSG_EXCESSIVE;
+ os_free(enc);
+
+ if (plain == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to decrypt CCMP frame");
+ return;
+ }
+
+ if (plain_len != sizeof(frame) - 24 ||
+ os_memcmp(plain, frame + 24, plain_len) != 0) {
+ wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+ plain, plain_len);
+ }
+
+ os_free(plain);
+}
+
+
+struct gcmp_test {
+ u8 tk[16];
+ u8 pn[6];
+ u8 frame[300];
+ size_t hdr_len;
+ size_t payload_len;
+ u8 mic[16];
+ u8 encr[300];
+};
+
+static const struct gcmp_test gcmp_vectors[] =
+{
+ {
+ .tk = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
+ .pn = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
+ .frame = {
+ 0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ },
+ .hdr_len = 24,
+ .payload_len = 256,
+ .mic = {
+ 0x80, 0xCB, 0x06, 0x62, 0xEA, 0x71, 0xAB, 0xFD,
+ 0x9F, 0x04, 0xC7, 0xF8, 0x72, 0xF5, 0x80, 0x90 },
+ .encr = {
+ 0x5F, 0x55, 0x78, 0xC1, 0x8F, 0x13, 0x7A, 0xD2,
+ 0x79, 0xBF, 0x3F, 0x2B, 0x24, 0xC7, 0xBD, 0x8F,
+ 0x27, 0x7A, 0x1B, 0xE6, 0x77, 0x0D, 0xA1, 0xD9,
+ 0x8B, 0x70, 0xC6, 0xD2, 0x8A, 0xE0, 0x1C, 0x55,
+ 0x9E, 0xCB, 0xA6, 0xA0, 0x1D, 0xB0, 0x67, 0xC5,
+ 0xA2, 0x7E, 0x4D, 0xB0, 0x8C, 0xDA, 0xDC, 0x77,
+ 0x52, 0xAD, 0x63, 0x7E, 0xAF, 0x0A, 0x18, 0xED,
+ 0x13, 0xFB, 0xAA, 0x14, 0x3B, 0xAF, 0xEF, 0x18,
+ 0xF8, 0xFB, 0xCE, 0x4C, 0x65, 0xE8, 0x6B, 0xD0,
+ 0x2A, 0x87, 0xB6, 0x01, 0xB7, 0xEA, 0xB9, 0x3F,
+ 0x2B, 0xBC, 0x87, 0x4C, 0x8A, 0x71, 0x05, 0x80,
+ 0xF5, 0x02, 0x34, 0x1A, 0x6A, 0x53, 0x39, 0x31,
+ 0x43, 0xDE, 0x4C, 0x9E, 0xC6, 0xA2, 0x86, 0xF1,
+ 0x25, 0x71, 0x83, 0x78, 0xAE, 0xDC, 0x84, 0xEB,
+ 0xA2, 0xB3, 0x0F, 0x5C, 0x28, 0xBB, 0x5D, 0x75,
+ 0xC6, 0xB0, 0x25, 0x46, 0x6D, 0x06, 0x51, 0xC7,
+ 0x22, 0xDC, 0x71, 0x15, 0x1F, 0x21, 0x2D, 0x68,
+ 0x87, 0x82, 0x8A, 0x03, 0x82, 0xE9, 0x28, 0x8A,
+ 0x7F, 0x43, 0xD5, 0x2B, 0x7D, 0x25, 0x08, 0x61,
+ 0x57, 0x64, 0x69, 0x54, 0xBB, 0x43, 0xB5, 0x7E,
+ 0xA5, 0x87, 0xA0, 0x25, 0xF4, 0x0C, 0xE7, 0x45,
+ 0x11, 0xE4, 0xDD, 0x22, 0x85, 0xB4, 0x0B, 0xA3,
+ 0xF3, 0xB9, 0x62, 0x62, 0xCB, 0xC2, 0x8C, 0x6A,
+ 0xA7, 0xBE, 0x44, 0x3E, 0x7B, 0x41, 0xE1, 0xEB,
+ 0xFF, 0x52, 0x48, 0x57, 0xA6, 0x81, 0x68, 0x97,
+ 0x75, 0x01, 0x15, 0xB0, 0x23, 0x1A, 0xB7, 0xC2,
+ 0x84, 0x72, 0xC0, 0x6D, 0xD0, 0xB4, 0x9B, 0xE9,
+ 0xF3, 0x69, 0xA8, 0xC3, 0x9C, 0xCD, 0x0D, 0xB7,
+ 0x98, 0x35, 0x10, 0xE1, 0xAE, 0x8F, 0x05, 0xD7,
+ 0x75, 0x45, 0xE0, 0x23, 0x5C, 0xDB, 0xD6, 0x12,
+ 0xF3, 0x15, 0x07, 0x54, 0xCE, 0xE5, 0xCE, 0x6A,
+ 0x12, 0x25, 0xD9, 0x95, 0x25, 0x02, 0x6F, 0x74
+ }
+ },
+ {
+ .tk = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+ 0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f },
+ .pn = { 0x00, 0x89, 0x5F, 0x5F, 0x2B, 0x08 },
+ .frame = {
+ 0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+ 0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+ 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+ 0x03, 0x00,
+
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+ },
+ .hdr_len = 26,
+ .payload_len = 40,
+ .mic = {
+ 0xde, 0xf6, 0x19, 0xc2, 0xa3, 0x74, 0xb6, 0xdf,
+ 0x66, 0xff, 0xa5, 0x3b, 0x6c, 0x69, 0xd7, 0x9e },
+ .encr = {
+ 0x60, 0xe9, 0x70, 0x0c, 0xc4, 0xd4, 0x0a, 0xc6,
+ 0xd2, 0x88, 0xb2, 0x01, 0xc3, 0x8f, 0x5b, 0xf0,
+ 0x8b, 0x80, 0x74, 0x42, 0x64, 0x0a, 0x15, 0x96,
+ 0xe5, 0xdb, 0xda, 0xd4, 0x1d, 0x1f, 0x36, 0x23,
+ 0xf4, 0x5d, 0x7a, 0x12, 0xdb, 0x7a, 0xfb, 0x23
+ }
+ }
+};
+
+
+static int run_gcmp(int idx, const struct gcmp_test *vector)
+{
+ u8 *enc, *plain;
+ size_t enc_len, plain_len;
+ u8 fcs[4];
+ int err = 0;
+
+ wpa_printf(MSG_INFO,
+ "\nIEEE Std 802.11ad-2012, M.11.1 GCMP test mpdu #%d\n",
+ idx);
+
+ wpa_hexdump(MSG_INFO, "TK", vector->tk, sizeof(vector->tk));
+ wpa_hexdump(MSG_INFO, "PN", vector->pn, sizeof(vector->pn));
+ wpa_hexdump(MSG_INFO, "802.11 Header", vector->frame, vector->hdr_len);
+ wpa_hexdump(MSG_INFO, "Plaintext Data",
+ vector->frame + vector->hdr_len,
+ vector->payload_len);
+
+ enc = gcmp_encrypt(vector->tk, sizeof(vector->tk),
+ vector->frame,
+ vector->hdr_len + vector->payload_len,
+ vector->hdr_len,
+ vector->hdr_len == 26 ?
+ vector->frame + vector->hdr_len - 2 : NULL,
+ vector->pn, 0, &enc_len);
+ if (enc == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to encrypt GCMP frame");
+ return 1;
+ }
+
+ wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+ if (os_memcmp(vector->encr, enc + vector->hdr_len + 8,
+ vector->payload_len) != 0) {
+ wpa_printf(MSG_ERROR, "GCMP test mpdu #%d enctypted data mismatch",
+ idx);
+ err++;
+ }
+ if (os_memcmp(vector->mic, enc + enc_len - sizeof(vector->mic),
+ sizeof(vector->mic)) != 0) {
+ wpa_printf(MSG_ERROR, "GCMP test mpdu #%d MIC mismatch", idx);
+ err++;
+ }
+ WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+ wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+ wpa_debug_level = MSG_INFO;
+ plain = gcmp_decrypt(vector->tk, sizeof(vector->tk),
+ (const struct ieee80211_hdr *) enc,
+ enc + vector->hdr_len,
+ enc_len - vector->hdr_len, &plain_len);
+ wpa_debug_level = MSG_EXCESSIVE;
+ os_free(enc);
+
+ if (plain == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to decrypt GCMP frame");
+ return 1;
+ }
+
+ if (plain_len != vector->payload_len ||
+ os_memcmp(plain, vector->frame + vector->hdr_len, plain_len) != 0) {
+ wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+ plain, plain_len);
+ err++;
+ }
+
+ os_free(plain);
+
+ return err;
+}
+
+
+static int test_vector_gcmp(void)
+{
+ int err = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(gcmp_vectors); i++) {
+ if (run_gcmp(i + 1, &gcmp_vectors[i]))
+ err++;
+
+ }
+
+ return err;
+}
+
+
+static int test_vector_gcmp_256(void)
+{
+ u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+ 0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
+ u8 pn[] = {
+ 0x00, 0x89, 0x5F, 0x5F, 0x2B, 0x08
+ };
+ u8 frame[] = {
+ 0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+ 0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+ 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+ 0x03, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
+ 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27
+ };
+ u8 encr[] = {
+ 0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+ 0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+ 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+ 0x03, 0x00, 0x08, 0x2b, 0x00, 0x20, 0x5f, 0x5f,
+ 0x89, 0x00, 0x65, 0x83, 0x43, 0xc8, 0xb1, 0x44,
+ 0x47, 0xd9, 0x21, 0x1d, 0xef, 0xd4, 0x6a, 0xd8,
+ 0x9c, 0x71, 0x0c, 0x6f, 0xc3, 0x33, 0x33, 0x23,
+ 0x6e, 0x39, 0x97, 0xb9, 0x17, 0x6a, 0x5a, 0x8b,
+ 0xe7, 0x79, 0xb2, 0x12, 0x66, 0x55, 0x5e, 0x70,
+ 0xad, 0x79, 0x11, 0x43, 0x16, 0x85, 0x90, 0x95,
+ 0x47, 0x3d, 0x5b, 0x1b, 0xd5, 0x96, 0xb3, 0xde,
+ 0xa3, 0xbf
+ };
+ u8 *enc, *plain;
+ size_t enc_len, plain_len;
+ u8 fcs[4];
+ int err = 0;
+
+ wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.11.1 GCMP-256 test vector\n");
+
+ wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+ wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+ wpa_hexdump(MSG_INFO, "802.11 Header", frame, 26);
+ wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 26, sizeof(frame) - 26);
+
+ enc = gcmp_encrypt(tk, sizeof(tk), frame, sizeof(frame), 26, frame + 24,
+ pn, 0, &enc_len);
+ if (enc == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to encrypt GCMP frame");
+ return 1;
+ }
+
+ wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+ if (enc_len != sizeof(encr) || os_memcmp(enc, encr, enc_len) != 0) {
+ wpa_printf(MSG_ERROR, "GCMP-256 test vector mismatch");
+ err++;
+ }
+ WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+ wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+ wpa_debug_level = MSG_INFO;
+ plain = gcmp_decrypt(tk, sizeof(tk), (const struct ieee80211_hdr *) enc,
+ enc + 26, enc_len - 26, &plain_len);
+ wpa_debug_level = MSG_EXCESSIVE;
+ os_free(enc);
+
+ if (plain == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to decrypt GCMP frame");
+ return 1;
+ }
+
+ if (plain_len != sizeof(frame) - 26 ||
+ os_memcmp(plain, frame + 26, plain_len) != 0) {
+ wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+ plain, plain_len);
+ err++;
+ }
+
+ os_free(plain);
+
+ return err;
+}
+
+
+static int test_vector_ccmp_256(void)
+{
+ u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+ 0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };
+ u8 pn[] = { 0xB5, 0x03, 0x97, 0x76, 0xE7, 0x0C };
+ u8 frame[] = {
+ 0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+ 0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+ 0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+ 0xf8, 0xba, 0x1a, 0x55, 0xd0, 0x2f, 0x85, 0xae,
+ 0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
+ 0x7e, 0x78, 0xa0, 0x50
+ };
+ u8 encr[] = {
+ 0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+ 0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+ 0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+ 0x0c, 0xe7, 0x00, 0x20, 0x76, 0x97, 0x03, 0xb5,
+ 0x6d, 0x15, 0x5d, 0x88, 0x32, 0x66, 0x82, 0x56,
+ 0xd6, 0xa9, 0x2b, 0x78, 0xe1, 0x1d, 0x8e, 0x54,
+ 0x49, 0x5d, 0xd1, 0x74, 0x80, 0xaa, 0x56, 0xc9,
+ 0x49, 0x2e, 0x88, 0x2b, 0x97, 0x64, 0x2f, 0x80,
+ 0xd5, 0x0f, 0xe9, 0x7b
+
+ };
+ u8 *enc, *plain;
+ size_t enc_len, plain_len;
+ u8 fcs[4];
+ int err = 0;
+
+ wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.6.4 CCMP-256 test vector\n");
+
+ wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
+ wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
+ wpa_hexdump(MSG_INFO, "802.11 Header", frame, 24);
+ wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 24, sizeof(frame) - 24);
+
+ enc = ccmp_256_encrypt(tk, frame, sizeof(frame), 24, NULL, pn, 0,
+ &enc_len);
+ if (enc == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
+ return 1;
+ }
+
+ wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+ if (enc_len != sizeof(encr) || os_memcmp(enc, encr, enc_len) != 0) {
+ wpa_printf(MSG_ERROR, "CCMP-256 test vector mismatch");
+ err++;
+ }
+ WPA_PUT_LE32(fcs, crc32(enc, enc_len));
+ wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
+
+ wpa_debug_level = MSG_INFO;
+ plain = ccmp_256_decrypt(tk, (const struct ieee80211_hdr *) enc,
+ enc + 24, enc_len - 24, &plain_len);
+ wpa_debug_level = MSG_EXCESSIVE;
+ os_free(enc);
+
+ if (plain == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to decrypt CCMP-256 frame");
+ return 1;
+ }
+
+ if (plain_len != sizeof(frame) - 24 ||
+ os_memcmp(plain, frame + 24, plain_len) != 0) {
+ wpa_hexdump(MSG_ERROR, "Decryption result did not match",
+ plain, plain_len);
+ err++;
+ }
+
+ os_free(plain);
+
+ return err;
+}
+
+
+static int test_vector_bip_gmac_128(void)
+{
+ u8 igtk[] = {
+ 0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
+ 0xca, 0x66, 0xff, 0xc5, 0x8b, 0xde, 0xcb, 0xcf
+ };
+ u8 ipn[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ u8 frame[] = {
+ 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x02, 0x00
+ };
+ u8 res[] = {
+ 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x02, 0x00, 0x4c, 0x18, 0x04, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x3e, 0xd8, 0x62, 0xfb,
+ 0x0f, 0x33, 0x38, 0xdd, 0x33, 0x86, 0xc8, 0x97,
+ 0xe2, 0xed, 0x05, 0x3d
+ };
+ u8 *prot;
+ size_t prot_len;
+ int err = 0;
+
+ wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.9.1 BIP-GMAC-128 with broadcast "
+ "Deauthentication frame\n");
+
+ wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
+ wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
+ wpa_hexdump(MSG_INFO, "Plaintext frame", frame, sizeof(frame));
+
+ prot = bip_gmac_protect(igtk, sizeof(igtk), frame, sizeof(frame),
+ ipn, 4, &prot_len);
+ if (prot == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to protect BIP-GMAC-128 frame");
+ return 1;
+ }
+
+ wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+ if (prot_len != sizeof(res) || os_memcmp(res, prot, prot_len) != 0) {
+ wpa_printf(MSG_ERROR, "BIP-GMAC-128 test vector mismatch");
+ err++;
+ }
+ os_free(prot);
+
+ return err;
+}
+
+
+static int test_vector_bip_gmac_256(void)
+{
+ u8 igtk[] = {
+ 0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
+ 0xca, 0x66, 0xff, 0xc5, 0x8b, 0xde, 0xcb, 0xcf,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
+ };
+ u8 ipn[] = { 0x04, 0x00, 0x00, 0x00, 0x00, 0x00 };
+ u8 frame[] = {
+ 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x02, 0x00
+ };
+ u8 res[] = {
+ 0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x02, 0x00, 0x4c, 0x18, 0x04, 0x00, 0x04, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x23, 0xbe, 0x59, 0xdc,
+ 0xc7, 0x02, 0x2e, 0xe3, 0x83, 0x62, 0x7e, 0xbb,
+ 0x10, 0x17, 0xdd, 0xfc
+ };
+ u8 *prot;
+ size_t prot_len;
+ int err = 0;
+
+ wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.9.1 BIP-GMAC-256 with broadcast Deauthentication frame\n");
+
+ wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
+ wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
+ wpa_hexdump(MSG_INFO, "Plaintext frame", frame, sizeof(frame));
+
+ prot = bip_gmac_protect(igtk, sizeof(igtk), frame, sizeof(frame),
+ ipn, 4, &prot_len);
+ if (prot == NULL) {
+ wpa_printf(MSG_ERROR, "Failed to protect BIP-GMAC-256 frame");
+ return 1;
+ }
+
+ wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+ if (prot_len != sizeof(res) || os_memcmp(res, prot, prot_len) != 0) {
+ wpa_printf(MSG_ERROR, "BIP-GMAC-128 test vector mismatch");
+ err++;
+ }
+ os_free(prot);
+
+ return err;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int errors = 0;
+
+ wpa_debug_level = MSG_EXCESSIVE;
+ wpa_debug_show_keys = 1;
+
+ if (os_program_init())
+ return -1;
+
+ test_vector_tkip();
+ test_vector_ccmp();
+ test_vector_ccmp_pv1();
+ test_vector_bip();
+ test_vector_ccmp_mgmt();
+ errors += test_vector_gcmp();
+ errors += test_vector_gcmp_256();
+ errors += test_vector_ccmp_256();
+ errors += test_vector_bip_gmac_128();
+ errors += test_vector_bip_gmac_256();
+
+ if (errors)
+ wpa_printf(MSG_INFO, "One or more test vectors failed");
+ os_program_deinit();
+
+ return errors ? -1 : 0;
+}
diff --git a/contrib/wpa/wlantest/tkip.c b/contrib/wpa/wlantest/tkip.c
new file mode 100644
index 000000000000..d616d4308ccd
--- /dev/null
+++ b/contrib/wpa/wlantest/tkip.c
@@ -0,0 +1,428 @@
+/*
+ * Temporal Key Integrity Protocol (CCMP)
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/crc32.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+void wep_crypt(u8 *key, u8 *buf, size_t plen);
+
+
+static inline u16 RotR1(u16 val)
+{
+ return (val >> 1) | (val << 15);
+}
+
+
+static inline u8 Lo8(u16 val)
+{
+ return val & 0xff;
+}
+
+
+static inline u8 Hi8(u16 val)
+{
+ return val >> 8;
+}
+
+
+static inline u16 Lo16(u32 val)
+{
+ return val & 0xffff;
+}
+
+
+static inline u16 Hi16(u32 val)
+{
+ return val >> 16;
+}
+
+
+static inline u16 Mk16(u8 hi, u8 lo)
+{
+ return lo | (((u16) hi) << 8);
+}
+
+
+static inline u16 Mk16_le(u16 *v)
+{
+ return le_to_host16(*v);
+}
+
+
+static const u16 Sbox[256] =
+{
+ 0xC6A5, 0xF884, 0xEE99, 0xF68D, 0xFF0D, 0xD6BD, 0xDEB1, 0x9154,
+ 0x6050, 0x0203, 0xCEA9, 0x567D, 0xE719, 0xB562, 0x4DE6, 0xEC9A,
+ 0x8F45, 0x1F9D, 0x8940, 0xFA87, 0xEF15, 0xB2EB, 0x8EC9, 0xFB0B,
+ 0x41EC, 0xB367, 0x5FFD, 0x45EA, 0x23BF, 0x53F7, 0xE496, 0x9B5B,
+ 0x75C2, 0xE11C, 0x3DAE, 0x4C6A, 0x6C5A, 0x7E41, 0xF502, 0x834F,
+ 0x685C, 0x51F4, 0xD134, 0xF908, 0xE293, 0xAB73, 0x6253, 0x2A3F,
+ 0x080C, 0x9552, 0x4665, 0x9D5E, 0x3028, 0x37A1, 0x0A0F, 0x2FB5,
+ 0x0E09, 0x2436, 0x1B9B, 0xDF3D, 0xCD26, 0x4E69, 0x7FCD, 0xEA9F,
+ 0x121B, 0x1D9E, 0x5874, 0x342E, 0x362D, 0xDCB2, 0xB4EE, 0x5BFB,
+ 0xA4F6, 0x764D, 0xB761, 0x7DCE, 0x527B, 0xDD3E, 0x5E71, 0x1397,
+ 0xA6F5, 0xB968, 0x0000, 0xC12C, 0x4060, 0xE31F, 0x79C8, 0xB6ED,
+ 0xD4BE, 0x8D46, 0x67D9, 0x724B, 0x94DE, 0x98D4, 0xB0E8, 0x854A,
+ 0xBB6B, 0xC52A, 0x4FE5, 0xED16, 0x86C5, 0x9AD7, 0x6655, 0x1194,
+ 0x8ACF, 0xE910, 0x0406, 0xFE81, 0xA0F0, 0x7844, 0x25BA, 0x4BE3,
+ 0xA2F3, 0x5DFE, 0x80C0, 0x058A, 0x3FAD, 0x21BC, 0x7048, 0xF104,
+ 0x63DF, 0x77C1, 0xAF75, 0x4263, 0x2030, 0xE51A, 0xFD0E, 0xBF6D,
+ 0x814C, 0x1814, 0x2635, 0xC32F, 0xBEE1, 0x35A2, 0x88CC, 0x2E39,
+ 0x9357, 0x55F2, 0xFC82, 0x7A47, 0xC8AC, 0xBAE7, 0x322B, 0xE695,
+ 0xC0A0, 0x1998, 0x9ED1, 0xA37F, 0x4466, 0x547E, 0x3BAB, 0x0B83,
+ 0x8CCA, 0xC729, 0x6BD3, 0x283C, 0xA779, 0xBCE2, 0x161D, 0xAD76,
+ 0xDB3B, 0x6456, 0x744E, 0x141E, 0x92DB, 0x0C0A, 0x486C, 0xB8E4,
+ 0x9F5D, 0xBD6E, 0x43EF, 0xC4A6, 0x39A8, 0x31A4, 0xD337, 0xF28B,
+ 0xD532, 0x8B43, 0x6E59, 0xDAB7, 0x018C, 0xB164, 0x9CD2, 0x49E0,
+ 0xD8B4, 0xACFA, 0xF307, 0xCF25, 0xCAAF, 0xF48E, 0x47E9, 0x1018,
+ 0x6FD5, 0xF088, 0x4A6F, 0x5C72, 0x3824, 0x57F1, 0x73C7, 0x9751,
+ 0xCB23, 0xA17C, 0xE89C, 0x3E21, 0x96DD, 0x61DC, 0x0D86, 0x0F85,
+ 0xE090, 0x7C42, 0x71C4, 0xCCAA, 0x90D8, 0x0605, 0xF701, 0x1C12,
+ 0xC2A3, 0x6A5F, 0xAEF9, 0x69D0, 0x1791, 0x9958, 0x3A27, 0x27B9,
+ 0xD938, 0xEB13, 0x2BB3, 0x2233, 0xD2BB, 0xA970, 0x0789, 0x33A7,
+ 0x2DB6, 0x3C22, 0x1592, 0xC920, 0x8749, 0xAAFF, 0x5078, 0xA57A,
+ 0x038F, 0x59F8, 0x0980, 0x1A17, 0x65DA, 0xD731, 0x84C6, 0xD0B8,
+ 0x82C3, 0x29B0, 0x5A77, 0x1E11, 0x7BCB, 0xA8FC, 0x6DD6, 0x2C3A,
+};
+
+
+static inline u16 _S_(u16 v)
+{
+ u16 t = Sbox[Hi8(v)];
+ return Sbox[Lo8(v)] ^ ((t << 8) | (t >> 8));
+}
+
+
+#define PHASE1_LOOP_COUNT 8
+
+static void tkip_mixing_phase1(u16 *TTAK, const u8 *TK, const u8 *TA, u32 IV32)
+{
+ int i, j;
+
+ /* Initialize the 80-bit TTAK from TSC (IV32) and TA[0..5] */
+ TTAK[0] = Lo16(IV32);
+ TTAK[1] = Hi16(IV32);
+ TTAK[2] = Mk16(TA[1], TA[0]);
+ TTAK[3] = Mk16(TA[3], TA[2]);
+ TTAK[4] = Mk16(TA[5], TA[4]);
+
+ for (i = 0; i < PHASE1_LOOP_COUNT; i++) {
+ j = 2 * (i & 1);
+ TTAK[0] += _S_(TTAK[4] ^ Mk16(TK[1 + j], TK[0 + j]));
+ TTAK[1] += _S_(TTAK[0] ^ Mk16(TK[5 + j], TK[4 + j]));
+ TTAK[2] += _S_(TTAK[1] ^ Mk16(TK[9 + j], TK[8 + j]));
+ TTAK[3] += _S_(TTAK[2] ^ Mk16(TK[13 + j], TK[12 + j]));
+ TTAK[4] += _S_(TTAK[3] ^ Mk16(TK[1 + j], TK[0 + j])) + i;
+ }
+}
+
+
+static void tkip_mixing_phase2(u8 *WEPSeed, const u8 *TK, const u16 *TTAK,
+ u16 IV16)
+{
+ u16 PPK[6];
+
+ /* Step 1 - make copy of TTAK and bring in TSC */
+ PPK[0] = TTAK[0];
+ PPK[1] = TTAK[1];
+ PPK[2] = TTAK[2];
+ PPK[3] = TTAK[3];
+ PPK[4] = TTAK[4];
+ PPK[5] = TTAK[4] + IV16;
+
+ /* Step 2 - 96-bit bijective mixing using S-box */
+ PPK[0] += _S_(PPK[5] ^ Mk16_le((u16 *) &TK[0]));
+ PPK[1] += _S_(PPK[0] ^ Mk16_le((u16 *) &TK[2]));
+ PPK[2] += _S_(PPK[1] ^ Mk16_le((u16 *) &TK[4]));
+ PPK[3] += _S_(PPK[2] ^ Mk16_le((u16 *) &TK[6]));
+ PPK[4] += _S_(PPK[3] ^ Mk16_le((u16 *) &TK[8]));
+ PPK[5] += _S_(PPK[4] ^ Mk16_le((u16 *) &TK[10]));
+
+ PPK[0] += RotR1(PPK[5] ^ Mk16_le((u16 *) &TK[12]));
+ PPK[1] += RotR1(PPK[0] ^ Mk16_le((u16 *) &TK[14]));
+ PPK[2] += RotR1(PPK[1]);
+ PPK[3] += RotR1(PPK[2]);
+ PPK[4] += RotR1(PPK[3]);
+ PPK[5] += RotR1(PPK[4]);
+
+ /* Step 3 - bring in last of TK bits, assign 24-bit WEP IV value
+ * WEPSeed[0..2] is transmitted as WEP IV */
+ WEPSeed[0] = Hi8(IV16);
+ WEPSeed[1] = (Hi8(IV16) | 0x20) & 0x7F;
+ WEPSeed[2] = Lo8(IV16);
+ WEPSeed[3] = Lo8((PPK[5] ^ Mk16_le((u16 *) &TK[0])) >> 1);
+ WPA_PUT_LE16(&WEPSeed[4], PPK[0]);
+ WPA_PUT_LE16(&WEPSeed[6], PPK[1]);
+ WPA_PUT_LE16(&WEPSeed[8], PPK[2]);
+ WPA_PUT_LE16(&WEPSeed[10], PPK[3]);
+ WPA_PUT_LE16(&WEPSeed[12], PPK[4]);
+ WPA_PUT_LE16(&WEPSeed[14], PPK[5]);
+}
+
+
+static inline u32 rotl(u32 val, int bits)
+{
+ return (val << bits) | (val >> (32 - bits));
+}
+
+
+static inline u32 rotr(u32 val, int bits)
+{
+ return (val >> bits) | (val << (32 - bits));
+}
+
+
+static inline u32 xswap(u32 val)
+{
+ return ((val & 0x00ff00ff) << 8) | ((val & 0xff00ff00) >> 8);
+}
+
+
+#define michael_block(l, r) \
+do { \
+ r ^= rotl(l, 17); \
+ l += r; \
+ r ^= xswap(l); \
+ l += r; \
+ r ^= rotl(l, 3); \
+ l += r; \
+ r ^= rotr(l, 2); \
+ l += r; \
+} while (0)
+
+
+static void michael_mic(const u8 *key, const u8 *hdr, const u8 *data,
+ size_t data_len, u8 *mic)
+{
+ u32 l, r;
+ int i, blocks, last;
+
+ l = WPA_GET_LE32(key);
+ r = WPA_GET_LE32(key + 4);
+
+ /* Michael MIC pseudo header: DA, SA, 3 x 0, Priority */
+ l ^= WPA_GET_LE32(hdr);
+ michael_block(l, r);
+ l ^= WPA_GET_LE32(&hdr[4]);
+ michael_block(l, r);
+ l ^= WPA_GET_LE32(&hdr[8]);
+ michael_block(l, r);
+ l ^= WPA_GET_LE32(&hdr[12]);
+ michael_block(l, r);
+
+ /* 32-bit blocks of data */
+ blocks = data_len / 4;
+ last = data_len % 4;
+ for (i = 0; i < blocks; i++) {
+ l ^= WPA_GET_LE32(&data[4 * i]);
+ michael_block(l, r);
+ }
+
+ /* Last block and padding (0x5a, 4..7 x 0) */
+ switch (last) {
+ case 0:
+ l ^= 0x5a;
+ break;
+ case 1:
+ l ^= data[4 * i] | 0x5a00;
+ break;
+ case 2:
+ l ^= data[4 * i] | (data[4 * i + 1] << 8) | 0x5a0000;
+ break;
+ case 3:
+ l ^= data[4 * i] | (data[4 * i + 1] << 8) |
+ (data[4 * i + 2] << 16) | 0x5a000000;
+ break;
+ }
+ michael_block(l, r);
+ /* l ^= 0; */
+ michael_block(l, r);
+
+ WPA_PUT_LE32(mic, l);
+ WPA_PUT_LE32(mic + 4, r);
+}
+
+
+static void michael_mic_hdr(const struct ieee80211_hdr *hdr11, u8 *hdr)
+{
+ int hdrlen = 24;
+ u16 fc = le_to_host16(hdr11->frame_control);
+
+ switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) {
+ case WLAN_FC_TODS:
+ os_memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ os_memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ case WLAN_FC_FROMDS:
+ os_memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ os_memcpy(hdr + ETH_ALEN, hdr11->addr3, ETH_ALEN); /* SA */
+ break;
+ case WLAN_FC_FROMDS | WLAN_FC_TODS:
+ os_memcpy(hdr, hdr11->addr3, ETH_ALEN); /* DA */
+ os_memcpy(hdr + ETH_ALEN, hdr11 + 1, ETH_ALEN); /* SA */
+ hdrlen += ETH_ALEN;
+ break;
+ case 0:
+ os_memcpy(hdr, hdr11->addr1, ETH_ALEN); /* DA */
+ os_memcpy(hdr + ETH_ALEN, hdr11->addr2, ETH_ALEN); /* SA */
+ break;
+ }
+
+ if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
+ (WLAN_FC_GET_STYPE(fc) & 0x08)) {
+ const u8 *qos = ((const u8 *) hdr11) + hdrlen;
+ hdr[12] = qos[0] & 0x0f; /* priority */
+ } else
+ hdr[12] = 0; /* priority */
+
+ hdr[13] = hdr[14] = hdr[15] = 0; /* reserved */
+}
+
+
+u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+ u16 iv16;
+ u32 iv32;
+ u16 ttak[5];
+ u8 rc4key[16];
+ u8 *plain;
+ size_t plain_len;
+ u32 icv, rx_icv;
+ const u8 *mic_key;
+ u8 michael_hdr[16];
+ u8 mic[8];
+ u16 fc = le_to_host16(hdr->frame_control);
+
+ if (data_len < 8 + 4)
+ return NULL;
+
+ iv16 = (data[0] << 8) | data[2];
+ iv32 = WPA_GET_LE32(&data[4]);
+ wpa_printf(MSG_EXCESSIVE, "TKIP decrypt: iv32=%08x iv16=%04x",
+ iv32, iv16);
+
+ tkip_mixing_phase1(ttak, tk, hdr->addr2, iv32);
+ wpa_hexdump(MSG_EXCESSIVE, "TKIP TTAK", (u8 *) ttak, sizeof(ttak));
+ tkip_mixing_phase2(rc4key, tk, ttak, iv16);
+ wpa_hexdump(MSG_EXCESSIVE, "TKIP RC4KEY", rc4key, sizeof(rc4key));
+
+ plain_len = data_len - 8;
+ plain = os_memdup(data + 8, plain_len);
+ if (plain == NULL)
+ return NULL;
+ wep_crypt(rc4key, plain, plain_len);
+
+ icv = crc32(plain, plain_len - 4);
+ rx_icv = WPA_GET_LE32(plain + plain_len - 4);
+ if (icv != rx_icv) {
+ wpa_printf(MSG_INFO, "TKIP ICV mismatch in frame from " MACSTR,
+ MAC2STR(hdr->addr2));
+ wpa_printf(MSG_DEBUG, "TKIP calculated ICV %08x received ICV "
+ "%08x", icv, rx_icv);
+ os_free(plain);
+ return NULL;
+ }
+ plain_len -= 4;
+
+ /* TODO: MSDU reassembly */
+
+ if (plain_len < 8) {
+ wpa_printf(MSG_INFO, "TKIP: Not enough room for Michael MIC "
+ "in a frame from " MACSTR, MAC2STR(hdr->addr2));
+ os_free(plain);
+ return NULL;
+ }
+
+ michael_mic_hdr(hdr, michael_hdr);
+ mic_key = tk + ((fc & WLAN_FC_FROMDS) ? 16 : 24);
+ michael_mic(mic_key, michael_hdr, plain, plain_len - 8, mic);
+ if (os_memcmp(mic, plain + plain_len - 8, 8) != 0) {
+ wpa_printf(MSG_INFO, "TKIP: Michael MIC mismatch in a frame "
+ "from " MACSTR, MAC2STR(hdr->addr2));
+ wpa_hexdump(MSG_DEBUG, "TKIP: Calculated MIC", mic, 8);
+ wpa_hexdump(MSG_DEBUG, "TKIP: Received MIC",
+ plain + plain_len - 8, 8);
+ os_free(plain);
+ return NULL;
+ }
+
+ *decrypted_len = plain_len - 8;
+ return plain;
+}
+
+
+void tkip_get_pn(u8 *pn, const u8 *data)
+{
+ pn[0] = data[7]; /* PN5 */
+ pn[1] = data[6]; /* PN4 */
+ pn[2] = data[5]; /* PN3 */
+ pn[3] = data[4]; /* PN2 */
+ pn[4] = data[0]; /* PN1 */
+ pn[5] = data[2]; /* PN0 */
+}
+
+
+u8 * tkip_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+ u8 *pn, int keyid, size_t *encrypted_len)
+{
+ u8 michael_hdr[16];
+ u8 mic[8];
+ struct ieee80211_hdr *hdr;
+ u16 fc;
+ const u8 *mic_key;
+ u8 *crypt, *pos;
+ u16 iv16;
+ u32 iv32;
+ u16 ttak[5];
+ u8 rc4key[16];
+
+ if (len < sizeof(*hdr) || len < hdrlen)
+ return NULL;
+ hdr = (struct ieee80211_hdr *) frame;
+ fc = le_to_host16(hdr->frame_control);
+
+ michael_mic_hdr(hdr, michael_hdr);
+ mic_key = tk + ((fc & WLAN_FC_FROMDS) ? 16 : 24);
+ michael_mic(mic_key, michael_hdr, frame + hdrlen, len - hdrlen, mic);
+ wpa_hexdump(MSG_EXCESSIVE, "TKIP: MIC", mic, sizeof(mic));
+
+ iv32 = WPA_GET_BE32(pn);
+ iv16 = WPA_GET_BE16(pn + 4);
+ tkip_mixing_phase1(ttak, tk, hdr->addr2, iv32);
+ wpa_hexdump(MSG_EXCESSIVE, "TKIP TTAK", (u8 *) ttak, sizeof(ttak));
+ tkip_mixing_phase2(rc4key, tk, ttak, iv16);
+ wpa_hexdump(MSG_EXCESSIVE, "TKIP RC4KEY", rc4key, sizeof(rc4key));
+
+ crypt = os_malloc(len + 8 + sizeof(mic) + 4);
+ if (crypt == NULL)
+ return NULL;
+ os_memcpy(crypt, frame, hdrlen);
+ pos = crypt + hdrlen;
+ os_memcpy(pos, rc4key, 3);
+ pos += 3;
+ *pos++ = keyid << 6 | BIT(5);
+ *pos++ = pn[3];
+ *pos++ = pn[2];
+ *pos++ = pn[1];
+ *pos++ = pn[0];
+
+ os_memcpy(pos, frame + hdrlen, len - hdrlen);
+ os_memcpy(pos + len - hdrlen, mic, sizeof(mic));
+ WPA_PUT_LE32(pos + len - hdrlen + sizeof(mic),
+ crc32(pos, len - hdrlen + sizeof(mic)));
+ wep_crypt(rc4key, pos, len - hdrlen + sizeof(mic) + 4);
+
+ *encrypted_len = len + 8 + sizeof(mic) + 4;
+ return crypt;
+}
diff --git a/contrib/wpa/wlantest/wep.c b/contrib/wpa/wlantest/wep.c
new file mode 100644
index 000000000000..50e371fc5103
--- /dev/null
+++ b/contrib/wpa/wlantest/wep.c
@@ -0,0 +1,104 @@
+/*
+ * Wired Equivalent Privacy (WEP)
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/crc32.h"
+#include "common/ieee802_11_defs.h"
+#include "wlantest.h"
+
+
+void wep_crypt(u8 *key, u8 *buf, size_t plen)
+{
+ u32 i, j, k;
+ u8 S[256];
+#define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0)
+ u8 *pos;
+
+ /* Setup RC4 state */
+ for (i = 0; i < 256; i++)
+ S[i] = i;
+ j = 0;
+ for (i = 0; i < 256; i++) {
+ j = (j + S[i] + key[i & 0x0f]) & 0xff;
+ S_SWAP(i, j);
+ }
+
+ /* Apply RC4 to data */
+ pos = buf;
+ i = j = 0;
+ for (k = 0; k < plen; k++) {
+ i = (i + 1) & 0xff;
+ j = (j + S[i]) & 0xff;
+ S_SWAP(i, j);
+ *pos ^= S[(S[i] + S[j]) & 0xff];
+ pos++;
+ }
+}
+
+
+static int try_wep(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *plain)
+{
+ u32 icv, rx_icv;
+ u8 k[16];
+ int i, j;
+
+ for (i = 0, j = 0; i < sizeof(k); i++) {
+ k[i] = key[j];
+ j++;
+ if (j >= key_len)
+ j = 0;
+ }
+
+ os_memcpy(plain, data, data_len);
+ wep_crypt(k, plain, data_len);
+ icv = crc32(plain, data_len - 4);
+ rx_icv = WPA_GET_LE32(plain + data_len - 4);
+ if (icv != rx_icv)
+ return -1;
+
+ return 0;
+}
+
+
+u8 * wep_decrypt(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+ u8 *plain;
+ struct wlantest_wep *w;
+ int found = 0;
+ u8 key[16];
+
+ if (dl_list_empty(&wt->wep))
+ return NULL;
+
+ if (data_len < 4 + 4)
+ return NULL;
+ plain = os_malloc(data_len - 4);
+ if (plain == NULL)
+ return NULL;
+
+ dl_list_for_each(w, &wt->wep, struct wlantest_wep, list) {
+ os_memcpy(key, data, 3);
+ os_memcpy(key + 3, w->key, w->key_len);
+ if (try_wep(key, 3 + w->key_len, data + 4, data_len - 4, plain)
+ == 0) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ os_free(plain);
+ return NULL;
+ }
+
+ *decrypted_len = data_len - 4 - 4;
+ return plain;
+}
diff --git a/contrib/wpa/wlantest/wired.c b/contrib/wpa/wlantest/wired.c
new file mode 100644
index 000000000000..67ae8a9afa9b
--- /dev/null
+++ b/contrib/wpa/wlantest/wired.c
@@ -0,0 +1,295 @@
+/*
+ * Received frame processing for wired interface
+ * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "utils/common.h"
+#include "radius/radius.h"
+#include "wlantest.h"
+
+
+static struct wlantest_radius * radius_get(struct wlantest *wt, u32 srv,
+ u32 cli)
+{
+ struct wlantest_radius *r;
+
+ dl_list_for_each(r, &wt->radius, struct wlantest_radius, list) {
+ if (r->srv == srv && r->cli == cli)
+ return r;
+ }
+
+ r = os_zalloc(sizeof(*r));
+ if (r == NULL)
+ return NULL;
+
+ r->srv = srv;
+ r->cli = cli;
+ dl_list_add(&wt->radius, &r->list);
+
+ return r;
+}
+
+
+static const char * radius_code_string(u8 code)
+{
+ switch (code) {
+ case RADIUS_CODE_ACCESS_REQUEST:
+ return "Access-Request";
+ case RADIUS_CODE_ACCESS_ACCEPT:
+ return "Access-Accept";
+ case RADIUS_CODE_ACCESS_REJECT:
+ return "Access-Reject";
+ case RADIUS_CODE_ACCOUNTING_REQUEST:
+ return "Accounting-Request";
+ case RADIUS_CODE_ACCOUNTING_RESPONSE:
+ return "Accounting-Response";
+ case RADIUS_CODE_ACCESS_CHALLENGE:
+ return "Access-Challenge";
+ case RADIUS_CODE_STATUS_SERVER:
+ return "Status-Server";
+ case RADIUS_CODE_STATUS_CLIENT:
+ return "Status-Client";
+ case RADIUS_CODE_RESERVED:
+ return "Reserved";
+ default:
+ return "?Unknown?";
+ }
+}
+
+
+static void process_radius_access_request(struct wlantest *wt, u32 dst,
+ u32 src, const u8 *data, size_t len)
+{
+ struct radius_msg *msg;
+ struct wlantest_radius *r;
+
+ msg = radius_msg_parse(data, len);
+ if (msg == NULL) {
+ wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Request");
+ return;
+ }
+
+ r = radius_get(wt, dst, src);
+ if (r) {
+ radius_msg_free(r->last_req);
+ r->last_req = msg;
+ return;
+ }
+ radius_msg_free(msg);
+}
+
+
+static void wlantest_add_pmk(struct wlantest *wt, const u8 *pmk, size_t pmk_len)
+{
+ struct wlantest_pmk *p;
+
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return;
+ os_memcpy(p->pmk, pmk, pmk_len);
+ p->pmk_len = pmk_len;
+ dl_list_add(&wt->pmk, &p->list);
+ wpa_hexdump(MSG_INFO, "Add PMK", pmk, pmk_len);
+}
+
+
+static void process_radius_access_accept(struct wlantest *wt, u32 dst, u32 src,
+ const u8 *data, size_t len)
+{
+ struct radius_msg *msg;
+ struct wlantest_radius *r;
+ struct radius_ms_mppe_keys *keys;
+ struct wlantest_radius_secret *s;
+
+ r = radius_get(wt, src, dst);
+ if (r == NULL || r->last_req == NULL) {
+ wpa_printf(MSG_DEBUG, "No RADIUS Access-Challenge found for "
+ "decrypting Access-Accept keys");
+ return;
+ }
+
+ msg = radius_msg_parse(data, len);
+ if (msg == NULL) {
+ wpa_printf(MSG_DEBUG, "Failed to parse RADIUS Access-Accept");
+ return;
+ }
+
+ dl_list_for_each(s, &wt->secret, struct wlantest_radius_secret, list) {
+ int found = 0;
+ keys = radius_msg_get_ms_keys(msg, r->last_req,
+ (u8 *) s->secret,
+ os_strlen(s->secret));
+ if (keys && keys->send && keys->recv) {
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len, len2;
+
+ wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key",
+ keys->send, keys->send_len);
+ wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key",
+ keys->recv, keys->recv_len);
+ pmk_len = keys->recv_len;
+ if (pmk_len > PMK_LEN_MAX)
+ pmk_len = PMK_LEN_MAX;
+ os_memcpy(pmk, keys->recv, pmk_len);
+ if (pmk_len < PMK_LEN_MAX) {
+ len2 = keys->send_len;
+ if (pmk_len + len2 > PMK_LEN_MAX)
+ len2 = PMK_LEN_MAX - pmk_len;
+ os_memcpy(pmk + pmk_len, keys->send, len2);
+ pmk_len += len2;
+ }
+ wlantest_add_pmk(wt, pmk, pmk_len);
+ found = 1;
+ }
+
+ if (keys) {
+ os_free(keys->send);
+ os_free(keys->recv);
+ os_free(keys);
+ }
+
+ if (found)
+ break;
+ }
+
+ radius_msg_free(msg);
+}
+
+
+static void process_radius(struct wlantest *wt, u32 dst, u16 dport, u32 src,
+ u16 sport, const u8 *data, size_t len)
+{
+ struct in_addr addr;
+ char buf[20];
+ const struct radius_hdr *hdr;
+ u16 rlen;
+
+ if (len < sizeof(*hdr))
+ return;
+ hdr = (const struct radius_hdr *) data;
+ rlen = be_to_host16(hdr->length);
+ if (len < rlen)
+ return;
+ if (len > rlen)
+ len = rlen;
+
+ addr.s_addr = dst;
+ snprintf(buf, sizeof(buf), "%s", inet_ntoa(addr));
+
+ addr.s_addr = src;
+ wpa_printf(MSG_DEBUG, "RADIUS %s:%u -> %s:%u id=%u %s",
+ inet_ntoa(addr), sport, buf, dport, hdr->identifier,
+ radius_code_string(hdr->code));
+
+ switch (hdr->code) {
+ case RADIUS_CODE_ACCESS_REQUEST:
+ process_radius_access_request(wt, dst, src, data, len);
+ break;
+ case RADIUS_CODE_ACCESS_ACCEPT:
+ process_radius_access_accept(wt, dst, src, data, len);
+ break;
+ }
+}
+
+
+static void process_udp(struct wlantest *wt, u32 dst, u32 src,
+ const u8 *data, size_t len)
+{
+ const struct udphdr *udp;
+ u16 sport, dport, ulen;
+ const u8 *payload;
+ size_t plen;
+
+ if (len < sizeof(*udp))
+ return;
+ udp = (const struct udphdr *) data;
+ /* TODO: check UDP checksum */
+ sport = be_to_host16(udp->uh_sport);
+ dport = be_to_host16(udp->uh_dport);
+ ulen = be_to_host16(udp->uh_ulen);
+
+ if (ulen > len)
+ return;
+ if (len < ulen)
+ len = ulen;
+
+ payload = (const u8 *) (udp + 1);
+ plen = len - sizeof(*udp);
+
+ if (sport == 1812 || dport == 1812)
+ process_radius(wt, dst, dport, src, sport, payload, plen);
+}
+
+
+static void process_ipv4(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ip *ip;
+ const u8 *payload;
+ size_t plen;
+ uint16_t frag_off, ip_len;
+
+ if (len < sizeof(*ip))
+ return;
+
+ ip = (const struct ip *) data;
+ if (ip->ip_v != 4)
+ return;
+ if (ip->ip_hl < 5)
+ return;
+
+ /* TODO: check header checksum in ip->check */
+
+ frag_off = be_to_host16(ip->ip_off);
+ if (frag_off & 0x1fff) {
+ wpa_printf(MSG_EXCESSIVE, "IP fragment reassembly not yet "
+ "supported");
+ return;
+ }
+
+ ip_len = be_to_host16(ip->ip_len);
+ if (ip_len > len)
+ return;
+ if (ip_len < len)
+ len = ip_len;
+
+ payload = data + 4 * ip->ip_hl;
+ plen = len - 4 * ip->ip_hl;
+ if (payload + plen > data + len)
+ return;
+
+ switch (ip->ip_p) {
+ case IPPROTO_UDP:
+ process_udp(wt, ip->ip_dst.s_addr, ip->ip_src.s_addr,
+ payload, plen);
+ break;
+ }
+}
+
+
+void wlantest_process_wired(struct wlantest *wt, const u8 *data, size_t len)
+{
+ const struct ether_header *eth;
+ u16 ethertype;
+
+ wpa_hexdump(MSG_EXCESSIVE, "Process wired frame", data, len);
+
+ if (len < sizeof(*eth))
+ return;
+
+ eth = (const struct ether_header *) data;
+ ethertype = be_to_host16(eth->ether_type);
+
+ switch (ethertype) {
+ case ETHERTYPE_IP:
+ process_ipv4(wt, data + sizeof(*eth), len - sizeof(*eth));
+ break;
+ }
+}
diff --git a/contrib/wpa/wlantest/wlantest.c b/contrib/wpa/wlantest/wlantest.c
new file mode 100644
index 000000000000..62c89e226150
--- /dev/null
+++ b/contrib/wpa/wlantest/wlantest.c
@@ -0,0 +1,505 @@
+/*
+ * wlantest - IEEE 802.11 protocol monitoring and testing tool
+ * Copyright (c) 2010-2019, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/eloop.h"
+#include "wlantest.h"
+
+
+static void wlantest_terminate(int sig, void *signal_ctx)
+{
+ eloop_terminate();
+}
+
+
+static void usage(void)
+{
+ printf("wlantest [-cddehqqFNt] [-i<ifname>] [-r<pcap file>] "
+ "[-p<passphrase>]\n"
+ " [-I<wired ifname>] [-R<wired pcap file>] "
+ "[-P<RADIUS shared secret>]\n"
+ " [-n<write pcapng file>]\n"
+ " [-w<write pcap file>] [-f<MSK/PMK file>]\n"
+ " [-L<log file>] [-T<PTK file>]\n");
+}
+
+
+static void passphrase_deinit(struct wlantest_passphrase *p)
+{
+ dl_list_del(&p->list);
+ os_free(p);
+}
+
+
+static void secret_deinit(struct wlantest_radius_secret *r)
+{
+ dl_list_del(&r->list);
+ os_free(r);
+}
+
+
+static void wlantest_init(struct wlantest *wt)
+{
+ int i;
+ os_memset(wt, 0, sizeof(*wt));
+ wt->monitor_sock = -1;
+ wt->ctrl_sock = -1;
+ for (i = 0; i < MAX_CTRL_CONNECTIONS; i++)
+ wt->ctrl_socks[i] = -1;
+ dl_list_init(&wt->passphrase);
+ dl_list_init(&wt->bss);
+ dl_list_init(&wt->secret);
+ dl_list_init(&wt->radius);
+ dl_list_init(&wt->pmk);
+ dl_list_init(&wt->ptk);
+ dl_list_init(&wt->wep);
+}
+
+
+void radius_deinit(struct wlantest_radius *r)
+{
+ dl_list_del(&r->list);
+ os_free(r);
+}
+
+
+static void ptk_deinit(struct wlantest_ptk *ptk)
+{
+ dl_list_del(&ptk->list);
+ os_free(ptk);
+}
+
+
+static void wlantest_deinit(struct wlantest *wt)
+{
+ struct wlantest_passphrase *p, *pn;
+ struct wlantest_radius_secret *s, *sn;
+ struct wlantest_radius *r, *rn;
+ struct wlantest_pmk *pmk, *np;
+ struct wlantest_ptk *ptk, *npt;
+ struct wlantest_wep *wep, *nw;
+
+ if (wt->ctrl_sock >= 0)
+ ctrl_deinit(wt);
+ if (wt->monitor_sock >= 0)
+ monitor_deinit(wt);
+ bss_flush(wt);
+ dl_list_for_each_safe(p, pn, &wt->passphrase,
+ struct wlantest_passphrase, list)
+ passphrase_deinit(p);
+ dl_list_for_each_safe(s, sn, &wt->secret,
+ struct wlantest_radius_secret, list)
+ secret_deinit(s);
+ dl_list_for_each_safe(r, rn, &wt->radius, struct wlantest_radius, list)
+ radius_deinit(r);
+ dl_list_for_each_safe(pmk, np, &wt->pmk, struct wlantest_pmk, list)
+ pmk_deinit(pmk);
+ dl_list_for_each_safe(ptk, npt, &wt->ptk, struct wlantest_ptk, list)
+ ptk_deinit(ptk);
+ dl_list_for_each_safe(wep, nw, &wt->wep, struct wlantest_wep, list)
+ os_free(wep);
+ write_pcap_deinit(wt);
+ write_pcapng_deinit(wt);
+ clear_notes(wt);
+ os_free(wt->decrypted);
+ wt->decrypted = NULL;
+}
+
+
+static void add_passphrase(struct wlantest *wt, const char *passphrase)
+{
+ struct wlantest_passphrase *p;
+ size_t len = os_strlen(passphrase);
+
+ if (len < 8 || len > 63)
+ return;
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ return;
+ os_memcpy(p->passphrase, passphrase, len);
+ dl_list_add(&wt->passphrase, &p->list);
+}
+
+
+static void add_secret(struct wlantest *wt, const char *secret)
+{
+ struct wlantest_radius_secret *s;
+ size_t len = os_strlen(secret);
+
+ if (len >= MAX_RADIUS_SECRET_LEN)
+ return;
+ s = os_zalloc(sizeof(*s));
+ if (s == NULL)
+ return;
+ os_memcpy(s->secret, secret, len);
+ dl_list_add(&wt->secret, &s->list);
+}
+
+
+static int add_pmk_file(struct wlantest *wt, const char *pmk_file)
+{
+ FILE *f;
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+ char buf[300], *pos;
+ struct wlantest_pmk *p;
+
+ f = fopen(pmk_file, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Could not open '%s'", pmk_file);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ pos = buf;
+ while (*pos && *pos != '\r' && *pos != '\n')
+ pos++;
+ *pos = '\0';
+ if (pos - buf < 2 * 32)
+ continue;
+ pmk_len = (pos - buf) / 2;
+ if (pmk_len > PMK_LEN_MAX)
+ pmk_len = PMK_LEN_MAX;
+ if (hexstr2bin(buf, pmk, pmk_len) < 0)
+ continue;
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ break;
+ os_memcpy(p->pmk, pmk, pmk_len);
+ p->pmk_len = pmk_len;
+ dl_list_add(&wt->pmk, &p->list);
+ wpa_hexdump(MSG_DEBUG, "Added PMK from file", pmk, pmk_len);
+
+ /* For FT, the send half of MSK is used */
+ if (hexstr2bin(&buf[2 * PMK_LEN], pmk, PMK_LEN) < 0)
+ continue;
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ break;
+ os_memcpy(p->pmk, pmk, PMK_LEN);
+ p->pmk_len = PMK_LEN;
+ dl_list_add(&wt->pmk, &p->list);
+ wpa_hexdump(MSG_DEBUG, "Added PMK from file (2nd half of MSK)",
+ pmk, PMK_LEN);
+ }
+
+ fclose(f);
+ return 0;
+}
+
+
+static int add_ptk_file(struct wlantest *wt, const char *ptk_file)
+{
+ FILE *f;
+ u8 ptk[64];
+ size_t ptk_len;
+ char buf[300], *pos;
+ struct wlantest_ptk *p;
+
+ f = fopen(ptk_file, "r");
+ if (f == NULL) {
+ wpa_printf(MSG_ERROR, "Could not open '%s'", ptk_file);
+ return -1;
+ }
+
+ while (fgets(buf, sizeof(buf), f)) {
+ pos = buf;
+ while (*pos && *pos != '\r' && *pos != '\n')
+ pos++;
+ *pos = '\0';
+ ptk_len = pos - buf;
+ if (ptk_len & 1)
+ continue;
+ ptk_len /= 2;
+ if (ptk_len != 16 && ptk_len != 32 &&
+ ptk_len != 48 && ptk_len != 64)
+ continue;
+ if (hexstr2bin(buf, ptk, ptk_len) < 0)
+ continue;
+ p = os_zalloc(sizeof(*p));
+ if (p == NULL)
+ break;
+ if (ptk_len < 48) {
+ os_memcpy(p->ptk.tk, ptk, ptk_len);
+ p->ptk.tk_len = ptk_len;
+ p->ptk_len = 32 + ptk_len;
+ } else {
+ os_memcpy(p->ptk.kck, ptk, 16);
+ p->ptk.kck_len = 16;
+ os_memcpy(p->ptk.kek, ptk + 16, 16);
+ p->ptk.kek_len = 16;
+ os_memcpy(p->ptk.tk, ptk + 32, ptk_len - 32);
+ p->ptk.tk_len = ptk_len - 32;
+ p->ptk_len = ptk_len;
+ }
+ dl_list_add(&wt->ptk, &p->list);
+ wpa_hexdump(MSG_DEBUG, "Added PTK from file", ptk, ptk_len);
+ }
+
+ fclose(f);
+ return 0;
+}
+
+
+int add_wep(struct wlantest *wt, const char *key)
+{
+ struct wlantest_wep *w;
+ size_t len = os_strlen(key);
+
+ if (len != 2 * 5 && len != 2 * 13) {
+ wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key);
+ return -1;
+ }
+ w = os_zalloc(sizeof(*w));
+ if (w == NULL)
+ return -1;
+ if (hexstr2bin(key, w->key, len / 2) < 0) {
+ os_free(w);
+ wpa_printf(MSG_INFO, "Invalid WEP key '%s'", key);
+ return -1;
+ }
+ w->key_len = len / 2;
+ dl_list_add(&wt->wep, &w->list);
+ return 0;
+}
+
+
+void add_note(struct wlantest *wt, int level, const char *fmt, ...)
+{
+ va_list ap;
+ size_t len = 1000;
+ int wlen;
+
+ if (wt->num_notes == MAX_NOTES)
+ return;
+
+ wt->notes[wt->num_notes] = os_malloc(len);
+ if (wt->notes[wt->num_notes] == NULL)
+ return;
+ va_start(ap, fmt);
+ wlen = vsnprintf(wt->notes[wt->num_notes], len, fmt, ap);
+ va_end(ap);
+ if (wlen < 0) {
+ os_free(wt->notes[wt->num_notes]);
+ wt->notes[wt->num_notes] = NULL;
+ return;
+ }
+ if (wlen >= len)
+ wt->notes[wt->num_notes][len - 1] = '\0';
+ wpa_printf(level, "%s", wt->notes[wt->num_notes]);
+ wt->num_notes++;
+}
+
+
+void clear_notes(struct wlantest *wt)
+{
+ size_t i;
+
+ for (i = 0; i < wt->num_notes; i++) {
+ os_free(wt->notes[i]);
+ wt->notes[i] = NULL;
+ }
+
+ wt->num_notes = 0;
+}
+
+
+size_t notes_len(struct wlantest *wt, size_t hdrlen)
+{
+ size_t i;
+ size_t len = wt->num_notes * hdrlen;
+
+ for (i = 0; i < wt->num_notes; i++)
+ len += os_strlen(wt->notes[i]);
+
+ return len;
+}
+
+
+void write_decrypted_note(struct wlantest *wt, const u8 *decrypted,
+ const u8 *tk, size_t tk_len, int keyid)
+{
+ char tk_hex[65];
+
+ if (!decrypted)
+ return;
+
+ wpa_snprintf_hex(tk_hex, sizeof(tk_hex), tk, tk_len);
+ add_note(wt, MSG_EXCESSIVE, "TK[%d] %s", keyid, tk_hex);
+}
+
+
+int wlantest_relog(struct wlantest *wt)
+{
+ int ret = 0;
+
+ wpa_printf(MSG_INFO, "Re-open log/capture files");
+ if (wpa_debug_reopen_file())
+ ret = -1;
+
+ if (wt->write_file) {
+ write_pcap_deinit(wt);
+ if (write_pcap_init(wt, wt->write_file) < 0)
+ ret = -1;
+ }
+
+ if (wt->pcapng_file) {
+ write_pcapng_deinit(wt);
+ if (write_pcapng_init(wt, wt->pcapng_file) < 0)
+ ret = -1;
+ }
+
+ return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+ int c;
+ const char *read_file = NULL;
+ const char *read_wired_file = NULL;
+ const char *ifname = NULL;
+ const char *ifname_wired = NULL;
+ const char *logfile = NULL;
+ struct wlantest wt;
+ int ctrl_iface = 0;
+
+ wpa_debug_level = MSG_INFO;
+ wpa_debug_show_keys = 1;
+
+ if (os_program_init())
+ return -1;
+
+ wlantest_init(&wt);
+
+ for (;;) {
+ c = getopt(argc, argv, "cdef:Fhi:I:L:n:Np:P:qr:R:tT:w:W:");
+ if (c < 0)
+ break;
+ switch (c) {
+ case 'c':
+ ctrl_iface = 1;
+ break;
+ case 'd':
+ if (wpa_debug_level > 0)
+ wpa_debug_level--;
+ break;
+ case 'e':
+ wt.ethernet = 1;
+ break;
+ case 'f':
+ if (add_pmk_file(&wt, optarg) < 0)
+ return -1;
+ break;
+ case 'F':
+ wt.assume_fcs = 1;
+ break;
+ case 'h':
+ usage();
+ return 0;
+ case 'i':
+ ifname = optarg;
+ break;
+ case 'I':
+ ifname_wired = optarg;
+ break;
+ case 'L':
+ logfile = optarg;
+ break;
+ case 'n':
+ wt.pcapng_file = optarg;
+ break;
+ case 'N':
+ wt.pcap_no_buffer = 1;
+ break;
+ case 'p':
+ add_passphrase(&wt, optarg);
+ break;
+ case 'P':
+ add_secret(&wt, optarg);
+ break;
+ case 'q':
+ wpa_debug_level++;
+ break;
+ case 'r':
+ read_file = optarg;
+ break;
+ case 'R':
+ read_wired_file = optarg;
+ break;
+ case 't':
+ wpa_debug_timestamp = 1;
+ break;
+ case 'T':
+ if (add_ptk_file(&wt, optarg) < 0)
+ return -1;
+ break;
+ case 'w':
+ wt.write_file = optarg;
+ break;
+ case 'W':
+ if (add_wep(&wt, optarg) < 0)
+ return -1;
+ break;
+ default:
+ usage();
+ return -1;
+ }
+ }
+
+ if (ifname == NULL && ifname_wired == NULL &&
+ read_file == NULL && read_wired_file == NULL) {
+ usage();
+ return 0;
+ }
+
+ if (eloop_init())
+ return -1;
+
+ if (logfile)
+ wpa_debug_open_file(logfile);
+
+ if (wt.write_file && write_pcap_init(&wt, wt.write_file) < 0)
+ return -1;
+
+ if (wt.pcapng_file && write_pcapng_init(&wt, wt.pcapng_file) < 0)
+ return -1;
+
+ if (read_wired_file && read_wired_cap_file(&wt, read_wired_file) < 0)
+ return -1;
+
+ if (read_file && read_cap_file(&wt, read_file) < 0)
+ return -1;
+
+ if (ifname && monitor_init(&wt, ifname) < 0)
+ return -1;
+
+ if (ifname_wired && monitor_init_wired(&wt, ifname_wired) < 0)
+ return -1;
+
+ if (ctrl_iface && ctrl_init(&wt) < 0)
+ return -1;
+
+ eloop_register_signal_terminate(wlantest_terminate, &wt);
+
+ eloop_run();
+
+ wpa_printf(MSG_INFO, "Processed: rx_mgmt=%u rx_ctrl=%u rx_data=%u "
+ "fcs_error=%u",
+ wt.rx_mgmt, wt.rx_ctrl, wt.rx_data, wt.fcs_error);
+
+ wlantest_deinit(&wt);
+
+ wpa_debug_close_file();
+ eloop_destroy();
+ os_program_deinit();
+
+ return 0;
+}
diff --git a/contrib/wpa/wlantest/wlantest.h b/contrib/wpa/wlantest/wlantest.h
new file mode 100644
index 000000000000..af29f578f0e5
--- /dev/null
+++ b/contrib/wpa/wlantest/wlantest.h
@@ -0,0 +1,336 @@
+/*
+ * wlantest - IEEE 802.11 protocol monitoring and testing tool
+ * Copyright (c) 2010-2020, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WLANTEST_H
+#define WLANTEST_H
+
+#include "utils/list.h"
+#include "common/wpa_common.h"
+#include "wlantest_ctrl.h"
+
+struct ieee802_11_elems;
+struct radius_msg;
+struct ieee80211_hdr;
+struct wlantest_bss;
+
+#define MAX_RADIUS_SECRET_LEN 128
+
+struct wlantest_radius_secret {
+ struct dl_list list;
+ char secret[MAX_RADIUS_SECRET_LEN];
+};
+
+struct wlantest_passphrase {
+ struct dl_list list;
+ char passphrase[64];
+ u8 ssid[32];
+ size_t ssid_len;
+ u8 bssid[ETH_ALEN];
+};
+
+struct wlantest_pmk {
+ struct dl_list list;
+ u8 pmk[PMK_LEN_MAX];
+ size_t pmk_len;
+};
+
+struct wlantest_ptk {
+ struct dl_list list;
+ struct wpa_ptk ptk;
+ size_t ptk_len;
+};
+
+struct wlantest_wep {
+ struct dl_list list;
+ size_t key_len;
+ u8 key[13];
+};
+
+struct wlantest_sta {
+ struct dl_list list;
+ struct wlantest_bss *bss;
+ u8 addr[ETH_ALEN];
+ enum {
+ STATE1 /* not authenticated */,
+ STATE2 /* authenticated */,
+ STATE3 /* associated */
+ } state;
+ u16 auth_alg;
+ bool ft_over_ds;
+ u16 aid;
+ u8 rsnie[257]; /* WPA/RSN IE */
+ u8 osenie[257]; /* OSEN IE */
+ int proto;
+ int pairwise_cipher;
+ int group_cipher;
+ int key_mgmt;
+ int rsn_capab;
+ /* ANonce from the previous EAPOL-Key msg 1/4 or 3/4 */
+ u8 anonce[WPA_NONCE_LEN];
+ /* SNonce from the previous EAPOL-Key msg 2/4 */
+ u8 snonce[WPA_NONCE_LEN];
+ u8 pmk_r0[PMK_LEN_MAX];
+ size_t pmk_r0_len;
+ u8 pmk_r0_name[WPA_PMK_NAME_LEN];
+ u8 pmk_r1[PMK_LEN_MAX];
+ size_t pmk_r1_len;
+ u8 pmk_r1_name[WPA_PMK_NAME_LEN];
+ struct wpa_ptk ptk; /* Derived PTK */
+ int ptk_set;
+ struct wpa_ptk tptk; /* Derived PTK during rekeying */
+ int tptk_set;
+ u8 rsc_tods[16 + 1][6];
+ u8 rsc_fromds[16 + 1][6];
+ u8 ap_sa_query_tr[2];
+ u8 sta_sa_query_tr[2];
+ u32 counters[NUM_WLANTEST_STA_COUNTER];
+ int assocreq_seen;
+ u16 assocreq_capab_info;
+ u16 assocreq_listen_int;
+ u8 *assocreq_ies;
+ size_t assocreq_ies_len;
+
+ /* Last ICMP Echo request information */
+ u32 icmp_echo_req_src;
+ u32 icmp_echo_req_dst;
+ u16 icmp_echo_req_id;
+ u16 icmp_echo_req_seq;
+
+ le16 seq_ctrl_to_sta[17];
+ le16 seq_ctrl_to_ap[17];
+ int allow_duplicate;
+
+ int pwrmgt;
+ int pspoll;
+
+ u8 gtk[32];
+ size_t gtk_len;
+ int gtk_idx;
+
+ u32 tx_tid[16 + 1];
+ u32 rx_tid[16 + 1];
+};
+
+struct wlantest_tdls {
+ struct dl_list list;
+ struct wlantest_sta *init;
+ struct wlantest_sta *resp;
+ struct tpk {
+ u8 kck[16];
+ u8 tk[16];
+ } tpk;
+ int link_up;
+ u8 dialog_token;
+ u8 rsc_init[16 + 1][6];
+ u8 rsc_resp[16 + 1][6];
+ u32 counters[NUM_WLANTEST_TDLS_COUNTER];
+ u8 inonce[32];
+ u8 rnonce[32];
+};
+
+struct wlantest_bss {
+ struct dl_list list;
+ u8 bssid[ETH_ALEN];
+ u16 capab_info;
+ u16 prev_capab_info;
+ u8 ssid[32];
+ size_t ssid_len;
+ int beacon_seen;
+ int proberesp_seen;
+ int ies_set;
+ int parse_error_reported;
+ u8 wpaie[257];
+ u8 rsnie[257];
+ u8 osenie[257];
+ int proto;
+ int pairwise_cipher;
+ int group_cipher;
+ int mgmt_group_cipher;
+ int key_mgmt;
+ int rsn_capab;
+ struct dl_list sta; /* struct wlantest_sta */
+ struct dl_list pmk; /* struct wlantest_pmk */
+ u8 gtk[4][32];
+ size_t gtk_len[4];
+ int gtk_idx;
+ u8 rsc[4][6];
+ u8 igtk[8][32];
+ size_t igtk_len[8];
+ int igtk_idx;
+ u8 ipn[8][6];
+ int bigtk_idx;
+ u32 counters[NUM_WLANTEST_BSS_COUNTER];
+ struct dl_list tdls; /* struct wlantest_tdls */
+ u8 mdid[MOBILITY_DOMAIN_ID_LEN];
+ u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
+ size_t r0kh_id_len;
+ u8 r1kh_id[FT_R1KH_ID_LEN];
+ bool mesh;
+};
+
+struct wlantest_radius {
+ struct dl_list list;
+ u32 srv;
+ u32 cli;
+ struct radius_msg *last_req;
+};
+
+
+#define MAX_CTRL_CONNECTIONS 10
+#define MAX_NOTES 10
+
+struct wlantest {
+ int monitor_sock;
+ int monitor_wired;
+
+ int ctrl_sock;
+ int ctrl_socks[MAX_CTRL_CONNECTIONS];
+
+ struct dl_list passphrase; /* struct wlantest_passphrase */
+ struct dl_list bss; /* struct wlantest_bss */
+ struct dl_list secret; /* struct wlantest_radius_secret */
+ struct dl_list radius; /* struct wlantest_radius */
+ struct dl_list pmk; /* struct wlantest_pmk */
+ struct dl_list ptk; /* struct wlantest_ptk */
+ struct dl_list wep; /* struct wlantest_wep */
+
+ unsigned int rx_mgmt;
+ unsigned int rx_ctrl;
+ unsigned int rx_data;
+ unsigned int fcs_error;
+ unsigned int frame_num;
+
+ void *write_pcap; /* pcap_t* */
+ void *write_pcap_dumper; /* pcpa_dumper_t */
+ struct timeval write_pcap_time;
+ u8 *decrypted;
+ size_t decrypted_len;
+ FILE *pcapng;
+ u32 write_pcapng_time_high;
+ u32 write_pcapng_time_low;
+
+ u8 last_hdr[30];
+ size_t last_len;
+ int last_mgmt_valid;
+
+ unsigned int assume_fcs:1;
+ unsigned int pcap_no_buffer:1;
+ unsigned int ethernet:1;
+
+ char *notes[MAX_NOTES];
+ size_t num_notes;
+
+ const char *write_file;
+ const char *pcapng_file;
+};
+
+void add_note(struct wlantest *wt, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+void clear_notes(struct wlantest *wt);
+size_t notes_len(struct wlantest *wt, size_t hdrlen);
+void write_decrypted_note(struct wlantest *wt, const u8 *decrypted,
+ const u8 *tk, size_t tk_len, int keyid);
+
+int add_wep(struct wlantest *wt, const char *key);
+int read_cap_file(struct wlantest *wt, const char *fname);
+int read_wired_cap_file(struct wlantest *wt, const char *fname);
+
+int write_pcap_init(struct wlantest *wt, const char *fname);
+void write_pcap_deinit(struct wlantest *wt);
+void write_pcap_captured(struct wlantest *wt, const u8 *buf, size_t len);
+void write_pcap_decrypted(struct wlantest *wt, const u8 *buf1, size_t len1,
+ const u8 *buf2, size_t len2);
+
+int write_pcapng_init(struct wlantest *wt, const char *fname);
+void write_pcapng_deinit(struct wlantest *wt);
+struct pcap_pkthdr;
+void write_pcapng_write_read(struct wlantest *wt, int dlt,
+ struct pcap_pkthdr *hdr, const u8 *data);
+void write_pcapng_captured(struct wlantest *wt, const u8 *buf, size_t len);
+
+void wlantest_process(struct wlantest *wt, const u8 *data, size_t len);
+void wlantest_process_prism(struct wlantest *wt, const u8 *data, size_t len);
+void wlantest_process_80211(struct wlantest *wt, const u8 *data, size_t len);
+void wlantest_process_wired(struct wlantest *wt, const u8 *data, size_t len);
+int monitor_init(struct wlantest *wt, const char *ifname);
+int monitor_init_wired(struct wlantest *wt, const char *ifname);
+void monitor_deinit(struct wlantest *wt);
+void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len);
+void rx_mgmt_ack(struct wlantest *wt, const struct ieee80211_hdr *hdr);
+void rx_data(struct wlantest *wt, const u8 *data, size_t len);
+void rx_data_eapol(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr,
+ const u8 *dst, const u8 *src,
+ const u8 *data, size_t len, int prot);
+void rx_data_ip(struct wlantest *wt, const u8 *bssid, const u8 *sta_addr,
+ const u8 *dst, const u8 *src, const u8 *data, size_t len,
+ const u8 *peer_addr);
+void rx_data_80211_encap(struct wlantest *wt, const u8 *bssid,
+ const u8 *sta_addr, const u8 *dst, const u8 *src,
+ const u8 *data, size_t len);
+
+struct wlantest_bss * bss_find(struct wlantest *wt, const u8 *bssid);
+struct wlantest_bss * bss_get(struct wlantest *wt, const u8 *bssid);
+void bss_deinit(struct wlantest_bss *bss);
+void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
+ struct ieee802_11_elems *elems, int beacon);
+void bss_flush(struct wlantest *wt);
+int bss_add_pmk_from_passphrase(struct wlantest_bss *bss,
+ const char *passphrase);
+void pmk_deinit(struct wlantest_pmk *pmk);
+void tdls_deinit(struct wlantest_tdls *tdls);
+
+struct wlantest_sta * sta_find(struct wlantest_bss *bss, const u8 *addr);
+struct wlantest_sta * sta_get(struct wlantest_bss *bss, const u8 *addr);
+void sta_deinit(struct wlantest_sta *sta);
+void sta_update_assoc(struct wlantest_sta *sta,
+ struct ieee802_11_elems *elems);
+
+u8 * ccmp_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * ccmp_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+ u8 *pn, int keyid, size_t *encrypted_len);
+u8 * ccmp_encrypt_pv1(const u8 *tk, const u8 *a1, const u8 *a2, const u8 *a3,
+ const u8 *frame, size_t len,
+ size_t hdrlen, const u8 *pn, int keyid,
+ size_t *encrypted_len);
+void ccmp_get_pn(u8 *pn, const u8 *data);
+u8 * ccmp_256_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * ccmp_256_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen,
+ u8 *qos, u8 *pn, int keyid, size_t *encrypted_len);
+
+u8 * tkip_decrypt(const u8 *tk, const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * tkip_encrypt(const u8 *tk, u8 *frame, size_t len, size_t hdrlen, u8 *qos,
+ u8 *pn, int keyid, size_t *encrypted_len);
+void tkip_get_pn(u8 *pn, const u8 *data);
+
+u8 * wep_decrypt(struct wlantest *wt, const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len);
+
+u8 * bip_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+ u8 *ipn, int keyid, size_t *prot_len);
+u8 * bip_gmac_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+ u8 *ipn, int keyid, size_t *prot_len);
+
+u8 * gcmp_decrypt(const u8 *tk, size_t tk_len, const struct ieee80211_hdr *hdr,
+ const u8 *data, size_t data_len, size_t *decrypted_len);
+u8 * gcmp_encrypt(const u8 *tk, size_t tk_len, const u8 *frame, size_t len,
+ size_t hdrlen, const u8 *qos,
+ const u8 *pn, int keyid, size_t *encrypted_len);
+
+int ctrl_init(struct wlantest *wt);
+void ctrl_deinit(struct wlantest *wt);
+
+int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
+ struct wlantest_sta *sta, u8 *frame, size_t len,
+ enum wlantest_inject_protection prot);
+
+int wlantest_relog(struct wlantest *wt);
+
+#endif /* WLANTEST_H */
diff --git a/contrib/wpa/wlantest/wlantest_cli.c b/contrib/wpa/wlantest/wlantest_cli.c
new file mode 100644
index 000000000000..ad5a48deaaf8
--- /dev/null
+++ b/contrib/wpa/wlantest/wlantest_cli.c
@@ -0,0 +1,1865 @@
+/*
+ * wlantest controller
+ * Copyright (c) 2010-2013, Jouni Malinen <j@w1.fi>
+ *
+ * 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 "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/edit.h"
+#include "wlantest_ctrl.h"
+
+
+static int get_cmd_arg_num(const char *str, int pos)
+{
+ int arg = 0, i;
+
+ for (i = 0; i <= pos; i++) {
+ if (str[i] != ' ') {
+ arg++;
+ while (i <= pos && str[i] != ' ')
+ i++;
+ }
+ }
+
+ if (arg > 0)
+ arg--;
+ return arg;
+}
+
+
+static int get_prev_arg_pos(const char *str, int pos)
+{
+ while (pos > 0 && str[pos - 1] != ' ')
+ pos--;
+ while (pos > 0 && str[pos - 1] == ' ')
+ pos--;
+ while (pos > 0 && str[pos - 1] != ' ')
+ pos--;
+ return pos;
+}
+
+
+static u8 * attr_get(u8 *buf, size_t buflen, enum wlantest_ctrl_attr attr,
+ size_t *len)
+{
+ u8 *pos = buf;
+
+ while (pos + 8 <= buf + buflen) {
+ enum wlantest_ctrl_attr a;
+ size_t alen;
+ a = WPA_GET_BE32(pos);
+ pos += 4;
+ alen = WPA_GET_BE32(pos);
+ pos += 4;
+ if (pos + alen > buf + buflen) {
+ printf("Invalid control message attribute\n");
+ return NULL;
+ }
+ if (a == attr) {
+ *len = alen;
+ return pos;
+ }
+ pos += alen;
+ }
+
+ return NULL;
+}
+
+
+static u8 * attr_hdr_add(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+ size_t len)
+{
+ if (pos == NULL || end - pos < 8 + len)
+ return NULL;
+ WPA_PUT_BE32(pos, attr);
+ pos += 4;
+ WPA_PUT_BE32(pos, len);
+ pos += 4;
+ return pos;
+}
+
+
+static u8 * attr_add_str(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+ const char *str)
+{
+ size_t len = os_strlen(str);
+
+ if (pos == NULL || end - pos < 8 + len)
+ return NULL;
+ WPA_PUT_BE32(pos, attr);
+ pos += 4;
+ WPA_PUT_BE32(pos, len);
+ pos += 4;
+ os_memcpy(pos, str, len);
+ pos += len;
+ return pos;
+}
+
+
+static u8 * attr_add_be32(u8 *pos, u8 *end, enum wlantest_ctrl_attr attr,
+ u32 val)
+{
+ if (pos == NULL || end - pos < 12)
+ return NULL;
+ WPA_PUT_BE32(pos, attr);
+ pos += 4;
+ WPA_PUT_BE32(pos, 4);
+ pos += 4;
+ WPA_PUT_BE32(pos, val);
+ pos += 4;
+ return pos;
+}
+
+
+static int cmd_send_and_recv(int s, const u8 *cmd, size_t cmd_len,
+ u8 *resp, size_t max_resp_len)
+{
+ int res;
+ enum wlantest_ctrl_cmd cmd_resp;
+
+ if (send(s, cmd, cmd_len, 0) < 0)
+ return -1;
+ res = recv(s, resp, max_resp_len, 0);
+ if (res < 4)
+ return -1;
+
+ cmd_resp = WPA_GET_BE32(resp);
+ if (cmd_resp == WLANTEST_CTRL_SUCCESS)
+ return res;
+
+ if (cmd_resp == WLANTEST_CTRL_UNKNOWN_CMD)
+ printf("Unknown command\n");
+ else if (cmd_resp == WLANTEST_CTRL_INVALID_CMD)
+ printf("Invalid command\n");
+
+ return -1;
+}
+
+
+static int cmd_simple(int s, enum wlantest_ctrl_cmd cmd)
+{
+ u8 buf[4];
+ int res;
+ WPA_PUT_BE32(buf, cmd);
+ res = cmd_send_and_recv(s, buf, sizeof(buf), buf, sizeof(buf));
+ return res < 0 ? -1 : 0;
+}
+
+
+static char ** get_bssid_list(int s)
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[4];
+ u8 *bssid;
+ size_t len;
+ int rlen, i;
+ char **res;
+
+ WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS);
+ rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
+ if (rlen < 0)
+ return NULL;
+
+ bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len);
+ if (bssid == NULL)
+ return NULL;
+
+ res = os_calloc(len / ETH_ALEN + 1, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ for (i = 0; i < len / ETH_ALEN; i++) {
+ res[i] = os_zalloc(18);
+ if (res[i] == NULL)
+ break;
+ os_snprintf(res[i], 18, MACSTR, MAC2STR(bssid + ETH_ALEN * i));
+ }
+
+ return res;
+}
+
+
+static char ** get_sta_list(int s, const u8 *bssid, int add_bcast)
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *pos, *end;
+ u8 *addr;
+ size_t len;
+ int rlen, i;
+ char **res;
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA);
+ pos += 4;
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+ os_memcpy(pos, bssid, ETH_ALEN);
+ pos += ETH_ALEN;
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return NULL;
+
+ addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len);
+ if (addr == NULL)
+ return NULL;
+
+ res = os_calloc(len / ETH_ALEN + 1 + add_bcast, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ for (i = 0; i < len / ETH_ALEN; i++) {
+ res[i] = os_zalloc(18);
+ if (res[i] == NULL)
+ break;
+ os_snprintf(res[i], 18, MACSTR, MAC2STR(addr + ETH_ALEN * i));
+ }
+ if (add_bcast)
+ res[i] = os_strdup("ff:ff:ff:ff:ff:ff");
+
+ return res;
+}
+
+
+static int cmd_ping(int s, int argc, char *argv[])
+{
+ int res = cmd_simple(s, WLANTEST_CTRL_PING);
+ if (res == 0)
+ printf("PONG\n");
+ return res == 0;
+}
+
+
+static int cmd_terminate(int s, int argc, char *argv[])
+{
+ return cmd_simple(s, WLANTEST_CTRL_TERMINATE);
+}
+
+
+static int cmd_list_bss(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[4];
+ u8 *bssid;
+ size_t len;
+ int rlen, i;
+
+ WPA_PUT_BE32(buf, WLANTEST_CTRL_LIST_BSS);
+ rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+
+ bssid = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_BSSID, &len);
+ if (bssid == NULL)
+ return -1;
+
+ for (i = 0; i < len / ETH_ALEN; i++)
+ printf(MACSTR " ", MAC2STR(bssid + ETH_ALEN * i));
+ printf("\n");
+
+ return 0;
+}
+
+
+static int cmd_list_sta(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *pos;
+ u8 *addr;
+ size_t len;
+ int rlen, i;
+
+ if (argc < 1) {
+ printf("list_sta needs one argument: BSSID\n");
+ return -1;
+ }
+
+ pos = buf;
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_LIST_STA);
+ pos += 4;
+ WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+ pos += 4;
+ WPA_PUT_BE32(pos, ETH_ALEN);
+ pos += 4;
+ if (hwaddr_aton(argv[0], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[0]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+
+ addr = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_STA_ADDR, &len);
+ if (addr == NULL)
+ return -1;
+
+ for (i = 0; i < len / ETH_ALEN; i++)
+ printf(MACSTR " ", MAC2STR(addr + ETH_ALEN * i));
+ printf("\n");
+
+ return 0;
+}
+
+
+static char ** complete_list_sta(int s, const char *str, int pos)
+{
+ if (get_cmd_arg_num(str, pos) == 1)
+ return get_bssid_list(s);
+ return NULL;
+}
+
+
+static int cmd_flush(int s, int argc, char *argv[])
+{
+ return cmd_simple(s, WLANTEST_CTRL_FLUSH);
+}
+
+
+static int cmd_clear_sta_counters(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *pos;
+ int rlen;
+
+ if (argc < 2) {
+ printf("clear_sta_counters needs two arguments: BSSID and "
+ "STA address\n");
+ return -1;
+ }
+
+ pos = buf;
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_STA_COUNTERS);
+ pos += 4;
+ WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+ pos += 4;
+ WPA_PUT_BE32(pos, ETH_ALEN);
+ pos += 4;
+ if (hwaddr_aton(argv[0], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[0]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
+ pos += 4;
+ WPA_PUT_BE32(pos, ETH_ALEN);
+ pos += 4;
+ if (hwaddr_aton(argv[1], pos) < 0) {
+ printf("Invalid STA address '%s'\n", argv[1]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+ printf("OK\n");
+ return 0;
+}
+
+
+static char ** complete_clear_sta_counters(int s, const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+ u8 addr[ETH_ALEN];
+
+ switch (arg) {
+ case 1:
+ res = get_bssid_list(s);
+ break;
+ case 2:
+ if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+ break;
+ res = get_sta_list(s, addr, 0);
+ break;
+ }
+
+ return res;
+}
+
+
+static int cmd_clear_bss_counters(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *pos;
+ int rlen;
+
+ if (argc < 1) {
+ printf("clear_bss_counters needs one argument: BSSID\n");
+ return -1;
+ }
+
+ pos = buf;
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_BSS_COUNTERS);
+ pos += 4;
+ WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+ pos += 4;
+ WPA_PUT_BE32(pos, ETH_ALEN);
+ pos += 4;
+ if (hwaddr_aton(argv[0], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[0]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+ printf("OK\n");
+ return 0;
+}
+
+
+static char ** complete_clear_bss_counters(int s, const char *str, int pos)
+{
+ if (get_cmd_arg_num(str, pos) == 1)
+ return get_bssid_list(s);
+ return NULL;
+}
+
+
+static int cmd_clear_tdls_counters(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *pos;
+ int rlen;
+
+ if (argc < 3) {
+ printf("clear_tdls_counters needs three arguments: BSSID, "
+ "STA1 address, STA2 address\n");
+ return -1;
+ }
+
+ pos = buf;
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_CLEAR_TDLS_COUNTERS);
+ pos += 4;
+ WPA_PUT_BE32(pos, WLANTEST_ATTR_BSSID);
+ pos += 4;
+ WPA_PUT_BE32(pos, ETH_ALEN);
+ pos += 4;
+ if (hwaddr_aton(argv[0], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[0]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ WPA_PUT_BE32(pos, WLANTEST_ATTR_STA_ADDR);
+ pos += 4;
+ WPA_PUT_BE32(pos, ETH_ALEN);
+ pos += 4;
+ if (hwaddr_aton(argv[1], pos) < 0) {
+ printf("Invalid STA1 address '%s'\n", argv[1]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ WPA_PUT_BE32(pos, WLANTEST_ATTR_STA2_ADDR);
+ pos += 4;
+ WPA_PUT_BE32(pos, ETH_ALEN);
+ pos += 4;
+ if (hwaddr_aton(argv[2], pos) < 0) {
+ printf("Invalid STA2 address '%s'\n", argv[2]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+ printf("OK\n");
+ return 0;
+}
+
+
+static char ** complete_clear_tdls_counters(int s, const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+ u8 addr[ETH_ALEN];
+
+ switch (arg) {
+ case 1:
+ res = get_bssid_list(s);
+ break;
+ case 2:
+ case 3:
+ if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+ break;
+ res = get_sta_list(s, addr, 0);
+ break;
+ }
+
+ return res;
+}
+
+
+struct sta_counters {
+ const char *name;
+ enum wlantest_sta_counter num;
+};
+
+static const struct sta_counters sta_counters[] = {
+ { "auth_tx", WLANTEST_STA_COUNTER_AUTH_TX },
+ { "auth_rx", WLANTEST_STA_COUNTER_AUTH_RX },
+ { "assocreq_tx", WLANTEST_STA_COUNTER_ASSOCREQ_TX },
+ { "reassocreq_tx", WLANTEST_STA_COUNTER_REASSOCREQ_TX },
+ { "ptk_learned", WLANTEST_STA_COUNTER_PTK_LEARNED },
+ { "valid_deauth_tx", WLANTEST_STA_COUNTER_VALID_DEAUTH_TX },
+ { "valid_deauth_rx", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX },
+ { "invalid_deauth_tx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX },
+ { "invalid_deauth_rx", WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX },
+ { "valid_disassoc_tx", WLANTEST_STA_COUNTER_VALID_DISASSOC_TX },
+ { "valid_disassoc_rx", WLANTEST_STA_COUNTER_VALID_DISASSOC_RX },
+ { "invalid_disassoc_tx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX },
+ { "invalid_disassoc_rx", WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX },
+ { "valid_saqueryreq_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX },
+ { "valid_saqueryreq_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX },
+ { "invalid_saqueryreq_tx",
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX },
+ { "invalid_saqueryreq_rx",
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX },
+ { "valid_saqueryresp_tx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX },
+ { "valid_saqueryresp_rx", WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX },
+ { "invalid_saqueryresp_tx",
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX },
+ { "invalid_saqueryresp_rx",
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX },
+ { "ping_ok", WLANTEST_STA_COUNTER_PING_OK },
+ { "assocresp_comeback", WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK },
+ { "reassocresp_comeback", WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK },
+ { "ping_ok_first_assoc", WLANTEST_STA_COUNTER_PING_OK_FIRST_ASSOC },
+ { "valid_deauth_rx_ack", WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK },
+ { "valid_disassoc_rx_ack",
+ WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK },
+ { "invalid_deauth_rx_ack",
+ WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK },
+ { "invalid_disassoc_rx_ack",
+ WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK },
+ { "deauth_rx_asleep", WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP },
+ { "deauth_rx_awake", WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE },
+ { "disassoc_rx_asleep", WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP },
+ { "disassoc_rx_awake", WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE },
+ { "prot_data_tx", WLANTEST_STA_COUNTER_PROT_DATA_TX },
+ { "deauth_rx_rc6", WLANTEST_STA_COUNTER_DEAUTH_RX_RC6 },
+ { "deauth_rx_rc7", WLANTEST_STA_COUNTER_DEAUTH_RX_RC7 },
+ { "disassoc_rx_rc6", WLANTEST_STA_COUNTER_DISASSOC_RX_RC6 },
+ { "disassoc_rx_rc7", WLANTEST_STA_COUNTER_DISASSOC_RX_RC7 },
+ { NULL, 0 }
+};
+
+static int cmd_get_sta_counter(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *end, *pos;
+ int rlen, i;
+ size_t len;
+
+ if (argc != 3) {
+ printf("get_sta_counter needs at three arguments: "
+ "counter name, BSSID, and STA address\n");
+ return -1;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_STA_COUNTER);
+ pos += 4;
+
+ for (i = 0; sta_counters[i].name; i++) {
+ if (os_strcasecmp(sta_counters[i].name, argv[0]) == 0)
+ break;
+ }
+ if (sta_counters[i].name == NULL) {
+ printf("Unknown STA counter '%s'\n", argv[0]);
+ printf("Counters:");
+ for (i = 0; sta_counters[i].name; i++)
+ printf(" %s", sta_counters[i].name);
+ printf("\n");
+ return -1;
+ }
+
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_COUNTER,
+ sta_counters[i].num);
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+ if (hwaddr_aton(argv[1], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[1]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+ if (hwaddr_aton(argv[2], pos) < 0) {
+ printf("Invalid STA address '%s'\n", argv[2]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+
+ pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+ if (pos == NULL || len != 4)
+ return -1;
+ printf("%u\n", WPA_GET_BE32(pos));
+ return 0;
+}
+
+
+static char ** complete_get_sta_counter(int s, const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+ int i, count;
+ u8 addr[ETH_ALEN];
+
+ switch (arg) {
+ case 1:
+ /* counter list */
+ count = ARRAY_SIZE(sta_counters);
+ res = os_calloc(count, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ for (i = 0; sta_counters[i].name; i++) {
+ res[i] = os_strdup(sta_counters[i].name);
+ if (res[i] == NULL)
+ break;
+ }
+ break;
+ case 2:
+ res = get_bssid_list(s);
+ break;
+ case 3:
+ if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+ break;
+ res = get_sta_list(s, addr, 0);
+ break;
+ }
+
+ return res;
+}
+
+
+struct bss_counters {
+ const char *name;
+ enum wlantest_bss_counter num;
+};
+
+static const struct bss_counters bss_counters[] = {
+ { "valid_bip_mmie", WLANTEST_BSS_COUNTER_VALID_BIP_MMIE },
+ { "invalid_bip_mmie", WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE },
+ { "missing_bip_mmie", WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE },
+ { "bip_deauth", WLANTEST_BSS_COUNTER_BIP_DEAUTH },
+ { "bip_disassoc", WLANTEST_BSS_COUNTER_BIP_DISASSOC },
+ { "probe_response", WLANTEST_BSS_COUNTER_PROBE_RESPONSE },
+ { NULL, 0 }
+};
+
+static int cmd_get_bss_counter(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *end, *pos;
+ int rlen, i;
+ size_t len;
+
+ if (argc != 2) {
+ printf("get_bss_counter needs at two arguments: "
+ "counter name and BSSID\n");
+ return -1;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_BSS_COUNTER);
+ pos += 4;
+
+ for (i = 0; bss_counters[i].name; i++) {
+ if (os_strcasecmp(bss_counters[i].name, argv[0]) == 0)
+ break;
+ }
+ if (bss_counters[i].name == NULL) {
+ printf("Unknown BSS counter '%s'\n", argv[0]);
+ printf("Counters:");
+ for (i = 0; bss_counters[i].name; i++)
+ printf(" %s", bss_counters[i].name);
+ printf("\n");
+ return -1;
+ }
+
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_COUNTER,
+ bss_counters[i].num);
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+ if (hwaddr_aton(argv[1], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[1]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+
+ pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+ if (pos == NULL || len != 4)
+ return -1;
+ printf("%u\n", WPA_GET_BE32(pos));
+ return 0;
+}
+
+
+static char ** complete_get_bss_counter(int s, const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+ int i, count;
+
+ switch (arg) {
+ case 1:
+ /* counter list */
+ count = ARRAY_SIZE(bss_counters);
+ res = os_calloc(count, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ for (i = 0; bss_counters[i].name; i++) {
+ res[i] = os_strdup(bss_counters[i].name);
+ if (res[i] == NULL)
+ break;
+ }
+ break;
+ case 2:
+ res = get_bssid_list(s);
+ break;
+ }
+
+ return res;
+}
+
+
+static int cmd_relog(int s, int argc, char *argv[])
+{
+ return cmd_simple(s, WLANTEST_CTRL_RELOG);
+}
+
+
+struct tdls_counters {
+ const char *name;
+ enum wlantest_tdls_counter num;
+};
+
+static const struct tdls_counters tdls_counters[] = {
+ { "valid_direct_link", WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK },
+ { "invalid_direct_link", WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK },
+ { "valid_ap_path", WLANTEST_TDLS_COUNTER_VALID_AP_PATH },
+ { "invalid_ap_path", WLANTEST_TDLS_COUNTER_INVALID_AP_PATH },
+ { "setup_req", WLANTEST_TDLS_COUNTER_SETUP_REQ },
+ { "setup_resp_ok", WLANTEST_TDLS_COUNTER_SETUP_RESP_OK },
+ { "setup_resp_fail", WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL },
+ { "setup_conf_ok", WLANTEST_TDLS_COUNTER_SETUP_CONF_OK },
+ { "setup_conf_fail", WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL },
+ { "teardown", WLANTEST_TDLS_COUNTER_TEARDOWN },
+ { NULL, 0 }
+};
+
+static int cmd_get_tdls_counter(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *end, *pos;
+ int rlen, i;
+ size_t len;
+
+ if (argc != 4) {
+ printf("get_tdls_counter needs four arguments: "
+ "counter name, BSSID, STA1 address, STA2 address\n");
+ return -1;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TDLS_COUNTER);
+ pos += 4;
+
+ for (i = 0; tdls_counters[i].name; i++) {
+ if (os_strcasecmp(tdls_counters[i].name, argv[0]) == 0)
+ break;
+ }
+ if (tdls_counters[i].name == NULL) {
+ printf("Unknown TDLS counter '%s'\n", argv[0]);
+ printf("Counters:");
+ for (i = 0; tdls_counters[i].name; i++)
+ printf(" %s", tdls_counters[i].name);
+ printf("\n");
+ return -1;
+ }
+
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_TDLS_COUNTER,
+ tdls_counters[i].num);
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+ if (hwaddr_aton(argv[1], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[1]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+ if (hwaddr_aton(argv[2], pos) < 0) {
+ printf("Invalid STA1 address '%s'\n", argv[2]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA2_ADDR, ETH_ALEN);
+ if (hwaddr_aton(argv[3], pos) < 0) {
+ printf("Invalid STA2 address '%s'\n", argv[3]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+
+ pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+ if (pos == NULL || len != 4)
+ return -1;
+ printf("%u\n", WPA_GET_BE32(pos));
+ return 0;
+}
+
+
+static char ** complete_get_tdls_counter(int s, const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+ int i, count;
+ u8 addr[ETH_ALEN];
+
+ switch (arg) {
+ case 1:
+ /* counter list */
+ count = ARRAY_SIZE(tdls_counters);
+ res = os_calloc(count, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ for (i = 0; tdls_counters[i].name; i++) {
+ res[i] = os_strdup(tdls_counters[i].name);
+ if (res[i] == NULL)
+ break;
+ }
+ break;
+ case 2:
+ res = get_bssid_list(s);
+ break;
+ case 3:
+ case 4:
+ if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+ break;
+ res = get_sta_list(s, addr, 0);
+ break;
+ }
+
+ return res;
+}
+
+
+struct inject_frames {
+ const char *name;
+ enum wlantest_inject_frame frame;
+};
+
+static const struct inject_frames inject_frames[] = {
+ { "auth", WLANTEST_FRAME_AUTH },
+ { "assocreq", WLANTEST_FRAME_ASSOCREQ },
+ { "reassocreq", WLANTEST_FRAME_REASSOCREQ },
+ { "deauth", WLANTEST_FRAME_DEAUTH },
+ { "disassoc", WLANTEST_FRAME_DISASSOC },
+ { "saqueryreq", WLANTEST_FRAME_SAQUERYREQ },
+ { NULL, 0 }
+};
+
+static int cmd_inject(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *end, *pos;
+ int rlen, i;
+ enum wlantest_inject_protection prot;
+
+ /* <frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff> */
+
+ if (argc < 5) {
+ printf("inject needs five arguments: frame, protection, "
+ "sender, BSSID, STA/ff:ff:ff:ff:ff:ff\n");
+ return -1;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_INJECT);
+ pos += 4;
+
+ for (i = 0; inject_frames[i].name; i++) {
+ if (os_strcasecmp(inject_frames[i].name, argv[0]) == 0)
+ break;
+ }
+ if (inject_frames[i].name == NULL) {
+ printf("Unknown inject frame '%s'\n", argv[0]);
+ printf("Frames:");
+ for (i = 0; inject_frames[i].name; i++)
+ printf(" %s", inject_frames[i].name);
+ printf("\n");
+ return -1;
+ }
+
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_FRAME,
+ inject_frames[i].frame);
+
+ if (os_strcasecmp(argv[1], "normal") == 0)
+ prot = WLANTEST_INJECT_NORMAL;
+ else if (os_strcasecmp(argv[1], "protected") == 0)
+ prot = WLANTEST_INJECT_PROTECTED;
+ else if (os_strcasecmp(argv[1], "unprotected") == 0)
+ prot = WLANTEST_INJECT_UNPROTECTED;
+ else if (os_strcasecmp(argv[1], "incorrect") == 0)
+ prot = WLANTEST_INJECT_INCORRECT_KEY;
+ else {
+ printf("Unknown protection type '%s'\n", argv[1]);
+ printf("Protection types: normal protected unprotected "
+ "incorrect\n");
+ return -1;
+ }
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot);
+
+ if (os_strcasecmp(argv[2], "ap") == 0) {
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
+ 1);
+ } else if (os_strcasecmp(argv[2], "sta") == 0) {
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_SENDER_AP,
+ 0);
+ } else {
+ printf("Unknown sender '%s'\n", argv[2]);
+ printf("Sender types: ap sta\n");
+ return -1;
+ }
+
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+ if (hwaddr_aton(argv[3], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[3]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+ if (hwaddr_aton(argv[4], pos) < 0) {
+ printf("Invalid STA '%s'\n", argv[4]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+ printf("OK\n");
+ return 0;
+}
+
+
+static char ** complete_inject(int s, const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+ int i, count;
+ u8 addr[ETH_ALEN];
+
+ switch (arg) {
+ case 1:
+ /* frame list */
+ count = ARRAY_SIZE(inject_frames);
+ res = os_calloc(count, sizeof(char *));
+ if (res == NULL)
+ break;
+ for (i = 0; inject_frames[i].name; i++) {
+ res[i] = os_strdup(inject_frames[i].name);
+ if (res[i] == NULL)
+ break;
+ }
+ break;
+ case 2:
+ res = os_calloc(5, sizeof(char *));
+ if (res == NULL)
+ break;
+ res[0] = os_strdup("normal");
+ if (res[0] == NULL)
+ break;
+ res[1] = os_strdup("protected");
+ if (res[1] == NULL)
+ break;
+ res[2] = os_strdup("unprotected");
+ if (res[2] == NULL)
+ break;
+ res[3] = os_strdup("incorrect");
+ if (res[3] == NULL)
+ break;
+ break;
+ case 3:
+ res = os_calloc(3, sizeof(char *));
+ if (res == NULL)
+ break;
+ res[0] = os_strdup("ap");
+ if (res[0] == NULL)
+ break;
+ res[1] = os_strdup("sta");
+ if (res[1] == NULL)
+ break;
+ break;
+ case 4:
+ res = get_bssid_list(s);
+ break;
+ case 5:
+ if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+ break;
+ res = get_sta_list(s, addr, 1);
+ break;
+ }
+
+ return res;
+}
+
+
+static u8 * add_hex(u8 *pos, u8 *end, const char *str)
+{
+ const char *s;
+ int val;
+
+ s = str;
+ while (*s) {
+ while (*s == ' ' || *s == '\t' || *s == '\r' || *s == '\n' ||
+ *s == ':')
+ s++;
+ if (*s == '\0')
+ break;
+ if (*s == '#') {
+ while (*s != '\0' && *s != '\r' && *s != '\n')
+ s++;
+ continue;
+ }
+
+ val = hex2byte(s);
+ if (val < 0) {
+ printf("Invalid hex encoding '%s'\n", s);
+ return NULL;
+ }
+ if (pos == end) {
+ printf("Too long frame\n");
+ return NULL;
+ }
+ *pos++ = val;
+ s += 2;
+ }
+
+ return pos;
+}
+
+
+static int cmd_send(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[WLANTEST_CTRL_MAX_CMD_LEN], *end, *pos, *len_pos;
+ int rlen;
+ enum wlantest_inject_protection prot;
+ int arg;
+
+ /* <prot> <raw frame as hex dump> */
+
+ if (argc < 2) {
+ printf("send needs two arguments: protected/unprotected, "
+ "raw frame as hex dump\n");
+ return -1;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_SEND);
+ pos += 4;
+
+ if (os_strcasecmp(argv[0], "normal") == 0)
+ prot = WLANTEST_INJECT_NORMAL;
+ else if (os_strcasecmp(argv[0], "protected") == 0)
+ prot = WLANTEST_INJECT_PROTECTED;
+ else if (os_strcasecmp(argv[0], "unprotected") == 0)
+ prot = WLANTEST_INJECT_UNPROTECTED;
+ else if (os_strcasecmp(argv[0], "incorrect") == 0)
+ prot = WLANTEST_INJECT_INCORRECT_KEY;
+ else {
+ printf("Unknown protection type '%s'\n", argv[1]);
+ printf("Protection types: normal protected unprotected "
+ "incorrect\n");
+ return -1;
+ }
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_INJECT_PROTECTION, prot);
+
+ WPA_PUT_BE32(pos, WLANTEST_ATTR_FRAME);
+ pos += 4;
+ len_pos = pos;
+ pos += 4;
+
+ for (arg = 1; pos && arg < argc; arg++)
+ pos = add_hex(pos, end, argv[arg]);
+ if (pos == NULL)
+ return -1;
+
+ WPA_PUT_BE32(len_pos, pos - len_pos - 4);
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+ printf("OK\n");
+ return 0;
+}
+
+
+static char ** complete_send(int s, const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+
+ switch (arg) {
+ case 1:
+ res = os_calloc(5, sizeof(char *));
+ if (res == NULL)
+ break;
+ res[0] = os_strdup("normal");
+ if (res[0] == NULL)
+ break;
+ res[1] = os_strdup("protected");
+ if (res[1] == NULL)
+ break;
+ res[2] = os_strdup("unprotected");
+ if (res[2] == NULL)
+ break;
+ res[3] = os_strdup("incorrect");
+ if (res[3] == NULL)
+ break;
+ break;
+ }
+
+ return res;
+}
+
+
+static int cmd_version(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[4];
+ char *version;
+ size_t len;
+ int rlen, i;
+
+ WPA_PUT_BE32(buf, WLANTEST_CTRL_VERSION);
+ rlen = cmd_send_and_recv(s, buf, sizeof(buf), resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+
+ version = (char *) attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_VERSION,
+ &len);
+ if (version == NULL)
+ return -1;
+
+ for (i = 0; i < len; i++)
+ putchar(version[i]);
+ printf("\n");
+
+ return 0;
+}
+
+
+static int cmd_add_passphrase(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *pos, *end;
+ size_t len;
+ int rlen;
+
+ if (argc < 1) {
+ printf("add_passphrase needs one argument: passphrase\n");
+ return -1;
+ }
+
+ len = os_strlen(argv[0]);
+ if (len < 8 || len > 63) {
+ printf("Invalid passphrase '%s'\n", argv[0]);
+ return -1;
+ }
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_ADD_PASSPHRASE);
+ pos += 4;
+ pos = attr_add_str(pos, end, WLANTEST_ATTR_PASSPHRASE,
+ argv[0]);
+ if (argc > 1) {
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+ if (hwaddr_aton(argv[1], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[3]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+ }
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+ return 0;
+}
+
+
+static int cmd_add_wepkey(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *pos, *end;
+ int rlen;
+
+ if (argc < 1) {
+ printf("add_wepkey needs one argument: WEP key\n");
+ return -1;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_ADD_PASSPHRASE);
+ pos += 4;
+ pos = attr_add_str(pos, end, WLANTEST_ATTR_WEPKEY, argv[0]);
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+ return 0;
+}
+
+
+struct sta_infos {
+ const char *name;
+ enum wlantest_sta_info num;
+};
+
+static const struct sta_infos sta_infos[] = {
+ { "proto", WLANTEST_STA_INFO_PROTO },
+ { "pairwise", WLANTEST_STA_INFO_PAIRWISE },
+ { "key_mgmt", WLANTEST_STA_INFO_KEY_MGMT },
+ { "rsn_capab", WLANTEST_STA_INFO_RSN_CAPAB },
+ { "state", WLANTEST_STA_INFO_STATE },
+ { "gtk", WLANTEST_STA_INFO_GTK },
+ { NULL, 0 }
+};
+
+static int cmd_info_sta(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *end, *pos;
+ int rlen, i;
+ size_t len;
+ char info[100];
+
+ if (argc != 3) {
+ printf("sta_info needs at three arguments: "
+ "counter name, BSSID, and STA address\n");
+ return -1;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_INFO_STA);
+ pos += 4;
+
+ for (i = 0; sta_infos[i].name; i++) {
+ if (os_strcasecmp(sta_infos[i].name, argv[0]) == 0)
+ break;
+ }
+ if (sta_infos[i].name == NULL) {
+ printf("Unknown STA info '%s'\n", argv[0]);
+ printf("Info fields:");
+ for (i = 0; sta_infos[i].name; i++)
+ printf(" %s", sta_infos[i].name);
+ printf("\n");
+ return -1;
+ }
+
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_STA_INFO,
+ sta_infos[i].num);
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+ if (hwaddr_aton(argv[1], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[1]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+ if (hwaddr_aton(argv[2], pos) < 0) {
+ printf("Invalid STA address '%s'\n", argv[2]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+
+ pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_INFO, &len);
+ if (pos == NULL)
+ return -1;
+ if (len >= sizeof(info))
+ len = sizeof(info) - 1;
+ os_memcpy(info, pos, len);
+ info[len] = '\0';
+ printf("%s\n", info);
+ return 0;
+}
+
+
+static char ** complete_info_sta(int s, const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+ int i, count;
+ u8 addr[ETH_ALEN];
+
+ switch (arg) {
+ case 1:
+ /* counter list */
+ count = ARRAY_SIZE(sta_infos);
+ res = os_calloc(count, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ for (i = 0; sta_infos[i].name; i++) {
+ res[i] = os_strdup(sta_infos[i].name);
+ if (res[i] == NULL)
+ break;
+ }
+ break;
+ case 2:
+ res = get_bssid_list(s);
+ break;
+ case 3:
+ if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+ break;
+ res = get_sta_list(s, addr, 0);
+ break;
+ }
+
+ return res;
+}
+
+
+struct bss_infos {
+ const char *name;
+ enum wlantest_bss_info num;
+};
+
+static const struct bss_infos bss_infos[] = {
+ { "proto", WLANTEST_BSS_INFO_PROTO },
+ { "pairwise", WLANTEST_BSS_INFO_PAIRWISE },
+ { "group", WLANTEST_BSS_INFO_GROUP },
+ { "group_mgmt", WLANTEST_BSS_INFO_GROUP_MGMT },
+ { "key_mgmt", WLANTEST_BSS_INFO_KEY_MGMT },
+ { "rsn_capab", WLANTEST_BSS_INFO_RSN_CAPAB },
+ { NULL, 0 }
+};
+
+static int cmd_info_bss(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *end, *pos;
+ int rlen, i;
+ size_t len;
+ char info[100];
+
+ if (argc != 2) {
+ printf("bss_info needs at two arguments: "
+ "field name and BSSID\n");
+ return -1;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_INFO_BSS);
+ pos += 4;
+
+ for (i = 0; bss_infos[i].name; i++) {
+ if (os_strcasecmp(bss_infos[i].name, argv[0]) == 0)
+ break;
+ }
+ if (bss_infos[i].name == NULL) {
+ printf("Unknown BSS info '%s'\n", argv[0]);
+ printf("Info fields:");
+ for (i = 0; bss_infos[i].name; i++)
+ printf(" %s", bss_infos[i].name);
+ printf("\n");
+ return -1;
+ }
+
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_BSS_INFO,
+ bss_infos[i].num);
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+ if (hwaddr_aton(argv[1], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[1]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+
+ pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_INFO, &len);
+ if (pos == NULL)
+ return -1;
+ if (len >= sizeof(info))
+ len = sizeof(info) - 1;
+ os_memcpy(info, pos, len);
+ info[len] = '\0';
+ printf("%s\n", info);
+ return 0;
+}
+
+
+static char ** complete_info_bss(int s, const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+ int i, count;
+
+ switch (arg) {
+ case 1:
+ /* counter list */
+ count = ARRAY_SIZE(bss_infos);
+ res = os_calloc(count, sizeof(char *));
+ if (res == NULL)
+ return NULL;
+ for (i = 0; bss_infos[i].name; i++) {
+ res[i] = os_strdup(bss_infos[i].name);
+ if (res[i] == NULL)
+ break;
+ }
+ break;
+ case 2:
+ res = get_bssid_list(s);
+ break;
+ }
+
+ return res;
+}
+
+
+static int cmd_get_tx_tid(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *end, *pos;
+ int rlen;
+ size_t len;
+
+ if (argc != 3) {
+ printf("get_tx_tid needs three arguments: "
+ "BSSID, STA address, and TID\n");
+ return -1;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TX_TID);
+ pos += 4;
+
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+ if (hwaddr_aton(argv[0], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[0]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+ if (hwaddr_aton(argv[1], pos) < 0) {
+ printf("Invalid STA address '%s'\n", argv[1]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_TID, atoi(argv[2]));
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+
+ pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+ if (pos == NULL || len != 4)
+ return -1;
+ printf("%u\n", WPA_GET_BE32(pos));
+ return 0;
+}
+
+
+static int cmd_get_rx_tid(int s, int argc, char *argv[])
+{
+ u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+ u8 buf[100], *end, *pos;
+ int rlen;
+ size_t len;
+
+ if (argc != 3) {
+ printf("get_tx_tid needs three arguments: "
+ "BSSID, STA address, and TID\n");
+ return -1;
+ }
+
+ pos = buf;
+ end = buf + sizeof(buf);
+ WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_RX_TID);
+ pos += 4;
+
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+ if (hwaddr_aton(argv[0], pos) < 0) {
+ printf("Invalid BSSID '%s'\n", argv[0]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+ if (hwaddr_aton(argv[1], pos) < 0) {
+ printf("Invalid STA address '%s'\n", argv[1]);
+ return -1;
+ }
+ pos += ETH_ALEN;
+
+ pos = attr_add_be32(pos, end, WLANTEST_ATTR_TID, atoi(argv[2]));
+
+ rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+ if (rlen < 0)
+ return -1;
+
+ pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+ if (pos == NULL || len != 4)
+ return -1;
+ printf("%u\n", WPA_GET_BE32(pos));
+ return 0;
+}
+
+
+static char ** complete_get_tid(int s, const char *str, int pos)
+{
+ int arg = get_cmd_arg_num(str, pos);
+ char **res = NULL;
+ u8 addr[ETH_ALEN];
+
+ switch (arg) {
+ case 1:
+ res = get_bssid_list(s);
+ break;
+ case 2:
+ if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+ break;
+ res = get_sta_list(s, addr, 0);
+ break;
+ }
+
+ return res;
+}
+
+
+struct wlantest_cli_cmd {
+ const char *cmd;
+ int (*handler)(int s, int argc, char *argv[]);
+ const char *usage;
+ char ** (*complete)(int s, const char *str, int pos);
+};
+
+static const struct wlantest_cli_cmd wlantest_cli_commands[] = {
+ { "ping", cmd_ping, "= test connection to wlantest", NULL },
+ { "terminate", cmd_terminate, "= terminate wlantest", NULL },
+ { "list_bss", cmd_list_bss, "= get BSS list", NULL },
+ { "list_sta", cmd_list_sta, "<BSSID> = get STA list",
+ complete_list_sta },
+ { "flush", cmd_flush, "= drop all collected BSS data", NULL },
+ { "clear_sta_counters", cmd_clear_sta_counters,
+ "<BSSID> <STA> = clear STA counters", complete_clear_sta_counters },
+ { "clear_bss_counters", cmd_clear_bss_counters,
+ "<BSSID> = clear BSS counters", complete_clear_bss_counters },
+ { "get_sta_counter", cmd_get_sta_counter,
+ "<counter> <BSSID> <STA> = get STA counter value",
+ complete_get_sta_counter },
+ { "get_bss_counter", cmd_get_bss_counter,
+ "<counter> <BSSID> = get BSS counter value",
+ complete_get_bss_counter },
+ { "inject", cmd_inject,
+ "<frame> <prot> <sender> <BSSID> <STA/ff:ff:ff:ff:ff:ff>",
+ complete_inject },
+ { "send", cmd_send,
+ "<prot> <raw frame as hex dump>",
+ complete_send },
+ { "version", cmd_version, "= get wlantest version", NULL },
+ { "add_passphrase", cmd_add_passphrase,
+ "<passphrase> = add a known passphrase", NULL },
+ { "add_wepkey", cmd_add_wepkey,
+ "<WEP key> = add a known WEP key", NULL },
+ { "info_sta", cmd_info_sta,
+ "<field> <BSSID> <STA> = get STA information",
+ complete_info_sta },
+ { "info_bss", cmd_info_bss,
+ "<field> <BSSID> = get BSS information",
+ complete_info_bss },
+ { "clear_tdls_counters", cmd_clear_tdls_counters,
+ "<BSSID> <STA1> <STA2> = clear TDLS counters",
+ complete_clear_tdls_counters },
+ { "get_tdls_counter", cmd_get_tdls_counter,
+ "<counter> <BSSID> <STA1> <STA2> = get TDLS counter value",
+ complete_get_tdls_counter },
+ { "get_bss_counter", cmd_get_bss_counter,
+ "<counter> <BSSID> = get BSS counter value",
+ complete_get_bss_counter },
+ { "relog", cmd_relog, "= re-open log-file (allow rolling logs)", NULL },
+ { "get_tx_tid", cmd_get_tx_tid,
+ "<BSSID> <STA> <TID> = get STA TX TID counter value",
+ complete_get_tid },
+ { "get_rx_tid", cmd_get_rx_tid,
+ "<BSSID> <STA> <TID> = get STA RX TID counter value",
+ complete_get_tid },
+ { NULL, NULL, NULL, NULL }
+};
+
+
+static int ctrl_command(int s, int argc, char *argv[])
+{
+ const struct wlantest_cli_cmd *cmd, *match = NULL;
+ int count = 0;
+ int ret = 0;
+
+ for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
+ if (os_strncasecmp(cmd->cmd, argv[0], os_strlen(argv[0])) == 0)
+ {
+ match = cmd;
+ if (os_strcasecmp(cmd->cmd, argv[0]) == 0) {
+ /* exact match */
+ count = 1;
+ break;
+ }
+ count++;
+ }
+ }
+
+ if (count > 1) {
+ printf("Ambiguous command '%s'; possible commands:", argv[0]);
+ for (cmd = wlantest_cli_commands; cmd->cmd; cmd++) {
+ if (os_strncasecmp(cmd->cmd, argv[0],
+ os_strlen(argv[0])) == 0) {
+ printf(" %s", cmd->cmd);
+ }
+ }
+ printf("\n");
+ ret = 1;
+ } else if (count == 0) {
+ printf("Unknown command '%s'\n", argv[0]);
+ ret = 1;
+ } else {
+ ret = match->handler(s, argc - 1, &argv[1]);
+ }
+
+ return ret;
+}
+
+
+struct wlantest_cli {
+ int s;
+};
+
+
+#define max_args 10
+
+static int tokenize_cmd(char *cmd, char *argv[])
+{
+ char *pos;
+ int argc = 0;
+
+ pos = cmd;
+ for (;;) {
+ while (*pos == ' ')
+ pos++;
+ if (*pos == '\0')
+ break;
+ argv[argc] = pos;
+ argc++;
+ if (argc == max_args)
+ break;
+ if (*pos == '"') {
+ char *pos2 = os_strrchr(pos, '"');
+ if (pos2)
+ pos = pos2 + 1;
+ }
+ while (*pos != '\0' && *pos != ' ')
+ pos++;
+ if (*pos == ' ')
+ *pos++ = '\0';
+ }
+
+ return argc;
+}
+
+
+static void wlantest_cli_edit_cmd_cb(void *ctx, char *cmd)
+{
+ struct wlantest_cli *cli = ctx;
+ char *argv[max_args];
+ int argc;
+ argc = tokenize_cmd(cmd, argv);
+ if (argc) {
+ int ret = ctrl_command(cli->s, argc, argv);
+ if (ret < 0)
+ printf("FAIL\n");
+ }
+}
+
+
+static void wlantest_cli_eloop_terminate(int sig, void *signal_ctx)
+{
+ eloop_terminate();
+}
+
+
+static void wlantest_cli_edit_eof_cb(void *ctx)
+{
+ eloop_terminate();
+}
+
+
+static char ** wlantest_cli_cmd_list(void)
+{
+ char **res;
+ int i;
+
+ res = os_calloc(ARRAY_SIZE(wlantest_cli_commands), sizeof(char *));
+ if (res == NULL)
+ return NULL;
+
+ for (i = 0; wlantest_cli_commands[i].cmd; i++) {
+ res[i] = os_strdup(wlantest_cli_commands[i].cmd);
+ if (res[i] == NULL)
+ break;
+ }
+
+ return res;
+}
+
+
+static char ** wlantest_cli_cmd_completion(struct wlantest_cli *cli,
+ const char *cmd, const char *str,
+ int pos)
+{
+ int i;
+
+ for (i = 0; wlantest_cli_commands[i].cmd; i++) {
+ const struct wlantest_cli_cmd *c = &wlantest_cli_commands[i];
+ if (os_strcasecmp(c->cmd, cmd) == 0) {
+ edit_clear_line();
+ printf("\r%s\n", c->usage);
+ edit_redraw();
+ if (c->complete)
+ return c->complete(cli->s, str, pos);
+ break;
+ }
+ }
+
+ return NULL;
+}
+
+
+static char ** wlantest_cli_edit_completion_cb(void *ctx, const char *str,
+ int pos)
+{
+ struct wlantest_cli *cli = ctx;
+ char **res;
+ const char *end;
+ char *cmd;
+
+ end = os_strchr(str, ' ');
+ if (end == NULL || str + pos < end)
+ return wlantest_cli_cmd_list();
+
+ cmd = os_malloc(pos + 1);
+ if (cmd == NULL)
+ return NULL;
+ os_memcpy(cmd, str, pos);
+ cmd[end - str] = '\0';
+ res = wlantest_cli_cmd_completion(cli, cmd, str, pos);
+ os_free(cmd);
+ return res;
+}
+
+
+static void wlantest_cli_interactive(int s)
+{
+ struct wlantest_cli cli;
+ char *home, *hfile = NULL;
+
+ if (eloop_init())
+ return;
+
+ home = getenv("HOME");
+ if (home) {
+ const char *fname = ".wlantest_cli_history";
+ int hfile_len = os_strlen(home) + 1 + os_strlen(fname) + 1;
+ hfile = os_malloc(hfile_len);
+ if (hfile)
+ os_snprintf(hfile, hfile_len, "%s/%s", home, fname);
+ }
+
+ cli.s = s;
+ eloop_register_signal_terminate(wlantest_cli_eloop_terminate, &cli);
+ edit_init(wlantest_cli_edit_cmd_cb, wlantest_cli_edit_eof_cb,
+ wlantest_cli_edit_completion_cb, &cli, hfile, NULL);
+
+ eloop_run();
+
+ edit_deinit(hfile, NULL);
+ os_free(hfile);
+ eloop_destroy();
+}
+
+
+int main(int argc, char *argv[])
+{
+ int s;
+ struct sockaddr_un addr;
+ int ret = 0;
+
+ if (os_program_init())
+ return -1;
+
+ s = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+ if (s < 0) {
+ perror("socket");
+ return -1;
+ }
+
+ os_memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ os_strlcpy(addr.sun_path + 1, WLANTEST_SOCK_NAME,
+ sizeof(addr.sun_path) - 1);
+ if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("connect");
+ close(s);
+ return -1;
+ }
+
+ if (argc > 1) {
+ ret = ctrl_command(s, argc - 1, &argv[1]);
+ if (ret < 0)
+ printf("FAIL\n");
+ } else {
+ wlantest_cli_interactive(s);
+ }
+
+ close(s);
+
+ os_program_deinit();
+
+ return ret;
+}
diff --git a/contrib/wpa/wlantest/wlantest_ctrl.h b/contrib/wpa/wlantest/wlantest_ctrl.h
new file mode 100644
index 000000000000..1af6838d07e6
--- /dev/null
+++ b/contrib/wpa/wlantest/wlantest_ctrl.h
@@ -0,0 +1,171 @@
+/*
+ * wlantest control interface
+ * Copyright (c) 2010-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WLANTEST_CTRL_H
+#define WLANTEST_CTRL_H
+
+#define WLANTEST_SOCK_NAME "w1.fi.wlantest"
+#define WLANTEST_CTRL_MAX_CMD_LEN 1000
+#define WLANTEST_CTRL_MAX_RESP_LEN 1000
+
+enum wlantest_ctrl_cmd {
+ WLANTEST_CTRL_SUCCESS,
+ WLANTEST_CTRL_FAILURE,
+ WLANTEST_CTRL_INVALID_CMD,
+ WLANTEST_CTRL_UNKNOWN_CMD,
+ WLANTEST_CTRL_PING,
+ WLANTEST_CTRL_TERMINATE,
+ WLANTEST_CTRL_LIST_BSS,
+ WLANTEST_CTRL_LIST_STA,
+ WLANTEST_CTRL_FLUSH,
+ WLANTEST_CTRL_CLEAR_STA_COUNTERS,
+ WLANTEST_CTRL_CLEAR_BSS_COUNTERS,
+ WLANTEST_CTRL_GET_STA_COUNTER,
+ WLANTEST_CTRL_GET_BSS_COUNTER,
+ WLANTEST_CTRL_INJECT,
+ WLANTEST_CTRL_VERSION,
+ WLANTEST_CTRL_ADD_PASSPHRASE,
+ WLANTEST_CTRL_INFO_STA,
+ WLANTEST_CTRL_INFO_BSS,
+ WLANTEST_CTRL_SEND,
+ WLANTEST_CTRL_CLEAR_TDLS_COUNTERS,
+ WLANTEST_CTRL_GET_TDLS_COUNTER,
+ WLANTEST_CTRL_RELOG,
+ WLANTEST_CTRL_GET_TX_TID,
+ WLANTEST_CTRL_GET_RX_TID,
+};
+
+enum wlantest_ctrl_attr {
+ WLANTEST_ATTR_BSSID,
+ WLANTEST_ATTR_STA_ADDR,
+ WLANTEST_ATTR_STA_COUNTER,
+ WLANTEST_ATTR_BSS_COUNTER,
+ WLANTEST_ATTR_COUNTER,
+ WLANTEST_ATTR_INJECT_FRAME,
+ WLANTEST_ATTR_INJECT_SENDER_AP,
+ WLANTEST_ATTR_INJECT_PROTECTION,
+ WLANTEST_ATTR_VERSION,
+ WLANTEST_ATTR_PASSPHRASE,
+ WLANTEST_ATTR_STA_INFO,
+ WLANTEST_ATTR_BSS_INFO,
+ WLANTEST_ATTR_INFO,
+ WLANTEST_ATTR_FRAME,
+ WLANTEST_ATTR_TDLS_COUNTER,
+ WLANTEST_ATTR_STA2_ADDR,
+ WLANTEST_ATTR_WEPKEY,
+ WLANTEST_ATTR_TID,
+};
+
+enum wlantest_bss_counter {
+ WLANTEST_BSS_COUNTER_VALID_BIP_MMIE,
+ WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE,
+ WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE,
+ WLANTEST_BSS_COUNTER_BIP_DEAUTH,
+ WLANTEST_BSS_COUNTER_BIP_DISASSOC,
+ WLANTEST_BSS_COUNTER_PROBE_RESPONSE,
+ NUM_WLANTEST_BSS_COUNTER
+};
+
+enum wlantest_sta_counter {
+ WLANTEST_STA_COUNTER_AUTH_TX,
+ WLANTEST_STA_COUNTER_AUTH_RX,
+ WLANTEST_STA_COUNTER_ASSOCREQ_TX,
+ WLANTEST_STA_COUNTER_REASSOCREQ_TX,
+ WLANTEST_STA_COUNTER_PTK_LEARNED,
+ WLANTEST_STA_COUNTER_VALID_DEAUTH_TX,
+ WLANTEST_STA_COUNTER_VALID_DEAUTH_RX,
+ WLANTEST_STA_COUNTER_INVALID_DEAUTH_TX,
+ WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX,
+ WLANTEST_STA_COUNTER_VALID_DISASSOC_TX,
+ WLANTEST_STA_COUNTER_VALID_DISASSOC_RX,
+ WLANTEST_STA_COUNTER_INVALID_DISASSOC_TX,
+ WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX,
+ WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_TX,
+ WLANTEST_STA_COUNTER_VALID_SAQUERYREQ_RX,
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_TX,
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYREQ_RX,
+ WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_TX,
+ WLANTEST_STA_COUNTER_VALID_SAQUERYRESP_RX,
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_TX,
+ WLANTEST_STA_COUNTER_INVALID_SAQUERYRESP_RX,
+ WLANTEST_STA_COUNTER_PING_OK,
+ WLANTEST_STA_COUNTER_ASSOCRESP_COMEBACK,
+ WLANTEST_STA_COUNTER_REASSOCRESP_COMEBACK,
+ WLANTEST_STA_COUNTER_PING_OK_FIRST_ASSOC,
+ WLANTEST_STA_COUNTER_VALID_DEAUTH_RX_ACK,
+ WLANTEST_STA_COUNTER_VALID_DISASSOC_RX_ACK,
+ WLANTEST_STA_COUNTER_INVALID_DEAUTH_RX_ACK,
+ WLANTEST_STA_COUNTER_INVALID_DISASSOC_RX_ACK,
+ WLANTEST_STA_COUNTER_DEAUTH_RX_ASLEEP,
+ WLANTEST_STA_COUNTER_DEAUTH_RX_AWAKE,
+ WLANTEST_STA_COUNTER_DISASSOC_RX_ASLEEP,
+ WLANTEST_STA_COUNTER_DISASSOC_RX_AWAKE,
+ WLANTEST_STA_COUNTER_PROT_DATA_TX,
+ WLANTEST_STA_COUNTER_DEAUTH_RX_RC6,
+ WLANTEST_STA_COUNTER_DEAUTH_RX_RC7,
+ WLANTEST_STA_COUNTER_DISASSOC_RX_RC6,
+ WLANTEST_STA_COUNTER_DISASSOC_RX_RC7,
+ NUM_WLANTEST_STA_COUNTER
+};
+
+enum wlantest_tdls_counter {
+ WLANTEST_TDLS_COUNTER_VALID_DIRECT_LINK,
+ WLANTEST_TDLS_COUNTER_INVALID_DIRECT_LINK,
+ WLANTEST_TDLS_COUNTER_VALID_AP_PATH,
+ WLANTEST_TDLS_COUNTER_INVALID_AP_PATH,
+ WLANTEST_TDLS_COUNTER_SETUP_REQ,
+ WLANTEST_TDLS_COUNTER_SETUP_RESP_OK,
+ WLANTEST_TDLS_COUNTER_SETUP_RESP_FAIL,
+ WLANTEST_TDLS_COUNTER_SETUP_CONF_OK,
+ WLANTEST_TDLS_COUNTER_SETUP_CONF_FAIL,
+ WLANTEST_TDLS_COUNTER_TEARDOWN,
+ NUM_WLANTEST_TDLS_COUNTER
+};
+
+enum wlantest_inject_frame {
+ WLANTEST_FRAME_AUTH,
+ WLANTEST_FRAME_ASSOCREQ,
+ WLANTEST_FRAME_REASSOCREQ,
+ WLANTEST_FRAME_DEAUTH,
+ WLANTEST_FRAME_DISASSOC,
+ WLANTEST_FRAME_SAQUERYREQ,
+};
+
+/**
+ * enum wlantest_inject_protection - WLANTEST_CTRL_INJECT protection
+ * @WLANTEST_INJECT_NORMAL: Use normal rules (protect if key is set)
+ * @WLANTEST_INJECT_PROTECTED: Force protection (fail if not possible)
+ * @WLANTEST_INJECT_UNPROTECTED: Force unprotected
+ * @WLANTEST_INJECT_INCORRECT_KEY: Force protection with incorrect key
+ */
+enum wlantest_inject_protection {
+ WLANTEST_INJECT_NORMAL,
+ WLANTEST_INJECT_PROTECTED,
+ WLANTEST_INJECT_UNPROTECTED,
+ WLANTEST_INJECT_INCORRECT_KEY,
+};
+
+enum wlantest_sta_info {
+ WLANTEST_STA_INFO_PROTO,
+ WLANTEST_STA_INFO_PAIRWISE,
+ WLANTEST_STA_INFO_KEY_MGMT,
+ WLANTEST_STA_INFO_RSN_CAPAB,
+ WLANTEST_STA_INFO_STATE,
+ WLANTEST_STA_INFO_GTK,
+};
+
+enum wlantest_bss_info {
+ WLANTEST_BSS_INFO_PROTO,
+ WLANTEST_BSS_INFO_PAIRWISE,
+ WLANTEST_BSS_INFO_GROUP,
+ WLANTEST_BSS_INFO_GROUP_MGMT,
+ WLANTEST_BSS_INFO_KEY_MGMT,
+ WLANTEST_BSS_INFO_RSN_CAPAB,
+};
+
+#endif /* WLANTEST_CTRL_H */
diff --git a/contrib/wpa/wlantest/writepcap.c b/contrib/wpa/wlantest/writepcap.c
new file mode 100644
index 000000000000..fee2c40dc478
--- /dev/null
+++ b/contrib/wpa/wlantest/writepcap.c
@@ -0,0 +1,373 @@
+/*
+ * PCAP capture file writer
+ * Copyright (c) 2010-2019, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <pcap.h>
+#include <pcap-bpf.h>
+
+#include "utils/common.h"
+#include "wlantest.h"
+#include "common/qca-vendor.h"
+
+
+int write_pcap_init(struct wlantest *wt, const char *fname)
+{
+ int linktype = wt->ethernet ? DLT_EN10MB : DLT_IEEE802_11_RADIO;
+
+ wt->write_pcap = pcap_open_dead(linktype, 4000);
+ if (wt->write_pcap == NULL)
+ return -1;
+ wt->write_pcap_dumper = pcap_dump_open(wt->write_pcap, fname);
+ if (wt->write_pcap_dumper == NULL) {
+ pcap_close(wt->write_pcap);
+ wt->write_pcap = NULL;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "Writing PCAP dump to '%s'", fname);
+
+ return 0;
+}
+
+
+void write_pcap_deinit(struct wlantest *wt)
+{
+ if (wt->write_pcap_dumper) {
+ pcap_dump_close(wt->write_pcap_dumper);
+ wt->write_pcap_dumper = NULL;
+ }
+ if (wt->write_pcap) {
+ pcap_close(wt->write_pcap);
+ wt->write_pcap = NULL;
+ }
+}
+
+
+void write_pcap_captured(struct wlantest *wt, const u8 *buf, size_t len)
+{
+ struct pcap_pkthdr h;
+
+ if (!wt->write_pcap_dumper)
+ return;
+
+ os_memset(&h, 0, sizeof(h));
+ gettimeofday(&wt->write_pcap_time, NULL);
+ h.ts = wt->write_pcap_time;
+ h.caplen = len;
+ h.len = len;
+ pcap_dump(wt->write_pcap_dumper, &h, buf);
+ if (wt->pcap_no_buffer)
+ pcap_dump_flush(wt->write_pcap_dumper);
+}
+
+
+void write_pcap_decrypted(struct wlantest *wt, const u8 *buf1, size_t len1,
+ const u8 *buf2, size_t len2)
+{
+ struct pcap_pkthdr h;
+ u8 rtap[] = {
+ 0x00 /* rev */,
+ 0x00 /* pad */,
+ 0x0e, 0x00, /* header len */
+ 0x00, 0x00, 0x00, 0x40, /* present flags */
+ 0x00, 0x13, 0x74, QCA_RADIOTAP_VID_WLANTEST,
+ 0x00, 0x00
+ };
+ u8 *buf;
+ size_t len;
+
+ if (!wt->write_pcap_dumper && !wt->pcapng)
+ return;
+
+ os_free(wt->decrypted);
+ len = sizeof(rtap) + len1 + len2;
+ wt->decrypted = buf = os_malloc(len);
+ if (buf == NULL)
+ return;
+ wt->decrypted_len = len;
+ os_memcpy(buf, rtap, sizeof(rtap));
+ if (buf1) {
+ os_memcpy(buf + sizeof(rtap), buf1, len1);
+ buf[sizeof(rtap) + 1] &= ~0x40; /* Clear Protected flag */
+ }
+ if (buf2)
+ os_memcpy(buf + sizeof(rtap) + len1, buf2, len2);
+
+ if (!wt->write_pcap_dumper)
+ return;
+
+ os_memset(&h, 0, sizeof(h));
+ h.ts = wt->write_pcap_time;
+ h.caplen = len;
+ h.len = len;
+ pcap_dump(wt->write_pcap_dumper, &h, buf);
+ if (wt->pcap_no_buffer)
+ pcap_dump_flush(wt->write_pcap_dumper);
+}
+
+
+struct pcapng_section_header {
+ u32 block_type; /* 0x0a0d0d0a */
+ u32 block_total_len;
+ u32 byte_order_magic;
+ u16 major_version;
+ u16 minor_version;
+ u64 section_len;
+ u32 block_total_len2;
+} STRUCT_PACKED;
+
+struct pcapng_interface_description {
+ u32 block_type; /* 0x00000001 */
+ u32 block_total_len;
+ u16 link_type;
+ u16 reserved;
+ u32 snap_len;
+ u32 block_total_len2;
+} STRUCT_PACKED;
+
+struct pcapng_enhanced_packet {
+ u32 block_type; /* 0x00000006 */
+ u32 block_total_len;
+ u32 interface_id;
+ u32 timestamp_high;
+ u32 timestamp_low;
+ u32 captured_len;
+ u32 packet_len;
+ /* Packet data - aligned to 32 bits */
+ /* Options (variable) */
+ /* Block Total Length copy */
+} STRUCT_PACKED;
+
+#define PCAPNG_BYTE_ORDER_MAGIC 0x1a2b3c4d
+#define PCAPNG_BLOCK_IFACE_DESC 0x00000001
+#define PCAPNG_BLOCK_PACKET 0x00000002
+#define PCAPNG_BLOCK_SIMPLE_PACKET 0x00000003
+#define PCAPNG_BLOCK_NAME_RESOLUTION 0x00000004
+#define PCAPNG_BLOCK_INTERFACE_STATISTICS 0x00000005
+#define PCAPNG_BLOCK_ENHANCED_PACKET 0x00000006
+#define PCAPNG_BLOCK_SECTION_HEADER 0x0a0d0d0a
+
+#define LINKTYPE_IEEE802_11 105
+#define LINKTYPE_IEEE802_11_RADIO 127
+
+#define PAD32(a) ((4 - ((a) & 3)) & 3)
+#define ALIGN32(a) ((a) + PAD32((a)))
+
+
+int write_pcapng_init(struct wlantest *wt, const char *fname)
+{
+ struct pcapng_section_header hdr;
+ struct pcapng_interface_description desc;
+
+ wt->pcapng = fopen(fname, "wb");
+ if (wt->pcapng == NULL)
+ return -1;
+
+ wpa_printf(MSG_DEBUG, "Writing PCAPNG dump to '%s'", fname);
+
+ os_memset(&hdr, 0, sizeof(hdr));
+ hdr.block_type = PCAPNG_BLOCK_SECTION_HEADER;
+ hdr.block_total_len = sizeof(hdr);
+ hdr.byte_order_magic = PCAPNG_BYTE_ORDER_MAGIC;
+ hdr.major_version = 1;
+ hdr.minor_version = 0;
+ hdr.section_len = -1;
+ hdr.block_total_len2 = hdr.block_total_len;
+ fwrite(&hdr, sizeof(hdr), 1, wt->pcapng);
+
+ os_memset(&desc, 0, sizeof(desc));
+ desc.block_type = PCAPNG_BLOCK_IFACE_DESC;
+ desc.block_total_len = sizeof(desc);
+ desc.block_total_len2 = desc.block_total_len;
+ desc.link_type = wt->ethernet ? DLT_EN10MB : LINKTYPE_IEEE802_11_RADIO;
+ desc.snap_len = 65535;
+ fwrite(&desc, sizeof(desc), 1, wt->pcapng);
+ if (wt->pcap_no_buffer)
+ fflush(wt->pcapng);
+
+ return 0;
+}
+
+
+void write_pcapng_deinit(struct wlantest *wt)
+{
+ if (wt->pcapng) {
+ fclose(wt->pcapng);
+ wt->pcapng = NULL;
+ }
+}
+
+
+static u8 * pcapng_add_comments(struct wlantest *wt, u8 *pos)
+{
+ size_t i;
+ u16 *len;
+
+ if (!wt->num_notes)
+ return pos;
+
+ *((u16 *) pos) = 1 /* opt_comment */;
+ pos += 2;
+ len = (u16 *) pos /* length to be filled in */;
+ pos += 2;
+
+ for (i = 0; i < wt->num_notes; i++) {
+ size_t nlen = os_strlen(wt->notes[i]);
+ if (i > 0)
+ *pos++ = '\n';
+ os_memcpy(pos, wt->notes[i], nlen);
+ pos += nlen;
+ }
+ *len = pos - (u8 *) len - 2;
+ pos += PAD32(*len);
+
+ *((u16 *) pos) = 0 /* opt_endofopt */;
+ pos += 2;
+ *((u16 *) pos) = 0;
+ pos += 2;
+
+ return pos;
+}
+
+
+static void write_pcapng_decrypted(struct wlantest *wt)
+{
+ size_t len;
+ struct pcapng_enhanced_packet *pkt;
+ u8 *pos;
+ u32 *block_len;
+
+ if (!wt->pcapng || wt->decrypted == NULL)
+ return;
+
+ add_note(wt, MSG_EXCESSIVE, "decrypted version of the previous frame");
+
+ len = sizeof(*pkt) + wt->decrypted_len + 100 + notes_len(wt, 32);
+ pkt = os_zalloc(len);
+ if (pkt == NULL)
+ return;
+
+ pkt->block_type = PCAPNG_BLOCK_ENHANCED_PACKET;
+ pkt->interface_id = 0;
+ pkt->timestamp_high = wt->write_pcapng_time_high;
+ pkt->timestamp_low = wt->write_pcapng_time_low;
+ pkt->captured_len = wt->decrypted_len;
+ pkt->packet_len = wt->decrypted_len;
+
+ pos = (u8 *) (pkt + 1);
+
+ os_memcpy(pos, wt->decrypted, wt->decrypted_len);
+ pos += ALIGN32(wt->decrypted_len);
+
+ pos = pcapng_add_comments(wt, pos);
+
+ block_len = (u32 *) pos;
+ pos += 4;
+ *block_len = pkt->block_total_len = pos - (u8 *) pkt;
+
+ fwrite(pkt, pos - (u8 *) pkt, 1, wt->pcapng);
+ if (wt->pcap_no_buffer)
+ fflush(wt->pcapng);
+
+ os_free(pkt);
+}
+
+
+void write_pcapng_write_read(struct wlantest *wt, int dlt,
+ struct pcap_pkthdr *hdr, const u8 *data)
+{
+ struct pcapng_enhanced_packet *pkt;
+ u8 *pos;
+ u32 *block_len;
+ u64 timestamp;
+ size_t len, datalen = hdr->caplen;
+ u8 rtap[] = {
+ 0x00 /* rev */,
+ 0x00 /* pad */,
+ 0x0a, 0x00, /* header len */
+ 0x02, 0x00, 0x00, 0x00, /* present flags */
+ 0x00, /* flags */
+ 0x00 /* pad */
+ };
+
+ if (wt->assume_fcs)
+ rtap[8] |= 0x10;
+
+ if (!wt->pcapng)
+ return;
+
+ len = sizeof(*pkt) + hdr->len + 100 + notes_len(wt, 32) + sizeof(rtap);
+ pkt = os_zalloc(len);
+ if (pkt == NULL)
+ return;
+
+ pkt->block_type = PCAPNG_BLOCK_ENHANCED_PACKET;
+ pkt->interface_id = 0;
+ timestamp = 1000000 * hdr->ts.tv_sec + hdr->ts.tv_usec;
+ pkt->timestamp_high = timestamp >> 32;
+ pkt->timestamp_low = timestamp & 0xffffffff;
+ wt->write_pcapng_time_high = pkt->timestamp_high;
+ wt->write_pcapng_time_low = pkt->timestamp_low;
+ pkt->captured_len = hdr->caplen;
+ pkt->packet_len = hdr->len;
+
+ pos = (u8 *) (pkt + 1);
+
+ switch (dlt) {
+ case DLT_EN10MB:
+ case DLT_IEEE802_11_RADIO:
+ break;
+ case DLT_PRISM_HEADER:
+ /* remove prism header (could be kept ... lazy) */
+ pkt->captured_len -= WPA_GET_LE32(data + 4);
+ pkt->packet_len -= WPA_GET_LE32(data + 4);
+ datalen -= WPA_GET_LE32(data + 4);
+ data += WPA_GET_LE32(data + 4);
+ /* fall through */
+ case DLT_IEEE802_11:
+ pkt->captured_len += sizeof(rtap);
+ pkt->packet_len += sizeof(rtap);
+ os_memcpy(pos, &rtap, sizeof(rtap));
+ pos += sizeof(rtap);
+ break;
+ default:
+ return;
+ }
+
+ os_memcpy(pos, data, datalen);
+ pos += datalen + PAD32(pkt->captured_len);
+ pos = pcapng_add_comments(wt, pos);
+
+ block_len = (u32 *) pos;
+ pos += 4;
+ *block_len = pkt->block_total_len = pos - (u8 *) pkt;
+
+ fwrite(pkt, pos - (u8 *) pkt, 1, wt->pcapng);
+ if (wt->pcap_no_buffer)
+ fflush(wt->pcapng);
+
+ os_free(pkt);
+
+ write_pcapng_decrypted(wt);
+}
+
+
+void write_pcapng_captured(struct wlantest *wt, const u8 *buf, size_t len)
+{
+ struct pcap_pkthdr h;
+
+ if (!wt->pcapng)
+ return;
+
+ os_memset(&h, 0, sizeof(h));
+ gettimeofday(&h.ts, NULL);
+ h.caplen = len;
+ h.len = len;
+ write_pcapng_write_read(wt, wt->ethernet ? DLT_EN10MB :
+ DLT_IEEE802_11_RADIO, &h, buf);
+}
diff --git a/contrib/wpa/wpa_supplicant/bssid_ignore.c b/contrib/wpa/wpa_supplicant/bssid_ignore.c
new file mode 100644
index 000000000000..e37857798a02
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/bssid_ignore.c
@@ -0,0 +1,221 @@
+/*
+ * wpa_supplicant - List of temporarily ignored BSSIDs
+ * Copyright (c) 2003-2021, 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 "common.h"
+#include "wpa_supplicant_i.h"
+#include "bssid_ignore.h"
+
+/**
+ * wpa_bssid_ignore_get - Get the ignore list entry for a BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Matching entry for the BSSID or %NULL if not found
+ */
+struct wpa_bssid_ignore * wpa_bssid_ignore_get(struct wpa_supplicant *wpa_s,
+ const u8 *bssid)
+{
+ struct wpa_bssid_ignore *e;
+
+ if (wpa_s == NULL || bssid == NULL)
+ return NULL;
+
+ if (wpa_s->current_ssid &&
+ wpa_s->current_ssid->was_recently_reconfigured) {
+ wpa_bssid_ignore_clear(wpa_s);
+ wpa_s->current_ssid->was_recently_reconfigured = false;
+ return NULL;
+ }
+
+ wpa_bssid_ignore_update(wpa_s);
+
+ e = wpa_s->bssid_ignore;
+ while (e) {
+ if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0)
+ return e;
+ e = e->next;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * wpa_bssid_ignore_add - Add an BSSID to the ignore list
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be added to the ignore list
+ * Returns: Current ignore list count on success, -1 on failure
+ *
+ * This function adds the specified BSSID to the ignore list or increases the
+ * ignore count if the BSSID was already listed. It should be called when
+ * an association attempt fails either due to the selected BSS rejecting
+ * association or due to timeout.
+ *
+ * This ignore list is used to force %wpa_supplicant to go through all available
+ * BSSes before retrying to associate with an BSS that rejected or timed out
+ * association. It does not prevent the listed BSS from being used; it only
+ * changes the order in which they are tried.
+ */
+int wpa_bssid_ignore_add(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_bssid_ignore *e;
+ struct os_reltime now;
+
+ if (wpa_s == NULL || bssid == NULL)
+ return -1;
+
+ e = wpa_bssid_ignore_get(wpa_s, bssid);
+ os_get_reltime(&now);
+ if (e) {
+ e->start = now;
+ e->count++;
+ if (e->count > 5)
+ e->timeout_secs = 1800;
+ else if (e->count == 5)
+ e->timeout_secs = 600;
+ else if (e->count == 4)
+ e->timeout_secs = 120;
+ else if (e->count == 3)
+ e->timeout_secs = 60;
+ else
+ e->timeout_secs = 10;
+ wpa_printf(MSG_INFO, "BSSID " MACSTR
+ " ignore list count incremented to %d, ignoring for %d seconds",
+ MAC2STR(bssid), e->count, e->timeout_secs);
+ return e->count;
+ }
+
+ e = os_zalloc(sizeof(*e));
+ if (e == NULL)
+ return -1;
+ os_memcpy(e->bssid, bssid, ETH_ALEN);
+ e->count = 1;
+ e->timeout_secs = 10;
+ e->start = now;
+ e->next = wpa_s->bssid_ignore;
+ wpa_s->bssid_ignore = e;
+ wpa_printf(MSG_DEBUG, "Added BSSID " MACSTR
+ " into ignore list, ignoring for %d seconds",
+ MAC2STR(bssid), e->timeout_secs);
+
+ return e->count;
+}
+
+
+/**
+ * wpa_bssid_ignore_del - Remove an BSSID from the ignore list
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be removed from the ignore list
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_bssid_ignore_del(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_bssid_ignore *e, *prev = NULL;
+
+ if (wpa_s == NULL || bssid == NULL)
+ return -1;
+
+ e = wpa_s->bssid_ignore;
+ while (e) {
+ if (os_memcmp(e->bssid, bssid, ETH_ALEN) == 0) {
+ if (prev == NULL) {
+ wpa_s->bssid_ignore = e->next;
+ } else {
+ prev->next = e->next;
+ }
+ wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR
+ " from ignore list", MAC2STR(bssid));
+ os_free(e);
+ return 0;
+ }
+ prev = e;
+ e = e->next;
+ }
+ return -1;
+}
+
+
+/**
+ * wpa_bssid_ignore_is_listed - Check whether a BSSID is ignored temporarily
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be checked
+ * Returns: count if BSS is currently considered to be ignored, 0 otherwise
+ */
+int wpa_bssid_ignore_is_listed(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_bssid_ignore *e;
+ struct os_reltime now;
+
+ e = wpa_bssid_ignore_get(wpa_s, bssid);
+ if (!e)
+ return 0;
+ os_get_reltime(&now);
+ if (os_reltime_expired(&now, &e->start, e->timeout_secs))
+ return 0;
+ return e->count;
+}
+
+
+/**
+ * wpa_bssid_ignore_clear - Clear the ignore list of all entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_bssid_ignore_clear(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bssid_ignore *e, *prev;
+
+ e = wpa_s->bssid_ignore;
+ wpa_s->bssid_ignore = NULL;
+ while (e) {
+ prev = e;
+ e = e->next;
+ wpa_printf(MSG_DEBUG, "Removed BSSID " MACSTR
+ " from ignore list (clear)", MAC2STR(prev->bssid));
+ os_free(prev);
+ }
+}
+
+
+/**
+ * wpa_bssid_ignore_update - Update the entries in the ignore list,
+ * deleting entries that have been expired for over an hour.
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+void wpa_bssid_ignore_update(struct wpa_supplicant *wpa_s)
+{
+ struct wpa_bssid_ignore *e, *prev = NULL;
+ struct os_reltime now;
+
+ if (!wpa_s)
+ return;
+
+ e = wpa_s->bssid_ignore;
+ os_get_reltime(&now);
+ while (e) {
+ if (os_reltime_expired(&now, &e->start,
+ e->timeout_secs + 3600)) {
+ struct wpa_bssid_ignore *to_delete = e;
+
+ if (prev) {
+ prev->next = e->next;
+ e = prev->next;
+ } else {
+ wpa_s->bssid_ignore = e->next;
+ e = wpa_s->bssid_ignore;
+ }
+ wpa_printf(MSG_INFO, "Removed BSSID " MACSTR
+ " from ignore list (expired)",
+ MAC2STR(to_delete->bssid));
+ os_free(to_delete);
+ } else {
+ prev = e;
+ e = e->next;
+ }
+ }
+}
diff --git a/contrib/wpa/wpa_supplicant/bssid_ignore.h b/contrib/wpa/wpa_supplicant/bssid_ignore.h
new file mode 100644
index 000000000000..721b0e12665f
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/bssid_ignore.h
@@ -0,0 +1,33 @@
+/*
+ * wpa_supplicant - List of temporarily ignored BSSIDs
+ * Copyright (c) 2003-2021, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BSSID_IGNORE_H
+#define BSSID_IGNORE_H
+
+struct wpa_bssid_ignore {
+ struct wpa_bssid_ignore *next;
+ u8 bssid[ETH_ALEN];
+ int count;
+ /* Time of the most recent trigger to ignore this BSSID. */
+ struct os_reltime start;
+ /*
+ * Number of seconds after start that the entey will be considered
+ * valid.
+ */
+ int timeout_secs;
+};
+
+struct wpa_bssid_ignore * wpa_bssid_ignore_get(struct wpa_supplicant *wpa_s,
+ const u8 *bssid);
+int wpa_bssid_ignore_add(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpa_bssid_ignore_del(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpa_bssid_ignore_is_listed(struct wpa_supplicant *wpa_s, const u8 *bssid);
+void wpa_bssid_ignore_clear(struct wpa_supplicant *wpa_s);
+void wpa_bssid_ignore_update(struct wpa_supplicant *wpa_s);
+
+#endif /* BSSID_IGNORE_H */
diff --git a/contrib/wpa/wpa_supplicant/examples/dpp-nfc.py b/contrib/wpa/wpa_supplicant/examples/dpp-nfc.py
new file mode 100755
index 000000000000..8e865f3fcd33
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/examples/dpp-nfc.py
@@ -0,0 +1,1186 @@
+#!/usr/bin/python3
+#
+# Example nfcpy to wpa_supplicant wrapper for DPP NFC operations
+# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+# Copyright (c) 2019-2020, The Linux Foundation
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import binascii
+import errno
+import os
+import struct
+import sys
+import time
+import threading
+import argparse
+
+import nfc
+import ndef
+
+import logging
+
+scriptsdir = os.path.dirname(os.path.realpath(sys.modules[__name__].__file__))
+sys.path.append(os.path.join(scriptsdir, '..', '..', 'wpaspy'))
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+ifname = None
+init_on_touch = False
+in_raw_mode = False
+prev_tcgetattr = 0
+no_input = False
+continue_loop = True
+terminate_now = False
+summary_file = None
+success_file = None
+netrole = None
+operation_success = False
+mutex = threading.Lock()
+
+C_NORMAL = '\033[0m'
+C_RED = '\033[91m'
+C_GREEN = '\033[92m'
+C_YELLOW = '\033[93m'
+C_BLUE = '\033[94m'
+C_MAGENTA = '\033[95m'
+C_CYAN = '\033[96m'
+
+def summary(txt, color=None):
+ with mutex:
+ if color:
+ print(color + txt + C_NORMAL)
+ else:
+ print(txt)
+ if summary_file:
+ with open(summary_file, 'a') as f:
+ f.write(txt + "\n")
+
+def success_report(txt):
+ summary(txt)
+ if success_file:
+ with open(success_file, 'a') as f:
+ f.write(txt + "\n")
+
+def wpas_connect():
+ ifaces = []
+ if os.path.isdir(wpas_ctrl):
+ try:
+ ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+ except OSError as error:
+ summary("Could not find wpa_supplicant: %s", str(error))
+ return None
+
+ if len(ifaces) < 1:
+ summary("No wpa_supplicant control interface found")
+ return None
+
+ for ctrl in ifaces:
+ if ifname and ifname not in ctrl:
+ continue
+ if os.path.basename(ctrl).startswith("p2p-dev-"):
+ # skip P2P management interface
+ continue
+ try:
+ summary("Trying to use control interface " + ctrl)
+ wpas = wpaspy.Ctrl(ctrl)
+ return wpas
+ except Exception as e:
+ pass
+ summary("Could not connect to wpa_supplicant")
+ return None
+
+def dpp_nfc_uri_process(uri):
+ wpas = wpas_connect()
+ if wpas is None:
+ return False
+ peer_id = wpas.request("DPP_NFC_URI " + uri)
+ if "FAIL" in peer_id:
+ summary("Could not parse DPP URI from NFC URI record", color=C_RED)
+ return False
+ peer_id = int(peer_id)
+ summary("peer_id=%d for URI from NFC Tag: %s" % (peer_id, uri))
+ cmd = "DPP_AUTH_INIT peer=%d" % peer_id
+ global enrollee_only, configurator_only, config_params
+ if enrollee_only:
+ cmd += " role=enrollee"
+ elif configurator_only:
+ cmd += " role=configurator"
+ if config_params:
+ cmd += " " + config_params
+ summary("Initiate DPP authentication: " + cmd)
+ res = wpas.request(cmd)
+ if "OK" not in res:
+ summary("Failed to initiate DPP Authentication", color=C_RED)
+ return False
+ summary("DPP Authentication initiated")
+ return True
+
+def dpp_hs_tag_read(record):
+ wpas = wpas_connect()
+ if wpas is None:
+ return False
+ summary(record)
+ if len(record.data) < 5:
+ summary("Too short DPP HS", color=C_RED)
+ return False
+ if record.data[0] != 0:
+ summary("Unexpected URI Identifier Code", color=C_RED)
+ return False
+ uribuf = record.data[1:]
+ try:
+ uri = uribuf.decode()
+ except:
+ summary("Invalid URI payload", color=C_RED)
+ return False
+ summary("URI: " + uri)
+ if not uri.startswith("DPP:"):
+ summary("Not a DPP URI", color=C_RED)
+ return False
+ return dpp_nfc_uri_process(uri)
+
+def get_status(wpas, extra=None):
+ if extra:
+ extra = "-" + extra
+ else:
+ extra = ""
+ res = wpas.request("STATUS" + extra)
+ lines = res.splitlines()
+ vals = dict()
+ for l in lines:
+ try:
+ [name, value] = l.split('=', 1)
+ except ValueError:
+ summary("Ignore unexpected status line: %s" % l)
+ continue
+ vals[name] = value
+ return vals
+
+def get_status_field(wpas, field, extra=None):
+ vals = get_status(wpas, extra)
+ if field in vals:
+ return vals[field]
+ return None
+
+def own_addr(wpas):
+ addr = get_status_field(wpas, "address")
+ if addr is None:
+ addr = get_status_field(wpas, "bssid[0]")
+ return addr
+
+def dpp_bootstrap_gen(wpas, type="qrcode", chan=None, mac=None, info=None,
+ curve=None, key=None):
+ cmd = "DPP_BOOTSTRAP_GEN type=" + type
+ if chan:
+ cmd += " chan=" + chan
+ if mac:
+ if mac is True:
+ mac = own_addr(wpas)
+ if mac is None:
+ summary("Could not determine local MAC address for bootstrap info")
+ else:
+ cmd += " mac=" + mac.replace(':', '')
+ if info:
+ cmd += " info=" + info
+ if curve:
+ cmd += " curve=" + curve
+ if key:
+ cmd += " key=" + key
+ res = wpas.request(cmd)
+ if "FAIL" in res:
+ raise Exception("Failed to generate bootstrapping info")
+ return int(res)
+
+def dpp_start_listen(wpas, freq):
+ if get_status_field(wpas, "bssid[0]"):
+ summary("Own AP freq: %s MHz" % str(get_status_field(wpas, "freq")))
+ if get_status_field(wpas, "beacon_set", extra="DRIVER") is None:
+ summary("Enable beaconing to have radio ready for RX")
+ wpas.request("DISABLE")
+ wpas.request("SET start_disabled 0")
+ wpas.request("ENABLE")
+ cmd = "DPP_LISTEN %d" % freq
+ global enrollee_only
+ global configurator_only
+ if enrollee_only:
+ cmd += " role=enrollee"
+ elif configurator_only:
+ cmd += " role=configurator"
+ global netrole
+ if netrole:
+ cmd += " netrole=" + netrole
+ summary(cmd)
+ res = wpas.request(cmd)
+ if "OK" not in res:
+ summary("Failed to start DPP listen", color=C_RED)
+ return False
+ return True
+
+def wpas_get_nfc_uri(start_listen=True, pick_channel=False, chan_override=None):
+ listen_freq = 2412
+ wpas = wpas_connect()
+ if wpas is None:
+ return None
+ global own_id, chanlist
+ if chan_override:
+ chan = chan_override
+ else:
+ chan = chanlist
+ if chan and chan.startswith("81/"):
+ listen_freq = int(chan[3:].split(',')[0]) * 5 + 2407
+ if chan is None and get_status_field(wpas, "bssid[0]"):
+ freq = get_status_field(wpas, "freq")
+ if freq:
+ freq = int(freq)
+ if freq >= 2412 and freq <= 2462:
+ chan = "81/%d" % ((freq - 2407) / 5)
+ summary("Use current AP operating channel (%d MHz) as the URI channel list (%s)" % (freq, chan))
+ listen_freq = freq
+ if chan is None and pick_channel:
+ chan = "81/6"
+ summary("Use channel 2437 MHz since no other preference provided")
+ listen_freq = 2437
+ own_id = dpp_bootstrap_gen(wpas, type="nfc-uri", chan=chan, mac=True)
+ res = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
+ if "FAIL" in res:
+ return None
+ if start_listen:
+ if not dpp_start_listen(wpas, listen_freq):
+ raise Exception("Failed to start listen operation on %d MHz" % listen_freq)
+ return res
+
+def wpas_report_handover_req(uri):
+ wpas = wpas_connect()
+ if wpas is None:
+ return None
+ global own_id
+ cmd = "DPP_NFC_HANDOVER_REQ own=%d uri=%s" % (own_id, uri)
+ return wpas.request(cmd)
+
+def wpas_report_handover_sel(uri):
+ wpas = wpas_connect()
+ if wpas is None:
+ return None
+ global own_id
+ cmd = "DPP_NFC_HANDOVER_SEL own=%d uri=%s" % (own_id, uri)
+ return wpas.request(cmd)
+
+def dpp_handover_client(handover, alt=False):
+ summary("About to start run_dpp_handover_client (alt=%s)" % str(alt))
+ if alt:
+ handover.i_m_selector = False
+ run_dpp_handover_client(handover, alt)
+ summary("Done run_dpp_handover_client (alt=%s)" % str(alt))
+
+def run_client_alt(handover, alt):
+ if handover.start_client_alt and not alt:
+ handover.start_client_alt = False
+ summary("Try to send alternative handover request")
+ dpp_handover_client(handover, alt=True)
+
+class HandoverClient(nfc.handover.HandoverClient):
+ def __init__(self, handover, llc):
+ super(HandoverClient, self).__init__(llc)
+ self.handover = handover
+
+ def recv_records(self, timeout=None):
+ msg = self.recv_octets(timeout)
+ if msg is None:
+ return None
+ records = list(ndef.message_decoder(msg, 'relax'))
+ if records and records[0].type == 'urn:nfc:wkt:Hs':
+ summary("Handover client received message '{0}'".format(records[0].type))
+ return list(ndef.message_decoder(msg, 'relax'))
+ summary("Handover client received invalid message: %s" + binascii.hexlify(msg))
+ return None
+
+ def recv_octets(self, timeout=None):
+ start = time.time()
+ msg = bytearray()
+ while True:
+ poll_timeout = 0.1 if timeout is None or timeout > 0.1 else timeout
+ if not self.socket.poll('recv', poll_timeout):
+ if timeout:
+ timeout -= time.time() - start
+ if timeout <= 0:
+ return None
+ start = time.time()
+ continue
+ try:
+ r = self.socket.recv()
+ if r is None:
+ return None
+ msg += r
+ except TypeError:
+ return b''
+ try:
+ list(ndef.message_decoder(msg, 'strict', {}))
+ return bytes(msg)
+ except ndef.DecodeError:
+ if timeout:
+ timeout -= time.time() - start
+ if timeout <= 0:
+ return None
+ start = time.time()
+ continue
+ return None
+
+def run_dpp_handover_client(handover, alt=False):
+ chan_override = None
+ if alt:
+ chan_override = handover.altchanlist
+ handover.alt_proposal_used = True
+ global test_uri, test_alt_uri
+ if test_uri:
+ summary("TEST MODE: Using specified URI (alt=%s)" % str(alt))
+ uri = test_alt_uri if alt else test_uri
+ else:
+ uri = wpas_get_nfc_uri(start_listen=False, chan_override=chan_override)
+ if uri is None:
+ summary("Cannot start handover client - no bootstrap URI available",
+ color=C_RED)
+ return
+ handover.my_uri = uri
+ uri = ndef.UriRecord(uri)
+ summary("NFC URI record for DPP: " + str(uri))
+ carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
+ global test_crn
+ if test_crn:
+ prev, = struct.unpack('>H', test_crn)
+ summary("TEST MODE: Use specified crn %d" % prev)
+ crn = test_crn
+ test_crn = struct.pack('>H', prev + 0x10)
+ else:
+ crn = os.urandom(2)
+ hr = ndef.HandoverRequestRecord(version="1.4", crn=crn)
+ hr.add_alternative_carrier('active', carrier.name)
+ message = [hr, carrier]
+ summary("NFC Handover Request message for DPP: " + str(message))
+
+ if handover.peer_crn is not None and not alt:
+ summary("NFC handover request from peer was already received - do not send own")
+ return
+ if handover.client:
+ summary("Use already started handover client")
+ client = handover.client
+ else:
+ summary("Start handover client")
+ client = HandoverClient(handover, handover.llc)
+ try:
+ summary("Trying to initiate NFC connection handover")
+ client.connect()
+ summary("Connected for handover")
+ except nfc.llcp.ConnectRefused:
+ summary("Handover connection refused")
+ client.close()
+ return
+ except Exception as e:
+ summary("Other exception: " + str(e))
+ client.close()
+ return
+ handover.client = client
+
+ if handover.peer_crn is not None and not alt:
+ summary("NFC handover request from peer was already received - do not send own")
+ return
+
+ summary("Sending handover request")
+
+ handover.my_crn_ready = True
+
+ if not client.send_records(message):
+ handover.my_crn_ready = False
+ summary("Failed to send handover request", color=C_RED)
+ run_client_alt(handover, alt)
+ return
+
+ handover.my_crn, = struct.unpack('>H', crn)
+
+ summary("Receiving handover response")
+ try:
+ start = time.time()
+ message = client.recv_records(timeout=3.0)
+ end = time.time()
+ summary("Received {} record(s) in {} seconds".format(len(message) if message is not None else -1, end - start))
+ except Exception as e:
+ # This is fine if we are the handover selector
+ if handover.hs_sent:
+ summary("Client receive failed as expected since I'm the handover server: %s" % str(e))
+ elif handover.alt_proposal_used and not alt:
+ summary("Client received failed for initial proposal as expected since alternative proposal was also used: %s" % str(e))
+ else:
+ summary("Client receive failed: %s" % str(e), color=C_RED)
+ message = None
+ if message is None:
+ if handover.hs_sent:
+ summary("No response received as expected since I'm the handover server")
+ elif handover.alt_proposal_used and not alt:
+ summary("No response received for initial proposal as expected since alternative proposal was also used")
+ elif handover.try_own and not alt:
+ summary("No response received for initial proposal as expected since alternative proposal will also be sent")
+ else:
+ summary("No response received", color=C_RED)
+ run_client_alt(handover, alt)
+ return
+ summary("Received message: " + str(message))
+ if len(message) < 1 or \
+ not isinstance(message[0], ndef.HandoverSelectRecord):
+ summary("Response was not Hs - received: " + message.type)
+ return
+
+ summary("Received handover select message")
+ summary("alternative carriers: " + str(message[0].alternative_carriers))
+ if handover.i_m_selector:
+ summary("Ignore the received select since I'm the handover selector")
+ run_client_alt(handover, alt)
+ return
+
+ if handover.alt_proposal_used and not alt:
+ summary("Ignore received handover select for the initial proposal since alternative proposal was sent")
+ client.close()
+ return
+
+ dpp_found = False
+ for carrier in message:
+ if isinstance(carrier, ndef.HandoverSelectRecord):
+ continue
+ summary("Remote carrier type: " + carrier.type)
+ if carrier.type == "application/vnd.wfa.dpp":
+ if len(carrier.data) == 0 or carrier.data[0] != 0:
+ summary("URI Identifier Code 'None' not seen", color=C_RED)
+ continue
+ summary("DPP carrier type match - send to wpa_supplicant")
+ dpp_found = True
+ uri = carrier.data[1:].decode("utf-8")
+ summary("DPP URI: " + uri)
+ handover.peer_uri = uri
+ if test_uri:
+ summary("TEST MODE: Fake processing")
+ break
+ res = wpas_report_handover_sel(uri)
+ if res is None or "FAIL" in res:
+ summary("DPP handover report rejected", color=C_RED)
+ break
+
+ success_report("DPP handover reported successfully (initiator)")
+ summary("peer_id=" + res)
+ peer_id = int(res)
+ wpas = wpas_connect()
+ if wpas is None:
+ break
+
+ global enrollee_only
+ global config_params
+ if enrollee_only:
+ extra = " role=enrollee"
+ elif config_params:
+ extra = " role=configurator " + config_params
+ else:
+ # TODO: Single Configurator instance
+ res = wpas.request("DPP_CONFIGURATOR_ADD")
+ if "FAIL" in res:
+ summary("Failed to initiate Configurator", color=C_RED)
+ break
+ conf_id = int(res)
+ extra = " conf=sta-dpp configurator=%d" % conf_id
+ global own_id
+ summary("Initiate DPP authentication")
+ cmd = "DPP_AUTH_INIT peer=%d own=%d" % (peer_id, own_id)
+ cmd += extra
+ res = wpas.request(cmd)
+ if "FAIL" in res:
+ summary("Failed to initiate DPP authentication", color=C_RED)
+ break
+
+ if not dpp_found and handover.no_alt_proposal:
+ summary("DPP carrier not seen in response - do not allow alternative proposal anymore")
+ elif not dpp_found:
+ summary("DPP carrier not seen in response - allow peer to initiate a new handover with different parameters")
+ handover.alt_proposal = True
+ handover.my_crn_ready = False
+ handover.my_crn = None
+ handover.peer_crn = None
+ handover.hs_sent = False
+ summary("Returning from dpp_handover_client")
+ return
+
+ summary("Remove peer")
+ handover.close()
+ summary("Done with handover")
+ global only_one
+ if only_one:
+ print("only_one -> stop loop")
+ global continue_loop
+ continue_loop = False
+
+ global no_wait
+ if no_wait or only_one:
+ summary("Trying to exit..")
+ global terminate_now
+ terminate_now = True
+
+ summary("Returning from dpp_handover_client")
+
+class HandoverServer(nfc.handover.HandoverServer):
+ def __init__(self, handover, llc):
+ super(HandoverServer, self).__init__(llc)
+ self.sent_carrier = None
+ self.ho_server_processing = False
+ self.success = False
+ self.llc = llc
+ self.handover = handover
+
+ def serve(self, socket):
+ peer_sap = socket.getpeername()
+ summary("Serving handover client on remote sap {0}".format(peer_sap))
+ send_miu = socket.getsockopt(nfc.llcp.SO_SNDMIU)
+ try:
+ while socket.poll("recv"):
+ req = bytearray()
+ while socket.poll("recv"):
+ r = socket.recv()
+ if r is None:
+ return None
+ summary("Received %d octets" % len(r))
+ req += r
+ if len(req) == 0:
+ continue
+ try:
+ list(ndef.message_decoder(req, 'strict', {}))
+ except ndef.DecodeError:
+ continue
+ summary("Full message received")
+ resp = self._process_request_data(req)
+ if resp is None or len(resp) == 0:
+ summary("No handover select to send out - wait for a possible alternative handover request")
+ handover.alt_proposal = True
+ req = bytearray()
+ continue
+
+ for offset in range(0, len(resp), send_miu):
+ if not socket.send(resp[offset:offset + send_miu]):
+ summary("Failed to send handover select - connection closed")
+ return
+ summary("Sent out full handover select")
+ if handover.terminate_on_hs_send_completion:
+ handover.delayed_exit()
+
+ except nfc.llcp.Error as e:
+ global terminate_now
+ summary("HandoverServer exception: %s" % e,
+ color=None if e.errno == errno.EPIPE or terminate_now else C_RED)
+ finally:
+ socket.close()
+ summary("Handover serve thread exiting")
+
+ def process_handover_request_message(self, records):
+ handover = self.handover
+ self.ho_server_processing = True
+ global in_raw_mode
+ was_in_raw_mode = in_raw_mode
+ clear_raw_mode()
+ if was_in_raw_mode:
+ print("\n")
+ summary("HandoverServer - request received: " + str(records))
+
+ for carrier in records:
+ if not isinstance(carrier, ndef.HandoverRequestRecord):
+ continue
+ if carrier.collision_resolution_number:
+ handover.peer_crn = carrier.collision_resolution_number
+ summary("peer_crn: %d" % handover.peer_crn)
+
+ if handover.my_crn is None and handover.my_crn_ready:
+ summary("Still trying to send own handover request - wait a moment to see if that succeeds before checking crn values")
+ for i in range(10):
+ if handover.my_crn is not None:
+ break
+ time.sleep(0.01)
+ if handover.my_crn is not None:
+ summary("my_crn: %d" % handover.my_crn)
+
+ if handover.my_crn is not None and handover.peer_crn is not None:
+ if handover.my_crn == handover.peer_crn:
+ summary("Same crn used - automatic collision resolution failed")
+ # TODO: Should generate a new Handover Request message
+ return ''
+ if ((handover.my_crn & 1) == (handover.peer_crn & 1) and \
+ handover.my_crn > handover.peer_crn) or \
+ ((handover.my_crn & 1) != (handover.peer_crn & 1) and \
+ handover.my_crn < handover.peer_crn):
+ summary("I'm the Handover Selector Device")
+ handover.i_m_selector = True
+ else:
+ summary("Peer is the Handover Selector device")
+ summary("Ignore the received request.")
+ return ''
+
+ hs = ndef.HandoverSelectRecord('1.4')
+ sel = [hs]
+
+ found = False
+
+ for carrier in records:
+ if isinstance(carrier, ndef.HandoverRequestRecord):
+ continue
+ summary("Remote carrier type: " + carrier.type)
+ if carrier.type == "application/vnd.wfa.dpp":
+ summary("DPP carrier type match - add DPP carrier record")
+ if len(carrier.data) == 0 or carrier.data[0] != 0:
+ summary("URI Identifier Code 'None' not seen", color=C_RED)
+ continue
+ uri = carrier.data[1:].decode("utf-8")
+ summary("Received DPP URI: " + uri)
+
+ global test_uri, test_alt_uri
+ if test_uri:
+ summary("TEST MODE: Using specified URI")
+ data = test_sel_uri if test_sel_uri else test_uri
+ elif handover.alt_proposal and handover.altchanlist:
+ summary("Use alternative channel list while processing alternative proposal from peer")
+ data = wpas_get_nfc_uri(start_listen=False,
+ chan_override=handover.altchanlist,
+ pick_channel=True)
+ else:
+ data = wpas_get_nfc_uri(start_listen=False,
+ pick_channel=True)
+ summary("Own URI (pre-processing): %s" % data)
+
+ if test_uri:
+ summary("TEST MODE: Fake processing")
+ res = "OK"
+ data += " [%s]" % uri
+ else:
+ res = wpas_report_handover_req(uri)
+ if res is None or "FAIL" in res:
+ summary("DPP handover request processing failed",
+ color=C_RED)
+ if handover.altchanlist:
+ data = wpas_get_nfc_uri(start_listen=False,
+ chan_override=handover.altchanlist)
+ summary("Own URI (try another channel list): %s" % data)
+ continue
+
+ if test_alt_uri:
+ summary("TEST MODE: Reject initial proposal")
+ continue
+
+ found = True
+
+ if not test_uri:
+ wpas = wpas_connect()
+ if wpas is None:
+ continue
+ global own_id
+ data = wpas.request("DPP_BOOTSTRAP_GET_URI %d" % own_id).rstrip()
+ if "FAIL" in data:
+ continue
+ summary("Own URI (post-processing): %s" % data)
+ handover.my_uri = data
+ handover.peer_uri = uri
+ uri = ndef.UriRecord(data)
+ summary("Own bootstrapping NFC URI record: " + str(uri))
+
+ if not test_uri:
+ info = wpas.request("DPP_BOOTSTRAP_INFO %d" % own_id)
+ freq = None
+ for line in info.splitlines():
+ if line.startswith("use_freq="):
+ freq = int(line.split('=')[1])
+ if freq is None or freq == 0:
+ summary("No channel negotiated over NFC - use channel 6")
+ freq = 2437
+ else:
+ summary("Negotiated channel: %d MHz" % freq)
+ if not dpp_start_listen(wpas, freq):
+ break
+
+ carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
+ summary("Own DPP carrier record: " + str(carrier))
+ hs.add_alternative_carrier('active', carrier.name)
+ sel = [hs, carrier]
+ break
+
+ summary("Sending handover select: " + str(sel))
+ if found:
+ summary("Handover completed successfully")
+ handover.terminate_on_hs_send_completion = True
+ self.success = True
+ handover.hs_sent = True
+ handover.i_m_selector = True
+ elif handover.no_alt_proposal:
+ summary("Do not try alternative proposal anymore - handover failed",
+ color=C_RED)
+ handover.hs_sent = True
+ else:
+ summary("Try to initiate with alternative parameters")
+ handover.try_own = True
+ handover.hs_sent = False
+ handover.no_alt_proposal = True
+ if handover.client_thread:
+ handover.start_client_alt = True
+ else:
+ handover.client_thread = threading.Thread(target=llcp_worker,
+ args=(self.llc, True))
+ handover.client_thread.start()
+ return sel
+
+def clear_raw_mode():
+ import sys, tty, termios
+ global prev_tcgetattr, in_raw_mode
+ if not in_raw_mode:
+ return
+ fd = sys.stdin.fileno()
+ termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
+ in_raw_mode = False
+
+def getch():
+ import sys, tty, termios, select
+ global prev_tcgetattr, in_raw_mode
+ fd = sys.stdin.fileno()
+ prev_tcgetattr = termios.tcgetattr(fd)
+ ch = None
+ try:
+ tty.setraw(fd)
+ in_raw_mode = True
+ [i, o, e] = select.select([fd], [], [], 0.05)
+ if i:
+ ch = sys.stdin.read(1)
+ finally:
+ termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
+ in_raw_mode = False
+ return ch
+
+def dpp_tag_read(tag):
+ success = False
+ for record in tag.ndef.records:
+ summary(record)
+ summary("record type " + record.type)
+ if record.type == "application/vnd.wfa.dpp":
+ summary("DPP HS tag - send to wpa_supplicant")
+ success = dpp_hs_tag_read(record)
+ break
+ if isinstance(record, ndef.UriRecord):
+ summary("URI record: uri=" + record.uri)
+ summary("URI record: iri=" + record.iri)
+ if record.iri.startswith("DPP:"):
+ summary("DPP URI")
+ if not dpp_nfc_uri_process(record.iri):
+ break
+ success = True
+ else:
+ summary("Ignore unknown URI")
+ break
+
+ if success:
+ success_report("Tag read succeeded")
+
+ return success
+
+def rdwr_connected_write_tag(tag):
+ summary("Tag found - writing - " + str(tag))
+ if not tag.ndef:
+ summary("Not a formatted NDEF tag", color=C_RED)
+ return
+ if not tag.ndef.is_writeable:
+ summary("Not a writable tag", color=C_RED)
+ return
+ global dpp_tag_data
+ if tag.ndef.capacity < len(dpp_tag_data):
+ summary("Not enough room for the message")
+ return
+ try:
+ tag.ndef.records = dpp_tag_data
+ except ValueError as e:
+ summary("Writing the tag failed: %s" % str(e), color=C_RED)
+ return
+ success_report("Tag write succeeded")
+ summary("Tag writing completed - remove tag", color=C_GREEN)
+ global only_one, operation_success
+ operation_success = True
+ if only_one:
+ global continue_loop
+ continue_loop = False
+ global dpp_sel_wait_remove
+ return dpp_sel_wait_remove
+
+def write_nfc_uri(clf, wait_remove=True):
+ summary("Write NFC URI record")
+ data = wpas_get_nfc_uri()
+ if data is None:
+ summary("Could not get NFC URI from wpa_supplicant", color=C_RED)
+ return
+
+ global dpp_sel_wait_remove
+ dpp_sel_wait_remove = wait_remove
+ summary("URI: %s" % data)
+ uri = ndef.UriRecord(data)
+ summary(uri)
+
+ summary("Touch an NFC tag to write URI record", color=C_CYAN)
+ global dpp_tag_data
+ dpp_tag_data = [uri]
+ clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
+
+def write_nfc_hs(clf, wait_remove=True):
+ summary("Write NFC Handover Select record on a tag")
+ data = wpas_get_nfc_uri()
+ if data is None:
+ summary("Could not get NFC URI from wpa_supplicant", color=C_RED)
+ return
+
+ global dpp_sel_wait_remove
+ dpp_sel_wait_remove = wait_remove
+ summary("URI: %s" % data)
+ uri = ndef.UriRecord(data)
+ summary(uri)
+ carrier = ndef.Record('application/vnd.wfa.dpp', 'A', uri.data)
+ hs = ndef.HandoverSelectRecord('1.4')
+ hs.add_alternative_carrier('active', carrier.name)
+ summary(hs)
+ summary(carrier)
+
+ summary("Touch an NFC tag to write HS record", color=C_CYAN)
+ global dpp_tag_data
+ dpp_tag_data = [hs, carrier]
+ summary(dpp_tag_data)
+ clf.connect(rdwr={'on-connect': rdwr_connected_write_tag})
+
+def rdwr_connected(tag):
+ global only_one, no_wait
+ summary("Tag connected: " + str(tag))
+
+ if tag.ndef:
+ summary("NDEF tag: " + tag.type)
+ summary(tag.ndef.records)
+ success = dpp_tag_read(tag)
+ if only_one and success:
+ global continue_loop
+ continue_loop = False
+ else:
+ summary("Not an NDEF tag - remove tag", color=C_RED)
+ return True
+
+ return not no_wait
+
+def llcp_worker(llc, try_alt):
+ global handover
+ print("Start of llcp_worker()")
+ if try_alt:
+ summary("Starting handover client (try_alt)")
+ dpp_handover_client(handover, alt=True)
+ summary("Exiting llcp_worker thread (try_alt)")
+ return
+ global init_on_touch
+ if init_on_touch:
+ summary("Starting handover client (init_on_touch)")
+ dpp_handover_client(handover)
+ summary("Exiting llcp_worker thread (init_on_touch)")
+ return
+
+ global no_input
+ if no_input:
+ summary("Wait for handover to complete")
+ else:
+ print("Wait for handover to complete - press 'i' to initiate")
+ while not handover.wait_connection and handover.srv.sent_carrier is None:
+ if handover.try_own:
+ handover.try_own = False
+ summary("Try to initiate another handover with own parameters")
+ handover.my_crn_ready = False
+ handover.my_crn = None
+ handover.peer_crn = None
+ handover.hs_sent = False
+ dpp_handover_client(handover, alt=True)
+ summary("Exiting llcp_worker thread (retry with own parameters)")
+ return
+ if handover.srv.ho_server_processing:
+ time.sleep(0.025)
+ elif no_input:
+ time.sleep(0.5)
+ else:
+ res = getch()
+ if res != 'i':
+ continue
+ clear_raw_mode()
+ summary("Starting handover client")
+ dpp_handover_client(handover)
+ summary("Exiting llcp_worker thread (manual init)")
+ return
+
+ global in_raw_mode
+ was_in_raw_mode = in_raw_mode
+ clear_raw_mode()
+ if was_in_raw_mode:
+ print("\r")
+ summary("Exiting llcp_worker thread")
+
+class ConnectionHandover():
+ def __init__(self):
+ self.client = None
+ self.client_thread = None
+ self.reset()
+ self.exit_thread = None
+
+ def reset(self):
+ self.wait_connection = False
+ self.my_crn_ready = False
+ self.my_crn = None
+ self.peer_crn = None
+ self.hs_sent = False
+ self.no_alt_proposal = False
+ self.alt_proposal_used = False
+ self.i_m_selector = False
+ self.start_client_alt = False
+ self.terminate_on_hs_send_completion = False
+ self.try_own = False
+ self.my_uri = None
+ self.peer_uri = None
+ self.connected = False
+ self.alt_proposal = False
+
+ def start_handover_server(self, llc):
+ summary("Start handover server")
+ self.llc = llc
+ self.srv = HandoverServer(self, llc)
+
+ def close(self):
+ if self.client:
+ self.client.close()
+ self.client = None
+
+ def run_delayed_exit(self):
+ summary("Trying to exit (delayed)..")
+ time.sleep(0.25)
+ summary("Trying to exit (after wait)..")
+ global terminate_now
+ terminate_now = True
+
+ def delayed_exit(self):
+ global only_one
+ if only_one:
+ self.exit_thread = threading.Thread(target=self.run_delayed_exit)
+ self.exit_thread.start()
+
+def llcp_startup(llc):
+ global handover
+ handover.start_handover_server(llc)
+ return llc
+
+def llcp_connected(llc):
+ summary("P2P LLCP connected")
+ global handover
+ handover.connected = True
+ handover.srv.start()
+ if init_on_touch or not no_input:
+ handover.client_thread = threading.Thread(target=llcp_worker,
+ args=(llc, False))
+ handover.client_thread.start()
+ return True
+
+def llcp_release(llc):
+ summary("LLCP release")
+ global handover
+ handover.close()
+ return True
+
+def terminate_loop():
+ global terminate_now
+ return terminate_now
+
+def main():
+ clf = nfc.ContactlessFrontend()
+
+ parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for DPP NFC operations')
+ parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+ action='store_const', dest='loglevel',
+ help='verbose debug output')
+ parser.add_argument('-q', const=logging.WARNING, action='store_const',
+ dest='loglevel', help='be quiet')
+ parser.add_argument('--only-one', '-1', action='store_true',
+ help='run only one operation and exit')
+ parser.add_argument('--init-on-touch', '-I', action='store_true',
+ help='initiate handover on touch')
+ parser.add_argument('--no-wait', action='store_true',
+ help='do not wait for tag to be removed before exiting')
+ parser.add_argument('--ifname', '-i',
+ help='network interface name')
+ parser.add_argument('--no-input', '-a', action='store_true',
+ help='do not use stdout input to initiate handover')
+ parser.add_argument('--tag-read-only', '-t', action='store_true',
+ help='tag read only (do not allow connection handover)')
+ parser.add_argument('--handover-only', action='store_true',
+ help='connection handover only (do not allow tag read)')
+ parser.add_argument('--enrollee', action='store_true',
+ help='run as Enrollee-only')
+ parser.add_argument('--configurator', action='store_true',
+ help='run as Configurator-only')
+ parser.add_argument('--config-params', default='',
+ help='configurator parameters')
+ parser.add_argument('--ctrl', default='/var/run/wpa_supplicant',
+ help='wpa_supplicant/hostapd control interface')
+ parser.add_argument('--summary',
+ help='summary file for writing status updates')
+ parser.add_argument('--success',
+ help='success file for writing success update')
+ parser.add_argument('--device', default='usb', help='NFC device to open')
+ parser.add_argument('--chan', default=None, help='channel list')
+ parser.add_argument('--altchan', default=None, help='alternative channel list')
+ parser.add_argument('--netrole', default=None, help='netrole for Enrollee')
+ parser.add_argument('--test-uri', default=None,
+ help='test mode: initial URI')
+ parser.add_argument('--test-alt-uri', default=None,
+ help='test mode: alternative URI')
+ parser.add_argument('--test-sel-uri', default=None,
+ help='test mode: handover select URI')
+ parser.add_argument('--test-crn', default=None,
+ help='test mode: hardcoded crn')
+ parser.add_argument('command', choices=['write-nfc-uri',
+ 'write-nfc-hs'],
+ nargs='?')
+ args = parser.parse_args()
+ summary(args)
+
+ global handover
+ handover = ConnectionHandover()
+
+ global only_one
+ only_one = args.only_one
+
+ global no_wait
+ no_wait = args.no_wait
+
+ global chanlist, netrole, test_uri, test_alt_uri, test_sel_uri
+ global test_crn
+ chanlist = args.chan
+ handover.altchanlist = args.altchan
+ netrole = args.netrole
+ test_uri = args.test_uri
+ test_alt_uri = args.test_alt_uri
+ test_sel_uri = args.test_sel_uri
+ if args.test_crn:
+ test_crn = struct.pack('>H', int(args.test_crn))
+ else:
+ test_crn = None
+
+ logging.basicConfig(level=args.loglevel)
+ for l in ['nfc.clf.rcs380',
+ 'nfc.clf.transport',
+ 'nfc.clf.device',
+ 'nfc.clf.__init__',
+ 'nfc.llcp',
+ 'nfc.handover']:
+ log = logging.getLogger(l)
+ log.setLevel(args.loglevel)
+
+ global init_on_touch
+ init_on_touch = args.init_on_touch
+
+ global enrollee_only
+ enrollee_only = args.enrollee
+
+ global configurator_only
+ configurator_only = args.configurator
+
+ global config_params
+ config_params = args.config_params
+
+ if args.ifname:
+ global ifname
+ ifname = args.ifname
+ summary("Selected ifname " + ifname)
+
+ if args.ctrl:
+ global wpas_ctrl
+ wpas_ctrl = args.ctrl
+
+ if args.summary:
+ global summary_file
+ summary_file = args.summary
+
+ if args.success:
+ global success_file
+ success_file = args.success
+
+ if args.no_input:
+ global no_input
+ no_input = True
+
+ clf = nfc.ContactlessFrontend()
+
+ try:
+ if not clf.open(args.device):
+ summary("Could not open connection with an NFC device", color=C_RED)
+ raise SystemExit(1)
+
+ if args.command == "write-nfc-uri":
+ write_nfc_uri(clf, wait_remove=not args.no_wait)
+ if not operation_success:
+ raise SystemExit(1)
+ raise SystemExit
+
+ if args.command == "write-nfc-hs":
+ write_nfc_hs(clf, wait_remove=not args.no_wait)
+ if not operation_success:
+ raise SystemExit(1)
+ raise SystemExit
+
+ global continue_loop
+ while continue_loop:
+ global in_raw_mode
+ was_in_raw_mode = in_raw_mode
+ clear_raw_mode()
+ if was_in_raw_mode:
+ print("\r")
+ if args.handover_only:
+ summary("Waiting a peer to be touched", color=C_MAGENTA)
+ elif args.tag_read_only:
+ summary("Waiting for a tag to be touched", color=C_BLUE)
+ else:
+ summary("Waiting for a tag or peer to be touched",
+ color=C_GREEN)
+ handover.wait_connection = True
+ try:
+ if args.tag_read_only:
+ if not clf.connect(rdwr={'on-connect': rdwr_connected}):
+ break
+ elif args.handover_only:
+ if not clf.connect(llcp={'on-startup': llcp_startup,
+ 'on-connect': llcp_connected,
+ 'on-release': llcp_release},
+ terminate=terminate_loop):
+ break
+ else:
+ if not clf.connect(rdwr={'on-connect': rdwr_connected},
+ llcp={'on-startup': llcp_startup,
+ 'on-connect': llcp_connected,
+ 'on-release': llcp_release},
+ terminate=terminate_loop):
+ break
+ except Exception as e:
+ summary("clf.connect failed: " + str(e))
+ break
+
+ if only_one and handover.connected:
+ role = "selector" if handover.i_m_selector else "requestor"
+ summary("Connection handover result: I'm the %s" % role,
+ color=C_YELLOW)
+ if handover.peer_uri:
+ summary("Peer URI: " + handover.peer_uri, color=C_YELLOW)
+ if handover.my_uri:
+ summary("My URI: " + handover.my_uri, color=C_YELLOW)
+ if not (handover.peer_uri and handover.my_uri):
+ summary("Negotiated connection handover failed",
+ color=C_YELLOW)
+ break
+
+ except KeyboardInterrupt:
+ raise SystemExit
+ finally:
+ clf.close()
+
+ raise SystemExit
+
+if __name__ == '__main__':
+ main()
diff --git a/contrib/wpa/wpa_supplicant/pasn_supplicant.c b/contrib/wpa/wpa_supplicant/pasn_supplicant.c
new file mode 100644
index 000000000000..baf4c2643e42
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/pasn_supplicant.c
@@ -0,0 +1,1710 @@
+/*
+ * wpa_supplicant - PASN processing
+ *
+ * Copyright (C) 2019 Intel Corporation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/dragonfly.h"
+#include "common/ptksa_cache.h"
+#include "utils/eloop.h"
+#include "drivers/driver.h"
+#include "crypto/crypto.h"
+#include "crypto/random.h"
+#include "eap_common/eap_defs.h"
+#include "rsn_supp/wpa.h"
+#include "rsn_supp/pmksa_cache.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "bss.h"
+#include "config.h"
+
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct wpa_pasn_auth_work {
+ u8 bssid[ETH_ALEN];
+ int akmp;
+ int cipher;
+ u16 group;
+ int network_id;
+ struct wpabuf *comeback;
+};
+
+
+static void wpas_pasn_free_auth_work(struct wpa_pasn_auth_work *awork)
+{
+ wpabuf_free(awork->comeback);
+ awork->comeback = NULL;
+ os_free(awork);
+}
+
+
+static void wpas_pasn_auth_work_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_supplicant *wpa_s = eloop_ctx;
+
+ wpa_printf(MSG_DEBUG, "PASN: Auth work timeout - stopping auth");
+
+ wpas_pasn_auth_stop(wpa_s);
+}
+
+
+static void wpas_pasn_cancel_auth_work(struct wpa_supplicant *wpa_s)
+{
+ wpa_printf(MSG_DEBUG, "PASN: Cancel pasn-start-auth work");
+
+ /* Remove pending/started work */
+ radio_remove_works(wpa_s, "pasn-start-auth", 0);
+}
+
+
+static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int akmp, int cipher, u8 status,
+ struct wpabuf *comeback,
+ u16 comeback_after)
+{
+ if (comeback) {
+ size_t comeback_len = wpabuf_len(comeback);
+ size_t buflen = comeback_len * 2 + 1;
+ char *comeback_txt = os_malloc(buflen);
+
+ if (comeback_txt) {
+ wpa_snprintf_hex(comeback_txt, buflen,
+ wpabuf_head(comeback), comeback_len);
+
+ wpa_msg(wpa_s, MSG_INFO, PASN_AUTH_STATUS MACSTR
+ " akmp=%s, status=%u comeback_after=%u comeback=%s",
+ MAC2STR(bssid),
+ wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
+ status, comeback_after, comeback_txt);
+
+ os_free(comeback_txt);
+ return;
+ }
+ }
+
+ wpa_msg(wpa_s, MSG_INFO,
+ PASN_AUTH_STATUS MACSTR " akmp=%s, status=%u",
+ MAC2STR(bssid), wpa_key_mgmt_txt(akmp, WPA_PROTO_RSN),
+ status);
+}
+
+
+#ifdef CONFIG_SAE
+
+static struct wpabuf * wpas_pasn_wd_sae_commit(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct wpabuf *buf = NULL;
+ int ret;
+
+ ret = sae_set_group(&pasn->sae, pasn->group);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
+ return NULL;
+ }
+
+ ret = sae_prepare_commit_pt(&pasn->sae, pasn->ssid->pt,
+ wpa_s->own_addr, pasn->bssid,
+ NULL, NULL);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
+ return NULL;
+ }
+
+ /* Need to add the entire Authentication frame body */
+ buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+ return NULL;
+ }
+
+ wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+ wpabuf_put_le16(buf, 1);
+ wpabuf_put_le16(buf, WLAN_STATUS_SAE_HASH_TO_ELEMENT);
+
+ sae_write_commit(&pasn->sae, buf, NULL, 0);
+ pasn->sae.state = SAE_COMMITTED;
+
+ return buf;
+}
+
+
+static int wpas_pasn_wd_sae_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ const u8 *data;
+ size_t buf_len;
+ u16 len, res, alg, seq, status;
+ int groups[] = { pasn->group, 0 };
+ int ret;
+
+ if (!wd)
+ return -1;
+
+ data = wpabuf_head_u8(wd);
+ buf_len = wpabuf_len(wd);
+
+ /* first handle the commit message */
+ if (buf_len < 2) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)");
+ return -1;
+ }
+
+ len = WPA_GET_LE16(data);
+ if (len < 6 || buf_len - 2 < len) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit");
+ return -1;
+ }
+
+ buf_len -= 2;
+ data += 2;
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_SAE || seq != 1 ||
+ status != WLAN_STATUS_SAE_HASH_TO_ELEMENT) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
+ return -1;
+ }
+
+ res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
+ 1);
+ if (res != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit");
+ return -1;
+ }
+
+ /* Process the commit message and derive the PMK */
+ ret = sae_process_commit(&pasn->sae);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+ return -1;
+ }
+
+ buf_len -= len;
+ data += len;
+
+ /* Handle the confirm message */
+ if (buf_len < 2) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)");
+ return -1;
+ }
+
+ len = WPA_GET_LE16(data);
+ if (len < 6 || buf_len - 2 < len) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm");
+ return -1;
+ }
+
+ buf_len -= 2;
+ data += 2;
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
+ return -1;
+ }
+
+ res = sae_check_confirm(&pasn->sae, data + 6, len - 6);
+ if (res != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully");
+ pasn->sae.state = SAE_ACCEPTED;
+
+ return 0;
+}
+
+
+static struct wpabuf * wpas_pasn_wd_sae_confirm(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct wpabuf *buf = NULL;
+
+ /* Need to add the entire authentication frame body */
+ buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+ return NULL;
+ }
+
+ wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+ wpabuf_put_le16(buf, 2);
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+ sae_write_confirm(&pasn->sae, buf);
+ pasn->sae.state = SAE_CONFIRMED;
+
+ return buf;
+}
+
+
+static int wpas_pasn_sae_setup_pt(struct wpa_supplicant *wpa_s,
+ struct wpa_ssid *ssid, int group)
+{
+ const char *password = ssid->sae_password;
+ int groups[2] = { group, 0 };
+
+ if (!password)
+ password = ssid->passphrase;
+
+ if (!password) {
+ wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
+ return -1;
+ }
+
+ if (ssid->pt)
+ return 0; /* PT already derived */
+
+ ssid->pt = sae_derive_pt(groups, ssid->ssid, ssid->ssid_len,
+ (const u8 *) password, os_strlen(password),
+ ssid->sae_password_id);
+
+ return ssid->pt ? 0 : -1;
+}
+
+#endif /* CONFIG_SAE */
+
+
+#ifdef CONFIG_FILS
+
+static struct wpabuf * wpas_pasn_fils_build_auth(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct wpabuf *buf = NULL;
+ struct wpabuf *erp_msg;
+ int ret;
+
+ erp_msg = eapol_sm_build_erp_reauth_start(wpa_s->eapol);
+ if (!erp_msg) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: ERP EAP-Initiate/Re-auth unavailable");
+ return NULL;
+ }
+
+ if (random_get_bytes(pasn->fils.nonce, FILS_NONCE_LEN) < 0 ||
+ random_get_bytes(pasn->fils.session, FILS_SESSION_LEN) < 0)
+ goto fail;
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Nonce", pasn->fils.nonce,
+ FILS_NONCE_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Session", pasn->fils.session,
+ FILS_SESSION_LEN);
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ goto fail;
+
+ /* Add the authentication algorithm */
+ wpabuf_put_le16(buf, WLAN_AUTH_FILS_SK);
+
+ /* Authentication Transaction seq# */
+ wpabuf_put_le16(buf, 1);
+
+ /* Status Code */
+ wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+ /* Own RSNE */
+ wpa_pasn_add_rsne(buf, NULL, pasn->akmp, pasn->cipher);
+
+ /* FILS Nonce */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + FILS_NONCE_LEN);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_NONCE);
+ wpabuf_put_data(buf, pasn->fils.nonce, FILS_NONCE_LEN);
+
+ /* FILS Session */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + FILS_SESSION_LEN);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_FILS_SESSION);
+ wpabuf_put_data(buf, pasn->fils.session, FILS_SESSION_LEN);
+
+ /* Wrapped Data (ERP) */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(buf, 1 + wpabuf_len(erp_msg));
+ wpabuf_put_u8(buf, WLAN_EID_EXT_WRAPPED_DATA);
+ wpabuf_put_buf(buf, erp_msg);
+
+ /*
+ * Calculate pending PMKID here so that we do not need to maintain a
+ * copy of the EAP-Initiate/Reauth message.
+ */
+ ret = fils_pmkid_erp(pasn->akmp, wpabuf_head(erp_msg),
+ wpabuf_len(erp_msg),
+ pasn->fils.erp_pmkid);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to get ERP PMKID");
+ goto fail;
+ }
+
+ wpabuf_free(erp_msg);
+ erp_msg = NULL;
+
+ wpa_hexdump_buf(MSG_DEBUG, "PASN: FILS: Authentication frame", buf);
+ return buf;
+fail:
+ wpabuf_free(erp_msg);
+ wpabuf_free(buf);
+ return NULL;
+}
+
+
+static void wpas_pasn_initiate_eapol(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct eapol_config eapol_conf;
+ struct wpa_ssid *ssid = pasn->ssid;
+
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Initiating EAPOL");
+
+ eapol_sm_notify_eap_success(wpa_s->eapol, false);
+ eapol_sm_notify_eap_fail(wpa_s->eapol, false);
+ eapol_sm_notify_portControl(wpa_s->eapol, Auto);
+
+ os_memset(&eapol_conf, 0, sizeof(eapol_conf));
+ eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+ eapol_conf.workaround = ssid->eap_workaround;
+
+ eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
+}
+
+
+static struct wpabuf * wpas_pasn_wd_fils_auth(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct wpa_bss *bss;
+ const u8 *indic;
+ u16 fils_info;
+
+ wpa_printf(MSG_DEBUG, "PASN: FILS: wrapped data - completed=%u",
+ pasn->fils.completed);
+
+ /* Nothing to add as we are done */
+ if (pasn->fils.completed)
+ return NULL;
+
+ if (!pasn->ssid) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: No network block");
+ return NULL;
+ }
+
+ bss = wpa_bss_get_bssid(wpa_s, pasn->bssid);
+ if (!bss) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: BSS not found");
+ return NULL;
+ }
+
+ indic = wpa_bss_get_ie(bss, WLAN_EID_FILS_INDICATION);
+ if (!indic || indic[1] < 2) {
+ wpa_printf(MSG_DEBUG, "PASN: Missing FILS Indication IE");
+ return NULL;
+ }
+
+ fils_info = WPA_GET_LE16(indic + 2);
+ if (!(fils_info & BIT(9))) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS auth without PFS not supported");
+ return NULL;
+ }
+
+ wpas_pasn_initiate_eapol(wpa_s);
+
+ return wpas_pasn_fils_build_auth(wpa_s);
+}
+
+
+static int wpas_pasn_wd_fils_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsne_data;
+ u8 rmsk[ERP_MAX_KEY_LEN];
+ size_t rmsk_len;
+ u8 anonce[FILS_NONCE_LEN];
+ const u8 *data;
+ size_t buf_len;
+ struct wpabuf *fils_wd = NULL;
+ u16 alg, seq, status;
+ int ret;
+
+ if (!wd)
+ return -1;
+
+ data = wpabuf_head(wd);
+ buf_len = wpabuf_len(wd);
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: Authentication frame len=%zu",
+ data, buf_len);
+
+ /* first handle the header */
+ if (buf_len < 6) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Buffer too short");
+ return -1;
+ }
+
+ alg = WPA_GET_LE16(data);
+ seq = WPA_GET_LE16(data + 2);
+ status = WPA_GET_LE16(data + 4);
+
+ wpa_printf(MSG_DEBUG, "PASN: FILS: commit: alg=%u, seq=%u, status=%u",
+ alg, seq, status);
+
+ if (alg != WLAN_AUTH_FILS_SK || seq != 2 ||
+ status != WLAN_STATUS_SUCCESS) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Dropping peer authentication");
+ return -1;
+ }
+
+ data += 6;
+ buf_len -= 6;
+
+ if (ieee802_11_parse_elems(data, buf_len, &elems, 1) == ParseFailed) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Could not parse elements");
+ return -1;
+ }
+
+ if (!elems.rsn_ie || !elems.fils_nonce || !elems.fils_nonce ||
+ !elems.wrapped_data) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Missing IEs");
+ return -1;
+ }
+
+ ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsne_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed parsing RNSE");
+ return -1;
+ }
+
+ ret = wpa_pasn_validate_rsne(&rsne_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed validating RSNE");
+ return -1;
+ }
+
+ if (rsne_data.num_pmkid) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Not expecting PMKID in RSNE");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: ANonce", elems.fils_nonce,
+ FILS_NONCE_LEN);
+ os_memcpy(anonce, elems.fils_nonce, FILS_NONCE_LEN);
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: FILS Session", elems.fils_session,
+ FILS_SESSION_LEN);
+
+ if (os_memcmp(pasn->fils.session, elems.fils_session,
+ FILS_SESSION_LEN)) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Session mismatch");
+ return -1;
+ }
+
+ fils_wd = ieee802_11_defrag(&elems, WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_WRAPPED_DATA);
+
+ if (!fils_wd) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FILS: Failed getting wrapped data");
+ return -1;
+ }
+
+ eapol_sm_process_erp_finish(wpa_s->eapol, wpabuf_head(fils_wd),
+ wpabuf_len(fils_wd));
+
+ wpabuf_free(fils_wd);
+ fils_wd = NULL;
+
+ if (eapol_sm_failed(wpa_s->eapol)) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: ERP finish failed");
+ return -1;
+ }
+
+ rmsk_len = ERP_MAX_KEY_LEN;
+ ret = eapol_sm_get_key(wpa_s->eapol, rmsk, rmsk_len);
+
+ if (ret == PMK_LEN) {
+ rmsk_len = PMK_LEN;
+ ret = eapol_sm_get_key(wpa_s->eapol, rmsk, rmsk_len);
+ }
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed getting RMSK");
+ return -1;
+ }
+
+ ret = fils_rmsk_to_pmk(pasn->akmp, rmsk, rmsk_len,
+ pasn->fils.nonce, anonce, NULL, 0,
+ pasn->pmk, &pasn->pmk_len);
+
+ forced_memzero(rmsk, sizeof(rmsk));
+
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: FILS: Failed to derive PMK");
+ return -1;
+ }
+
+ wpa_hexdump(MSG_DEBUG, "PASN: FILS: PMKID", pasn->fils.erp_pmkid,
+ PMKID_LEN);
+
+ wpa_printf(MSG_DEBUG, "PASN: FILS: ERP processing succeeded");
+
+ wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
+ pasn->pmk_len, pasn->fils.erp_pmkid,
+ pasn->bssid, pasn->akmp);
+
+ pasn->fils.completed = true;
+ return 0;
+}
+
+#endif /* CONFIG_FILS */
+
+
+static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+
+ if (pasn->using_pmksa)
+ return NULL;
+
+ switch (pasn->akmp) {
+ case WPA_KEY_MGMT_PASN:
+ /* no wrapped data */
+ return NULL;
+ case WPA_KEY_MGMT_SAE:
+#ifdef CONFIG_SAE
+ if (pasn->trans_seq == 0)
+ return wpas_pasn_wd_sae_commit(wpa_s);
+ if (pasn->trans_seq == 2)
+ return wpas_pasn_wd_sae_confirm(wpa_s);
+#endif /* CONFIG_SAE */
+ wpa_printf(MSG_ERROR,
+ "PASN: SAE: Cannot derive wrapped data");
+ return NULL;
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+#ifdef CONFIG_FILS
+ return wpas_pasn_wd_fils_auth(wpa_s);
+#endif /* CONFIG_FILS */
+ case WPA_KEY_MGMT_FT_PSK:
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ /*
+ * Wrapped data with these AKMs is optional and is only needed
+ * for further validation of FT security parameters. For now do
+ * not use them.
+ */
+ return NULL;
+ default:
+ wpa_printf(MSG_ERROR,
+ "PASN: TODO: Wrapped data for akmp=0x%x",
+ pasn->akmp);
+ return NULL;
+ }
+}
+
+
+static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
+{
+ if (pasn->using_pmksa)
+ return WPA_PASN_WRAPPED_DATA_NO;
+
+ /* Note: Valid AKMP is expected to already be validated */
+ switch (pasn->akmp) {
+ case WPA_KEY_MGMT_SAE:
+ return WPA_PASN_WRAPPED_DATA_SAE;
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+ return WPA_PASN_WRAPPED_DATA_FILS_SK;
+ case WPA_KEY_MGMT_FT_PSK:
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ /*
+ * Wrapped data with these AKMs is optional and is only needed
+ * for further validation of FT security parameters. For now do
+ * not use them.
+ */
+ return WPA_PASN_WRAPPED_DATA_NO;
+ case WPA_KEY_MGMT_PASN:
+ default:
+ return WPA_PASN_WRAPPED_DATA_NO;
+ }
+}
+
+
+static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s,
+ const struct wpabuf *comeback)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct wpabuf *buf, *pubkey = NULL, *wrapped_data_buf = NULL;
+ const u8 *pmkid;
+ u8 wrapped_data;
+ int ret;
+ u16 capab;
+
+ wpa_printf(MSG_DEBUG, "PASN: Building frame 1");
+
+ if (pasn->trans_seq)
+ return NULL;
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ goto fail;
+
+ /* Get public key */
+ pubkey = crypto_ecdh_get_pubkey(pasn->ecdh, 0);
+ pubkey = wpabuf_zeropad(pubkey, crypto_ecdh_prime_len(pasn->ecdh));
+ if (!pubkey) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to get pubkey");
+ goto fail;
+ }
+
+ wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+
+ wpa_pasn_build_auth_header(buf, pasn->bssid,
+ wpa_s->own_addr, pasn->bssid,
+ pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
+
+ pmkid = NULL;
+ if (wpa_key_mgmt_ft(pasn->akmp)) {
+ ret = wpa_pasn_ft_derive_pmk_r1(wpa_s->wpa, pasn->akmp,
+ pasn->bssid,
+ pasn->pmk_r1,
+ &pasn->pmk_r1_len,
+ pasn->pmk_r1_name);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: FT: Failed to derive keys");
+ goto fail;
+ }
+
+ pmkid = pasn->pmk_r1_name;
+ } else if (wrapped_data != WPA_PASN_WRAPPED_DATA_NO) {
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
+ NULL, NULL, pasn->akmp);
+ if (pmksa)
+ pmkid = pmksa->pmkid;
+
+ /*
+ * Note: Even when PMKSA is available, also add wrapped data as
+ * it is possible that the PMKID is no longer valid at the AP.
+ */
+ wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
+ }
+
+ if (wpa_pasn_add_rsne(buf, pmkid, pasn->akmp, pasn->cipher) < 0)
+ goto fail;
+
+ if (!wrapped_data_buf)
+ wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
+
+ wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
+ pubkey, true, comeback, -1);
+
+ if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
+ goto fail;
+
+ /* Add own RNSXE */
+ capab = 0;
+ capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
+ if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_LTF);
+ if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_RTT)
+ capab |= BIT(WLAN_RSNX_CAPAB_SECURE_RTT);
+ if (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_PROT_RANGE_NEG)
+ capab |= BIT(WLAN_RSNX_CAPAB_PROT_RANGE_NEG);
+ wpa_pasn_add_rsnxe(buf, capab);
+
+ ret = pasn_auth_frame_hash(pasn->akmp, pasn->cipher,
+ wpabuf_head_u8(buf) + IEEE80211_HDRLEN,
+ wpabuf_len(buf) - IEEE80211_HDRLEN,
+ pasn->hash);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to compute hash");
+ goto fail;
+ }
+
+ pasn->trans_seq++;
+
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(pubkey);
+
+ wpa_printf(MSG_DEBUG, "PASN: Frame 1: Success");
+ return buf;
+fail:
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(pubkey);
+ wpabuf_free(buf);
+ return NULL;
+}
+
+
+static struct wpabuf * wpas_pasn_build_auth_3(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct wpabuf *buf, *wrapped_data_buf = NULL;
+ u8 mic[WPA_PASN_MAX_MIC_LEN];
+ u8 mic_len, data_len;
+ const u8 *data;
+ u8 *ptr;
+ u8 wrapped_data;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "PASN: Building frame 3");
+
+ if (pasn->trans_seq != 2)
+ return NULL;
+
+ buf = wpabuf_alloc(1500);
+ if (!buf)
+ goto fail;
+
+ wrapped_data = wpas_pasn_get_wrapped_data_format(pasn);
+
+ wpa_pasn_build_auth_header(buf, pasn->bssid,
+ wpa_s->own_addr, pasn->bssid,
+ pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
+
+ wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
+
+ if (!wrapped_data_buf)
+ wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
+
+ wpa_pasn_add_parameter_ie(buf, pasn->group, wrapped_data,
+ NULL, false, NULL, -1);
+
+ if (wpa_pasn_add_wrapped_data(buf, wrapped_data_buf) < 0)
+ goto fail;
+ wpabuf_free(wrapped_data_buf);
+ wrapped_data_buf = NULL;
+
+ /* Add the MIC */
+ mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+ wpabuf_put_u8(buf, WLAN_EID_MIC);
+ wpabuf_put_u8(buf, mic_len);
+ ptr = wpabuf_put(buf, mic_len);
+
+ os_memset(ptr, 0, mic_len);
+
+ data = wpabuf_head_u8(buf) + IEEE80211_HDRLEN;
+ data_len = wpabuf_len(buf) - IEEE80211_HDRLEN;
+
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ wpa_s->own_addr, pasn->bssid,
+ pasn->hash, mic_len * 2, data, data_len, mic);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: frame 3: Failed MIC calculation");
+ goto fail;
+ }
+
+#ifdef CONFIG_TESTING_OPTIONS
+ if (wpa_s->conf->pasn_corrupt_mic) {
+ wpa_printf(MSG_DEBUG, "PASN: frame 3: Corrupt MIC");
+ mic[0] = ~mic[0];
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+ os_memcpy(ptr, mic, mic_len);
+
+ pasn->trans_seq++;
+
+ wpa_printf(MSG_DEBUG, "PASN: frame 3: Success");
+ return buf;
+fail:
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ wpabuf_free(wrapped_data_buf);
+ wpabuf_free(buf);
+ return NULL;
+}
+
+
+static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+
+ wpa_printf(MSG_DEBUG, "PASN: Reset");
+
+ crypto_ecdh_deinit(pasn->ecdh);
+ pasn->ecdh = NULL;
+
+ wpas_pasn_cancel_auth_work(wpa_s);
+ wpa_s->pasn_auth_work = NULL;
+
+ eloop_cancel_timeout(wpas_pasn_auth_work_timeout, wpa_s, NULL);
+
+ pasn->akmp = 0;
+ pasn->cipher = 0;
+ pasn->group = 0;
+ pasn->trans_seq = 0;
+ pasn->pmk_len = 0;
+ pasn->using_pmksa = false;
+
+ forced_memzero(pasn->pmk, sizeof(pasn->pmk));
+ forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
+ forced_memzero(&pasn->hash, sizeof(pasn->hash));
+
+ wpabuf_free(pasn->beacon_rsne_rsnxe);
+ pasn->beacon_rsne_rsnxe = NULL;
+
+ wpabuf_free(pasn->comeback);
+ pasn->comeback = NULL;
+ pasn->comeback_after = 0;
+
+#ifdef CONFIG_SAE
+ sae_clear_data(&pasn->sae);
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ os_memset(&pasn->fils, 0, sizeof(pasn->fils));
+#endif /* CONFIG_FILS*/
+
+#ifdef CONFIG_IEEE80211R
+ forced_memzero(pasn->pmk_r1, sizeof(pasn->pmk_r1));
+ pasn->pmk_r1_len = 0;
+ os_memset(pasn->pmk_r1_name, 0, sizeof(pasn->pmk_r1_name));
+#endif /* CONFIG_IEEE80211R */
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+}
+
+
+static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
+ struct wpa_ie_data *rsn_data,
+ struct wpa_pasn_params_data *pasn_data,
+ struct wpabuf *wrapped_data)
+{
+ static const u8 pasn_default_pmk[] = {'P', 'M', 'K', 'z'};
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+
+ os_memset(pasn->pmk, 0, sizeof(pasn->pmk));
+ pasn->pmk_len = 0;
+
+ if (pasn->akmp == WPA_KEY_MGMT_PASN) {
+ wpa_printf(MSG_DEBUG, "PASN: Using default PMK");
+
+ pasn->pmk_len = WPA_PASN_PMK_LEN;
+ os_memcpy(pasn->pmk, pasn_default_pmk,
+ sizeof(pasn_default_pmk));
+ return 0;
+ }
+
+ if (wpa_key_mgmt_ft(pasn->akmp)) {
+#ifdef CONFIG_IEEE80211R
+ wpa_printf(MSG_DEBUG, "PASN: FT: Using PMK-R1");
+ pasn->pmk_len = pasn->pmk_r1_len;
+ os_memcpy(pasn->pmk, pasn->pmk_r1, pasn->pmk_r1_len);
+ pasn->using_pmksa = true;
+ return 0;
+#else /* CONFIG_IEEE80211R */
+ wpa_printf(MSG_DEBUG, "PASN: FT: Not supported");
+ return -1;
+#endif /* CONFIG_IEEE80211R */
+ }
+
+ if (rsn_data->num_pmkid) {
+ struct rsn_pmksa_cache_entry *pmksa;
+
+ pmksa = wpa_sm_pmksa_cache_get(wpa_s->wpa, pasn->bssid,
+ rsn_data->pmkid, NULL,
+ pasn->akmp);
+ if (pmksa) {
+ wpa_printf(MSG_DEBUG, "PASN: Using PMKSA");
+
+ pasn->pmk_len = pmksa->pmk_len;
+ os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
+ pasn->using_pmksa = true;
+
+ return 0;
+ }
+ }
+
+#ifdef CONFIG_SAE
+ if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+ int ret;
+
+ ret = wpas_pasn_wd_sae_rx(wpa_s, wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing SAE wrapped data");
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE");
+ pasn->pmk_len = PMK_LEN;
+ os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN);
+
+ wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
+ pasn->pmk_len, pasn->sae.pmkid,
+ pasn->bssid, pasn->akmp);
+ return 0;
+ }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_FILS
+ if (pasn->akmp == WPA_KEY_MGMT_FILS_SHA256 ||
+ pasn->akmp == WPA_KEY_MGMT_FILS_SHA384) {
+ int ret;
+
+ ret = wpas_pasn_wd_fils_rx(wpa_s, wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed processing FILS wrapped data");
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -1;
+ }
+
+ return 0;
+ }
+#endif /* CONFIG_FILS */
+
+ /* TODO: Derive PMK based on wrapped data */
+ wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK");
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ return -1;
+}
+
+
+static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int akmp, int cipher, u16 group, int freq,
+ const u8 *beacon_rsne, u8 beacon_rsne_len,
+ const u8 *beacon_rsnxe, u8 beacon_rsnxe_len,
+ int network_id, struct wpabuf *comeback)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct wpa_ssid *ssid = NULL;
+ struct wpabuf *frame;
+ int ret;
+
+ /* TODO: Currently support only ECC groups */
+ if (!dragonfly_suitable_group(group, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Reject unsuitable group %u", group);
+ return -1;
+ }
+
+ ssid = wpa_config_get_network(wpa_s->conf, network_id);
+
+ switch (akmp) {
+ case WPA_KEY_MGMT_PASN:
+ break;
+#ifdef CONFIG_SAE
+ case WPA_KEY_MGMT_SAE:
+ if (!ssid) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: No network profile found for SAE");
+ return -1;
+ }
+
+ if (!ieee802_11_rsnx_capab(beacon_rsnxe,
+ WLAN_RSNX_CAPAB_SAE_H2E)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: AP does not support SAE H2E");
+ return -1;
+ }
+
+ if (wpas_pasn_sae_setup_pt(wpa_s, ssid, group) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to derive PT");
+ return -1;
+ }
+
+ pasn->sae.state = SAE_NOTHING;
+ pasn->sae.send_confirm = 0;
+ pasn->ssid = ssid;
+ break;
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_FILS
+ case WPA_KEY_MGMT_FILS_SHA256:
+ case WPA_KEY_MGMT_FILS_SHA384:
+ pasn->ssid = ssid;
+ break;
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R
+ case WPA_KEY_MGMT_FT_PSK:
+ case WPA_KEY_MGMT_FT_IEEE8021X:
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ break;
+#endif /* CONFIG_IEEE80211R */
+ default:
+ wpa_printf(MSG_ERROR, "PASN: Unsupported AKMP=0x%x", akmp);
+ return -1;
+ }
+
+ pasn->ecdh = crypto_ecdh_init(group);
+ if (!pasn->ecdh) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to init ECDH");
+ goto fail;
+ }
+
+ pasn->beacon_rsne_rsnxe = wpabuf_alloc(beacon_rsne_len +
+ beacon_rsnxe_len);
+ if (!pasn->beacon_rsne_rsnxe) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed storing beacon RSNE/RSNXE");
+ goto fail;
+ }
+
+ wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsne, beacon_rsne_len);
+ if (beacon_rsnxe && beacon_rsnxe_len)
+ wpabuf_put_data(pasn->beacon_rsne_rsnxe, beacon_rsnxe,
+ beacon_rsnxe_len);
+
+ pasn->akmp = akmp;
+ pasn->cipher = cipher;
+ pasn->group = group;
+ pasn->freq = freq;
+
+ if (wpa_s->conf->force_kdk_derivation ||
+ (wpa_s->drv_flags2 & WPA_DRIVER_FLAGS2_SEC_LTF &&
+ ieee802_11_rsnx_capab(beacon_rsnxe, WLAN_RSNX_CAPAB_SECURE_LTF)))
+ pasn->kdk_len = WPA_KDK_MAX_LEN;
+ else
+ pasn->kdk_len = 0;
+ wpa_printf(MSG_DEBUG, "PASN: kdk_len=%zu", pasn->kdk_len);
+
+ os_memcpy(pasn->bssid, bssid, ETH_ALEN);
+
+ wpa_printf(MSG_DEBUG,
+ "PASN: Init: " MACSTR " akmp=0x%x, cipher=0x%x, group=%u",
+ MAC2STR(pasn->bssid), pasn->akmp, pasn->cipher,
+ pasn->group);
+
+ frame = wpas_pasn_build_auth_1(wpa_s, comeback);
+ if (!frame) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed building 1st auth frame");
+ goto fail;
+ }
+
+ ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
+ pasn->freq, 1000);
+
+ wpabuf_free(frame);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed sending 1st auth frame");
+ goto fail;
+ }
+
+ eloop_register_timeout(2, 0, wpas_pasn_auth_work_timeout, wpa_s, NULL);
+ return 0;
+
+fail:
+ return -1;
+}
+
+
+static struct wpa_bss * wpas_pasn_allowed(struct wpa_supplicant *wpa_s,
+ const u8 *bssid, int akmp, int cipher)
+{
+ struct wpa_bss *bss;
+ const u8 *rsne;
+ struct wpa_ie_data rsne_data;
+ int ret;
+
+ if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Not doing authentication with current BSS");
+ return NULL;
+ }
+
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+ if (!bss) {
+ wpa_printf(MSG_DEBUG, "PASN: BSS not found");
+ return NULL;
+ }
+
+ rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (!rsne) {
+ wpa_printf(MSG_DEBUG, "PASN: BSS without RSNE");
+ return NULL;
+ }
+
+ ret = wpa_parse_wpa_ie(rsne, *(rsne + 1) + 2, &rsne_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed parsing RSNE data");
+ return NULL;
+ }
+
+ if (!(rsne_data.key_mgmt & akmp) ||
+ !(rsne_data.pairwise_cipher & cipher)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: AP does not support requested AKMP or cipher");
+ return NULL;
+ }
+
+ return bss;
+}
+
+
+static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
+{
+ struct wpa_supplicant *wpa_s = work->wpa_s;
+ struct wpa_pasn_auth_work *awork = work->ctx;
+ struct wpa_bss *bss;
+ const u8 *rsne, *rsnxe;
+ int ret;
+
+ wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: deinit=%d", deinit);
+
+ if (deinit) {
+ if (work->started) {
+ eloop_cancel_timeout(wpas_pasn_auth_work_timeout,
+ wpa_s, NULL);
+ wpa_s->pasn_auth_work = NULL;
+ }
+
+ wpas_pasn_free_auth_work(awork);
+ return;
+ }
+
+ /*
+ * It is possible that by the time the callback is called, the PASN
+ * authentication is not allowed, e.g., a connection with the AP was
+ * established.
+ */
+ bss = wpas_pasn_allowed(wpa_s, awork->bssid, awork->akmp,
+ awork->cipher);
+ if (!bss) {
+ wpa_printf(MSG_DEBUG, "PASN: auth_start_cb: Not allowed");
+ goto fail;
+ }
+
+ rsne = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+ if (!rsne) {
+ wpa_printf(MSG_DEBUG, "PASN: BSS without RSNE");
+ goto fail;
+ }
+
+ rsnxe = wpa_bss_get_ie(bss, WLAN_EID_RSNX);
+
+ ret = wpas_pasn_start(wpa_s, awork->bssid, awork->akmp, awork->cipher,
+ awork->group, bss->freq, rsne, *(rsne + 1) + 2,
+ rsnxe, rsnxe ? *(rsnxe + 1) + 2 : 0,
+ awork->network_id, awork->comeback);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed to start PASN authentication");
+ goto fail;
+ }
+
+ /* comeback token is no longer needed at this stage */
+ wpabuf_free(awork->comeback);
+ awork->comeback = NULL;
+
+ wpa_s->pasn_auth_work = work;
+ return;
+fail:
+ wpas_pasn_free_auth_work(awork);
+ work->ctx = NULL;
+ radio_work_done(work);
+}
+
+
+int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ int akmp, int cipher, u16 group, int network_id,
+ const u8 *comeback, size_t comeback_len)
+{
+ struct wpa_pasn_auth_work *awork;
+ struct wpa_bss *bss;
+
+ wpa_printf(MSG_DEBUG, "PASN: Start: " MACSTR " akmp=0x%x, cipher=0x%x",
+ MAC2STR(bssid), akmp, cipher);
+
+ /*
+ * TODO: Consider modifying the offchannel logic to handle additional
+ * Management frames other then Action frames. For now allow PASN only
+ * with drivers that support off-channel TX.
+ */
+ if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Driver does not support offchannel TX");
+ return -1;
+ }
+
+ if (radio_work_pending(wpa_s, "pasn-start-auth")) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: send_auth: Work is already pending");
+ return -1;
+ }
+
+ if (wpa_s->pasn_auth_work) {
+ wpa_printf(MSG_DEBUG, "PASN: send_auth: Already in progress");
+ return -1;
+ }
+
+ bss = wpas_pasn_allowed(wpa_s, bssid, akmp, cipher);
+ if (!bss)
+ return -1;
+
+ wpas_pasn_reset(wpa_s);
+
+ awork = os_zalloc(sizeof(*awork));
+ if (!awork)
+ return -1;
+
+ os_memcpy(awork->bssid, bssid, ETH_ALEN);
+ awork->akmp = akmp;
+ awork->cipher = cipher;
+ awork->group = group;
+ awork->network_id = network_id;
+
+ if (comeback && comeback_len) {
+ awork->comeback = wpabuf_alloc_copy(comeback, comeback_len);
+ if (!awork->comeback) {
+ wpas_pasn_free_auth_work(awork);
+ return -1;
+ }
+ }
+
+ if (radio_add_work(wpa_s, bss->freq, "pasn-start-auth", 1,
+ wpas_pasn_auth_start_cb, awork) < 0) {
+ wpas_pasn_free_auth_work(awork);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: Auth work successfully added");
+ return 0;
+}
+
+
+void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+
+ if (!wpa_s->pasn.ecdh)
+ return;
+
+ wpa_printf(MSG_DEBUG, "PASN: Stopping authentication");
+
+ wpas_pasn_auth_status(wpa_s, pasn->bssid, pasn->akmp, pasn->cipher,
+ pasn->status, pasn->comeback,
+ pasn->comeback_after);
+
+ wpas_pasn_reset(wpa_s);
+}
+
+
+static int wpas_pasn_immediate_retry(struct wpa_supplicant *wpa_s,
+ struct wpas_pasn *pasn,
+ struct wpa_pasn_params_data *params)
+{
+ int akmp = pasn->akmp;
+ int cipher = pasn->cipher;
+ u16 group = pasn->group;
+ u8 bssid[ETH_ALEN];
+ int network_id = pasn->ssid ? pasn->ssid->id : 0;
+
+ wpa_printf(MSG_DEBUG, "PASN: Immediate retry");
+ os_memcpy(bssid, pasn->bssid, ETH_ALEN);
+ wpas_pasn_reset(wpa_s);
+
+ return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group,
+ network_id,
+ params->comeback, params->comeback_len);
+}
+
+
+int wpas_pasn_auth_rx(struct wpa_supplicant *wpa_s,
+ const struct ieee80211_mgmt *mgmt, size_t len)
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ struct ieee802_11_elems elems;
+ struct wpa_ie_data rsn_data;
+ struct wpa_pasn_params_data pasn_params;
+ struct wpabuf *wrapped_data = NULL, *secret = NULL, *frame = NULL;
+ u8 mic[WPA_PASN_MAX_MIC_LEN], out_mic[WPA_PASN_MAX_MIC_LEN];
+ u8 mic_len;
+ u16 status;
+ int ret, inc_y;
+ u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_AUTH << 4));
+
+ if (!wpa_s->pasn_auth_work || !mgmt ||
+ len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+ return -2;
+
+ /* Not an Authentication frame; do nothing */
+ if ((mgmt->frame_control & fc) != fc)
+ return -2;
+
+ /* Not our frame; do nothing */
+ if (os_memcmp(mgmt->da, wpa_s->own_addr, ETH_ALEN) != 0 ||
+ os_memcmp(mgmt->sa, pasn->bssid, ETH_ALEN) != 0 ||
+ os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN) != 0)
+ return -2;
+
+ /* Not PASN; do nothing */
+ if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
+ return -2;
+
+ if (mgmt->u.auth.auth_transaction !=
+ host_to_le16(pasn->trans_seq + 1)) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: RX: Invalid transaction sequence: (%u != %u)",
+ le_to_host16(mgmt->u.auth.auth_transaction),
+ pasn->trans_seq + 1);
+ return -1;
+ }
+
+ status = le_to_host16(mgmt->u.auth.status_code);
+
+ if (status != WLAN_STATUS_SUCCESS &&
+ status != WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Authentication rejected - status=%u", status);
+ pasn->status = status;
+ wpas_pasn_auth_stop(wpa_s);
+ 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,
+ "PASN: Failed parsing Authentication frame");
+ goto fail;
+ }
+
+ /* Check that the MIC IE exists. Save it and zero out the memory */
+ mic_len = pasn_mic_len(pasn->akmp, pasn->cipher);
+ if (status == WLAN_STATUS_SUCCESS) {
+ if (!elems.mic || elems.mic_len != mic_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid MIC. Expecting len=%u",
+ mic_len);
+ goto fail;
+ } else {
+ os_memcpy(mic, elems.mic, mic_len);
+ /* TODO: Clean this up.. Should not be modifying the
+ * received message buffer. */
+ os_memset((u8 *) elems.mic, 0, mic_len);
+ }
+ }
+
+ if (!elems.pasn_params || !elems.pasn_params_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Missing PASN Parameters IE");
+ goto fail;
+ }
+
+ ret = wpa_pasn_parse_parameter_ie(elems.pasn_params - 3,
+ elems.pasn_params_len + 3,
+ true, &pasn_params);
+ if (ret) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Failed validation PASN of Parameters IE");
+ goto fail;
+ }
+
+ if (status == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Authentication temporarily rejected");
+
+ if (pasn_params.comeback && pasn_params.comeback_len) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Comeback token available. After=%u",
+ pasn_params.after);
+
+ if (!pasn_params.after)
+ return wpas_pasn_immediate_retry(wpa_s, pasn,
+ &pasn_params);
+
+ pasn->comeback = wpabuf_alloc_copy(
+ pasn_params.comeback, pasn_params.comeback_len);
+ if (pasn->comeback)
+ pasn->comeback_after = pasn_params.after;
+ }
+
+ pasn->status = status;
+ goto fail;
+ }
+
+ ret = wpa_parse_wpa_ie(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed parsing RNSE");
+ goto fail;
+ }
+
+ ret = wpa_pasn_validate_rsne(&rsn_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed validating RSNE");
+ goto fail;
+ }
+
+ if (pasn->akmp != rsn_data.key_mgmt ||
+ pasn->cipher != rsn_data.pairwise_cipher) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in AKMP/cipher");
+ goto fail;
+ }
+
+ if (pasn->group != pasn_params.group) {
+ wpa_printf(MSG_DEBUG, "PASN: Mismatch in group");
+ goto fail;
+ }
+
+ if (!pasn_params.pubkey || !pasn_params.pubkey_len) {
+ wpa_printf(MSG_DEBUG, "PASN: Invalid public key");
+ goto fail;
+ }
+
+ if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_UNCOMPRESSED) {
+ inc_y = 1;
+ } else if (pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_0 ||
+ pasn_params.pubkey[0] == WPA_PASN_PUBKEY_COMPRESSED_1) {
+ inc_y = 0;
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Invalid first octet in pubkey=0x%x",
+ pasn_params.pubkey[0]);
+ goto fail;
+ }
+
+ secret = crypto_ecdh_set_peerkey(pasn->ecdh, inc_y,
+ pasn_params.pubkey + 1,
+ pasn_params.pubkey_len - 1);
+
+ if (!secret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive shared secret");
+ goto fail;
+ }
+
+ if (pasn_params.wrapped_data_format != WPA_PASN_WRAPPED_DATA_NO) {
+ wrapped_data = ieee802_11_defrag(&elems,
+ WLAN_EID_EXTENSION,
+ WLAN_EID_EXT_WRAPPED_DATA);
+
+ if (!wrapped_data) {
+ wpa_printf(MSG_DEBUG, "PASN: Missing wrapped data");
+ goto fail;
+ }
+ }
+
+ ret = wpas_pasn_set_pmk(wpa_s, &rsn_data, &pasn_params, wrapped_data);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to set PMK");
+ goto fail;
+ }
+
+ ret = pasn_pmk_to_ptk(pasn->pmk, pasn->pmk_len,
+ wpa_s->own_addr, pasn->bssid,
+ wpabuf_head(secret), wpabuf_len(secret),
+ &pasn->ptk, pasn->akmp, pasn->cipher,
+ pasn->kdk_len);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed to derive PTK");
+ goto fail;
+ }
+
+ wpabuf_free(wrapped_data);
+ wrapped_data = NULL;
+ wpabuf_free(secret);
+ secret = NULL;
+
+ /* Verify the MIC */
+ ret = pasn_mic(pasn->ptk.kck, pasn->akmp, pasn->cipher,
+ pasn->bssid, wpa_s->own_addr,
+ wpabuf_head(pasn->beacon_rsne_rsnxe),
+ wpabuf_len(pasn->beacon_rsne_rsnxe),
+ (u8 *) &mgmt->u.auth,
+ len - offsetof(struct ieee80211_mgmt, u.auth),
+ out_mic);
+
+ wpa_hexdump_key(MSG_DEBUG, "PASN: Frame MIC", mic, mic_len);
+ if (ret || os_memcmp(mic, out_mic, mic_len) != 0) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed MIC verification");
+ goto fail;
+ }
+
+ pasn->trans_seq++;
+
+ wpa_printf(MSG_DEBUG, "PASN: Success verifying Authentication frame");
+
+ frame = wpas_pasn_build_auth_3(wpa_s);
+ if (!frame) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed building 3rd auth frame");
+ goto fail;
+ }
+
+ ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(frame), wpabuf_len(frame), 0,
+ pasn->freq, 100);
+ wpabuf_free(frame);
+ if (ret) {
+ wpa_printf(MSG_DEBUG, "PASN: Failed sending 3st auth frame");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: Success sending last frame. Store PTK");
+
+ ptksa_cache_add(wpa_s->ptksa, pasn->bssid, pasn->cipher,
+ dot11RSNAConfigPMKLifetime, &pasn->ptk);
+
+ forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
+
+ pasn->status = WLAN_STATUS_SUCCESS;
+ return 0;
+fail:
+ wpa_printf(MSG_DEBUG, "PASN: Failed RX processing - terminating");
+ wpabuf_free(wrapped_data);
+ wpabuf_free(secret);
+
+ /*
+ * TODO: In case of an error the standard allows to silently drop
+ * the frame and terminate the authentication exchange. However, better
+ * reply to the AP with an error status.
+ */
+ if (status == WLAN_STATUS_SUCCESS)
+ pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ else
+ pasn->status = status;
+
+ wpas_pasn_auth_stop(wpa_s);
+ return -1;
+}
+
+
+int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s,
+ const u8 *data, size_t data_len, u8 acked)
+
+{
+ struct wpas_pasn *pasn = &wpa_s->pasn;
+ const struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) data;
+ u16 fc = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_AUTH << 4));
+
+ wpa_printf(MSG_DEBUG, "PASN: auth_tx_status: acked=%u", acked);
+
+ if (!wpa_s->pasn_auth_work) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: auth_tx_status: no work in progress");
+ return -1;
+ }
+
+ if (!mgmt ||
+ data_len < offsetof(struct ieee80211_mgmt, u.auth.variable))
+ return -1;
+
+ /* Not an authentication frame; do nothing */
+ if ((mgmt->frame_control & fc) != fc)
+ return -1;
+
+ /* Not our frame; do nothing */
+ if (os_memcmp(mgmt->da, pasn->bssid, ETH_ALEN) ||
+ os_memcmp(mgmt->sa, wpa_s->own_addr, ETH_ALEN) ||
+ os_memcmp(mgmt->bssid, pasn->bssid, ETH_ALEN))
+ return -1;
+
+ /* Not PASN; do nothing */
+ if (mgmt->u.auth.auth_alg != host_to_le16(WLAN_AUTH_PASN))
+ return -1;
+
+ if (mgmt->u.auth.auth_transaction != host_to_le16(pasn->trans_seq)) {
+ wpa_printf(MSG_ERROR,
+ "PASN: Invalid transaction sequence: (%u != %u)",
+ pasn->trans_seq,
+ le_to_host16(mgmt->u.auth.auth_transaction));
+ return 0;
+ }
+
+ wpa_printf(MSG_ERROR,
+ "PASN: auth with trans_seq=%u, acked=%u", pasn->trans_seq,
+ acked);
+
+ /*
+ * Even if the frame was not acked, do not treat this is an error, and
+ * try to complete the flow, relying on the PASN timeout callback to
+ * clean up.
+ */
+ if (pasn->trans_seq == 3) {
+ wpa_printf(MSG_DEBUG, "PASN: auth complete with: " MACSTR,
+ MAC2STR(pasn->bssid));
+ /*
+ * Either frame was not ACKed or it was ACKed but the trans_seq
+ * != 1, i.e., not expecting an RX frame, so we are done.
+ */
+ wpas_pasn_auth_stop(wpa_s);
+ }
+
+ return 0;
+}
+
+
+int wpas_pasn_deauthenticate(struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+ struct wpa_bss *bss;
+ struct wpabuf *buf;
+ struct ieee80211_mgmt *deauth;
+ int ret;
+
+ if (os_memcmp(wpa_s->bssid, bssid, ETH_ALEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "PASN: Cannot deauthenticate from current BSS");
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "PASN: deauth: Flushing all PTKSA entries for "
+ MACSTR, MAC2STR(bssid));
+ ptksa_cache_flush(wpa_s->ptksa, bssid, WPA_CIPHER_NONE);
+
+ bss = wpa_bss_get_bssid(wpa_s, bssid);
+ if (!bss) {
+ wpa_printf(MSG_DEBUG, "PASN: deauth: BSS not found");
+ return -1;
+ }
+
+ buf = wpabuf_alloc(64);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG, "PASN: deauth: Failed wpabuf allocate");
+ return -1;
+ }
+
+ deauth = wpabuf_put(buf, offsetof(struct ieee80211_mgmt,
+ u.deauth.variable));
+
+ deauth->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
+ (WLAN_FC_STYPE_DEAUTH << 4));
+
+ os_memcpy(deauth->da, bssid, ETH_ALEN);
+ os_memcpy(deauth->sa, wpa_s->own_addr, ETH_ALEN);
+ os_memcpy(deauth->bssid, bssid, ETH_ALEN);
+ deauth->u.deauth.reason_code =
+ host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+
+ /*
+ * Since we do not expect any response from the AP, implement the
+ * Deauthentication frame transmission using direct call to the driver
+ * without a radio work.
+ */
+ ret = wpa_drv_send_mlme(wpa_s, wpabuf_head(buf), wpabuf_len(buf), 1,
+ bss->freq, 0);
+
+ wpabuf_free(buf);
+ wpa_printf(MSG_DEBUG, "PASN: deauth: send_mlme ret=%d", ret);
+
+ return ret;
+}
diff --git a/contrib/wpa/wpa_supplicant/robust_av.c b/contrib/wpa/wpa_supplicant/robust_av.c
new file mode 100644
index 000000000000..39d282f0870d
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/robust_av.c
@@ -0,0 +1,155 @@
+/*
+ * wpa_supplicant - Robust AV procedures
+ * Copyright (c) 2020, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "common/wpa_ctrl.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "bss.h"
+
+
+void wpas_populate_mscs_descriptor_ie(struct robust_av_data *robust_av,
+ struct wpabuf *buf)
+{
+ u8 *len, *len1;
+
+ /* MSCS descriptor element */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ len = wpabuf_put(buf, 1);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_MSCS_DESCRIPTOR);
+ wpabuf_put_u8(buf, robust_av->request_type);
+ wpabuf_put_u8(buf, robust_av->up_bitmap);
+ wpabuf_put_u8(buf, robust_av->up_limit);
+ wpabuf_put_le32(buf, robust_av->stream_timeout);
+
+ if (robust_av->request_type != SCS_REQ_REMOVE) {
+ /* TCLAS mask element */
+ wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
+ len1 = wpabuf_put(buf, 1);
+ wpabuf_put_u8(buf, WLAN_EID_EXT_TCLAS_MASK);
+
+ /* Frame classifier */
+ wpabuf_put_data(buf, robust_av->frame_classifier,
+ robust_av->frame_classifier_len);
+ *len1 = (u8 *) wpabuf_put(buf, 0) - len1 - 1;
+ }
+
+ *len = (u8 *) wpabuf_put(buf, 0) - len - 1;
+}
+
+
+int wpas_send_mscs_req(struct wpa_supplicant *wpa_s)
+{
+ struct wpabuf *buf;
+ size_t buf_len;
+ int ret;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid)
+ return 0;
+
+ if (!wpa_bss_ext_capab(wpa_s->current_bss, WLAN_EXT_CAPAB_MSCS)) {
+ wpa_dbg(wpa_s, MSG_INFO,
+ "AP does not support MSCS - could not send MSCS Req");
+ return -1;
+ }
+
+ if (!wpa_s->mscs_setup_done &&
+ wpa_s->robust_av.request_type != SCS_REQ_ADD) {
+ wpa_msg(wpa_s, MSG_INFO,
+ "MSCS: Failed to send MSCS Request: request type invalid");
+ return -1;
+ }
+
+ buf_len = 3 + /* Action frame header */
+ 3 + /* MSCS descriptor IE header */
+ 1 + /* Request type */
+ 2 + /* User priority control */
+ 4 + /* Stream timeout */
+ 3 + /* TCLAS Mask IE header */
+ wpa_s->robust_av.frame_classifier_len;
+
+ buf = wpabuf_alloc(buf_len);
+ if (!buf) {
+ wpa_printf(MSG_ERROR, "Failed to allocate MSCS req");
+ return -1;
+ }
+
+ wpabuf_put_u8(buf, WLAN_ACTION_ROBUST_AV_STREAMING);
+ wpabuf_put_u8(buf, ROBUST_AV_MSCS_REQ);
+ wpa_s->robust_av.dialog_token++;
+ wpabuf_put_u8(buf, wpa_s->robust_av.dialog_token);
+
+ /* MSCS descriptor element */
+ wpas_populate_mscs_descriptor_ie(&wpa_s->robust_av, buf);
+
+ wpa_hexdump_buf(MSG_MSGDUMP, "MSCS Request", buf);
+ ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0);
+ if (ret < 0)
+ wpa_dbg(wpa_s, MSG_INFO, "MSCS: Failed to send MSCS Request");
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+void wpas_handle_robust_av_recv_action(struct wpa_supplicant *wpa_s,
+ const u8 *src, const u8 *buf, size_t len)
+{
+ u8 dialog_token;
+ u16 status_code;
+
+ if (len < 3)
+ return;
+
+ dialog_token = *buf++;
+ if (dialog_token != wpa_s->robust_av.dialog_token) {
+ wpa_printf(MSG_INFO,
+ "MSCS: Drop received frame due to dialog token mismatch: received:%u expected:%u",
+ dialog_token, wpa_s->robust_av.dialog_token);
+ return;
+ }
+
+ status_code = WPA_GET_LE16(buf);
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
+ " status_code=%u", MAC2STR(src), status_code);
+ wpa_s->mscs_setup_done = status_code == WLAN_STATUS_SUCCESS;
+}
+
+
+void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
+ const u8 *ies, size_t ies_len)
+{
+ const u8 *mscs_desc_ie, *mscs_status;
+ u16 status;
+
+ /* Process optional MSCS Status subelement when MSCS IE is in
+ * (Re)Association Response frame */
+ if (!ies || ies_len == 0 || !wpa_s->robust_av.valid_config)
+ return;
+
+ mscs_desc_ie = get_ie_ext(ies, ies_len, WLAN_EID_EXT_MSCS_DESCRIPTOR);
+ if (!mscs_desc_ie || mscs_desc_ie[1] <= 8)
+ return;
+
+ /* Subelements start after (ie_id(1) + ie_len(1) + ext_id(1) +
+ * request type(1) + upc(2) + stream timeout(4) =) 10.
+ */
+ mscs_status = get_ie(&mscs_desc_ie[10], mscs_desc_ie[1] - 8,
+ MCSC_SUBELEM_STATUS);
+ if (!mscs_status || mscs_status[1] < 2)
+ return;
+
+ status = WPA_GET_LE16(mscs_status + 2);
+ wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_MSCS_RESULT "bssid=" MACSTR
+ " status_code=%u", MAC2STR(bssid), status);
+ wpa_s->mscs_setup_done = status == WLAN_STATUS_SUCCESS;
+}
diff --git a/contrib/wpa/wpa_supplicant/twt.c b/contrib/wpa/wpa_supplicant/twt.c
new file mode 100644
index 000000000000..8ec2c85acb8d
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/twt.c
@@ -0,0 +1,142 @@
+/*
+ * wpa_supplicant - TWT
+ * Copyright (c) 2003-2016, 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 "utils/common.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+/**
+ * wpas_twt_send_setup - Send TWT Setup frame (Request) to our AP
+ * @wpa_s: Pointer to wpa_supplicant
+ * @dtok: Dialog token
+ * @exponent: Wake-interval exponent
+ * @mantissa: Wake-interval mantissa
+ * @min_twt: Minimum TWT wake duration in units of 256 usec
+ * @setup_cmd: 0 == request, 1 == suggest, etc. Table 9-297
+ * Returns: 0 in case of success, negative error code otherwise
+ *
+ */
+int wpas_twt_send_setup(struct wpa_supplicant *wpa_s, u8 dtok, int exponent,
+ int mantissa, u8 min_twt, int setup_cmd, u64 twt,
+ bool requestor, bool trigger, bool implicit,
+ bool flow_type, u8 flow_id, bool protection,
+ u8 twt_channel, u8 control)
+{
+ struct wpabuf *buf;
+ u16 req_type = 0;
+ int ret = 0;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) {
+ wpa_printf(MSG_DEBUG,
+ "TWT: No connection - cannot send TWT Setup frame");
+ return -ENOTCONN;
+ }
+
+ /* 3 = Action category + Action code + Dialog token */
+ /* 17 = TWT element */
+ buf = wpabuf_alloc(3 + 17);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG,
+ "TWT: Failed to allocate TWT Setup frame (Request)");
+ return -ENOMEM;
+ }
+
+ wpa_printf(MSG_DEBUG,
+ "TWT: Setup request, dtok: %d exponent: %d mantissa: %d min-twt: %d",
+ dtok, exponent, mantissa, min_twt);
+
+ wpabuf_put_u8(buf, WLAN_ACTION_S1G);
+ wpabuf_put_u8(buf, S1G_ACT_TWT_SETUP);
+ wpabuf_put_u8(buf, dtok);
+
+ wpabuf_put_u8(buf, WLAN_EID_TWT);
+ wpabuf_put_u8(buf, 15); /* len */
+
+ wpabuf_put_u8(buf, control);
+
+ if (requestor)
+ req_type |= BIT(0); /* This STA is a TWT Requesting STA */
+ /* TWT Setup Command field */
+ req_type |= (setup_cmd & 0x7) << 1;
+ if (trigger)
+ req_type |= BIT(4); /* TWT SP includes trigger frames */
+ if (implicit)
+ req_type |= BIT(5); /* Implicit TWT */
+ if (flow_type)
+ req_type |= BIT(6); /* Flow Type: Unannounced TWT */
+ req_type |= (flow_id & 0x7) << 7;
+ req_type |= (exponent & 0x1f) << 10; /* TWT Wake Interval Exponent */
+ if (protection)
+ req_type |= BIT(15);
+ wpabuf_put_le16(buf, req_type);
+ wpabuf_put_le64(buf, twt);
+ wpabuf_put_u8(buf, min_twt); /* Nominal Minimum TWT Wake Duration */
+ wpabuf_put_le16(buf, mantissa); /* TWT Wake Interval Mantissa */
+ wpabuf_put_u8(buf, twt_channel); /* TWT Channel */
+
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+ wpa_printf(MSG_DEBUG, "TWT: Failed to send TWT Setup Request");
+ ret = -ECANCELED;
+ }
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+
+/**
+ * wpas_twt_send_teardown - Send TWT teardown request to our AP
+ * @wpa_s: Pointer to wpa_supplicant
+ * @flags: The byte that goes inside the TWT Teardown element
+ * Returns: 0 in case of success, negative error code otherwise
+ *
+ */
+int wpas_twt_send_teardown(struct wpa_supplicant *wpa_s, u8 flags)
+{
+ struct wpabuf *buf;
+ int ret = 0;
+
+ if (wpa_s->wpa_state != WPA_COMPLETED || !wpa_s->current_ssid) {
+ wpa_printf(MSG_DEBUG,
+ "TWT: No connection - cannot send TWT Teardown frame");
+ return -ENOTCONN;
+ }
+
+ /* 3 = Action category + Action code + flags */
+ buf = wpabuf_alloc(3);
+ if (!buf) {
+ wpa_printf(MSG_DEBUG,
+ "TWT: Failed to allocate TWT Teardown frame");
+ return -ENOMEM;
+ }
+
+ wpa_printf(MSG_DEBUG, "TWT: Teardown request, flags: 0x%x", flags);
+
+ wpabuf_put_u8(buf, WLAN_ACTION_S1G);
+ wpabuf_put_u8(buf, S1G_ACT_TWT_TEARDOWN);
+ wpabuf_put_u8(buf, flags);
+
+ if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+ wpa_s->own_addr, wpa_s->bssid,
+ wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+ wpa_printf(MSG_DEBUG, "TWT: Failed to send TWT Teardown frame");
+ ret = -ECANCELED;
+ }
+
+ wpabuf_free(buf);
+ return ret;
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
diff --git a/contrib/wpa/wpa_supplicant/wpa_gui-qt4/icons/.gitignore b/contrib/wpa/wpa_supplicant/wpa_gui-qt4/icons/.gitignore
new file mode 100644
index 000000000000..8d772cc93884
--- /dev/null
+++ b/contrib/wpa/wpa_supplicant/wpa_gui-qt4/icons/.gitignore
@@ -0,0 +1,2 @@
+hicolor
+pixmaps
diff --git a/contrib/wpa/wpadebug/.gitignore b/contrib/wpa/wpadebug/.gitignore
new file mode 100644
index 000000000000..baf2c7838a0d
--- /dev/null
+++ b/contrib/wpa/wpadebug/.gitignore
@@ -0,0 +1,4 @@
+bin
+gen
+local.properties
+proguard-project.txt
diff --git a/contrib/wpa/wpadebug/AndroidManifest.xml b/contrib/wpa/wpadebug/AndroidManifest.xml
new file mode 100644
index 000000000000..0d8dec396dd6
--- /dev/null
+++ b/contrib/wpa/wpadebug/AndroidManifest.xml
@@ -0,0 +1,86 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="w1.fi.wpadebug"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="10" android:targetSdkVersion="17" />
+ <uses-permission android:name="android.permission.NFC" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <application android:label="wpadebug" android:usesCleartextTraffic="true">
+ <activity android:name="w1.fi.wpadebug.MainActivity"
+ android:label="wpadebug">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name="w1.fi.wpadebug.DisplayMessageActivity"
+ android:label="Operation result"
+ android:parentActivityName="w1.fi.wpadebug.MainActivity">
+ </activity>
+ <activity android:name="w1.fi.wpadebug.WpaNfcActivity"
+ android:label="wpa_supplicant NFC operation"
+ android:parentActivityName="w1.fi.wpadebug.MainActivity">
+ <intent-filter>
+ <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ <data android:mimeType="application/vnd.wfa.wsc" />
+ </intent-filter>
+ </activity>
+ <activity android:name="w1.fi.wpadebug.CommandListActivity"
+ android:label="Command list"
+ android:parentActivityName="w1.fi.wpadebug.MainActivity">
+ </activity>
+ <activity android:name="w1.fi.wpadebug.WpaCommandListActivity"
+ android:label="WPA command list"
+ android:parentActivityName="w1.fi.wpadebug.MainActivity">
+ </activity>
+ <activity android:name="w1.fi.wpadebug.WpaCredActivity"
+ android:label="Credentials"
+ android:parentActivityName="w1.fi.wpadebug.MainActivity">
+ </activity>
+ <activity android:name="w1.fi.wpadebug.WpaCredEditActivity"
+ android:label="Credential"
+ android:parentActivityName="w1.fi.wpadebug.WpaCredActivity">
+ </activity>
+ <activity android:name="w1.fi.wpadebug.QrCodeScannerActivity"
+ android:label="QR Code Reader"
+ android:parentActivityName="w1.fi.wpadebug.MainActivity">
+ </activity>
+ <activity android:name="w1.fi.wpadebug.QrCodeDisplayActivity"
+ android:label="QR Code Display"
+ android:parentActivityName="w1.fi.wpadebug.MainActivity">
+ </activity>
+ <activity
+ android:name="w1.fi.wpadebug.InputUri"
+ android:label="Input URI"
+ android:parentActivityName="w1.fi.wpadebug.MainActivity">
+ </activity>
+ <activity
+ android:name="w1.fi.wpadebug.QrCodeReadActivity"
+ android:label="Start Scan"
+ android:parentActivityName="w1.fi.wpadebug.MainActivity">
+ </activity>
+ <activity android:name="w1.fi.wpadebug.WpaWebViewActivity"
+ android:label="WebView"
+ android:launchMode="singleTop"
+ android:noHistory="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ </intent-filter>
+ </activity>
+ <receiver android:name="w1.fi.wpadebug.WifiReceiver">
+ <intent-filter>
+ <action android:name="android.net.wifi.STATE_CHANGE" />
+ <action android:name="android.net.wifi.RSSI_CHANGED" />
+ <action android:name="android.net.wifi.SCAN_RESULTS" />
+ <action android:name="android.net.wifi.supplicant.CONNECTION_CHANGE" />
+ <action android:name="android.net.wifi.supplicant.STATE_CHANGE" />
+ <action android:name="android.net.wifi.WIFI_STATE_CHANGED" />
+ </intent-filter>
+ </receiver>
+ </application>
+</manifest>
diff --git a/contrib/wpa/wpadebug/README b/contrib/wpa/wpadebug/README
new file mode 100644
index 000000000000..f66f0c212dc9
--- /dev/null
+++ b/contrib/wpa/wpadebug/README
@@ -0,0 +1,78 @@
+wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+Copyright (c) 2013, Jouni Malinen <j@w1.fi> and contributors
+All Rights Reserved.
+
+This program is licensed under the BSD license (the one with
+advertisement clause removed). See the top level README for detailed
+license text.
+
+If you are submitting changes to the project, please see CONTRIBUTIONS
+file for more instructions.
+
+
+NOTE! This Android app is for debugging and testing purposes only. It is
+not supposed to be installed on a production use device and doing so may
+result in complete loss of security protections on the device.
+
+
+
+Build
+-----
+
+- Install Android SDK and build tools
+
+wpadebug depends on zxing core to launch QR code display/scanning.
+To build zxing core:
+
+- mkdir hostap/wpadebug/libs # target for the jar file
+- Install maven tool
+- clone latest zxing code [git clone https://github.com/zxing/zxing.git]
+- cd zxing/core
+- run: mvn install -DskipTests
+- copy target/core-*.*.*-SNAPSHOT.jar to hostap/wpadebug/libs
+
+To build wpadebug application:
+
+- update project target if desired; for example:
+ android list targets
+ android update project --target 1 --path $PWD
+- run: ant debug
+
+
+Installation (with adb over USB)
+------------
+
+adb install bin/wpadebug-debug.apk
+
+NOTE: Following steps enable any app on the system to get root access!
+This is not suitable for any production use. This is needed for direct
+wpa_supplicant access and some networking operating in general. You can
+still use rest of the wpadebug app without doing this, but those
+functions will not work unless this step part of installation is
+done. It should be obvious that these steps require a rooted device. In
+addition, if you do not understand what the following commands do,
+please do not run them.
+
+adb root
+adb remount
+adb shell cp /system/bin/mksh /system/bin/mksh-su
+adb shell chmod 6755 /system/bin/mksh-su
+
+Optionally, a text file with a set of command can be installed to allow
+arbitrary shell commands to be executed. This text file need to be in
+/data/local/wpadebug.cmds and use title@command format per line. For
+example:
+version@cat /proc/version
+
+Similarly, /data/local/wpadebug.wpacmds can be used to define additional
+wpa_supplicant control interface commands.
+
+
+Uninstallation
+--------------
+
+adb root
+adb remount
+adb shell rm /system/bin/mksh-su
+
+adb uninstall w1.fi.wpadebug
diff --git a/contrib/wpa/wpadebug/build.xml b/contrib/wpa/wpadebug/build.xml
new file mode 100644
index 000000000000..5301e69bcc7f
--- /dev/null
+++ b/contrib/wpa/wpadebug/build.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="wpadebug" default="help">
+ <property file="local.properties" />
+ <property file="ant.properties" />
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+ <loadproperties srcFile="project.properties" />
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir"
+ />
+ <import file="custom_rules.xml" optional="true" />
+ <!-- version-tag: 1 -->
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+</project>
diff --git a/contrib/wpa/wpadebug/project.properties b/contrib/wpa/wpadebug/project.properties
new file mode 100644
index 000000000000..36cc0ce32096
--- /dev/null
+++ b/contrib/wpa/wpadebug/project.properties
@@ -0,0 +1,2 @@
+# Project target.
+target=android-22
diff --git a/contrib/wpa/wpadebug/res/layout/cred_edit.xml b/contrib/wpa/wpadebug/res/layout/cred_edit.xml
new file mode 100644
index 000000000000..292b30abbffd
--- /dev/null
+++ b/contrib/wpa/wpadebug/res/layout/cred_edit.xml
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Username"
+ />
+ <EditText android:id="@+id/cred_edit_username"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:lines="1"
+ />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Realm"
+ />
+ <EditText android:id="@+id/cred_edit_realm"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:lines="1"
+ />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Password"
+ />
+ <EditText android:id="@+id/cred_edit_password"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:lines="1"
+ android:inputType="textPassword"
+ />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Domain"
+ />
+ <EditText android:id="@+id/cred_edit_domain"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:lines="1"
+ />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="IMSI"
+ />
+ <EditText android:id="@+id/cred_edit_imsi"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:singleLine="true"
+ android:lines="1"
+ android:hint="Used only with SIM/USIM testing"
+ />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Save"
+ android:onClick="credSave"
+ />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Cancel"
+ android:onClick="credCancel"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/contrib/wpa/wpadebug/res/layout/input_uri.xml b/contrib/wpa/wpadebug/res/layout/input_uri.xml
new file mode 100644
index 000000000000..ab391fbed72e
--- /dev/null
+++ b/contrib/wpa/wpadebug/res/layout/input_uri.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ tools:context="w1.fi.wpadebug.InputUri">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:layout_margin="30dp"
+ android:layout_height="wrap_content">
+
+ <EditText
+ android:id="@+id/edit_uri"
+ android:layout_width="match_parent"
+ android:layout_height="130dp" />
+
+ <Button
+ android:id="@+id/submit_uri"
+ android:layout_width="wrap_content"
+ android:text="Submit"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
+</LinearLayout>
diff --git a/contrib/wpa/wpadebug/res/layout/main.xml b/contrib/wpa/wpadebug/res/layout/main.xml
new file mode 100644
index 000000000000..cbdbfb961980
--- /dev/null
+++ b/contrib/wpa/wpadebug/res/layout/main.xml
@@ -0,0 +1,160 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Framework commands"
+ />
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="WifiManager"
+ android:onClick="wifiManagerInfo"
+ />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="WifiInfo"
+ android:onClick="wifiInfo"
+ />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Networks"
+ android:onClick="wifiConfiguredNetworks"
+ />
+ </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="wpa_supplicant commands"
+ />
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="wpa_supplicant commands"
+ android:onClick="runWpaCommands"
+ />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Credentials"
+ android:onClick="runWpaCredentials"
+ />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="log:info"
+ android:onClick="wpaLogLevelInfo"
+ />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="log:debug"
+ android:onClick="wpaLogLevelDebug"
+ />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="log:excessive"
+ android:onClick="wpaLogLevelExcessive"
+ />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <EditText android:id="@+id/edit_cmd"
+ android:layout_weight="1"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:hint="wpa_cli command"
+ />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Run"
+ android:onClick="runWpaCliCmd"
+ />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Shell commands"
+ android:onClick="runCommands"
+ />
+ </LinearLayout>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="QR Scan"
+ android:onClick="runQrScan"
+ />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="QR Input"
+ android:onClick="runQrInput"
+ />
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="QR Display"
+ android:onClick="runQrDisplay"
+ />
+ </LinearLayout>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="NFC commands"
+ />
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ >
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="WPS handover request"
+ android:onClick="nfcWpsHandoverRequest"
+ />
+ </LinearLayout>
+</LinearLayout>
diff --git a/contrib/wpa/wpadebug/res/layout/qrcode.xml b/contrib/wpa/wpadebug/res/layout/qrcode.xml
new file mode 100644
index 000000000000..8cf50de374e1
--- /dev/null
+++ b/contrib/wpa/wpadebug/res/layout/qrcode.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center_horizontal">
+ <ImageView
+ android:id="@+id/qrCode"
+ android:layout_width="350dp"
+ android:layout_height="350dp"
+ android:layout_marginTop="20dp"
+ />
+</LinearLayout>
\ No newline at end of file
diff --git a/contrib/wpa/wpadebug/res/raw/shell_commands.txt b/contrib/wpa/wpadebug/res/raw/shell_commands.txt
new file mode 100644
index 000000000000..9b45d652a065
--- /dev/null
+++ b/contrib/wpa/wpadebug/res/raw/shell_commands.txt
@@ -0,0 +1,2 @@
+id@id
+version@cat /proc/version
diff --git a/contrib/wpa/wpadebug/res/raw/wpa_commands.txt b/contrib/wpa/wpadebug/res/raw/wpa_commands.txt
new file mode 100644
index 000000000000..3baa01c8bb5b
--- /dev/null
+++ b/contrib/wpa/wpadebug/res/raw/wpa_commands.txt
@@ -0,0 +1,9 @@
+Status@STATUS
+PMKSA cache@PMKSA
+Networks@LIST_NETWORKS
+Interworking connect@INTERWORKING_SELECT auto
+Creds@LIST_CREDS
+Scan results@SCAN_RESULTS
+Flush@FLUSH
+Disconnect@DISCONNECT
+Reassociate@REASSOCIATE
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/CommandListActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/CommandListActivity.java
new file mode 100644
index 000000000000..6d7ad4dd6678
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/CommandListActivity.java
@@ -0,0 +1,130 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.io.IOException;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+
+class CmdList
+{
+ String title;
+ String command;
+
+ public CmdList(String _title, String _command)
+ {
+ title = _title;
+ command = _command;
+ }
+
+ @Override
+ public String toString()
+ {
+ return title;
+ }
+}
+
+public class CommandListActivity extends ListActivity
+{
+ private static final String TAG = "wpadebug";
+ private static final String cmdfile = "/data/local/wpadebug.cmds";
+
+ private void read_commands(ArrayList<CmdList> list, Scanner in)
+ {
+ in.useDelimiter("@");
+ while (in.hasNext()) {
+ String title = in.next();
+ String cmd = in.nextLine().substring(1);
+ list.add(new CmdList(title, cmd));
+ }
+ in.close();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ ArrayList<CmdList> list = new ArrayList<CmdList>();
+
+ FileReader in;
+ try {
+ in = new FileReader(cmdfile);
+ read_commands(list, new Scanner(in));
+ } catch (IOException e) {
+ Toast.makeText(this, "Could not read " + cmdfile,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ InputStream inres;
+ try {
+ inres = getResources().openRawResource(R.raw.shell_commands);
+ read_commands(list, new Scanner(inres));
+ } catch (android.content.res.Resources.NotFoundException e) {
+ Toast.makeText(this, "Could not read internal resource",
+ Toast.LENGTH_SHORT).show();
+ }
+
+ ArrayAdapter<CmdList> listAdapter;
+ listAdapter = new ArrayAdapter<CmdList>(this, android.R.layout.simple_list_item_1, list);
+
+ setListAdapter(listAdapter);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id)
+ {
+ CmdList item = (CmdList) getListAdapter().getItem(position);
+ Toast.makeText(this, "Running: " + item.command,
+ Toast.LENGTH_SHORT).show();
+ String message = run(item.command);
+ if (message == null)
+ return;
+ Intent intent = new Intent(this, DisplayMessageActivity.class);
+ intent.putExtra(MainActivity.EXTRA_MESSAGE, message);
+ startActivity(intent);
+ }
+
+ private String run(String cmd)
+ {
+ try {
+ Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", cmd});
+ BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ StringBuffer output = new StringBuffer();
+ int read;
+ char[] buffer = new char[1024];
+ while ((read = reader.read(buffer)) > 0)
+ output.append(buffer, 0, read);
+ reader.close();
+ proc.waitFor();
+ return output.toString();
+ } catch (IOException e) {
+ Toast.makeText(this, "Could not run command",
+ Toast.LENGTH_LONG).show();
+ return null;
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/DisplayMessageActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/DisplayMessageActivity.java
new file mode 100644
index 000000000000..28ef85f39169
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/DisplayMessageActivity.java
@@ -0,0 +1,49 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.MenuItem;
+import android.content.Intent;
+import android.widget.TextView;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+
+public class DisplayMessageActivity extends Activity
+{
+ private static final String TAG = "wpadebug";
+
+ String byteArrayHex(byte[] a) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b: a)
+ sb.append(String.format("%02x", b));
+ return sb.toString();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ Log.d(TAG, "onCreate");
+ super.onCreate(savedInstanceState);
+
+ // Get the message from the intent
+ Intent intent = getIntent();
+ String action = intent.getAction();
+ Log.d(TAG, "onCreate: action=" + action);
+
+ String message = intent.getStringExtra(MainActivity.EXTRA_MESSAGE);
+
+ TextView textView = new TextView(this);
+ textView.setText(message);
+ textView.setMovementMethod(new ScrollingMovementMethod());
+ setContentView(textView);
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/InputUri.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/InputUri.java
new file mode 100644
index 000000000000..ea1fa99d2a3e
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/InputUri.java
@@ -0,0 +1,108 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2018, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.View;
+import android.widget.Button;
+import android.widget.EditText;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+public class InputUri extends Activity {
+
+ private EditText mEditText;
+ private Button mSubmitButton;
+ private String mUriText;
+ private static final String FILE_NAME = "wpadebug_qrdata.txt";
+ private static final String TAG = "wpadebug";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.input_uri);
+ mEditText = (EditText)findViewById(R.id.edit_uri);
+ mSubmitButton = (Button)findViewById(R.id.submit_uri);
+
+ mEditText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before,
+ int count) {
+ mUriText = mEditText.getText().toString();
+ if (mUriText.startsWith("DPP:") &&
+ mUriText.endsWith(";;")) {
+ writeToFile(mUriText);
+ finish();
+ }
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start,
+ int count, int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ }
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mSubmitButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mUriText = mEditText.getText().toString();
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ writeToFile(mUriText);
+
+ InputUri.this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ });
+ }
+ }).start();
+
+ }
+
+ });
+ }
+
+ public void writeToFile(String data)
+ {
+ File file = new File("/sdcard", FILE_NAME);
+ try
+ {
+ file.createNewFile();
+ FileOutputStream fOut = new FileOutputStream(file);
+ OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
+ myOutWriter.append(mUriText);
+ myOutWriter.close();
+
+ fOut.flush();
+ fOut.close();
+ }
+ catch (IOException e)
+ {
+ Log.e(TAG, "File write failed: " + e.toString());
+ }
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/MainActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/MainActivity.java
new file mode 100644
index 000000000000..4c37b481f1bf
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/MainActivity.java
@@ -0,0 +1,209 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.view.View;
+import android.content.Intent;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.widget.EditText;
+import android.widget.Toast;
+import android.util.Log;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiConfiguration;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+
+public class MainActivity extends Activity
+{
+ public final static String EXTRA_MESSAGE = "w1.fi.wpadebug.MESSAGE";
+ private static final String TAG = "wpadebug";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.main);
+ }
+
+ public void runCommands(View view)
+ {
+ Intent intent = new Intent(this, CommandListActivity.class);
+ startActivity(intent);
+ }
+
+ public void runQrScan(View view)
+ {
+ Intent intent = new Intent(this, QrCodeScannerActivity.class);
+ startActivity(intent);
+ }
+
+ public void runQrInput(View view)
+ {
+ Intent intent = new Intent(this, InputUri.class);
+ startActivity(intent);
+ }
+
+ public void runQrDisplay(View view)
+ {
+ Intent intent = new Intent(this, QrCodeDisplayActivity.class);
+ startActivity(intent);
+ }
+
+ public void runWpaCommands(View view)
+ {
+ Intent intent = new Intent(this, WpaCommandListActivity.class);
+ startActivity(intent);
+ }
+
+ public void runWpaCredentials(View view)
+ {
+ Intent intent = new Intent(this, WpaCredActivity.class);
+ startActivity(intent);
+ }
+
+ public void runWpaCliCmd(View view)
+ {
+ Intent intent = new Intent(this, DisplayMessageActivity.class);
+ EditText editText = (EditText) findViewById(R.id.edit_cmd);
+ String cmd = editText.getText().toString();
+ if (cmd.trim().length() == 0) {
+ show_alert("wpa_cli command", "Invalid command");
+ return;
+ }
+ wpaCmd(view, cmd);
+ }
+
+ public void wpaLogLevelInfo(View view)
+ {
+ wpaCmd(view, "LOG_LEVEL INFO 1");
+ }
+
+ public void wpaLogLevelDebug(View view)
+ {
+ wpaCmd(view, "LOG_LEVEL DEBUG 1");
+ }
+
+ public void wpaLogLevelExcessive(View view)
+ {
+ wpaCmd(view, "LOG_LEVEL EXCESSIVE 1");
+ }
+
+ private void wpaCmd(View view, String cmd)
+ {
+ Intent intent = new Intent(this, DisplayMessageActivity.class);
+ String message = run("wpa_cli " + cmd);
+ if (message == null)
+ return;
+ intent.putExtra(EXTRA_MESSAGE, message);
+ startActivity(intent);
+ }
+
+ private String run(String cmd)
+ {
+ try {
+ Log.d(TAG, "Running external process: " + cmd);
+ Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", cmd});
+ BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ StringBuffer output = new StringBuffer();
+ int read;
+ char[] buffer = new char[1024];
+ while ((read = reader.read(buffer)) > 0)
+ output.append(buffer, 0, read);
+ reader.close();
+ proc.waitFor();
+ Log.d(TAG, "External process completed - exitValue " +
+ proc.exitValue());
+ return output.toString();
+ } catch (IOException e) {
+ show_alert("Could not run external program",
+ "Execution of an external program failed. " +
+ "Maybe mksh-su was not installed.");
+ return null;
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private void show_alert(String title, String message)
+ {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+ alert.setTitle(title);
+ alert.setMessage(message);
+ alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id)
+ {
+ }
+ });
+ alert.create().show();
+ }
+
+ public void wifiManagerInfo(View view)
+ {
+ Intent intent = new Intent(this, DisplayMessageActivity.class);
+ WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ String message = "WifiState: " + manager.getWifiState() + "\n" +
+ "WifiEnabled: " + manager.isWifiEnabled() + "\n" +
+ "pingSupplicant: " + manager.pingSupplicant() + "\n" +
+ "DhcpInfo: " + manager.getDhcpInfo().toString() + "\n";
+ intent.putExtra(EXTRA_MESSAGE, message);
+ startActivity(intent);
+ }
+
+ public void wifiInfo(View view)
+ {
+ Intent intent = new Intent(this, DisplayMessageActivity.class);
+ WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ WifiInfo wifi = manager.getConnectionInfo();
+ String message = wifi.toString() + "\n" + wifi.getSupplicantState();
+ intent.putExtra(EXTRA_MESSAGE, message);
+ startActivity(intent);
+ }
+
+ public void wifiConfiguredNetworks(View view)
+ {
+ Intent intent = new Intent(this, DisplayMessageActivity.class);
+ WifiManager manager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ StringBuilder sb = new StringBuilder();
+ for (WifiConfiguration n: manager.getConfiguredNetworks())
+ sb.append(n.toString() + "\n");
+ intent.putExtra(EXTRA_MESSAGE, sb.toString());
+ startActivity(intent);
+ }
+
+ public void nfcWpsHandoverRequest(View view)
+ {
+ NfcAdapter nfc;
+ nfc = NfcAdapter.getDefaultAdapter(this);
+ if (nfc == null) {
+ Toast.makeText(this, "NFC is not available",
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ NdefMessage msg;
+ msg = new NdefMessage(new NdefRecord[] {
+ NdefRecord.createMime("application/vnd.wfa.wsc",
+ new byte[0])
+ });
+
+ nfc.setNdefPushMessage(msg, this);
+ Toast.makeText(this, "NFC push message (WSC) configured",
+ Toast.LENGTH_LONG).show();
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/QrCodeDisplayActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/QrCodeDisplayActivity.java
new file mode 100644
index 000000000000..10c9c0144fe4
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/QrCodeDisplayActivity.java
@@ -0,0 +1,109 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2018, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.widget.ImageView;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class QrCodeDisplayActivity extends Activity {
+
+ private static final String TAG = "wpadebug";
+ private static final String FILE_NAME = "wpadebug_qrdata.txt";
+ private ImageView imageView;
+
+ // Below set of configs are used for QR code display window
+ private final static int WHITE = 0xFFFFFFFF;
+ private final static int BLACK = 0xFF000000;
+ private final static int WIDTH = 400;
+ private final static int HEIGHT = 400;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // create imageview for this and attach to this activity.
+ setContentView(R.layout.qrcode);
+ imageView = (ImageView) findViewById(R.id.qrCode);
+ String str = readFromFile(FILE_NAME);
+
+ //Encode and launch qrcode now
+ try {
+ Bitmap bitmap = (TextUtils.isEmpty(str)) ? null : encodeAsBitmap(str);
+ if (bitmap != null) {
+ imageView.setImageBitmap(bitmap);
+ } else {
+ Log.e(TAG, "Failed to generate bitmap for uri=" + str);
+ finish();
+ }
+ } catch (WriterException e) {
+ e.printStackTrace();
+ finish();
+ }
+ }
+
+ private Bitmap encodeAsBitmap(String str) throws WriterException {
+ BitMatrix result;
+ try {
+ result = new MultiFormatWriter().encode(str, BarcodeFormat.QR_CODE, WIDTH, HEIGHT, null);
+ } catch (IllegalArgumentException iae) {
+ // Unsupported format
+ return null;
+ }
+
+ int width = result.getWidth();
+ int height = result.getHeight();
+ int[] pixels = new int[width * height];
+ for (int y = 0; y < height; y++) {
+ int offset = y * width;
+ for (int x = 0; x < width; x++) {
+ pixels[offset + x] = result.get(x, y) ? BLACK : WHITE;
+ }
+ }
+
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ return bitmap;
+ }
+
+ private String readFromFile(String filePath) {
+ try {
+ FileInputStream fis = new FileInputStream(new File("/sdcard", filePath));
+ BufferedReader br = new BufferedReader(new InputStreamReader(fis, "UTF-8"));
+ StringBuilder sb = new StringBuilder();
+ String line;
+ while(( line = br.readLine()) != null ) {
+ sb.append( line );
+ sb.append( '\n' );
+ }
+ return sb.toString();
+ }
+ catch (FileNotFoundException e) {
+ Log.e(TAG, "File not found: " + e.toString());
+ } catch (IOException e) {
+ Log.e(TAG, "Can not read file: " + e.toString());
+ }
+
+ return null;
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/QrCodeReadActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/QrCodeReadActivity.java
new file mode 100644
index 000000000000..f21eccba8660
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/QrCodeReadActivity.java
@@ -0,0 +1,40 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2018, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.app.Activity;
+import android.util.Log;
+import android.content.Intent;
+import android.hardware.Camera;
+import android.os.Bundle;
+
+public class QrCodeReadActivity extends Activity {
+
+ private static final String TAG = "wpadebug";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ int numberOfCameras = Camera.getNumberOfCameras();
+
+ if (numberOfCameras > 0) {
+ Log.e(TAG, "Number of cameras found: " + numberOfCameras);
+ Intent QrCodeScanIntent = new Intent(QrCodeReadActivity.this,
+ QrCodeScannerActivity.class);
+ QrCodeReadActivity.this.startActivity(QrCodeScanIntent);
+ finish();
+ } else {
+ Log.e(TAG, "No cameras found, input the QR Code");
+ Intent QrCodeInputIntent = new Intent(QrCodeReadActivity.this,
+ InputUri.class);
+ QrCodeReadActivity.this.startActivity(QrCodeInputIntent);
+ finish();
+ }
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/QrCodeScannerActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/QrCodeScannerActivity.java
new file mode 100644
index 000000000000..4b3591c725dc
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/QrCodeScannerActivity.java
@@ -0,0 +1,82 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2018, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+
+public class QrCodeScannerActivity extends Activity {
+
+ private static final String TAG = "wpadebug";
+ private static final String RESULT = "SCAN_RESULT";
+ private static final String FILE_NAME = "wpadebug_qrdata.txt";
+ private static final String ACTION = "com.google.zxing.client.android.SCAN";
+
+ private static final int QRCODE = 1;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = new Intent();
+ intent.setAction(ACTION);
+ intent.putExtra("SCAN_MODE", "QR_CODE_MODE");
+ intent.putExtra("PROMPT_MESSAGE",
+ "Place a QR Code inside the viewfinder rectangle to scan it.");
+ try {
+ startActivityForResult(intent, QRCODE);
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No QR code scanner found with name=" + ACTION);
+ Toast.makeText(QrCodeScannerActivity.this, "QR code scanner not found", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(TAG, "onActivityResult: requestCode=" + requestCode + " resultCode=" + resultCode);
+ if (requestCode == QRCODE && resultCode == RESULT_OK) {
+ String contents = data.getStringExtra(RESULT);
+ writeToFile(contents);
+ Log.d(TAG, "onActivityResult: QRCODE RESULT_OK: " + contents);
+ finishActivity(requestCode);
+ finish();
+ }
+ }
+
+ public void writeToFile(String data)
+ {
+ File file = new File("/sdcard", FILE_NAME);
+ try
+ {
+ file.createNewFile();
+ FileOutputStream fOut = new FileOutputStream(file);
+ OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
+ myOutWriter.append(data);
+
+ myOutWriter.close();
+
+ fOut.flush();
+ fOut.close();
+ }
+ catch (IOException e)
+ {
+ Log.e(TAG, "File write failed: " + e.toString());
+ }
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WifiReceiver.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WifiReceiver.java
new file mode 100644
index 000000000000..d69e05d69ebb
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WifiReceiver.java
@@ -0,0 +1,95 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.NetworkInfo;
+import android.net.wifi.SupplicantState;
+import android.net.wifi.WifiInfo;
+import android.os.Bundle;
+import android.util.Log;
+
+public class WifiReceiver extends BroadcastReceiver
+{
+ private static final String TAG = "wpadebug";
+
+ @Override
+ public void onReceive(Context c, Intent intent)
+ {
+ String act = intent.getAction();
+ Log.d(TAG, "Received broadcast intent: action=" + act);
+
+ Bundle bundles = intent.getExtras();
+ if (bundles == null)
+ return;
+
+ if (bundles.containsKey("bssid")) {
+ String val;
+ val = intent.getStringExtra("bssid");
+ if (val != null)
+ Log.d(TAG, " bssid: " + val);
+ }
+
+ if (bundles.containsKey("networkInfo")) {
+ NetworkInfo info;
+ info = (NetworkInfo) intent.getParcelableExtra("networkInfo");
+ if (info != null)
+ Log.d(TAG, " networkInfo: " + info);
+ }
+
+ if (bundles.containsKey("newRssi")) {
+ int val;
+ val = intent.getIntExtra("newRssi", -1);
+ Log.d(TAG, " newRssi: " + val);
+ }
+
+ if (bundles.containsKey("newState")) {
+ SupplicantState state;
+ state = (SupplicantState) intent.getParcelableExtra("newState");
+ if (state != null)
+ Log.d(TAG, " newState: " + state);
+ }
+
+ if (bundles.containsKey("previous_wifi_state")) {
+ int wifi_state;
+ wifi_state = intent.getIntExtra("previous_wifi_state", -1);
+ if (wifi_state != -1)
+ Log.d(TAG, " previous_wifi_state: " + wifi_state);
+ }
+
+ if (bundles.containsKey("connected")) {
+ boolean connected;
+ connected = intent.getBooleanExtra("connected", false);
+ Log.d(TAG, " connected: " + connected);
+ }
+
+ if (bundles.containsKey("supplicantError")) {
+ int error;
+ error = intent.getIntExtra("supplicantError", -1);
+ if (error != -1)
+ Log.d(TAG, " supplicantError: " + error);
+ }
+
+ if (bundles.containsKey("wifiInfo")) {
+ WifiInfo info;
+ info = (WifiInfo) intent.getParcelableExtra("wifiInfo");
+ if (info != null)
+ Log.d(TAG, " wifiInfo: " + info);
+ }
+
+ if (bundles.containsKey("wifi_state")) {
+ int wifi_state;
+ wifi_state = intent.getIntExtra("wifi_state", -1);
+ if (wifi_state != -1)
+ Log.d(TAG, " wifi_state: " + wifi_state);
+ }
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaCommandListActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaCommandListActivity.java
new file mode 100644
index 000000000000..e089179340ee
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaCommandListActivity.java
@@ -0,0 +1,112 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import java.util.ArrayList;
+import java.util.Scanner;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.io.IOException;
+
+import android.app.ListActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+
+public class WpaCommandListActivity extends ListActivity
+{
+ private static final String TAG = "wpadebug";
+ private static final String cmdfile = "/data/local/wpadebug.wpacmds";
+
+ private void read_commands(ArrayList<CmdList> list, Scanner in)
+ {
+ in.useDelimiter("@");
+ while (in.hasNext()) {
+ String title = in.next();
+ String cmd = in.nextLine().substring(1);
+ list.add(new CmdList(title, cmd));
+ }
+ in.close();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ ArrayList<CmdList> list = new ArrayList<CmdList>();
+
+ FileReader in;
+ try {
+ in = new FileReader(cmdfile);
+ read_commands(list, new Scanner(in));
+ } catch (IOException e) {
+ Toast.makeText(this, "Could not read " + cmdfile,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ InputStream inres;
+ try {
+ inres = getResources().openRawResource(R.raw.wpa_commands);
+ read_commands(list, new Scanner(inres));
+ } catch (android.content.res.Resources.NotFoundException e) {
+ Toast.makeText(this, "Could not read internal resource",
+ Toast.LENGTH_SHORT).show();
+ }
+
+ ArrayAdapter<CmdList> listAdapter;
+ listAdapter = new ArrayAdapter<CmdList>(this, android.R.layout.simple_list_item_1, list);
+
+ setListAdapter(listAdapter);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id)
+ {
+ CmdList item = (CmdList) getListAdapter().getItem(position);
+ Toast.makeText(this, "Running: " + item.command,
+ Toast.LENGTH_SHORT).show();
+ String message = run(item.command);
+ if (message == null)
+ return;
+ Intent intent = new Intent(this, DisplayMessageActivity.class);
+ intent.putExtra(MainActivity.EXTRA_MESSAGE, message);
+ startActivity(intent);
+ }
+
+ private String run(String cmd)
+ {
+ try {
+ Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", "wpa_cli " + cmd});
+ BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ StringBuffer output = new StringBuffer();
+ int read;
+ char[] buffer = new char[1024];
+ while ((read = reader.read(buffer)) > 0)
+ output.append(buffer, 0, read);
+ reader.close();
+ proc.waitFor();
+ return output.toString();
+ } catch (IOException e) {
+ Toast.makeText(this, "Could not run command",
+ Toast.LENGTH_LONG).show();
+ return null;
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaCredActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaCredActivity.java
new file mode 100644
index 000000000000..3902f0964d0a
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaCredActivity.java
@@ -0,0 +1,263 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import java.util.ArrayList;
+import java.util.ListIterator;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.InputStream;
+import java.io.IOException;
+
+import android.app.ListActivity;
+import android.app.ActionBar;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.widget.ListView;
+import android.widget.ArrayAdapter;
+import android.widget.Toast;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+
+class Credential
+{
+ int id;
+ String realm;
+ String username;
+ String domain;
+ String imsi;
+
+ public Credential(String entry)
+ {
+ String fields[] = entry.split("\t");
+ id = Integer.parseInt(fields[0]);
+ if (fields.length > 1)
+ realm = fields[1];
+ else
+ realm = "";
+ if (fields.length > 2)
+ username = fields[2];
+ else
+ username = "";
+ if (fields.length > 3 && fields[3].length() > 0)
+ domain = fields[3];
+ else
+ domain = null;
+ if (fields.length > 4 && fields[4].length() > 0)
+ imsi = fields[4];
+ else
+ imsi = null;
+ }
+
+ public Credential(int _id, String _username, String _realm, String _domain,
+ String _imsi)
+ {
+ id = _id;
+ username = _username;
+ realm = _realm;
+ domain = _domain;
+ imsi = _imsi;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ String res = id + " - " + username + "@" + realm;
+ if (domain != null)
+ res += " (domain=" + domain + ")";
+ if (imsi != null)
+ res += " (imsi=" + imsi + ")";
+ return res;
+ }
+}
+
+public class WpaCredActivity extends ListActivity
+{
+ private static final String TAG = "wpadebug";
+ static final int CRED_EDIT_REQ = 0;
+ private ArrayList<Credential> mList;
+ private ArrayAdapter<Credential> mListAdapter;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ mList = new ArrayList<Credential>();
+
+ String res = run("LIST_CREDS");
+ if (res == null) {
+ Toast.makeText(this, "Could not get credential list",
+ Toast.LENGTH_LONG).show();
+ finish();
+ return;
+ }
+
+ String creds[] = res.split("\n");
+ for (String cred: creds) {
+ if (Character.isDigit(cred.charAt(0)))
+ mList.add(new Credential(cred));
+ }
+
+ mListAdapter = new ArrayAdapter<Credential>(this, android.R.layout.simple_list_item_1, mList);
+
+ setListAdapter(mListAdapter);
+ registerForContextMenu(getListView());
+
+ ActionBar abar = getActionBar();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu)
+ {
+ menu.add(0, 0, 0, "Add credential");
+ return true;
+ }
+
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data)
+ {
+ if (requestCode == CRED_EDIT_REQ) {
+ if (resultCode != RESULT_OK)
+ return;
+
+ String username = data.getStringExtra("username");
+
+ String realm = data.getStringExtra("realm");
+
+ String domain = data.getStringExtra("domain");
+ if (domain != null && domain.length() == 0)
+ domain = null;
+
+ String imsi = data.getStringExtra("imsi");
+ if (imsi != null && imsi.length() == 0)
+ imsi = null;
+
+ String password = data.getStringExtra("password");
+ if (password != null && password.length() == 0)
+ password = null;
+
+ String res = run("ADD_CRED");
+ if (res == null || res.contains("FAIL")) {
+ Toast.makeText(this, "Failed to add credential",
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ int id = -1;
+ String lines[] = res.split("\n");
+ for (String line: lines) {
+ if (Character.isDigit(line.charAt(0))) {
+ id = Integer.parseInt(line);
+ break;
+ }
+ }
+
+ if (id < 0) {
+ Toast.makeText(this, "Failed to add credential (invalid id)",
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ if (!set_cred_quoted(id, "username", username) ||
+ !set_cred_quoted(id, "realm", realm) ||
+ (password != null &&
+ !set_cred_quoted(id, "password", password)) ||
+ (domain != null && !set_cred_quoted(id, "domain", domain)) ||
+ (imsi != null && !set_cred_quoted(id, "imsi", imsi))) {
+ run("REMOVE_CRED " + id);
+ Toast.makeText(this, "Failed to set credential field",
+ Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ mListAdapter.add(new Credential(id, username, realm, domain, imsi));
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ if (item.getTitle().equals("Add credential")) {
+ startActivityForResult(new Intent(this, WpaCredEditActivity.class),
+ CRED_EDIT_REQ);
+ return true;
+ }
+ return false;
+ }
+
+ public void onCreateContextMenu(android.view.ContextMenu menu, View v,
+ android.view.ContextMenu.ContextMenuInfo menuInfo)
+ {
+ menu.add(0, v.getId(), 0, "Delete");
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item)
+ {
+ if (item.getTitle().equals("Delete")) {
+ AdapterContextMenuInfo info =
+ (AdapterContextMenuInfo) item.getMenuInfo();
+ Credential cred = (Credential) getListAdapter().getItem(info.position);
+ String res = run("REMOVE_CRED " + cred.id);
+ if (res == null || !res.contains("OK")) {
+ Toast.makeText(this, "Failed to delete credential",
+ Toast.LENGTH_LONG).show();
+ } else
+ mListAdapter.remove(cred);
+ return true;
+ }
+ return super.onContextItemSelected(item);
+ }
+
+ @Override
+ protected void onListItemClick(ListView l, View v, int position, long id)
+ {
+ Credential item = (Credential) getListAdapter().getItem(position);
+ Toast.makeText(this, "Credential selected: " + item,
+ Toast.LENGTH_SHORT).show();
+ }
+
+ private String run(String cmd)
+ {
+ try {
+ Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", "wpa_cli " + cmd});
+ BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ StringBuffer output = new StringBuffer();
+ int read;
+ char[] buffer = new char[1024];
+ while ((read = reader.read(buffer)) > 0)
+ output.append(buffer, 0, read);
+ reader.close();
+ proc.waitFor();
+ return output.toString();
+ } catch (IOException e) {
+ Toast.makeText(this, "Could not run command",
+ Toast.LENGTH_LONG).show();
+ return null;
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private boolean set_cred(int id, String field, String value)
+ {
+ String res = run("SET_CRED " + id + " " + field + " " + value);
+ return res != null && res.contains("OK");
+ }
+
+ private boolean set_cred_quoted(int id, String field, String value)
+ {
+ String value2 = "'\"" + value + "\"'";
+ return set_cred(id, field, value2);
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaCredEditActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaCredEditActivity.java
new file mode 100644
index 000000000000..3f846c7b4e82
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaCredEditActivity.java
@@ -0,0 +1,55 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.EditText;
+
+public class WpaCredEditActivity extends Activity
+{
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.cred_edit);
+ }
+
+ public void credSave(View view)
+ {
+ Intent data = new Intent();
+ EditText edit;
+
+ edit = (EditText) findViewById(R.id.cred_edit_username);
+ data.putExtra("username", edit.getText().toString());
+
+ edit = (EditText) findViewById(R.id.cred_edit_realm);
+ data.putExtra("realm", edit.getText().toString());
+
+ edit = (EditText) findViewById(R.id.cred_edit_password);
+ data.putExtra("password", edit.getText().toString());
+
+ edit = (EditText) findViewById(R.id.cred_edit_domain);
+ data.putExtra("domain", edit.getText().toString());
+
+ edit = (EditText) findViewById(R.id.cred_edit_imsi);
+ data.putExtra("imsi", edit.getText().toString());
+
+ setResult(Activity.RESULT_OK, data);
+ finish();
+ }
+
+ public void credCancel(View view)
+ {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaNfcActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaNfcActivity.java
new file mode 100644
index 000000000000..6a1601723b6a
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaNfcActivity.java
@@ -0,0 +1,131 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.os.Bundle;
+import android.os.Parcelable;
+import android.view.MenuItem;
+import android.content.Intent;
+import android.content.DialogInterface;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.text.method.ScrollingMovementMethod;
+import android.util.Log;
+import android.nfc.NdefMessage;
+import android.nfc.NdefRecord;
+import android.nfc.NfcAdapter;
+
+public class WpaNfcActivity extends Activity
+{
+ private static final String TAG = "wpadebug";
+
+ String byteArrayHex(byte[] a) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b: a)
+ sb.append(String.format("%02x", b));
+ return sb.toString();
+ }
+
+ private void show_alert(String title, String message)
+ {
+ AlertDialog.Builder alert = new AlertDialog.Builder(this);
+ alert.setTitle(title);
+ alert.setMessage(message);
+ alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id)
+ {
+ finish();
+ }
+ });
+ alert.create().show();
+ }
+
+ private String wpaCmd(String cmd)
+ {
+ try {
+ Log.d(TAG, "Executing wpaCmd: " + cmd);
+ Process proc = Runtime.getRuntime().exec(new String[]{"/system/bin/mksh-su", "-c", "wpa_cli " + cmd});
+ BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()));
+ StringBuffer output = new StringBuffer();
+ int read;
+ char[] buffer = new char[1024];
+ while ((read = reader.read(buffer)) > 0)
+ output.append(buffer, 0, read);
+ reader.close();
+ proc.waitFor();
+ Log.d(TAG, "External process completed - exitValue " +
+ proc.exitValue());
+ return output.toString();
+ } catch (IOException e) {
+ show_alert("Could not run external program",
+ "Execution of an external program failed. " +
+ "Maybe mksh-su was not installed.");
+ return null;
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public boolean report_tag_read(byte[] payload)
+ {
+ String res = wpaCmd("WPS_NFC_TAG_READ " + byteArrayHex(payload));
+ if (res == null)
+ return false;
+ if (!res.contains("OK")) {
+ Toast.makeText(this, "Failed to report WSC tag read to " +
+ "wpa_supplicant", Toast.LENGTH_LONG).show();
+ } else {
+ Toast.makeText(this, "Reported WSC tag read to wpa_supplicant",
+ Toast.LENGTH_LONG).show();
+ }
+ finish();
+ return true;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ String action = intent.getAction();
+ Log.d(TAG, "onCreate: action=" + action);
+
+ if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
+ Log.d(TAG, "NDEF discovered");
+ Parcelable[] raw = intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
+ if (raw != null) {
+ Log.d(TAG, "NDEF message count: " + raw.length);
+ NdefMessage[] msgs = new NdefMessage[raw.length];
+ for (int i = 0; i < raw.length; i++) {
+ msgs[i] = (NdefMessage) raw[i];
+ NdefRecord rec = msgs[i].getRecords()[0];
+ Log.d(TAG, "MIME type: " + rec.toMimeType());
+ byte[] a = rec.getPayload();
+ Log.d(TAG, "NDEF record: " + byteArrayHex(a));
+ if (rec.getTnf() == NdefRecord.TNF_MIME_MEDIA &&
+ rec.toMimeType().equals("application/vnd/wfa.wsc")) {
+ Log.d(TAG, "WSC tag read");
+ }
+
+ if (!report_tag_read(a))
+ return;
+ }
+ }
+ }
+
+ finish();
+ }
+}
diff --git a/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaWebViewActivity.java b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaWebViewActivity.java
new file mode 100644
index 000000000000..a7c54fc680c9
--- /dev/null
+++ b/contrib/wpa/wpadebug/src/w1/fi/wpadebug/WpaWebViewActivity.java
@@ -0,0 +1,146 @@
+/*
+ * wpadebug - wpa_supplicant and Wi-Fi debugging app for Android
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+package w1.fi.wpadebug;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.net.http.SslError;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Window;
+import android.webkit.SslErrorHandler;
+import android.webkit.WebChromeClient;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.Toast;
+
+public class WpaWebViewActivity extends Activity
+{
+ private static final String TAG = "wpadebug";
+ private static final String EXTRA_MESSAGE = "w1.fi.wpadebug.URL";
+ private WebView mWebView;
+ final Activity activity = this;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ Log.d(TAG, "WpaWebViewActivity::onCreate");
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ String url = intent.getStringExtra(EXTRA_MESSAGE);
+ Log.d(TAG, "url=" + url);
+ if (url.equals("FINISH")) {
+ finish();
+ return;
+ }
+
+ mWebView = new WebView(this);
+ mWebView.getSettings().setJavaScriptEnabled(true);
+ mWebView.setWebViewClient(new WpaWebViewClient());
+
+ getWindow().requestFeature(Window.FEATURE_PROGRESS);
+
+ mWebView.setWebChromeClient(new WebChromeClient()
+ {
+ public void onProgressChanged(WebView view, int progress)
+ {
+ Log.d(TAG, "progress=" + progress);
+ activity.setProgress(progress * 1000);
+ }
+ });
+
+ setContentView(mWebView);
+
+ mWebView.loadUrl(url);
+ }
+
+ @Override
+ public void onResume()
+ {
+ Log.d(TAG, "WpaWebViewActivity::onResume");
+ super.onResume();
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent)
+ {
+ Log.d(TAG, "WpaWebViewActivity::onNewIntent");
+ super.onNewIntent(intent);
+ String url = intent.getStringExtra(EXTRA_MESSAGE);
+ Log.d(TAG, "url=" + url);
+ setIntent(intent);
+ if (url.equals("FINISH")) {
+ finish();
+ return;
+ }
+ mWebView.loadUrl(url);
+ }
+
+ private class WpaWebViewClient extends WebViewClient {
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url)
+ {
+ Log.d(TAG, "shouldOverrideUrlLoading: url=" + url);
+ Intent intent = getIntent();
+ intent.putExtra(EXTRA_MESSAGE, url);
+
+ view.loadUrl(url);
+ return true;
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url)
+ {
+ Log.d(TAG, "onPageFinished: url=" + url);
+ }
+
+ public void onReceivedError(WebView view, int errorCode,
+ String description, String failingUrl)
+ {
+ Log.d(TAG, "Failed to load page: errorCode=" +
+ errorCode + " description=" + description +
+ " URL=" + failingUrl);
+ Toast.makeText(activity, "Failed to load page: " +
+ description + " (URL=" + failingUrl + ")",
+ Toast.LENGTH_LONG).show();
+ }
+
+ @Override
+ public void onReceivedSslError(WebView view, SslErrorHandler handler,
+ SslError error)
+ {
+ Log.d(TAG, "SSL error: " + error);
+
+ final SslErrorHandler h = handler;
+ AlertDialog.Builder alert = new AlertDialog.Builder(activity);
+ alert.setTitle("SSL error - Continue?");
+ alert.setMessage(error.toString())
+ .setCancelable(false)
+ .setPositiveButton("Yes", new DialogInterface.OnClickListener()
+ {
+ public void onClick(DialogInterface dialog, int id)
+ {
+ h.proceed();
+ }
+ })
+ .setNegativeButton("No", new DialogInterface.OnClickListener()
+ {
+ public void onClick(DialogInterface dialog, int id)
+ {
+ h.cancel();
+ }
+ });
+ alert.show();
+ }
+ }
+}
diff --git a/contrib/wpa/wpaspy/Makefile b/contrib/wpa/wpaspy/Makefile
new file mode 100644
index 000000000000..6f720a9fe121
--- /dev/null
+++ b/contrib/wpa/wpaspy/Makefile
@@ -0,0 +1,15 @@
+all: build
+
+SRC=wpaspy.c
+
+.PHONY: build
+build: $(SRC) setup.py
+ python setup.py build
+
+install:
+ python setup.py install
+
+clean:
+ python setup.py clean
+ rm -f *~
+ rm -rf build
diff --git a/contrib/wpa/wpaspy/setup.py b/contrib/wpa/wpaspy/setup.py
new file mode 100644
index 000000000000..4dbf76540a23
--- /dev/null
+++ b/contrib/wpa/wpaspy/setup.py
@@ -0,0 +1,22 @@
+#!/usr/bin/python
+#
+# Python bindings for wpa_ctrl (wpa_supplicant/hostapd control interface)
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+from distutils.core import setup, Extension
+
+ext = Extension(name = 'wpaspy',
+ sources = ['../src/common/wpa_ctrl.c',
+ '../src/utils/os_unix.c',
+ 'wpaspy.c'],
+ extra_compile_args = ["-I../src/common",
+ "-I../src/utils",
+ "-DCONFIG_CTRL_IFACE",
+ "-DCONFIG_CTRL_IFACE_UNIX"])
+
+setup(name = 'wpaspy',
+ ext_modules = [ext],
+ description = 'Python bindings for wpa_ctrl (wpa_supplicant/hostapd)')
diff --git a/contrib/wpa/wpaspy/test.py b/contrib/wpa/wpaspy/test.py
new file mode 100755
index 000000000000..5e18fb23f744
--- /dev/null
+++ b/contrib/wpa/wpaspy/test.py
@@ -0,0 +1,82 @@
+#!/usr/bin/python
+#
+# Test script for wpaspy
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import sys
+import time
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+
+def wpas_connect(host=None, port=9877):
+ ifaces = []
+
+ if host != None:
+ try:
+ wpas = wpaspy.Ctrl(host, port)
+ return wpas
+ except:
+ print("Could not connect to host: ", host)
+ return None
+
+ if os.path.isdir(wpas_ctrl):
+ try:
+ ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+ except OSError as error:
+ print("Could not find wpa_supplicant: ", error)
+ return None
+
+ if len(ifaces) < 1:
+ print("No wpa_supplicant control interface found")
+ return None
+
+ for ctrl in ifaces:
+ try:
+ wpas = wpaspy.Ctrl(ctrl)
+ return wpas
+ except Exception as e:
+ pass
+ return None
+
+
+def main(host=None, port=9877):
+ print("Testing wpa_supplicant control interface connection")
+ wpas = wpas_connect(host, port)
+ if wpas is None:
+ return
+ print("Connected to wpa_supplicant")
+ print(wpas.request('PING'))
+
+ mon = wpas_connect(host, port)
+ if mon is None:
+ print("Could not open event monitor connection")
+ return
+
+ mon.attach()
+ print("Scan")
+ print(wpas.request('SCAN'))
+
+ count = 0
+ while count < 10:
+ count += 1
+ time.sleep(1)
+ while mon.pending():
+ ev = mon.recv()
+ print(ev)
+ if 'CTRL-EVENT-SCAN-RESULTS' in ev:
+ print('Scan completed')
+ print(wpas.request('SCAN_RESULTS'))
+ count = 10
+ pass
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 2:
+ main(host=sys.argv[1], port=int(sys.argv[2]))
+ else:
+ main()
diff --git a/contrib/wpa/wpaspy/wpaspy.c b/contrib/wpa/wpaspy/wpaspy.c
new file mode 100644
index 000000000000..4d4c2a49569f
--- /dev/null
+++ b/contrib/wpa/wpaspy/wpaspy.c
@@ -0,0 +1,245 @@
+/*
+ * Python bindings for wpa_ctrl (wpa_supplicant/hostapd control interface)
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <Python.h>
+#include <structmember.h>
+
+#include "wpa_ctrl.h"
+
+
+struct wpaspy_obj {
+ PyObject_HEAD
+ struct wpa_ctrl *ctrl;
+ int attached;
+};
+
+static PyObject *wpaspy_error;
+
+
+static int wpaspy_open(struct wpaspy_obj *self, PyObject *args)
+{
+ const char *path;
+
+ if (!PyArg_ParseTuple(args, "s", &path))
+ return -1;
+ self->ctrl = wpa_ctrl_open(path);
+ if (self->ctrl == NULL)
+ return -1;
+ self->attached = 0;
+ return 0;
+}
+
+
+static void wpaspy_close(struct wpaspy_obj *self)
+{
+ if (self->ctrl) {
+ if (self->attached)
+ wpa_ctrl_detach(self->ctrl);
+ wpa_ctrl_close(self->ctrl);
+ self->ctrl = NULL;
+ }
+
+ PyObject_Del(self);
+}
+
+
+static PyObject * wpaspy_request(struct wpaspy_obj *self, PyObject *args)
+{
+ const char *cmd;
+ char buf[4096];
+ size_t buflen;
+ int ret;
+
+ if (!PyArg_ParseTuple(args, "s", &cmd))
+ return NULL;
+
+ buflen = sizeof(buf) - 1;
+ ret = wpa_ctrl_request(self->ctrl, cmd, strlen(cmd), buf, &buflen,
+ NULL);
+ if (ret == -2) {
+ PyErr_SetString(wpaspy_error, "Request timed out");
+ return NULL;
+ }
+ if (ret) {
+ PyErr_SetString(wpaspy_error, "Request failed");
+ return NULL;
+ }
+
+ buf[buflen] = '\0';
+ return Py_BuildValue("s", buf);
+}
+
+
+static PyObject * wpaspy_attach(struct wpaspy_obj *self)
+{
+ int ret;
+
+ if (self->attached)
+ Py_RETURN_NONE;
+
+ ret = wpa_ctrl_attach(self->ctrl);
+ if (ret) {
+ PyErr_SetString(wpaspy_error, "Attach failed");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+
+static PyObject * wpaspy_detach(struct wpaspy_obj *self)
+{
+ int ret;
+
+ if (!self->attached)
+ Py_RETURN_NONE;
+
+ ret = wpa_ctrl_detach(self->ctrl);
+ if (ret) {
+ PyErr_SetString(wpaspy_error, "Detach failed");
+ return NULL;
+ }
+ Py_RETURN_NONE;
+}
+
+
+static PyObject * wpaspy_pending(struct wpaspy_obj *self)
+{
+ switch (wpa_ctrl_pending(self->ctrl)) {
+ case 1:
+ Py_RETURN_TRUE;
+ case 0:
+ Py_RETURN_FALSE;
+ default:
+ PyErr_SetString(wpaspy_error, "wpa_ctrl_pending failed");
+ break;
+ }
+
+ return NULL;
+}
+
+
+static PyObject * wpaspy_recv(struct wpaspy_obj *self)
+{
+ int ret;
+ char buf[4096];
+ size_t buflen;
+
+ buflen = sizeof(buf) - 1;
+ Py_BEGIN_ALLOW_THREADS
+ ret = wpa_ctrl_recv(self->ctrl, buf, &buflen);
+ Py_END_ALLOW_THREADS
+
+ if (ret) {
+ PyErr_SetString(wpaspy_error, "wpa_ctrl_recv failed");
+ return NULL;
+ }
+
+ buf[buflen] = '\0';
+ return Py_BuildValue("s", buf);
+}
+
+
+static PyMethodDef wpaspy_methods[] = {
+ {
+ "request", (PyCFunction) wpaspy_request, METH_VARARGS,
+ "Send a control interface command and return response"
+ },
+ {
+ "attach", (PyCFunction) wpaspy_attach, METH_NOARGS,
+ "Attach as an event monitor"
+ },
+ {
+ "detach", (PyCFunction) wpaspy_detach, METH_NOARGS,
+ "Detach an event monitor"
+ },
+ {
+ "pending", (PyCFunction) wpaspy_pending, METH_NOARGS,
+ "Check whether any events are pending"
+ },
+ {
+ "recv", (PyCFunction) wpaspy_recv, METH_NOARGS,
+ "Received pending event"
+ },
+ { NULL, NULL, 0, NULL }
+};
+
+static PyMemberDef wpaspy_members[] = {
+ {
+ "attached", T_INT, offsetof(struct wpaspy_obj, attached),
+ READONLY,
+ "Whether instance is attached as event monitor"
+ },
+ { NULL }
+};
+
+static PyTypeObject wpaspy_ctrl = {
+ PyObject_HEAD_INIT(NULL)
+ .tp_name = "wpaspy.Ctrl",
+ .tp_basicsize = sizeof(struct wpaspy_obj),
+ .tp_getattro = PyObject_GenericGetAttr,
+ .tp_setattro = PyObject_GenericSetAttr,
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
+ .tp_methods = wpaspy_methods,
+ .tp_members = wpaspy_members,
+ .tp_init = (initproc) wpaspy_open,
+ .tp_dealloc = (destructor) wpaspy_close,
+ .tp_new = PyType_GenericNew,
+};
+
+
+#if PY_MAJOR_VERSION < 3
+static PyMethodDef module_methods[] = {
+ { NULL, NULL, 0, NULL }
+};
+
+
+PyMODINIT_FUNC initwpaspy(void)
+{
+ PyObject *mod;
+
+ PyType_Ready(&wpaspy_ctrl);
+ mod = Py_InitModule("wpaspy", module_methods);
+ wpaspy_error = PyErr_NewException("wpaspy.error", NULL, NULL);
+
+ Py_INCREF(&wpaspy_ctrl);
+ Py_INCREF(wpaspy_error);
+
+ PyModule_AddObject(mod, "Ctrl", (PyObject *) &wpaspy_ctrl);
+ PyModule_AddObject(mod, "error", wpaspy_error);
+}
+#else
+static struct PyModuleDef wpaspy_def = {
+ PyModuleDef_HEAD_INIT,
+ "wpaspy",
+};
+
+
+PyMODINIT_FUNC initwpaspy(void)
+{
+ PyObject *mod;
+
+ mod = PyModule_Create(&wpaspy_def);
+ if (!mod)
+ return NULL;
+
+ wpaspy_error = PyErr_NewException("wpaspy.error", NULL, NULL);
+
+ Py_INCREF(&wpaspy_ctrl);
+ Py_INCREF(wpaspy_error);
+
+ if (PyModule_AddObject(mod, "Ctrl", (PyObject *) &wpaspy_ctrl) < 0 ||
+ PyModule_AddObject(mod, "error", wpaspy_error) < 0) {
+ Py_DECREF(&wpaspy_ctrl);
+ Py_DECREF(wpaspy_error);
+ Py_DECREF(mod);
+ mod = NULL;
+ }
+
+ return mod;
+}
+#endif
diff --git a/contrib/wpa/wpaspy/wpaspy.py b/contrib/wpa/wpaspy/wpaspy.py
new file mode 100644
index 000000000000..5b8140b7c99f
--- /dev/null
+++ b/contrib/wpa/wpaspy/wpaspy.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+#
+# wpa_supplicant/hostapd control interface using Python
+# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import stat
+import socket
+import select
+
+counter = 0
+
+class Ctrl:
+ def __init__(self, path, port=9877):
+ global counter
+ self.started = False
+ self.attached = False
+ self.path = path
+ self.port = port
+
+ self.udp = False
+ if not path.startswith('/'):
+ try:
+ mode = os.stat(path).st_mode
+ if not stat.S_ISSOCK(mode):
+ self.udp = True
+ except:
+ self.udp = True
+
+ if not self.udp:
+ self.s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ self.dest = path
+ self.local = "/tmp/wpa_ctrl_" + str(os.getpid()) + '-' + str(counter)
+ counter += 1
+ self.s.bind(self.local)
+ try:
+ self.s.connect(self.dest)
+ except Exception as e:
+ self.s.close()
+ os.unlink(self.local)
+ raise
+ else:
+ try:
+ self.s = None
+ ai_list = socket.getaddrinfo(path, port, socket.AF_INET,
+ socket.SOCK_DGRAM)
+ for af, socktype, proto, cn, sockaddr in ai_list:
+ self.sockaddr = sockaddr
+ break
+ self.s = socket.socket(af, socktype)
+ self.s.settimeout(5)
+ self.s.sendto(b"GET_COOKIE", sockaddr)
+ reply, server = self.s.recvfrom(4096)
+ self.cookie = reply
+ self.port = port
+ except:
+ print("connect exception ", path, str(port))
+ if self.s != None:
+ self.s.close()
+ raise
+ self.started = True
+
+ def __del__(self):
+ self.close()
+
+ def close(self):
+ if self.attached:
+ try:
+ self.detach()
+ except Exception as e:
+ # Need to ignore this allow the socket to be closed
+ self.attached = False
+ pass
+ if self.started:
+ self.s.close()
+ if not self.udp:
+ os.unlink(self.local)
+ self.started = False
+
+ def request(self, cmd, timeout=10):
+ if type(cmd) == str:
+ try:
+ cmd2 = cmd.encode()
+ cmd = cmd2
+ except UnicodeDecodeError as e:
+ pass
+ if self.udp:
+ self.s.sendto(self.cookie + cmd, self.sockaddr)
+ else:
+ self.s.send(cmd)
+ [r, w, e] = select.select([self.s], [], [], timeout)
+ if r:
+ res = self.s.recv(4096).decode()
+ try:
+ r = str(res)
+ except UnicodeDecodeError as e:
+ r = res
+ return r
+ raise Exception("Timeout on waiting response")
+
+ def attach(self):
+ if self.attached:
+ return None
+ res = self.request("ATTACH")
+ if "OK" in res:
+ self.attached = True
+ return None
+ raise Exception("ATTACH failed")
+
+ def detach(self):
+ if not self.attached:
+ return None
+ if self.s.fileno() == -1:
+ self.attached = False
+ return None
+ while self.pending():
+ ev = self.recv()
+ res = self.request("DETACH")
+ if "FAIL" not in res:
+ self.attached = False
+ return None
+ raise Exception("DETACH failed")
+
+ def terminate(self):
+ if self.attached:
+ try:
+ self.detach()
+ except Exception as e:
+ # Need to ignore this to allow the socket to be closed
+ self.attached = False
+ self.request("TERMINATE")
+ self.close()
+
+ def pending(self, timeout=0):
+ [r, w, e] = select.select([self.s], [], [], timeout)
+ if r:
+ return True
+ return False
+
+ def recv(self):
+ res = self.s.recv(4096).decode()
+ try:
+ r = str(res)
+ except UnicodeDecodeError as e:
+ r = res
+ return r
diff --git a/sys/dev/netmap/netmap.c b/sys/dev/netmap/netmap.c
index 13c412f3ba5b..4835c47d2785 100644
--- a/sys/dev/netmap/netmap.c
+++ b/sys/dev/netmap/netmap.c
@@ -1,4611 +1,4583 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (C) 2011-2014 Matteo Landi
* Copyright (C) 2011-2016 Luigi Rizzo
* Copyright (C) 2011-2016 Giuseppe Lettieri
* Copyright (C) 2011-2016 Vincenzo Maffione
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* $FreeBSD$
*
* This module supports memory mapped access to network devices,
* see netmap(4).
*
* The module uses a large, memory pool allocated by the kernel
* and accessible as mmapped memory by multiple userspace threads/processes.
* The memory pool contains packet buffers and "netmap rings",
* i.e. user-accessible copies of the interface's queues.
*
* Access to the network card works like this:
* 1. a process/thread issues one or more open() on /dev/netmap, to create
* select()able file descriptor on which events are reported.
* 2. on each descriptor, the process issues an ioctl() to identify
* the interface that should report events to the file descriptor.
* 3. on each descriptor, the process issues an mmap() request to
* map the shared memory region within the process' address space.
* The list of interesting queues is indicated by a location in
* the shared memory region.
* 4. using the functions in the netmap(4) userspace API, a process
* can look up the occupation state of a queue, access memory buffers,
* and retrieve received packets or enqueue packets to transmit.
* 5. using some ioctl()s the process can synchronize the userspace view
* of the queue with the actual status in the kernel. This includes both
* receiving the notification of new packets, and transmitting new
* packets on the output interface.
* 6. select() or poll() can be used to wait for events on individual
* transmit or receive queues (or all queues for a given interface).
*
SYNCHRONIZATION (USER)
The netmap rings and data structures may be shared among multiple
user threads or even independent processes.
Any synchronization among those threads/processes is delegated
to the threads themselves. Only one thread at a time can be in
a system call on the same netmap ring. The OS does not enforce
this and only guarantees against system crashes in case of
invalid usage.
LOCKING (INTERNAL)
Within the kernel, access to the netmap rings is protected as follows:
- a spinlock on each ring, to handle producer/consumer races on
RX rings attached to the host stack (against multiple host
threads writing from the host stack to the same ring),
and on 'destination' rings attached to a VALE switch
(i.e. RX rings in VALE ports, and TX rings in NIC/host ports)
protecting multiple active senders for the same destination)
- an atomic variable to guarantee that there is at most one
instance of *_*xsync() on the ring at any time.
For rings connected to user file
descriptors, an atomic_test_and_set() protects this, and the
lock on the ring is not actually used.
For NIC RX rings connected to a VALE switch, an atomic_test_and_set()
is also used to prevent multiple executions (the driver might indeed
already guarantee this).
For NIC TX rings connected to a VALE switch, the lock arbitrates
access to the queue (both when allocating buffers and when pushing
them out).
- *xsync() should be protected against initializations of the card.
On FreeBSD most devices have the reset routine protected by
a RING lock (ixgbe, igb, em) or core lock (re). lem is missing
the RING protection on rx_reset(), this should be added.
On linux there is an external lock on the tx path, which probably
also arbitrates access to the reset routine. XXX to be revised
- a per-interface core_lock protecting access from the host stack
while interfaces may be detached from netmap mode.
XXX there should be no need for this lock if we detach the interfaces
only while they are down.
--- VALE SWITCH ---
NMG_LOCK() serializes all modifications to switches and ports.
A switch cannot be deleted until all ports are gone.
For each switch, an SX lock (RWlock on linux) protects
deletion of ports. When configuring or deleting a new port, the
lock is acquired in exclusive mode (after holding NMG_LOCK).
When forwarding, the lock is acquired in shared mode (without NMG_LOCK).
The lock is held throughout the entire forwarding cycle,
during which the thread may incur in a page fault.
Hence it is important that sleepable shared locks are used.
On the rx ring, the per-port lock is grabbed initially to reserve
a number of slot in the ring, then the lock is released,
packets are copied from source to destination, and then
the lock is acquired again and the receive ring is updated.
(A similar thing is done on the tx ring for NIC and host stack
ports attached to the switch)
*/
/* --- internals ----
*
* Roadmap to the code that implements the above.
*
* > 1. a process/thread issues one or more open() on /dev/netmap, to create
* > select()able file descriptor on which events are reported.
*
* Internally, we allocate a netmap_priv_d structure, that will be
* initialized on ioctl(NIOCREGIF). There is one netmap_priv_d
* structure for each open().
*
* os-specific:
* FreeBSD: see netmap_open() (netmap_freebsd.c)
* linux: see linux_netmap_open() (netmap_linux.c)
*
* > 2. on each descriptor, the process issues an ioctl() to identify
* > the interface that should report events to the file descriptor.
*
* Implemented by netmap_ioctl(), NIOCREGIF case, with nmr->nr_cmd==0.
* Most important things happen in netmap_get_na() and
* netmap_do_regif(), called from there. Additional details can be
* found in the comments above those functions.
*
* In all cases, this action creates/takes-a-reference-to a
* netmap_*_adapter describing the port, and allocates a netmap_if
* and all necessary netmap rings, filling them with netmap buffers.
*
* In this phase, the sync callbacks for each ring are set (these are used
* in steps 5 and 6 below). The callbacks depend on the type of adapter.
* The adapter creation/initialization code puts them in the
* netmap_adapter (fields na->nm_txsync and na->nm_rxsync). Then, they
* are copied from there to the netmap_kring's during netmap_do_regif(), by
* the nm_krings_create() callback. All the nm_krings_create callbacks
* actually call netmap_krings_create() to perform this and the other
* common stuff. netmap_krings_create() also takes care of the host rings,
* if needed, by setting their sync callbacks appropriately.
*
* Additional actions depend on the kind of netmap_adapter that has been
* registered:
*
* - netmap_hw_adapter: [netmap.c]
* This is a system netdev/ifp with native netmap support.
* The ifp is detached from the host stack by redirecting:
* - transmissions (from the network stack) to netmap_transmit()
* - receive notifications to the nm_notify() callback for
* this adapter. The callback is normally netmap_notify(), unless
* the ifp is attached to a bridge using bwrap, in which case it
* is netmap_bwrap_intr_notify().
*
* - netmap_generic_adapter: [netmap_generic.c]
* A system netdev/ifp without native netmap support.
*
* (the decision about native/non native support is taken in
* netmap_get_hw_na(), called by netmap_get_na())
*
* - netmap_vp_adapter [netmap_vale.c]
* Returned by netmap_get_bdg_na().
* This is a persistent or ephemeral VALE port. Ephemeral ports
* are created on the fly if they don't already exist, and are
* always attached to a bridge.
* Persistent VALE ports must must be created separately, and i
* then attached like normal NICs. The NIOCREGIF we are examining
* will find them only if they had previously been created and
* attached (see VALE_CTL below).
*
* - netmap_pipe_adapter [netmap_pipe.c]
* Returned by netmap_get_pipe_na().
* Both pipe ends are created, if they didn't already exist.
*
* - netmap_monitor_adapter [netmap_monitor.c]
* Returned by netmap_get_monitor_na().
* If successful, the nm_sync callbacks of the monitored adapter
* will be intercepted by the returned monitor.
*
* - netmap_bwrap_adapter [netmap_vale.c]
* Cannot be obtained in this way, see VALE_CTL below
*
*
* os-specific:
* linux: we first go through linux_netmap_ioctl() to
* adapt the FreeBSD interface to the linux one.
*
*
* > 3. on each descriptor, the process issues an mmap() request to
* > map the shared memory region within the process' address space.
* > The list of interesting queues is indicated by a location in
* > the shared memory region.
*
* os-specific:
* FreeBSD: netmap_mmap_single (netmap_freebsd.c).
* linux: linux_netmap_mmap (netmap_linux.c).
*
* > 4. using the functions in the netmap(4) userspace API, a process
* > can look up the occupation state of a queue, access memory buffers,
* > and retrieve received packets or enqueue packets to transmit.
*
* these actions do not involve the kernel.
*
* > 5. using some ioctl()s the process can synchronize the userspace view
* > of the queue with the actual status in the kernel. This includes both
* > receiving the notification of new packets, and transmitting new
* > packets on the output interface.
*
* These are implemented in netmap_ioctl(), NIOCTXSYNC and NIOCRXSYNC
* cases. They invoke the nm_sync callbacks on the netmap_kring
* structures, as initialized in step 2 and maybe later modified
* by a monitor. Monitors, however, will always call the original
* callback before doing anything else.
*
*
* > 6. select() or poll() can be used to wait for events on individual
* > transmit or receive queues (or all queues for a given interface).
*
* Implemented in netmap_poll(). This will call the same nm_sync()
* callbacks as in step 5 above.
*
* os-specific:
* linux: we first go through linux_netmap_poll() to adapt
* the FreeBSD interface to the linux one.
*
*
* ---- VALE_CTL -----
*
* VALE switches are controlled by issuing a NIOCREGIF with a non-null
* nr_cmd in the nmreq structure. These subcommands are handled by
* netmap_bdg_ctl() in netmap_vale.c. Persistent VALE ports are created
* and destroyed by issuing the NETMAP_BDG_NEWIF and NETMAP_BDG_DELIF
* subcommands, respectively.
*
* Any network interface known to the system (including a persistent VALE
* port) can be attached to a VALE switch by issuing the
* NETMAP_REQ_VALE_ATTACH command. After the attachment, persistent VALE ports
* look exactly like ephemeral VALE ports (as created in step 2 above). The
* attachment of other interfaces, instead, requires the creation of a
* netmap_bwrap_adapter. Moreover, the attached interface must be put in
* netmap mode. This may require the creation of a netmap_generic_adapter if
* we have no native support for the interface, or if generic adapters have
* been forced by sysctl.
*
* Both persistent VALE ports and bwraps are handled by netmap_get_bdg_na(),
* called by nm_bdg_ctl_attach(), and discriminated by the nm_bdg_attach()
* callback. In the case of the bwrap, the callback creates the
* netmap_bwrap_adapter. The initialization of the bwrap is then
* completed by calling netmap_do_regif() on it, in the nm_bdg_ctl()
* callback (netmap_bwrap_bdg_ctl in netmap_vale.c).
* A generic adapter for the wrapped ifp will be created if needed, when
* netmap_get_bdg_na() calls netmap_get_hw_na().
*
*
* ---- DATAPATHS -----
*
* -= SYSTEM DEVICE WITH NATIVE SUPPORT =-
*
* na == NA(ifp) == netmap_hw_adapter created in DEVICE_netmap_attach()
*
* - tx from netmap userspace:
* concurrently:
* 1) ioctl(NIOCTXSYNC)/netmap_poll() in process context
* kring->nm_sync() == DEVICE_netmap_txsync()
* 2) device interrupt handler
* na->nm_notify() == netmap_notify()
* - rx from netmap userspace:
* concurrently:
* 1) ioctl(NIOCRXSYNC)/netmap_poll() in process context
* kring->nm_sync() == DEVICE_netmap_rxsync()
* 2) device interrupt handler
* na->nm_notify() == netmap_notify()
* - rx from host stack
* concurrently:
* 1) host stack
* netmap_transmit()
* na->nm_notify == netmap_notify()
* 2) ioctl(NIOCRXSYNC)/netmap_poll() in process context
* kring->nm_sync() == netmap_rxsync_from_host
* netmap_rxsync_from_host(na, NULL, NULL)
* - tx to host stack
* ioctl(NIOCTXSYNC)/netmap_poll() in process context
* kring->nm_sync() == netmap_txsync_to_host
* netmap_txsync_to_host(na)
* nm_os_send_up()
* FreeBSD: na->if_input() == ether_input()
* linux: netif_rx() with NM_MAGIC_PRIORITY_RX
*
*
* -= SYSTEM DEVICE WITH GENERIC SUPPORT =-
*
* na == NA(ifp) == generic_netmap_adapter created in generic_netmap_attach()
*
* - tx from netmap userspace:
* concurrently:
* 1) ioctl(NIOCTXSYNC)/netmap_poll() in process context
* kring->nm_sync() == generic_netmap_txsync()
* nm_os_generic_xmit_frame()
* linux: dev_queue_xmit() with NM_MAGIC_PRIORITY_TX
* ifp->ndo_start_xmit == generic_ndo_start_xmit()
* gna->save_start_xmit == orig. dev. start_xmit
* FreeBSD: na->if_transmit() == orig. dev if_transmit
* 2) generic_mbuf_destructor()
* na->nm_notify() == netmap_notify()
* - rx from netmap userspace:
* 1) ioctl(NIOCRXSYNC)/netmap_poll() in process context
* kring->nm_sync() == generic_netmap_rxsync()
* mbq_safe_dequeue()
* 2) device driver
* generic_rx_handler()
* mbq_safe_enqueue()
* na->nm_notify() == netmap_notify()
* - rx from host stack
* FreeBSD: same as native
* Linux: same as native except:
* 1) host stack
* dev_queue_xmit() without NM_MAGIC_PRIORITY_TX
* ifp->ndo_start_xmit == generic_ndo_start_xmit()
* netmap_transmit()
* na->nm_notify() == netmap_notify()
* - tx to host stack (same as native):
*
*
* -= VALE =-
*
* INCOMING:
*
* - VALE ports:
* ioctl(NIOCTXSYNC)/netmap_poll() in process context
* kring->nm_sync() == netmap_vp_txsync()
*
* - system device with native support:
* from cable:
* interrupt
* na->nm_notify() == netmap_bwrap_intr_notify(ring_nr != host ring)
* kring->nm_sync() == DEVICE_netmap_rxsync()
* netmap_vp_txsync()
* kring->nm_sync() == DEVICE_netmap_rxsync()
* from host stack:
* netmap_transmit()
* na->nm_notify() == netmap_bwrap_intr_notify(ring_nr == host ring)
* kring->nm_sync() == netmap_rxsync_from_host()
* netmap_vp_txsync()
*
* - system device with generic support:
* from device driver:
* generic_rx_handler()
* na->nm_notify() == netmap_bwrap_intr_notify(ring_nr != host ring)
* kring->nm_sync() == generic_netmap_rxsync()
* netmap_vp_txsync()
* kring->nm_sync() == generic_netmap_rxsync()
* from host stack:
* netmap_transmit()
* na->nm_notify() == netmap_bwrap_intr_notify(ring_nr == host ring)
* kring->nm_sync() == netmap_rxsync_from_host()
* netmap_vp_txsync()
*
* (all cases) --> nm_bdg_flush()
* dest_na->nm_notify() == (see below)
*
* OUTGOING:
*
* - VALE ports:
* concurrently:
* 1) ioctl(NIOCRXSYNC)/netmap_poll() in process context
* kring->nm_sync() == netmap_vp_rxsync()
* 2) from nm_bdg_flush()
* na->nm_notify() == netmap_notify()
*
* - system device with native support:
* to cable:
* na->nm_notify() == netmap_bwrap_notify()
* netmap_vp_rxsync()
* kring->nm_sync() == DEVICE_netmap_txsync()
* netmap_vp_rxsync()
* to host stack:
* netmap_vp_rxsync()
* kring->nm_sync() == netmap_txsync_to_host
* netmap_vp_rxsync_locked()
*
* - system device with generic adapter:
* to device driver:
* na->nm_notify() == netmap_bwrap_notify()
* netmap_vp_rxsync()
* kring->nm_sync() == generic_netmap_txsync()
* netmap_vp_rxsync()
* to host stack:
* netmap_vp_rxsync()
* kring->nm_sync() == netmap_txsync_to_host
* netmap_vp_rxsync()
*
*/
/*
* OS-specific code that is used only within this file.
* Other OS-specific code that must be accessed by drivers
* is present in netmap_kern.h
*/
#if defined(__FreeBSD__)
#include <sys/cdefs.h> /* prerequisite */
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/param.h> /* defines used in kernel.h */
#include <sys/kernel.h> /* types used in module initialization */
#include <sys/conf.h> /* cdevsw struct, UID, GID */
#include <sys/filio.h> /* FIONBIO */
#include <sys/sockio.h>
#include <sys/socketvar.h> /* struct socket */
#include <sys/malloc.h>
#include <sys/poll.h>
#include <sys/proc.h>
#include <sys/rwlock.h>
#include <sys/socket.h> /* sockaddrs */
#include <sys/selinfo.h>
#include <sys/sysctl.h>
#include <sys/jail.h>
#include <sys/epoch.h>
#include <net/vnet.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/bpf.h> /* BIOCIMMEDIATE */
#include <machine/bus.h> /* bus_dmamap_* */
#include <sys/endian.h>
#include <sys/refcount.h>
#include <net/ethernet.h> /* ETHER_BPF_MTAP */
#elif defined(linux)
#include "bsd_glue.h"
#elif defined(__APPLE__)
#warning OSX support is only partial
#include "osx_glue.h"
#elif defined (_WIN32)
#include "win_glue.h"
#else
#error Unsupported platform
#endif /* unsupported */
/*
* common headers
*/
#include <net/netmap.h>
#include <dev/netmap/netmap_kern.h>
#include <dev/netmap/netmap_mem2.h>
/* user-controlled variables */
int netmap_verbose;
#ifdef CONFIG_NETMAP_DEBUG
int netmap_debug;
#endif /* CONFIG_NETMAP_DEBUG */
static int netmap_no_timestamp; /* don't timestamp on rxsync */
int netmap_no_pendintr = 1;
int netmap_txsync_retry = 2;
static int netmap_fwd = 0; /* force transparent forwarding */
/*
* netmap_admode selects the netmap mode to use.
* Invalid values are reset to NETMAP_ADMODE_BEST
*/
enum { NETMAP_ADMODE_BEST = 0, /* use native, fallback to generic */
NETMAP_ADMODE_NATIVE, /* either native or none */
NETMAP_ADMODE_GENERIC, /* force generic */
NETMAP_ADMODE_LAST };
static int netmap_admode = NETMAP_ADMODE_BEST;
/* netmap_generic_mit controls mitigation of RX notifications for
* the generic netmap adapter. The value is a time interval in
* nanoseconds. */
int netmap_generic_mit = 100*1000;
/* We use by default netmap-aware qdiscs with generic netmap adapters,
* even if there can be a little performance hit with hardware NICs.
* However, using the qdisc is the safer approach, for two reasons:
* 1) it prevents non-fifo qdiscs to break the TX notification
* scheme, which is based on mbuf destructors when txqdisc is
* not used.
* 2) it makes it possible to transmit over software devices that
* change skb->dev, like bridge, veth, ...
*
* Anyway users looking for the best performance should
* use native adapters.
*/
#ifdef linux
int netmap_generic_txqdisc = 1;
#endif
/* Default number of slots and queues for generic adapters. */
int netmap_generic_ringsize = 1024;
int netmap_generic_rings = 1;
/* Non-zero to enable checksum offloading in NIC drivers */
int netmap_generic_hwcsum = 0;
/* Non-zero if ptnet devices are allowed to use virtio-net headers. */
int ptnet_vnet_hdr = 1;
/*
* SYSCTL calls are grouped between SYSBEGIN and SYSEND to be emulated
* in some other operating systems
*/
SYSBEGIN(main_init);
SYSCTL_DECL(_dev_netmap);
SYSCTL_NODE(_dev, OID_AUTO, netmap, CTLFLAG_RW | CTLFLAG_MPSAFE, 0,
"Netmap args");
SYSCTL_INT(_dev_netmap, OID_AUTO, verbose,
CTLFLAG_RW, &netmap_verbose, 0, "Verbose mode");
#ifdef CONFIG_NETMAP_DEBUG
SYSCTL_INT(_dev_netmap, OID_AUTO, debug,
CTLFLAG_RW, &netmap_debug, 0, "Debug messages");
#endif /* CONFIG_NETMAP_DEBUG */
SYSCTL_INT(_dev_netmap, OID_AUTO, no_timestamp,
CTLFLAG_RW, &netmap_no_timestamp, 0, "no_timestamp");
SYSCTL_INT(_dev_netmap, OID_AUTO, no_pendintr, CTLFLAG_RW, &netmap_no_pendintr,
0, "Always look for new received packets.");
SYSCTL_INT(_dev_netmap, OID_AUTO, txsync_retry, CTLFLAG_RW,
&netmap_txsync_retry, 0, "Number of txsync loops in bridge's flush.");
SYSCTL_INT(_dev_netmap, OID_AUTO, fwd, CTLFLAG_RW, &netmap_fwd, 0,
"Force NR_FORWARD mode");
SYSCTL_INT(_dev_netmap, OID_AUTO, admode, CTLFLAG_RW, &netmap_admode, 0,
"Adapter mode. 0 selects the best option available,"
"1 forces native adapter, 2 forces emulated adapter");
SYSCTL_INT(_dev_netmap, OID_AUTO, generic_hwcsum, CTLFLAG_RW, &netmap_generic_hwcsum,
0, "Hardware checksums. 0 to disable checksum generation by the NIC (default),"
"1 to enable checksum generation by the NIC");
SYSCTL_INT(_dev_netmap, OID_AUTO, generic_mit, CTLFLAG_RW, &netmap_generic_mit,
0, "RX notification interval in nanoseconds");
SYSCTL_INT(_dev_netmap, OID_AUTO, generic_ringsize, CTLFLAG_RW,
&netmap_generic_ringsize, 0,
"Number of per-ring slots for emulated netmap mode");
SYSCTL_INT(_dev_netmap, OID_AUTO, generic_rings, CTLFLAG_RW,
&netmap_generic_rings, 0,
"Number of TX/RX queues for emulated netmap adapters");
#ifdef linux
SYSCTL_INT(_dev_netmap, OID_AUTO, generic_txqdisc, CTLFLAG_RW,
&netmap_generic_txqdisc, 0, "Use qdisc for generic adapters");
#endif
SYSCTL_INT(_dev_netmap, OID_AUTO, ptnet_vnet_hdr, CTLFLAG_RW, &ptnet_vnet_hdr,
0, "Allow ptnet devices to use virtio-net headers");
SYSEND;
NMG_LOCK_T netmap_global_lock;
/*
* mark the ring as stopped, and run through the locks
* to make sure other users get to see it.
* stopped must be either NR_KR_STOPPED (for unbounded stop)
* of NR_KR_LOCKED (brief stop for mutual exclusion purposes)
*/
static void
netmap_disable_ring(struct netmap_kring *kr, int stopped)
{
nm_kr_stop(kr, stopped);
// XXX check if nm_kr_stop is sufficient
mtx_lock(&kr->q_lock);
mtx_unlock(&kr->q_lock);
nm_kr_put(kr);
}
/* stop or enable a single ring */
void
netmap_set_ring(struct netmap_adapter *na, u_int ring_id, enum txrx t, int stopped)
{
if (stopped)
netmap_disable_ring(NMR(na, t)[ring_id], stopped);
else
NMR(na, t)[ring_id]->nkr_stopped = 0;
}
/* stop or enable all the rings of na */
void
netmap_set_all_rings(struct netmap_adapter *na, int stopped)
{
int i;
enum txrx t;
if (!nm_netmap_on(na))
return;
if (netmap_verbose) {
nm_prinf("%s: %sable all rings", na->name,
(stopped ? "dis" : "en"));
}
for_rx_tx(t) {
for (i = 0; i < netmap_real_rings(na, t); i++) {
netmap_set_ring(na, i, t, stopped);
}
}
}
/*
* Convenience function used in drivers. Waits for current txsync()s/rxsync()s
* to finish and prevents any new one from starting. Call this before turning
* netmap mode off, or before removing the hardware rings (e.g., on module
* onload).
*/
void
netmap_disable_all_rings(struct ifnet *ifp)
{
if (NM_NA_VALID(ifp)) {
netmap_set_all_rings(NA(ifp), NM_KR_LOCKED);
}
}
/*
* Convenience function used in drivers. Re-enables rxsync and txsync on the
* adapter's rings In linux drivers, this should be placed near each
* napi_enable().
*/
void
netmap_enable_all_rings(struct ifnet *ifp)
{
if (NM_NA_VALID(ifp)) {
netmap_set_all_rings(NA(ifp), 0 /* enabled */);
}
}
void
netmap_make_zombie(struct ifnet *ifp)
{
if (NM_NA_VALID(ifp)) {
struct netmap_adapter *na = NA(ifp);
netmap_set_all_rings(na, NM_KR_LOCKED);
na->na_flags |= NAF_ZOMBIE;
netmap_set_all_rings(na, 0);
}
}
void
netmap_undo_zombie(struct ifnet *ifp)
{
if (NM_NA_VALID(ifp)) {
struct netmap_adapter *na = NA(ifp);
if (na->na_flags & NAF_ZOMBIE) {
netmap_set_all_rings(na, NM_KR_LOCKED);
na->na_flags &= ~NAF_ZOMBIE;
netmap_set_all_rings(na, 0);
}
}
}
/*
* generic bound_checking function
*/
u_int
nm_bound_var(u_int *v, u_int dflt, u_int lo, u_int hi, const char *msg)
{
u_int oldv = *v;
const char *op = NULL;
if (dflt < lo)
dflt = lo;
if (dflt > hi)
dflt = hi;
if (oldv < lo) {
*v = dflt;
op = "Bump";
} else if (oldv > hi) {
*v = hi;
op = "Clamp";
}
if (op && msg)
nm_prinf("%s %s to %d (was %d)", op, msg, *v, oldv);
return *v;
}
/*
* packet-dump function, user-supplied or static buffer.
* The destination buffer must be at least 30+4*len
*/
const char *
nm_dump_buf(char *p, int len, int lim, char *dst)
{
static char _dst[8192];
int i, j, i0;
static char hex[] ="0123456789abcdef";
char *o; /* output position */
#define P_HI(x) hex[((x) & 0xf0)>>4]
#define P_LO(x) hex[((x) & 0xf)]
#define P_C(x) ((x) >= 0x20 && (x) <= 0x7e ? (x) : '.')
if (!dst)
dst = _dst;
if (lim <= 0 || lim > len)
lim = len;
o = dst;
sprintf(o, "buf 0x%p len %d lim %d\n", p, len, lim);
o += strlen(o);
/* hexdump routine */
for (i = 0; i < lim; ) {
sprintf(o, "%5d: ", i);
o += strlen(o);
memset(o, ' ', 48);
i0 = i;
for (j=0; j < 16 && i < lim; i++, j++) {
o[j*3] = P_HI(p[i]);
o[j*3+1] = P_LO(p[i]);
}
i = i0;
for (j=0; j < 16 && i < lim; i++, j++)
o[j + 48] = P_C(p[i]);
o[j+48] = '\n';
o += j+49;
}
*o = '\0';
#undef P_HI
#undef P_LO
#undef P_C
return dst;
}
/*
* Fetch configuration from the device, to cope with dynamic
* reconfigurations after loading the module.
*/
/* call with NMG_LOCK held */
int
netmap_update_config(struct netmap_adapter *na)
{
struct nm_config_info info;
bzero(&info, sizeof(info));
if (na->nm_config == NULL ||
na->nm_config(na, &info)) {
/* take whatever we had at init time */
info.num_tx_rings = na->num_tx_rings;
info.num_tx_descs = na->num_tx_desc;
info.num_rx_rings = na->num_rx_rings;
info.num_rx_descs = na->num_rx_desc;
info.rx_buf_maxsize = na->rx_buf_maxsize;
}
if (na->num_tx_rings == info.num_tx_rings &&
na->num_tx_desc == info.num_tx_descs &&
na->num_rx_rings == info.num_rx_rings &&
na->num_rx_desc == info.num_rx_descs &&
na->rx_buf_maxsize == info.rx_buf_maxsize)
return 0; /* nothing changed */
if (na->active_fds == 0) {
na->num_tx_rings = info.num_tx_rings;
na->num_tx_desc = info.num_tx_descs;
na->num_rx_rings = info.num_rx_rings;
na->num_rx_desc = info.num_rx_descs;
na->rx_buf_maxsize = info.rx_buf_maxsize;
if (netmap_verbose)
nm_prinf("configuration changed for %s: txring %d x %d, "
"rxring %d x %d, rxbufsz %d",
na->name, na->num_tx_rings, na->num_tx_desc,
na->num_rx_rings, na->num_rx_desc, na->rx_buf_maxsize);
return 0;
}
nm_prerr("WARNING: configuration changed for %s while active: "
"txring %d x %d, rxring %d x %d, rxbufsz %d",
na->name, info.num_tx_rings, info.num_tx_descs,
info.num_rx_rings, info.num_rx_descs,
info.rx_buf_maxsize);
return 1;
}
/* nm_sync callbacks for the host rings */
static int netmap_txsync_to_host(struct netmap_kring *kring, int flags);
static int netmap_rxsync_from_host(struct netmap_kring *kring, int flags);
static int
netmap_default_bufcfg(struct netmap_kring *kring, uint64_t target)
{
kring->hwbuf_len = target;
kring->buf_align = 0; /* no alignment */
return 0;
}
/* create the krings array and initialize the fields common to all adapters.
* The array layout is this:
*
* +----------+
* na->tx_rings ----->| | \
* | | } na->num_tx_ring
* | | /
* +----------+
* | | host tx kring
* na->rx_rings ----> +----------+
* | | \
* | | } na->num_rx_rings
* | | /
* +----------+
* | | host rx kring
* +----------+
* na->tailroom ----->| | \
* | | } tailroom bytes
* | | /
* +----------+
*
* Note: for compatibility, host krings are created even when not needed.
* The tailroom space is currently used by vale ports for allocating leases.
*/
/* call with NMG_LOCK held */
int
netmap_krings_create(struct netmap_adapter *na, u_int tailroom)
{
u_int i, len, ndesc;
struct netmap_kring *kring;
u_int n[NR_TXRX];
enum txrx t;
int err = 0;
if (na->tx_rings != NULL) {
if (netmap_debug & NM_DEBUG_ON)
nm_prerr("warning: krings were already created");
return 0;
}
/* account for the (possibly fake) host rings */
n[NR_TX] = netmap_all_rings(na, NR_TX);
n[NR_RX] = netmap_all_rings(na, NR_RX);
len = (n[NR_TX] + n[NR_RX]) *
(sizeof(struct netmap_kring) + sizeof(struct netmap_kring *))
+ tailroom;
na->tx_rings = nm_os_malloc((size_t)len);
if (na->tx_rings == NULL) {
nm_prerr("Cannot allocate krings");
return ENOMEM;
}
na->rx_rings = na->tx_rings + n[NR_TX];
na->tailroom = na->rx_rings + n[NR_RX];
/* link the krings in the krings array */
kring = (struct netmap_kring *)((char *)na->tailroom + tailroom);
for (i = 0; i < n[NR_TX] + n[NR_RX]; i++) {
na->tx_rings[i] = kring;
kring++;
}
/*
* All fields in krings are 0 except the one initialized below.
* but better be explicit on important kring fields.
*/
for_rx_tx(t) {
ndesc = nma_get_ndesc(na, t);
for (i = 0; i < n[t]; i++) {
kring = NMR(na, t)[i];
bzero(kring, sizeof(*kring));
kring->notify_na = na;
kring->ring_id = i;
kring->tx = t;
kring->nkr_num_slots = ndesc;
kring->nr_mode = NKR_NETMAP_OFF;
kring->nr_pending_mode = NKR_NETMAP_OFF;
if (i < nma_get_nrings(na, t)) {
kring->nm_sync = (t == NR_TX ? na->nm_txsync : na->nm_rxsync);
kring->nm_bufcfg = na->nm_bufcfg;
if (kring->nm_bufcfg == NULL)
kring->nm_bufcfg = netmap_default_bufcfg;
} else {
if (!(na->na_flags & NAF_HOST_RINGS))
kring->nr_kflags |= NKR_FAKERING;
kring->nm_sync = (t == NR_TX ?
netmap_txsync_to_host:
netmap_rxsync_from_host);
kring->nm_bufcfg = netmap_default_bufcfg;
}
kring->nm_notify = na->nm_notify;
kring->rhead = kring->rcur = kring->nr_hwcur = 0;
/*
* IMPORTANT: Always keep one slot empty.
*/
kring->rtail = kring->nr_hwtail = (t == NR_TX ? ndesc - 1 : 0);
snprintf(kring->name, sizeof(kring->name) - 1, "%s %s%d", na->name,
nm_txrx2str(t), i);
nm_prdis("ktx %s h %d c %d t %d",
kring->name, kring->rhead, kring->rcur, kring->rtail);
err = nm_os_selinfo_init(&kring->si, kring->name);
if (err) {
netmap_krings_delete(na);
return err;
}
mtx_init(&kring->q_lock, (t == NR_TX ? "nm_txq_lock" : "nm_rxq_lock"), NULL, MTX_DEF);
kring->na = na; /* setting this field marks the mutex as initialized */
}
err = nm_os_selinfo_init(&na->si[t], na->name);
if (err) {
netmap_krings_delete(na);
return err;
}
}
return 0;
}
/* undo the actions performed by netmap_krings_create */
/* call with NMG_LOCK held */
void
netmap_krings_delete(struct netmap_adapter *na)
{
struct netmap_kring **kring = na->tx_rings;
enum txrx t;
if (na->tx_rings == NULL) {
if (netmap_debug & NM_DEBUG_ON)
nm_prerr("warning: krings were already deleted");
return;
}
for_rx_tx(t)
nm_os_selinfo_uninit(&na->si[t]);
/* we rely on the krings layout described above */
for ( ; kring != na->tailroom; kring++) {
if ((*kring)->na != NULL)
mtx_destroy(&(*kring)->q_lock);
nm_os_selinfo_uninit(&(*kring)->si);
}
nm_os_free(na->tx_rings);
na->tx_rings = na->rx_rings = na->tailroom = NULL;
}
/*
* Destructor for NIC ports. They also have an mbuf queue
* on the rings connected to the host so we need to purge
* them first.
*/
/* call with NMG_LOCK held */
void
netmap_hw_krings_delete(struct netmap_adapter *na)
{
u_int lim = netmap_real_rings(na, NR_RX), i;
for (i = nma_get_nrings(na, NR_RX); i < lim; i++) {
struct mbq *q = &NMR(na, NR_RX)[i]->rx_queue;
nm_prdis("destroy sw mbq with len %d", mbq_len(q));
mbq_purge(q);
mbq_safe_fini(q);
}
netmap_krings_delete(na);
}
void
netmap_mem_restore(struct netmap_adapter *na)
{
if (na->nm_mem_prev) {
netmap_mem_put(na->nm_mem);
na->nm_mem = na->nm_mem_prev;
na->nm_mem_prev = NULL;
}
}
static void
netmap_mem_drop(struct netmap_adapter *na)
{
/* if the native allocator had been overridden on regif,
* restore it now and drop the temporary one
*/
if (netmap_mem_deref(na->nm_mem, na)) {
netmap_mem_restore(na);
}
}
-static void
-netmap_update_hostrings_mode(struct netmap_adapter *na)
-{
- enum txrx t;
- struct netmap_kring *kring;
- int i;
-
- for_rx_tx(t) {
- for (i = nma_get_nrings(na, t);
- i < netmap_real_rings(na, t); i++) {
- kring = NMR(na, t)[i];
- kring->nr_mode = kring->nr_pending_mode;
- }
- }
-}
-
/*
* Undo everything that was done in netmap_do_regif(). In particular,
* call nm_register(ifp,0) to stop netmap mode on the interface and
* revert to normal operation.
*/
/* call with NMG_LOCK held */
static void netmap_unset_ringid(struct netmap_priv_d *);
static void netmap_krings_put(struct netmap_priv_d *);
void
netmap_do_unregif(struct netmap_priv_d *priv)
{
struct netmap_adapter *na = priv->np_na;
NMG_LOCK_ASSERT();
na->active_fds--;
/* unset nr_pending_mode and possibly release exclusive mode */
netmap_krings_put(priv);
#ifdef WITH_MONITOR
/* XXX check whether we have to do something with monitor
* when rings change nr_mode. */
if (na->active_fds <= 0) {
/* walk through all the rings and tell any monitor
* that the port is going to exit netmap mode
*/
netmap_monitor_stop(na);
}
#endif
- netmap_update_hostrings_mode(na);
-
if (na->active_fds <= 0 || nm_kring_pending(priv)) {
- netmap_set_all_rings(na, NM_KR_LOCKED);
na->nm_register(na, 0);
- netmap_set_all_rings(na, 0);
}
/* delete rings and buffers that are no longer needed */
netmap_mem_rings_delete(na);
if (na->active_fds <= 0) { /* last instance */
/*
* (TO CHECK) We enter here
* when the last reference to this file descriptor goes
* away. This means we cannot have any pending poll()
* or interrupt routine operating on the structure.
* XXX The file may be closed in a thread while
* another thread is using it.
* Linux keeps the file opened until the last reference
* by any outstanding ioctl/poll or mmap is gone.
* FreeBSD does not track mmap()s (but we do) and
* wakes up any sleeping poll(). Need to check what
* happens if the close() occurs while a concurrent
* syscall is running.
*/
if (netmap_debug & NM_DEBUG_ON)
nm_prinf("deleting last instance for %s", na->name);
if (nm_netmap_on(na)) {
nm_prerr("BUG: netmap on while going to delete the krings");
}
na->nm_krings_delete(na);
/* restore the default number of host tx and rx rings */
if (na->na_flags & NAF_HOST_RINGS) {
na->num_host_tx_rings = 1;
na->num_host_rx_rings = 1;
} else {
na->num_host_tx_rings = 0;
na->num_host_rx_rings = 0;
}
}
/* possibly decrement counter of tx_si/rx_si users */
netmap_unset_ringid(priv);
/* delete the nifp */
netmap_mem_if_delete(na, priv->np_nifp);
/* drop the allocator */
netmap_mem_drop(na);
/* mark the priv as unregistered */
priv->np_na = NULL;
priv->np_nifp = NULL;
}
struct netmap_priv_d*
netmap_priv_new(void)
{
struct netmap_priv_d *priv;
priv = nm_os_malloc(sizeof(struct netmap_priv_d));
if (priv == NULL)
return NULL;
priv->np_refs = 1;
nm_os_get_module();
return priv;
}
/*
* Destructor of the netmap_priv_d, called when the fd is closed
* Action: undo all the things done by NIOCREGIF,
* On FreeBSD we need to track whether there are active mmap()s,
* and we use np_active_mmaps for that. On linux, the field is always 0.
* Return: 1 if we can free priv, 0 otherwise.
*
*/
/* call with NMG_LOCK held */
void
netmap_priv_delete(struct netmap_priv_d *priv)
{
struct netmap_adapter *na = priv->np_na;
/* number of active references to this fd */
if (--priv->np_refs > 0) {
return;
}
nm_os_put_module();
if (na) {
netmap_do_unregif(priv);
}
netmap_unget_na(na, priv->np_ifp);
bzero(priv, sizeof(*priv)); /* for safety */
nm_os_free(priv);
}
/* call with NMG_LOCK *not* held */
void
netmap_dtor(void *data)
{
struct netmap_priv_d *priv = data;
NMG_LOCK();
netmap_priv_delete(priv);
NMG_UNLOCK();
}
/*
* Handlers for synchronization of the rings from/to the host stack.
* These are associated to a network interface and are just another
* ring pair managed by userspace.
*
* Netmap also supports transparent forwarding (NS_FORWARD and NR_FORWARD
* flags):
*
* - Before releasing buffers on hw RX rings, the application can mark
* them with the NS_FORWARD flag. During the next RXSYNC or poll(), they
* will be forwarded to the host stack, similarly to what happened if
* the application moved them to the host TX ring.
*
* - Before releasing buffers on the host RX ring, the application can
* mark them with the NS_FORWARD flag. During the next RXSYNC or poll(),
* they will be forwarded to the hw TX rings, saving the application
* from doing the same task in user-space.
*
* Transparent forwarding can be enabled per-ring, by setting the NR_FORWARD
* flag, or globally with the netmap_fwd sysctl.
*
* The transfer NIC --> host is relatively easy, just encapsulate
* into mbufs and we are done. The host --> NIC side is slightly
* harder because there might not be room in the tx ring so it
* might take a while before releasing the buffer.
*/
/*
* Pass a whole queue of mbufs to the host stack as coming from 'dst'
* We do not need to lock because the queue is private.
* After this call the queue is empty.
*/
static void
netmap_send_up(struct ifnet *dst, struct mbq *q)
{
struct mbuf *m;
struct mbuf *head = NULL, *prev = NULL;
#ifdef __FreeBSD__
struct epoch_tracker et;
NET_EPOCH_ENTER(et);
#endif /* __FreeBSD__ */
/* Send packets up, outside the lock; head/prev machinery
* is only useful for Windows. */
while ((m = mbq_dequeue(q)) != NULL) {
if (netmap_debug & NM_DEBUG_HOST)
nm_prinf("sending up pkt %p size %d", m, MBUF_LEN(m));
prev = nm_os_send_up(dst, m, prev);
if (head == NULL)
head = prev;
}
if (head)
nm_os_send_up(dst, NULL, head);
#ifdef __FreeBSD__
NET_EPOCH_EXIT(et);
#endif /* __FreeBSD__ */
mbq_fini(q);
}
/*
* Scan the buffers from hwcur to ring->head, and put a copy of those
* marked NS_FORWARD (or all of them if forced) into a queue of mbufs.
* Drop remaining packets in the unlikely event
* of an mbuf shortage.
*/
static void
netmap_grab_packets(struct netmap_kring *kring, struct mbq *q, int force)
{
u_int const lim = kring->nkr_num_slots - 1;
u_int const head = kring->rhead;
u_int n;
struct netmap_adapter *na = kring->na;
for (n = kring->nr_hwcur; n != head; n = nm_next(n, lim)) {
struct mbuf *m;
struct netmap_slot *slot = &kring->ring->slot[n];
if ((slot->flags & NS_FORWARD) == 0 && !force)
continue;
if (slot->len < 14 || slot->len > NETMAP_BUF_SIZE(na)) {
nm_prlim(5, "bad pkt at %d len %d", n, slot->len);
continue;
}
slot->flags &= ~NS_FORWARD; // XXX needed ?
/* XXX TODO: adapt to the case of a multisegment packet */
m = m_devget(NMB(na, slot), slot->len, 0, na->ifp, NULL);
if (m == NULL)
break;
mbq_enqueue(q, m);
}
}
static inline int
_nm_may_forward(struct netmap_kring *kring)
{
return ((netmap_fwd || kring->ring->flags & NR_FORWARD) &&
kring->na->na_flags & NAF_HOST_RINGS &&
kring->tx == NR_RX);
}
static inline int
nm_may_forward_up(struct netmap_kring *kring)
{
return _nm_may_forward(kring) &&
kring->ring_id != kring->na->num_rx_rings;
}
static inline int
nm_may_forward_down(struct netmap_kring *kring, int sync_flags)
{
return _nm_may_forward(kring) &&
(sync_flags & NAF_CAN_FORWARD_DOWN) &&
kring->ring_id == kring->na->num_rx_rings;
}
/*
* Send to the NIC rings packets marked NS_FORWARD between
* kring->nr_hwcur and kring->rhead.
* Called under kring->rx_queue.lock on the sw rx ring.
*
* It can only be called if the user opened all the TX hw rings,
* see NAF_CAN_FORWARD_DOWN flag.
* We can touch the TX netmap rings (slots, head and cur) since
* we are in poll/ioctl system call context, and the application
* is not supposed to touch the ring (using a different thread)
* during the execution of the system call.
*/
static u_int
netmap_sw_to_nic(struct netmap_adapter *na)
{
struct netmap_kring *kring = na->rx_rings[na->num_rx_rings];
struct netmap_slot *rxslot = kring->ring->slot;
u_int i, rxcur = kring->nr_hwcur;
u_int const head = kring->rhead;
u_int const src_lim = kring->nkr_num_slots - 1;
u_int sent = 0;
/* scan rings to find space, then fill as much as possible */
for (i = 0; i < na->num_tx_rings; i++) {
struct netmap_kring *kdst = na->tx_rings[i];
struct netmap_ring *rdst = kdst->ring;
u_int const dst_lim = kdst->nkr_num_slots - 1;
/* XXX do we trust ring or kring->rcur,rtail ? */
for (; rxcur != head && !nm_ring_empty(rdst);
rxcur = nm_next(rxcur, src_lim) ) {
struct netmap_slot *src, *dst, tmp;
u_int dst_head = rdst->head;
src = &rxslot[rxcur];
if ((src->flags & NS_FORWARD) == 0 && !netmap_fwd)
continue;
sent++;
dst = &rdst->slot[dst_head];
tmp = *src;
src->buf_idx = dst->buf_idx;
src->flags = NS_BUF_CHANGED;
dst->buf_idx = tmp.buf_idx;
dst->len = tmp.len;
dst->flags = NS_BUF_CHANGED;
rdst->head = rdst->cur = nm_next(dst_head, dst_lim);
}
/* if (sent) XXX txsync ? it would be just an optimization */
}
return sent;
}
/*
* netmap_txsync_to_host() passes packets up. We are called from a
* system call in user process context, and the only contention
* can be among multiple user threads erroneously calling
* this routine concurrently.
*/
static int
netmap_txsync_to_host(struct netmap_kring *kring, int flags)
{
struct netmap_adapter *na = kring->na;
u_int const lim = kring->nkr_num_slots - 1;
u_int const head = kring->rhead;
struct mbq q;
/* Take packets from hwcur to head and pass them up.
* Force hwcur = head since netmap_grab_packets() stops at head
*/
mbq_init(&q);
netmap_grab_packets(kring, &q, 1 /* force */);
nm_prdis("have %d pkts in queue", mbq_len(&q));
kring->nr_hwcur = head;
kring->nr_hwtail = head + lim;
if (kring->nr_hwtail > lim)
kring->nr_hwtail -= lim + 1;
netmap_send_up(na->ifp, &q);
return 0;
}
/*
* rxsync backend for packets coming from the host stack.
* They have been put in kring->rx_queue by netmap_transmit().
* We protect access to the kring using kring->rx_queue.lock
*
* also moves to the nic hw rings any packet the user has marked
* for transparent-mode forwarding, then sets the NR_FORWARD
* flag in the kring to let the caller push them out
*/
static int
netmap_rxsync_from_host(struct netmap_kring *kring, int flags)
{
struct netmap_adapter *na = kring->na;
struct netmap_ring *ring = kring->ring;
u_int nm_i, n;
u_int const lim = kring->nkr_num_slots - 1;
u_int const head = kring->rhead;
int ret = 0;
struct mbq *q = &kring->rx_queue, fq;
mbq_init(&fq); /* fq holds packets to be freed */
mbq_lock(q);
/* First part: import newly received packets */
n = mbq_len(q);
if (n) { /* grab packets from the queue */
struct mbuf *m;
uint32_t stop_i;
nm_i = kring->nr_hwtail;
stop_i = nm_prev(kring->nr_hwcur, lim);
while ( nm_i != stop_i && (m = mbq_dequeue(q)) != NULL ) {
int len = MBUF_LEN(m);
struct netmap_slot *slot = &ring->slot[nm_i];
m_copydata(m, 0, len, NMB(na, slot));
nm_prdis("nm %d len %d", nm_i, len);
if (netmap_debug & NM_DEBUG_HOST)
nm_prinf("%s", nm_dump_buf(NMB(na, slot),len, 128, NULL));
slot->len = len;
slot->flags = 0;
nm_i = nm_next(nm_i, lim);
mbq_enqueue(&fq, m);
}
kring->nr_hwtail = nm_i;
}
/*
* Second part: skip past packets that userspace has released.
*/
nm_i = kring->nr_hwcur;
if (nm_i != head) { /* something was released */
if (nm_may_forward_down(kring, flags)) {
ret = netmap_sw_to_nic(na);
if (ret > 0) {
kring->nr_kflags |= NR_FORWARD;
ret = 0;
}
}
kring->nr_hwcur = head;
}
mbq_unlock(q);
mbq_purge(&fq);
mbq_fini(&fq);
return ret;
}
/* Get a netmap adapter for the port.
*
* If it is possible to satisfy the request, return 0
* with *na containing the netmap adapter found.
* Otherwise return an error code, with *na containing NULL.
*
* When the port is attached to a bridge, we always return
* EBUSY.
* Otherwise, if the port is already bound to a file descriptor,
* then we unconditionally return the existing adapter into *na.
* In all the other cases, we return (into *na) either native,
* generic or NULL, according to the following table:
*
* native_support
* active_fds dev.netmap.admode YES NO
* -------------------------------------------------------
* >0 * NA(ifp) NA(ifp)
*
* 0 NETMAP_ADMODE_BEST NATIVE GENERIC
* 0 NETMAP_ADMODE_NATIVE NATIVE NULL
* 0 NETMAP_ADMODE_GENERIC GENERIC GENERIC
*
*/
static void netmap_hw_dtor(struct netmap_adapter *); /* needed by NM_IS_NATIVE() */
int
netmap_get_hw_na(struct ifnet *ifp, struct netmap_mem_d *nmd, struct netmap_adapter **na)
{
/* generic support */
int i = netmap_admode; /* Take a snapshot. */
struct netmap_adapter *prev_na;
int error = 0;
*na = NULL; /* default */
/* reset in case of invalid value */
if (i < NETMAP_ADMODE_BEST || i >= NETMAP_ADMODE_LAST)
i = netmap_admode = NETMAP_ADMODE_BEST;
if (NM_NA_VALID(ifp)) {
prev_na = NA(ifp);
/* If an adapter already exists, return it if
* there are active file descriptors or if
* netmap is not forced to use generic
* adapters.
*/
if (NETMAP_OWNED_BY_ANY(prev_na)
|| i != NETMAP_ADMODE_GENERIC
|| prev_na->na_flags & NAF_FORCE_NATIVE
#ifdef WITH_PIPES
/* ugly, but we cannot allow an adapter switch
* if some pipe is referring to this one
*/
|| prev_na->na_next_pipe > 0
#endif
) {
*na = prev_na;
goto assign_mem;
}
}
/* If there isn't native support and netmap is not allowed
* to use generic adapters, we cannot satisfy the request.
*/
if (!NM_IS_NATIVE(ifp) && i == NETMAP_ADMODE_NATIVE)
return EOPNOTSUPP;
/* Otherwise, create a generic adapter and return it,
* saving the previously used netmap adapter, if any.
*
* Note that here 'prev_na', if not NULL, MUST be a
* native adapter, and CANNOT be a generic one. This is
* true because generic adapters are created on demand, and
* destroyed when not used anymore. Therefore, if the adapter
* currently attached to an interface 'ifp' is generic, it
* must be that
* (NA(ifp)->active_fds > 0 || NETMAP_OWNED_BY_KERN(NA(ifp))).
* Consequently, if NA(ifp) is generic, we will enter one of
* the branches above. This ensures that we never override
* a generic adapter with another generic adapter.
*/
error = generic_netmap_attach(ifp);
if (error)
return error;
*na = NA(ifp);
assign_mem:
if (nmd != NULL && !((*na)->na_flags & NAF_MEM_OWNER) &&
(*na)->active_fds == 0 && ((*na)->nm_mem != nmd)) {
(*na)->nm_mem_prev = (*na)->nm_mem;
(*na)->nm_mem = netmap_mem_get(nmd);
}
return 0;
}
/*
* MUST BE CALLED UNDER NMG_LOCK()
*
* Get a refcounted reference to a netmap adapter attached
* to the interface specified by req.
* This is always called in the execution of an ioctl().
*
* Return ENXIO if the interface specified by the request does
* not exist, ENOTSUP if netmap is not supported by the interface,
* EBUSY if the interface is already attached to a bridge,
* EINVAL if parameters are invalid, ENOMEM if needed resources
* could not be allocated.
* If successful, hold a reference to the netmap adapter.
*
* If the interface specified by req is a system one, also keep
* a reference to it and return a valid *ifp.
*/
int
netmap_get_na(struct nmreq_header *hdr,
struct netmap_adapter **na, struct ifnet **ifp,
struct netmap_mem_d *nmd, int create)
{
struct nmreq_register *req = (struct nmreq_register *)(uintptr_t)hdr->nr_body;
int error = 0;
struct netmap_adapter *ret = NULL;
int nmd_ref = 0;
*na = NULL; /* default return value */
*ifp = NULL;
if (hdr->nr_reqtype != NETMAP_REQ_REGISTER) {
return EINVAL;
}
if (req->nr_mode == NR_REG_PIPE_MASTER ||
req->nr_mode == NR_REG_PIPE_SLAVE) {
/* Do not accept deprecated pipe modes. */
nm_prerr("Deprecated pipe nr_mode, use xx{yy or xx}yy syntax");
return EINVAL;
}
NMG_LOCK_ASSERT();
/* if the request contain a memid, try to find the
* corresponding memory region
*/
if (nmd == NULL && req->nr_mem_id) {
nmd = netmap_mem_find(req->nr_mem_id);
if (nmd == NULL)
return EINVAL;
/* keep the rereference */
nmd_ref = 1;
}
/* We cascade through all possible types of netmap adapter.
* All netmap_get_*_na() functions return an error and an na,
* with the following combinations:
*
* error na
* 0 NULL type doesn't match
* !0 NULL type matches, but na creation/lookup failed
* 0 !NULL type matches and na created/found
* !0 !NULL impossible
*/
error = netmap_get_null_na(hdr, na, nmd, create);
if (error || *na != NULL)
goto out;
/* try to see if this is a monitor port */
error = netmap_get_monitor_na(hdr, na, nmd, create);
if (error || *na != NULL)
goto out;
/* try to see if this is a pipe port */
error = netmap_get_pipe_na(hdr, na, nmd, create);
if (error || *na != NULL)
goto out;
/* try to see if this is a vale port */
error = netmap_get_vale_na(hdr, na, nmd, create);
if (error)
goto out;
if (*na != NULL) /* valid match in netmap_get_bdg_na() */
goto out;
/*
* This must be a hardware na, lookup the name in the system.
* Note that by hardware we actually mean "it shows up in ifconfig".
* This may still be a tap, a veth/epair, or even a
* persistent VALE port.
*/
*ifp = ifunit_ref(hdr->nr_name);
if (*ifp == NULL) {
error = ENXIO;
goto out;
}
error = netmap_get_hw_na(*ifp, nmd, &ret);
if (error)
goto out;
*na = ret;
netmap_adapter_get(ret);
/*
* if the adapter supports the host rings and it is not already open,
* try to set the number of host rings as requested by the user
*/
if (((*na)->na_flags & NAF_HOST_RINGS) && (*na)->active_fds == 0) {
if (req->nr_host_tx_rings)
(*na)->num_host_tx_rings = req->nr_host_tx_rings;
if (req->nr_host_rx_rings)
(*na)->num_host_rx_rings = req->nr_host_rx_rings;
}
nm_prdis("%s: host tx %d rx %u", (*na)->name, (*na)->num_host_tx_rings,
(*na)->num_host_rx_rings);
out:
if (error) {
if (ret)
netmap_adapter_put(ret);
if (*ifp) {
if_rele(*ifp);
*ifp = NULL;
}
}
if (nmd_ref)
netmap_mem_put(nmd);
return error;
}
/* undo netmap_get_na() */
void
netmap_unget_na(struct netmap_adapter *na, struct ifnet *ifp)
{
if (ifp)
if_rele(ifp);
if (na)
netmap_adapter_put(na);
}
#define NM_FAIL_ON(t) do { \
if (unlikely(t)) { \
nm_prlim(5, "%s: fail '" #t "' " \
"h %d c %d t %d " \
"rh %d rc %d rt %d " \
"hc %d ht %d", \
kring->name, \
head, cur, ring->tail, \
kring->rhead, kring->rcur, kring->rtail, \
kring->nr_hwcur, kring->nr_hwtail); \
return kring->nkr_num_slots; \
} \
} while (0)
/*
* validate parameters on entry for *_txsync()
* Returns ring->cur if ok, or something >= kring->nkr_num_slots
* in case of error.
*
* rhead, rcur and rtail=hwtail are stored from previous round.
* hwcur is the next packet to send to the ring.
*
* We want
* hwcur <= *rhead <= head <= cur <= tail = *rtail <= hwtail
*
* hwcur, rhead, rtail and hwtail are reliable
*/
u_int
nm_txsync_prologue(struct netmap_kring *kring, struct netmap_ring *ring)
{
u_int head = ring->head; /* read only once */
u_int cur = ring->cur; /* read only once */
u_int n = kring->nkr_num_slots;
nm_prdis(5, "%s kcur %d ktail %d head %d cur %d tail %d",
kring->name,
kring->nr_hwcur, kring->nr_hwtail,
ring->head, ring->cur, ring->tail);
#if 1 /* kernel sanity checks; but we can trust the kring. */
NM_FAIL_ON(kring->nr_hwcur >= n || kring->rhead >= n ||
kring->rtail >= n || kring->nr_hwtail >= n);
#endif /* kernel sanity checks */
/*
* user sanity checks. We only use head,
* A, B, ... are possible positions for head:
*
* 0 A rhead B rtail C n-1
* 0 D rtail E rhead F n-1
*
* B, F, D are valid. A, C, E are wrong
*/
if (kring->rtail >= kring->rhead) {
/* want rhead <= head <= rtail */
NM_FAIL_ON(head < kring->rhead || head > kring->rtail);
/* and also head <= cur <= rtail */
NM_FAIL_ON(cur < head || cur > kring->rtail);
} else { /* here rtail < rhead */
/* we need head outside rtail .. rhead */
NM_FAIL_ON(head > kring->rtail && head < kring->rhead);
/* two cases now: head <= rtail or head >= rhead */
if (head <= kring->rtail) {
/* want head <= cur <= rtail */
NM_FAIL_ON(cur < head || cur > kring->rtail);
} else { /* head >= rhead */
/* cur must be outside rtail..head */
NM_FAIL_ON(cur > kring->rtail && cur < head);
}
}
if (ring->tail != kring->rtail) {
nm_prlim(5, "%s tail overwritten was %d need %d", kring->name,
ring->tail, kring->rtail);
ring->tail = kring->rtail;
}
kring->rhead = head;
kring->rcur = cur;
return head;
}
/*
* validate parameters on entry for *_rxsync()
* Returns ring->head if ok, kring->nkr_num_slots on error.
*
* For a valid configuration,
* hwcur <= head <= cur <= tail <= hwtail
*
* We only consider head and cur.
* hwcur and hwtail are reliable.
*
*/
u_int
nm_rxsync_prologue(struct netmap_kring *kring, struct netmap_ring *ring)
{
uint32_t const n = kring->nkr_num_slots;
uint32_t head, cur;
nm_prdis(5,"%s kc %d kt %d h %d c %d t %d",
kring->name,
kring->nr_hwcur, kring->nr_hwtail,
ring->head, ring->cur, ring->tail);
/*
* Before storing the new values, we should check they do not
* move backwards. However:
* - head is not an issue because the previous value is hwcur;
* - cur could in principle go back, however it does not matter
* because we are processing a brand new rxsync()
*/
cur = kring->rcur = ring->cur; /* read only once */
head = kring->rhead = ring->head; /* read only once */
#if 1 /* kernel sanity checks */
NM_FAIL_ON(kring->nr_hwcur >= n || kring->nr_hwtail >= n);
#endif /* kernel sanity checks */
/* user sanity checks */
if (kring->nr_hwtail >= kring->nr_hwcur) {
/* want hwcur <= rhead <= hwtail */
NM_FAIL_ON(head < kring->nr_hwcur || head > kring->nr_hwtail);
/* and also rhead <= rcur <= hwtail */
NM_FAIL_ON(cur < head || cur > kring->nr_hwtail);
} else {
/* we need rhead outside hwtail..hwcur */
NM_FAIL_ON(head < kring->nr_hwcur && head > kring->nr_hwtail);
/* two cases now: head <= hwtail or head >= hwcur */
if (head <= kring->nr_hwtail) {
/* want head <= cur <= hwtail */
NM_FAIL_ON(cur < head || cur > kring->nr_hwtail);
} else {
/* cur must be outside hwtail..head */
NM_FAIL_ON(cur < head && cur > kring->nr_hwtail);
}
}
if (ring->tail != kring->rtail) {
nm_prlim(5, "%s tail overwritten was %d need %d",
kring->name,
ring->tail, kring->rtail);
ring->tail = kring->rtail;
}
return head;
}
/*
* Error routine called when txsync/rxsync detects an error.
* Can't do much more than resetting head = cur = hwcur, tail = hwtail
* Return 1 on reinit.
*
* This routine is only called by the upper half of the kernel.
* It only reads hwcur (which is changed only by the upper half, too)
* and hwtail (which may be changed by the lower half, but only on
* a tx ring and only to increase it, so any error will be recovered
* on the next call). For the above, we don't strictly need to call
* it under lock.
*/
int
netmap_ring_reinit(struct netmap_kring *kring)
{
struct netmap_ring *ring = kring->ring;
u_int i, lim = kring->nkr_num_slots - 1;
int errors = 0;
// XXX KASSERT nm_kr_tryget
nm_prlim(10, "called for %s", kring->name);
// XXX probably wrong to trust userspace
kring->rhead = ring->head;
kring->rcur = ring->cur;
kring->rtail = ring->tail;
if (ring->cur > lim)
errors++;
if (ring->head > lim)
errors++;
if (ring->tail > lim)
errors++;
for (i = 0; i <= lim; i++) {
u_int idx = ring->slot[i].buf_idx;
u_int len = ring->slot[i].len;
if (idx < 2 || idx >= kring->na->na_lut.objtotal) {
nm_prlim(5, "bad index at slot %d idx %d len %d ", i, idx, len);
ring->slot[i].buf_idx = 0;
ring->slot[i].len = 0;
} else if (len > NETMAP_BUF_SIZE(kring->na)) {
ring->slot[i].len = 0;
nm_prlim(5, "bad len at slot %d idx %d len %d", i, idx, len);
}
}
if (errors) {
nm_prlim(10, "total %d errors", errors);
nm_prlim(10, "%s reinit, cur %d -> %d tail %d -> %d",
kring->name,
ring->cur, kring->nr_hwcur,
ring->tail, kring->nr_hwtail);
ring->head = kring->rhead = kring->nr_hwcur;
ring->cur = kring->rcur = kring->nr_hwcur;
ring->tail = kring->rtail = kring->nr_hwtail;
}
return (errors ? 1 : 0);
}
/* interpret the ringid and flags fields of an nmreq, by translating them
* into a pair of intervals of ring indices:
*
* [priv->np_txqfirst, priv->np_txqlast) and
* [priv->np_rxqfirst, priv->np_rxqlast)
*
*/
int
netmap_interp_ringid(struct netmap_priv_d *priv, struct nmreq_header *hdr)
{
struct netmap_adapter *na = priv->np_na;
struct nmreq_register *reg = (struct nmreq_register *)hdr->nr_body;
int excluded_direction[] = { NR_TX_RINGS_ONLY, NR_RX_RINGS_ONLY };
enum txrx t;
u_int j;
u_int nr_flags = reg->nr_flags, nr_mode = reg->nr_mode,
nr_ringid = reg->nr_ringid;
for_rx_tx(t) {
if (nr_flags & excluded_direction[t]) {
priv->np_qfirst[t] = priv->np_qlast[t] = 0;
continue;
}
switch (nr_mode) {
case NR_REG_ALL_NIC:
case NR_REG_NULL:
priv->np_qfirst[t] = 0;
priv->np_qlast[t] = nma_get_nrings(na, t);
nm_prdis("ALL/PIPE: %s %d %d", nm_txrx2str(t),
priv->np_qfirst[t], priv->np_qlast[t]);
break;
case NR_REG_SW:
case NR_REG_NIC_SW:
if (!(na->na_flags & NAF_HOST_RINGS)) {
nm_prerr("host rings not supported");
return EINVAL;
}
priv->np_qfirst[t] = (nr_mode == NR_REG_SW ?
nma_get_nrings(na, t) : 0);
priv->np_qlast[t] = netmap_all_rings(na, t);
nm_prdis("%s: %s %d %d", nr_mode == NR_REG_SW ? "SW" : "NIC+SW",
nm_txrx2str(t),
priv->np_qfirst[t], priv->np_qlast[t]);
break;
case NR_REG_ONE_NIC:
if (nr_ringid >= na->num_tx_rings &&
nr_ringid >= na->num_rx_rings) {
nm_prerr("invalid ring id %d", nr_ringid);
return EINVAL;
}
/* if not enough rings, use the first one */
j = nr_ringid;
if (j >= nma_get_nrings(na, t))
j = 0;
priv->np_qfirst[t] = j;
priv->np_qlast[t] = j + 1;
nm_prdis("ONE_NIC: %s %d %d", nm_txrx2str(t),
priv->np_qfirst[t], priv->np_qlast[t]);
break;
case NR_REG_ONE_SW:
if (!(na->na_flags & NAF_HOST_RINGS)) {
nm_prerr("host rings not supported");
return EINVAL;
}
if (nr_ringid >= na->num_host_tx_rings &&
nr_ringid >= na->num_host_rx_rings) {
nm_prerr("invalid ring id %d", nr_ringid);
return EINVAL;
}
/* if not enough rings, use the first one */
j = nr_ringid;
if (j >= nma_get_host_nrings(na, t))
j = 0;
priv->np_qfirst[t] = nma_get_nrings(na, t) + j;
priv->np_qlast[t] = nma_get_nrings(na, t) + j + 1;
nm_prdis("ONE_SW: %s %d %d", nm_txrx2str(t),
priv->np_qfirst[t], priv->np_qlast[t]);
break;
default:
nm_prerr("invalid regif type %d", nr_mode);
return EINVAL;
}
}
priv->np_flags = nr_flags;
/* Allow transparent forwarding mode in the host --> nic
* direction only if all the TX hw rings have been opened. */
if (priv->np_qfirst[NR_TX] == 0 &&
priv->np_qlast[NR_TX] >= na->num_tx_rings) {
priv->np_sync_flags |= NAF_CAN_FORWARD_DOWN;
}
if (netmap_verbose) {
nm_prinf("%s: tx [%d,%d) rx [%d,%d) id %d",
na->name,
priv->np_qfirst[NR_TX],
priv->np_qlast[NR_TX],
priv->np_qfirst[NR_RX],
priv->np_qlast[NR_RX],
nr_ringid);
}
return 0;
}
/*
* Set the ring ID. For devices with a single queue, a request
* for all rings is the same as a single ring.
*/
static int
netmap_set_ringid(struct netmap_priv_d *priv, struct nmreq_header *hdr)
{
struct netmap_adapter *na = priv->np_na;
struct nmreq_register *reg = (struct nmreq_register *)hdr->nr_body;
int error;
enum txrx t;
error = netmap_interp_ringid(priv, hdr);
if (error) {
return error;
}
priv->np_txpoll = (reg->nr_flags & NR_NO_TX_POLL) ? 0 : 1;
/* optimization: count the users registered for more than
* one ring, which are the ones sleeping on the global queue.
* The default netmap_notify() callback will then
* avoid signaling the global queue if nobody is using it
*/
for_rx_tx(t) {
if (nm_si_user(priv, t))
na->si_users[t]++;
}
return 0;
}
static void
netmap_unset_ringid(struct netmap_priv_d *priv)
{
struct netmap_adapter *na = priv->np_na;
enum txrx t;
for_rx_tx(t) {
if (nm_si_user(priv, t))
na->si_users[t]--;
priv->np_qfirst[t] = priv->np_qlast[t] = 0;
}
priv->np_flags = 0;
priv->np_txpoll = 0;
priv->np_kloop_state = 0;
}
#define within_sel(p_, t_, i_) \
((i_) < (p_)->np_qlast[(t_)])
#define nonempty_sel(p_, t_) \
(within_sel((p_), (t_), (p_)->np_qfirst[(t_)]))
#define foreach_selected_ring(p_, t_, i_, kring_) \
for ((t_) = nonempty_sel((p_), NR_RX) ? NR_RX : NR_TX, \
(i_) = (p_)->np_qfirst[(t_)]; \
(t_ == NR_RX || \
(t == NR_TX && within_sel((p_), (t_), (i_)))) && \
((kring_) = NMR((p_)->np_na, (t_))[(i_)]); \
(i_) = within_sel((p_), (t_), (i_) + 1) ? (i_) + 1 : \
(++(t_) < NR_TXRX ? (p_)->np_qfirst[(t_)] : (i_)))
/* Set the nr_pending_mode for the requested rings.
* If requested, also try to get exclusive access to the rings, provided
* the rings we want to bind are not exclusively owned by a previous bind.
*/
static int
netmap_krings_get(struct netmap_priv_d *priv)
{
struct netmap_adapter *na = priv->np_na;
u_int i;
struct netmap_kring *kring;
int excl = (priv->np_flags & NR_EXCLUSIVE);
enum txrx t;
if (netmap_debug & NM_DEBUG_ON)
nm_prinf("%s: grabbing tx [%d, %d) rx [%d, %d)",
na->name,
priv->np_qfirst[NR_TX],
priv->np_qlast[NR_TX],
priv->np_qfirst[NR_RX],
priv->np_qlast[NR_RX]);
/* first round: check that all the requested rings
* are neither already exclusively owned, nor we
* want exclusive ownership when they are already in use
*/
foreach_selected_ring(priv, t, i, kring) {
if ((kring->nr_kflags & NKR_EXCLUSIVE) ||
(kring->users && excl))
{
nm_prdis("ring %s busy", kring->name);
return EBUSY;
}
}
/* second round: increment usage count (possibly marking them
* as exclusive) and set the nr_pending_mode
*/
foreach_selected_ring(priv, t, i, kring) {
kring->users++;
if (excl)
kring->nr_kflags |= NKR_EXCLUSIVE;
kring->nr_pending_mode = NKR_NETMAP_ON;
}
return 0;
}
/* Undo netmap_krings_get(). This is done by clearing the exclusive mode
* if was asked on regif, and unset the nr_pending_mode if we are the
* last users of the involved rings. */
static void
netmap_krings_put(struct netmap_priv_d *priv)
{
u_int i;
struct netmap_kring *kring;
int excl = (priv->np_flags & NR_EXCLUSIVE);
enum txrx t;
nm_prdis("%s: releasing tx [%d, %d) rx [%d, %d)",
na->name,
priv->np_qfirst[NR_TX],
priv->np_qlast[NR_TX],
priv->np_qfirst[NR_RX],
priv->np_qlast[MR_RX]);
foreach_selected_ring(priv, t, i, kring) {
if (excl)
kring->nr_kflags &= ~NKR_EXCLUSIVE;
kring->users--;
if (kring->users == 0)
kring->nr_pending_mode = NKR_NETMAP_OFF;
}
}
static int
nm_priv_rx_enabled(struct netmap_priv_d *priv)
{
return (priv->np_qfirst[NR_RX] != priv->np_qlast[NR_RX]);
}
/* Validate the CSB entries for both directions (atok and ktoa).
* To be called under NMG_LOCK(). */
static int
netmap_csb_validate(struct netmap_priv_d *priv, struct nmreq_opt_csb *csbo)
{
struct nm_csb_atok *csb_atok_base =
(struct nm_csb_atok *)(uintptr_t)csbo->csb_atok;
struct nm_csb_ktoa *csb_ktoa_base =
(struct nm_csb_ktoa *)(uintptr_t)csbo->csb_ktoa;
enum txrx t;
int num_rings[NR_TXRX], tot_rings;
size_t entry_size[2];
void *csb_start[2];
int i;
if (priv->np_kloop_state & NM_SYNC_KLOOP_RUNNING) {
nm_prerr("Cannot update CSB while kloop is running");
return EBUSY;
}
tot_rings = 0;
for_rx_tx(t) {
num_rings[t] = priv->np_qlast[t] - priv->np_qfirst[t];
tot_rings += num_rings[t];
}
if (tot_rings <= 0)
return 0;
if (!(priv->np_flags & NR_EXCLUSIVE)) {
nm_prerr("CSB mode requires NR_EXCLUSIVE");
return EINVAL;
}
entry_size[0] = sizeof(*csb_atok_base);
entry_size[1] = sizeof(*csb_ktoa_base);
csb_start[0] = (void *)csb_atok_base;
csb_start[1] = (void *)csb_ktoa_base;
for (i = 0; i < 2; i++) {
/* On Linux we could use access_ok() to simplify
* the validation. However, the advantage of
* this approach is that it works also on
* FreeBSD. */
size_t csb_size = tot_rings * entry_size[i];
void *tmp;
int err;
if ((uintptr_t)csb_start[i] & (entry_size[i]-1)) {
nm_prerr("Unaligned CSB address");
return EINVAL;
}
tmp = nm_os_malloc(csb_size);
if (!tmp)
return ENOMEM;
if (i == 0) {
/* Application --> kernel direction. */
err = copyin(csb_start[i], tmp, csb_size);
} else {
/* Kernel --> application direction. */
memset(tmp, 0, csb_size);
err = copyout(tmp, csb_start[i], csb_size);
}
nm_os_free(tmp);
if (err) {
nm_prerr("Invalid CSB address");
return err;
}
}
priv->np_csb_atok_base = csb_atok_base;
priv->np_csb_ktoa_base = csb_ktoa_base;
/* Initialize the CSB. */
for_rx_tx(t) {
for (i = 0; i < num_rings[t]; i++) {
struct netmap_kring *kring =
NMR(priv->np_na, t)[i + priv->np_qfirst[t]];
struct nm_csb_atok *csb_atok = csb_atok_base + i;
struct nm_csb_ktoa *csb_ktoa = csb_ktoa_base + i;
if (t == NR_RX) {
csb_atok += num_rings[NR_TX];
csb_ktoa += num_rings[NR_TX];
}
CSB_WRITE(csb_atok, head, kring->rhead);
CSB_WRITE(csb_atok, cur, kring->rcur);
CSB_WRITE(csb_atok, appl_need_kick, 1);
CSB_WRITE(csb_atok, sync_flags, 1);
CSB_WRITE(csb_ktoa, hwcur, kring->nr_hwcur);
CSB_WRITE(csb_ktoa, hwtail, kring->nr_hwtail);
CSB_WRITE(csb_ktoa, kern_need_kick, 1);
nm_prinf("csb_init for kring %s: head %u, cur %u, "
"hwcur %u, hwtail %u", kring->name,
kring->rhead, kring->rcur, kring->nr_hwcur,
kring->nr_hwtail);
}
}
return 0;
}
/* Ensure that the netmap adapter can support the given MTU.
* @return EINVAL if the na cannot be set to mtu, 0 otherwise.
*/
int
netmap_buf_size_validate(const struct netmap_adapter *na, unsigned mtu) {
unsigned nbs = NETMAP_BUF_SIZE(na);
if (mtu <= na->rx_buf_maxsize) {
/* The MTU fits a single NIC slot. We only
* Need to check that netmap buffers are
* large enough to hold an MTU. NS_MOREFRAG
* cannot be used in this case. */
if (nbs < mtu) {
nm_prerr("error: netmap buf size (%u) "
"< device MTU (%u)", nbs, mtu);
return EINVAL;
}
} else {
/* More NIC slots may be needed to receive
* or transmit a single packet. Check that
* the adapter supports NS_MOREFRAG and that
* netmap buffers are large enough to hold
* the maximum per-slot size. */
if (!(na->na_flags & NAF_MOREFRAG)) {
nm_prerr("error: large MTU (%d) needed "
"but %s does not support "
"NS_MOREFRAG", mtu,
na->ifp->if_xname);
return EINVAL;
} else if (nbs < na->rx_buf_maxsize) {
nm_prerr("error: using NS_MOREFRAG on "
"%s requires netmap buf size "
">= %u", na->ifp->if_xname,
na->rx_buf_maxsize);
return EINVAL;
} else {
nm_prinf("info: netmap application on "
"%s needs to support "
"NS_MOREFRAG "
"(MTU=%u,netmap_buf_size=%u)",
na->ifp->if_xname, mtu, nbs);
}
}
return 0;
}
/* Handle the offset option, if present in the hdr.
* Returns 0 on success, or an error.
*/
static int
netmap_offsets_init(struct netmap_priv_d *priv, struct nmreq_header *hdr)
{
struct nmreq_opt_offsets *opt;
struct netmap_adapter *na = priv->np_na;
struct netmap_kring *kring;
uint64_t mask = 0, bits = 0, maxbits = sizeof(uint64_t) * 8,
max_offset = 0, initial_offset = 0, min_gap = 0;
u_int i;
enum txrx t;
int error = 0;
opt = (struct nmreq_opt_offsets *)
nmreq_getoption(hdr, NETMAP_REQ_OPT_OFFSETS);
if (opt == NULL)
return 0;
if (!(na->na_flags & NAF_OFFSETS)) {
if (netmap_verbose)
nm_prerr("%s does not support offsets",
na->name);
error = EOPNOTSUPP;
goto out;
}
/* check sanity of the opt values */
max_offset = opt->nro_max_offset;
min_gap = opt->nro_min_gap;
initial_offset = opt->nro_initial_offset;
bits = opt->nro_offset_bits;
if (bits > maxbits) {
if (netmap_verbose)
nm_prerr("bits: %llu too large (max %llu)",
(unsigned long long)bits,
(unsigned long long)maxbits);
error = EINVAL;
goto out;
}
/* we take bits == 0 as a request to use the entire field */
if (bits == 0 || bits == maxbits) {
/* shifting a type by sizeof(type) is undefined */
bits = maxbits;
mask = 0xffffffffffffffff;
} else {
mask = (1ULL << bits) - 1;
}
if (max_offset > NETMAP_BUF_SIZE(na)) {
if (netmap_verbose)
nm_prerr("max offset %llu > buf size %u",
(unsigned long long)max_offset, NETMAP_BUF_SIZE(na));
error = EINVAL;
goto out;
}
if ((max_offset & mask) != max_offset) {
if (netmap_verbose)
nm_prerr("max offset %llu to large for %llu bits",
(unsigned long long)max_offset,
(unsigned long long)bits);
error = EINVAL;
goto out;
}
if (initial_offset > max_offset) {
if (netmap_verbose)
nm_prerr("initial offset %llu > max offset %llu",
(unsigned long long)initial_offset,
(unsigned long long)max_offset);
error = EINVAL;
goto out;
}
/* initialize the kring and ring fields. */
foreach_selected_ring(priv, t, i, kring) {
struct netmap_kring *kring = NMR(na, t)[i];
struct netmap_ring *ring = kring->ring;
u_int j;
/* it the ring is already in use we check that the
* new request is compatible with the existing one
*/
if (kring->offset_mask) {
if ((kring->offset_mask & mask) != mask ||
kring->offset_max < max_offset) {
if (netmap_verbose)
nm_prinf("%s: cannot increase"
"offset mask and/or max"
"(current: mask=%llx,max=%llu",
kring->name,
(unsigned long long)kring->offset_mask,
(unsigned long long)kring->offset_max);
error = EBUSY;
goto out;
}
mask = kring->offset_mask;
max_offset = kring->offset_max;
} else {
kring->offset_mask = mask;
*(uint64_t *)(uintptr_t)&ring->offset_mask = mask;
kring->offset_max = max_offset;
kring->offset_gap = min_gap;
}
/* if there is an initial offset, put it into
* all the slots
*
* Note: we cannot change the offsets if the
* ring is already in use.
*/
if (!initial_offset || kring->users > 1)
continue;
for (j = 0; j < kring->nkr_num_slots; j++) {
struct netmap_slot *slot = ring->slot + j;
nm_write_offset(kring, slot, initial_offset);
}
}
out:
opt->nro_opt.nro_status = error;
if (!error) {
opt->nro_max_offset = max_offset;
}
return error;
}
static int
netmap_compute_buf_len(struct netmap_priv_d *priv)
{
enum txrx t;
u_int i;
struct netmap_kring *kring;
int error = 0;
unsigned mtu = 0;
struct netmap_adapter *na = priv->np_na;
uint64_t target, maxframe;
if (na->ifp != NULL)
mtu = nm_os_ifnet_mtu(na->ifp);
foreach_selected_ring(priv, t, i, kring) {
if (kring->users > 1)
continue;
target = NETMAP_BUF_SIZE(kring->na) -
kring->offset_max;
if (!kring->offset_gap)
kring->offset_gap =
NETMAP_BUF_SIZE(kring->na);
if (kring->offset_gap < target)
target = kring->offset_gap;
if (mtu) {
maxframe = mtu + ETH_HLEN +
ETH_FCS_LEN + VLAN_HLEN;
if (maxframe < target) {
target = maxframe;
}
}
error = kring->nm_bufcfg(kring, target);
if (error)
goto out;
*(uint64_t *)(uintptr_t)&kring->ring->buf_align = kring->buf_align;
if (mtu && t == NR_RX && kring->hwbuf_len < mtu) {
if (!(na->na_flags & NAF_MOREFRAG)) {
nm_prerr("error: large MTU (%d) needed "
"but %s does not support "
"NS_MOREFRAG", mtu,
na->name);
error = EINVAL;
goto out;
} else {
nm_prinf("info: netmap application on "
"%s needs to support "
"NS_MOREFRAG "
"(MTU=%u,buf_size=%llu)",
kring->name, mtu,
(unsigned long long)kring->hwbuf_len);
}
}
}
out:
return error;
}
/*
* possibly move the interface to netmap-mode.
* If success it returns a pointer to netmap_if, otherwise NULL.
* This must be called with NMG_LOCK held.
*
* The following na callbacks are called in the process:
*
* na->nm_config() [by netmap_update_config]
* (get current number and size of rings)
*
* We have a generic one for linux (netmap_linux_config).
* The bwrap has to override this, since it has to forward
* the request to the wrapped adapter (netmap_bwrap_config).
*
*
* na->nm_krings_create()
* (create and init the krings array)
*
* One of the following:
*
* * netmap_hw_krings_create, (hw ports)
* creates the standard layout for the krings
* and adds the mbq (used for the host rings).
*
* * netmap_vp_krings_create (VALE ports)
* add leases and scratchpads
*
* * netmap_pipe_krings_create (pipes)
* create the krings and rings of both ends and
* cross-link them
*
* * netmap_monitor_krings_create (monitors)
* avoid allocating the mbq
*
* * netmap_bwrap_krings_create (bwraps)
* create both the brap krings array,
* the krings array of the wrapped adapter, and
* (if needed) the fake array for the host adapter
*
* na->nm_register(, 1)
* (put the adapter in netmap mode)
*
* This may be one of the following:
*
* * netmap_hw_reg (hw ports)
* checks that the ifp is still there, then calls
* the hardware specific callback;
*
* * netmap_vp_reg (VALE ports)
* If the port is connected to a bridge,
* set the NAF_NETMAP_ON flag under the
* bridge write lock.
*
* * netmap_pipe_reg (pipes)
* inform the other pipe end that it is no
* longer responsible for the lifetime of this
* pipe end
*
* * netmap_monitor_reg (monitors)
* intercept the sync callbacks of the monitored
* rings
*
* * netmap_bwrap_reg (bwraps)
* cross-link the bwrap and hwna rings,
* forward the request to the hwna, override
* the hwna notify callback (to get the frames
* coming from outside go through the bridge).
*
*
*/
int
netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
struct nmreq_header *hdr)
{
struct netmap_if *nifp = NULL;
int error;
NMG_LOCK_ASSERT();
priv->np_na = na; /* store the reference */
error = netmap_mem_finalize(na->nm_mem, na);
if (error)
goto err;
if (na->active_fds == 0) {
/* cache the allocator info in the na */
error = netmap_mem_get_lut(na->nm_mem, &na->na_lut);
if (error)
goto err_drop_mem;
nm_prdis("lut %p bufs %u size %u", na->na_lut.lut, na->na_lut.objtotal,
na->na_lut.objsize);
/* ring configuration may have changed, fetch from the card */
netmap_update_config(na);
}
/* compute the range of tx and rx rings to monitor */
error = netmap_set_ringid(priv, hdr);
if (error)
goto err_put_lut;
if (na->active_fds == 0) {
/*
* If this is the first registration of the adapter,
* perform sanity checks and create the in-kernel view
* of the netmap rings (the netmap krings).
*/
if (na->ifp && nm_priv_rx_enabled(priv)) {
/* This netmap adapter is attached to an ifnet. */
unsigned mtu = nm_os_ifnet_mtu(na->ifp);
nm_prdis("%s: mtu %d rx_buf_maxsize %d netmap_buf_size %d",
na->name, mtu, na->rx_buf_maxsize, NETMAP_BUF_SIZE(na));
if (na->rx_buf_maxsize == 0) {
nm_prerr("%s: error: rx_buf_maxsize == 0", na->name);
error = EIO;
goto err_drop_mem;
}
error = netmap_buf_size_validate(na, mtu);
if (error)
goto err_drop_mem;
}
/*
* Depending on the adapter, this may also create
* the netmap rings themselves
*/
error = na->nm_krings_create(na);
if (error)
goto err_put_lut;
}
/* now the krings must exist and we can check whether some
* previous bind has exclusive ownership on them, and set
* nr_pending_mode
*/
error = netmap_krings_get(priv);
if (error)
goto err_del_krings;
/* create all needed missing netmap rings */
error = netmap_mem_rings_create(na);
if (error)
goto err_rel_excl;
/* initialize offsets if requested */
error = netmap_offsets_init(priv, hdr);
if (error)
goto err_rel_excl;
/* compute and validate the buf lengths */
error = netmap_compute_buf_len(priv);
if (error)
goto err_rel_excl;
/* in all cases, create a new netmap if */
nifp = netmap_mem_if_new(na, priv);
if (nifp == NULL) {
error = ENOMEM;
goto err_rel_excl;
}
- /* make sure we don't call na->nm_register() when only
- * host rings are changing mode
- */
- netmap_update_hostrings_mode(na);
-
if (nm_kring_pending(priv)) {
/* Some kring is switching mode, tell the adapter to
* react on this. */
- netmap_set_all_rings(na, NM_KR_LOCKED);
error = na->nm_register(na, 1);
- netmap_set_all_rings(na, 0);
if (error)
goto err_del_if;
}
/* Commit the reference. */
na->active_fds++;
/*
* advertise that the interface is ready by setting np_nifp.
* The barrier is needed because readers (poll, *SYNC and mmap)
* check for priv->np_nifp != NULL without locking
*/
mb(); /* make sure previous writes are visible to all CPUs */
priv->np_nifp = nifp;
return 0;
err_del_if:
netmap_mem_if_delete(na, nifp);
err_rel_excl:
netmap_krings_put(priv);
- netmap_update_hostrings_mode(na);
netmap_mem_rings_delete(na);
err_del_krings:
if (na->active_fds == 0)
na->nm_krings_delete(na);
err_put_lut:
if (na->active_fds == 0)
memset(&na->na_lut, 0, sizeof(na->na_lut));
err_drop_mem:
netmap_mem_drop(na);
err:
priv->np_na = NULL;
return error;
}
/*
* update kring and ring at the end of rxsync/txsync.
*/
static inline void
nm_sync_finalize(struct netmap_kring *kring)
{
/*
* Update ring tail to what the kernel knows
* After txsync: head/rhead/hwcur might be behind cur/rcur
* if no carrier.
*/
kring->ring->tail = kring->rtail = kring->nr_hwtail;
nm_prdis(5, "%s now hwcur %d hwtail %d head %d cur %d tail %d",
kring->name, kring->nr_hwcur, kring->nr_hwtail,
kring->rhead, kring->rcur, kring->rtail);
}
/* set ring timestamp */
static inline void
ring_timestamp_set(struct netmap_ring *ring)
{
if (netmap_no_timestamp == 0 || ring->flags & NR_TIMESTAMP) {
microtime(&ring->ts);
}
}
static int nmreq_copyin(struct nmreq_header *, int);
static int nmreq_copyout(struct nmreq_header *, int);
static int nmreq_checkoptions(struct nmreq_header *);
/*
* ioctl(2) support for the "netmap" device.
*
* Following a list of accepted commands:
* - NIOCCTRL device control API
* - NIOCTXSYNC sync TX rings
* - NIOCRXSYNC sync RX rings
* - SIOCGIFADDR just for convenience
* - NIOCGINFO deprecated (legacy API)
* - NIOCREGIF deprecated (legacy API)
*
* Return 0 on success, errno otherwise.
*/
int
netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
struct thread *td, int nr_body_is_user)
{
struct mbq q; /* packets from RX hw queues to host stack */
struct netmap_adapter *na = NULL;
struct netmap_mem_d *nmd = NULL;
struct ifnet *ifp = NULL;
int error = 0;
u_int i, qfirst, qlast;
struct netmap_kring **krings;
int sync_flags;
enum txrx t;
switch (cmd) {
case NIOCCTRL: {
struct nmreq_header *hdr = (struct nmreq_header *)data;
if (hdr->nr_version < NETMAP_MIN_API ||
hdr->nr_version > NETMAP_MAX_API) {
nm_prerr("API mismatch: got %d need %d",
hdr->nr_version, NETMAP_API);
return EINVAL;
}
/* Make a kernel-space copy of the user-space nr_body.
* For convenience, the nr_body pointer and the pointers
* in the options list will be replaced with their
* kernel-space counterparts. The original pointers are
* saved internally and later restored by nmreq_copyout
*/
error = nmreq_copyin(hdr, nr_body_is_user);
if (error) {
return error;
}
/* Sanitize hdr->nr_name. */
hdr->nr_name[sizeof(hdr->nr_name) - 1] = '\0';
switch (hdr->nr_reqtype) {
case NETMAP_REQ_REGISTER: {
struct nmreq_register *req =
(struct nmreq_register *)(uintptr_t)hdr->nr_body;
struct netmap_if *nifp;
/* Protect access to priv from concurrent requests. */
NMG_LOCK();
do {
struct nmreq_option *opt;
u_int memflags;
if (priv->np_nifp != NULL) { /* thread already registered */
error = EBUSY;
break;
}
#ifdef WITH_EXTMEM
opt = nmreq_getoption(hdr, NETMAP_REQ_OPT_EXTMEM);
if (opt != NULL) {
struct nmreq_opt_extmem *e =
(struct nmreq_opt_extmem *)opt;
nmd = netmap_mem_ext_create(e->nro_usrptr,
&e->nro_info, &error);
opt->nro_status = error;
if (nmd == NULL)
break;
}
#endif /* WITH_EXTMEM */
if (nmd == NULL && req->nr_mem_id) {
/* find the allocator and get a reference */
nmd = netmap_mem_find(req->nr_mem_id);
if (nmd == NULL) {
if (netmap_verbose) {
nm_prerr("%s: failed to find mem_id %u",
hdr->nr_name, req->nr_mem_id);
}
error = EINVAL;
break;
}
}
/* find the interface and a reference */
error = netmap_get_na(hdr, &na, &ifp, nmd,
1 /* create */); /* keep reference */
if (error)
break;
if (NETMAP_OWNED_BY_KERN(na)) {
error = EBUSY;
break;
}
if (na->virt_hdr_len && !(req->nr_flags & NR_ACCEPT_VNET_HDR)) {
nm_prerr("virt_hdr_len=%d, but application does "
"not accept it", na->virt_hdr_len);
error = EIO;
break;
}
error = netmap_do_regif(priv, na, hdr);
if (error) { /* reg. failed, release priv and ref */
break;
}
opt = nmreq_getoption(hdr, NETMAP_REQ_OPT_CSB);
if (opt != NULL) {
struct nmreq_opt_csb *csbo =
(struct nmreq_opt_csb *)opt;
error = netmap_csb_validate(priv, csbo);
opt->nro_status = error;
if (error) {
netmap_do_unregif(priv);
break;
}
}
nifp = priv->np_nifp;
/* return the offset of the netmap_if object */
req->nr_rx_rings = na->num_rx_rings;
req->nr_tx_rings = na->num_tx_rings;
req->nr_rx_slots = na->num_rx_desc;
req->nr_tx_slots = na->num_tx_desc;
req->nr_host_tx_rings = na->num_host_tx_rings;
req->nr_host_rx_rings = na->num_host_rx_rings;
error = netmap_mem_get_info(na->nm_mem, &req->nr_memsize, &memflags,
&req->nr_mem_id);
if (error) {
netmap_do_unregif(priv);
break;
}
if (memflags & NETMAP_MEM_PRIVATE) {
*(uint32_t *)(uintptr_t)&nifp->ni_flags |= NI_PRIV_MEM;
}
for_rx_tx(t) {
priv->np_si[t] = nm_si_user(priv, t) ?
&na->si[t] : &NMR(na, t)[priv->np_qfirst[t]]->si;
}
if (req->nr_extra_bufs) {
if (netmap_verbose)
nm_prinf("requested %d extra buffers",
req->nr_extra_bufs);
req->nr_extra_bufs = netmap_extra_alloc(na,
&nifp->ni_bufs_head, req->nr_extra_bufs);
if (netmap_verbose)
nm_prinf("got %d extra buffers", req->nr_extra_bufs);
- } else {
- nifp->ni_bufs_head = 0;
}
req->nr_offset = netmap_mem_if_offset(na->nm_mem, nifp);
error = nmreq_checkoptions(hdr);
if (error) {
netmap_do_unregif(priv);
break;
}
/* store ifp reference so that priv destructor may release it */
priv->np_ifp = ifp;
} while (0);
if (error) {
netmap_unget_na(na, ifp);
}
/* release the reference from netmap_mem_find() or
* netmap_mem_ext_create()
*/
if (nmd)
netmap_mem_put(nmd);
NMG_UNLOCK();
break;
}
case NETMAP_REQ_PORT_INFO_GET: {
struct nmreq_port_info_get *req =
(struct nmreq_port_info_get *)(uintptr_t)hdr->nr_body;
int nmd_ref = 0;
NMG_LOCK();
do {
u_int memflags;
if (hdr->nr_name[0] != '\0') {
/* Build a nmreq_register out of the nmreq_port_info_get,
* so that we can call netmap_get_na(). */
struct nmreq_register regreq;
bzero(&regreq, sizeof(regreq));
regreq.nr_mode = NR_REG_ALL_NIC;
regreq.nr_tx_slots = req->nr_tx_slots;
regreq.nr_rx_slots = req->nr_rx_slots;
regreq.nr_tx_rings = req->nr_tx_rings;
regreq.nr_rx_rings = req->nr_rx_rings;
regreq.nr_host_tx_rings = req->nr_host_tx_rings;
regreq.nr_host_rx_rings = req->nr_host_rx_rings;
regreq.nr_mem_id = req->nr_mem_id;
/* get a refcount */
hdr->nr_reqtype = NETMAP_REQ_REGISTER;
hdr->nr_body = (uintptr_t)&regreq;
error = netmap_get_na(hdr, &na, &ifp, NULL, 1 /* create */);
hdr->nr_reqtype = NETMAP_REQ_PORT_INFO_GET; /* reset type */
hdr->nr_body = (uintptr_t)req; /* reset nr_body */
if (error) {
na = NULL;
ifp = NULL;
break;
}
nmd = na->nm_mem; /* get memory allocator */
} else {
nmd = netmap_mem_find(req->nr_mem_id ? req->nr_mem_id : 1);
if (nmd == NULL) {
if (netmap_verbose)
nm_prerr("%s: failed to find mem_id %u",
hdr->nr_name,
req->nr_mem_id ? req->nr_mem_id : 1);
error = EINVAL;
break;
}
nmd_ref = 1;
}
error = netmap_mem_get_info(nmd, &req->nr_memsize, &memflags,
&req->nr_mem_id);
if (error)
break;
if (na == NULL) /* only memory info */
break;
netmap_update_config(na);
req->nr_rx_rings = na->num_rx_rings;
req->nr_tx_rings = na->num_tx_rings;
req->nr_rx_slots = na->num_rx_desc;
req->nr_tx_slots = na->num_tx_desc;
req->nr_host_tx_rings = na->num_host_tx_rings;
req->nr_host_rx_rings = na->num_host_rx_rings;
} while (0);
netmap_unget_na(na, ifp);
if (nmd_ref)
netmap_mem_put(nmd);
NMG_UNLOCK();
break;
}
#ifdef WITH_VALE
case NETMAP_REQ_VALE_ATTACH: {
error = netmap_bdg_attach(hdr, NULL /* userspace request */);
break;
}
case NETMAP_REQ_VALE_DETACH: {
error = netmap_bdg_detach(hdr, NULL /* userspace request */);
break;
}
case NETMAP_REQ_PORT_HDR_SET: {
struct nmreq_port_hdr *req =
(struct nmreq_port_hdr *)(uintptr_t)hdr->nr_body;
/* Build a nmreq_register out of the nmreq_port_hdr,
* so that we can call netmap_get_bdg_na(). */
struct nmreq_register regreq;
bzero(&regreq, sizeof(regreq));
regreq.nr_mode = NR_REG_ALL_NIC;
/* For now we only support virtio-net headers, and only for
* VALE ports, but this may change in future. Valid lengths
* for the virtio-net header are 0 (no header), 10 and 12. */
if (req->nr_hdr_len != 0 &&
req->nr_hdr_len != sizeof(struct nm_vnet_hdr) &&
req->nr_hdr_len != 12) {
if (netmap_verbose)
nm_prerr("invalid hdr_len %u", req->nr_hdr_len);
error = EINVAL;
break;
}
NMG_LOCK();
hdr->nr_reqtype = NETMAP_REQ_REGISTER;
hdr->nr_body = (uintptr_t)&regreq;
error = netmap_get_vale_na(hdr, &na, NULL, 0);
hdr->nr_reqtype = NETMAP_REQ_PORT_HDR_SET;
hdr->nr_body = (uintptr_t)req;
if (na && !error) {
struct netmap_vp_adapter *vpna =
(struct netmap_vp_adapter *)na;
na->virt_hdr_len = req->nr_hdr_len;
if (na->virt_hdr_len) {
vpna->mfs = NETMAP_BUF_SIZE(na);
}
if (netmap_verbose)
nm_prinf("Using vnet_hdr_len %d for %p", na->virt_hdr_len, na);
netmap_adapter_put(na);
} else if (!na) {
error = ENXIO;
}
NMG_UNLOCK();
break;
}
case NETMAP_REQ_PORT_HDR_GET: {
/* Get vnet-header length for this netmap port */
struct nmreq_port_hdr *req =
(struct nmreq_port_hdr *)(uintptr_t)hdr->nr_body;
/* Build a nmreq_register out of the nmreq_port_hdr,
* so that we can call netmap_get_bdg_na(). */
struct nmreq_register regreq;
struct ifnet *ifp;
bzero(&regreq, sizeof(regreq));
regreq.nr_mode = NR_REG_ALL_NIC;
NMG_LOCK();
hdr->nr_reqtype = NETMAP_REQ_REGISTER;
hdr->nr_body = (uintptr_t)&regreq;
error = netmap_get_na(hdr, &na, &ifp, NULL, 0);
hdr->nr_reqtype = NETMAP_REQ_PORT_HDR_GET;
hdr->nr_body = (uintptr_t)req;
if (na && !error) {
req->nr_hdr_len = na->virt_hdr_len;
}
netmap_unget_na(na, ifp);
NMG_UNLOCK();
break;
}
case NETMAP_REQ_VALE_LIST: {
error = netmap_vale_list(hdr);
break;
}
case NETMAP_REQ_VALE_NEWIF: {
error = nm_vi_create(hdr);
break;
}
case NETMAP_REQ_VALE_DELIF: {
error = nm_vi_destroy(hdr->nr_name);
break;
}
#endif /* WITH_VALE */
case NETMAP_REQ_VALE_POLLING_ENABLE:
case NETMAP_REQ_VALE_POLLING_DISABLE: {
error = nm_bdg_polling(hdr);
break;
}
case NETMAP_REQ_POOLS_INFO_GET: {
/* Get information from the memory allocator used for
* hdr->nr_name. */
struct nmreq_pools_info *req =
(struct nmreq_pools_info *)(uintptr_t)hdr->nr_body;
NMG_LOCK();
do {
/* Build a nmreq_register out of the nmreq_pools_info,
* so that we can call netmap_get_na(). */
struct nmreq_register regreq;
bzero(&regreq, sizeof(regreq));
regreq.nr_mem_id = req->nr_mem_id;
regreq.nr_mode = NR_REG_ALL_NIC;
hdr->nr_reqtype = NETMAP_REQ_REGISTER;
hdr->nr_body = (uintptr_t)&regreq;
error = netmap_get_na(hdr, &na, &ifp, NULL, 1 /* create */);
hdr->nr_reqtype = NETMAP_REQ_POOLS_INFO_GET; /* reset type */
hdr->nr_body = (uintptr_t)req; /* reset nr_body */
if (error) {
na = NULL;
ifp = NULL;
break;
}
nmd = na->nm_mem; /* grab the memory allocator */
if (nmd == NULL) {
error = EINVAL;
break;
}
/* Finalize the memory allocator, get the pools
* information and release the allocator. */
error = netmap_mem_finalize(nmd, na);
if (error) {
break;
}
error = netmap_mem_pools_info_get(req, nmd);
netmap_mem_drop(na);
} while (0);
netmap_unget_na(na, ifp);
NMG_UNLOCK();
break;
}
case NETMAP_REQ_CSB_ENABLE: {
struct nmreq_option *opt;
opt = nmreq_getoption(hdr, NETMAP_REQ_OPT_CSB);
if (opt == NULL) {
error = EINVAL;
} else {
struct nmreq_opt_csb *csbo =
(struct nmreq_opt_csb *)opt;
NMG_LOCK();
error = netmap_csb_validate(priv, csbo);
NMG_UNLOCK();
opt->nro_status = error;
}
break;
}
case NETMAP_REQ_SYNC_KLOOP_START: {
error = netmap_sync_kloop(priv, hdr);
break;
}
case NETMAP_REQ_SYNC_KLOOP_STOP: {
error = netmap_sync_kloop_stop(priv);
break;
}
default: {
error = EINVAL;
break;
}
}
/* Write back request body to userspace and reset the
* user-space pointer. */
error = nmreq_copyout(hdr, error);
break;
}
case NIOCTXSYNC:
case NIOCRXSYNC: {
if (unlikely(priv->np_nifp == NULL)) {
error = ENXIO;
break;
}
mb(); /* make sure following reads are not from cache */
if (unlikely(priv->np_csb_atok_base)) {
nm_prerr("Invalid sync in CSB mode");
error = EBUSY;
break;
}
na = priv->np_na; /* we have a reference */
mbq_init(&q);
t = (cmd == NIOCTXSYNC ? NR_TX : NR_RX);
krings = NMR(na, t);
qfirst = priv->np_qfirst[t];
qlast = priv->np_qlast[t];
sync_flags = priv->np_sync_flags;
for (i = qfirst; i < qlast; i++) {
struct netmap_kring *kring = krings[i];
struct netmap_ring *ring = kring->ring;
if (unlikely(nm_kr_tryget(kring, 1, &error))) {
error = (error ? EIO : 0);
continue;
}
if (cmd == NIOCTXSYNC) {
if (netmap_debug & NM_DEBUG_TXSYNC)
nm_prinf("pre txsync ring %d cur %d hwcur %d",
i, ring->cur,
kring->nr_hwcur);
if (nm_txsync_prologue(kring, ring) >= kring->nkr_num_slots) {
netmap_ring_reinit(kring);
} else if (kring->nm_sync(kring, sync_flags | NAF_FORCE_RECLAIM) == 0) {
nm_sync_finalize(kring);
}
if (netmap_debug & NM_DEBUG_TXSYNC)
nm_prinf("post txsync ring %d cur %d hwcur %d",
i, ring->cur,
kring->nr_hwcur);
} else {
if (nm_rxsync_prologue(kring, ring) >= kring->nkr_num_slots) {
netmap_ring_reinit(kring);
}
if (nm_may_forward_up(kring)) {
/* transparent forwarding, see netmap_poll() */
netmap_grab_packets(kring, &q, netmap_fwd);
}
if (kring->nm_sync(kring, sync_flags | NAF_FORCE_READ) == 0) {
nm_sync_finalize(kring);
}
ring_timestamp_set(ring);
}
nm_kr_put(kring);
}
if (mbq_peek(&q)) {
netmap_send_up(na->ifp, &q);
}
break;
}
default: {
return netmap_ioctl_legacy(priv, cmd, data, td);
break;
}
}
return (error);
}
size_t
nmreq_size_by_type(uint16_t nr_reqtype)
{
switch (nr_reqtype) {
case NETMAP_REQ_REGISTER:
return sizeof(struct nmreq_register);
case NETMAP_REQ_PORT_INFO_GET:
return sizeof(struct nmreq_port_info_get);
case NETMAP_REQ_VALE_ATTACH:
return sizeof(struct nmreq_vale_attach);
case NETMAP_REQ_VALE_DETACH:
return sizeof(struct nmreq_vale_detach);
case NETMAP_REQ_VALE_LIST:
return sizeof(struct nmreq_vale_list);
case NETMAP_REQ_PORT_HDR_SET:
case NETMAP_REQ_PORT_HDR_GET:
return sizeof(struct nmreq_port_hdr);
case NETMAP_REQ_VALE_NEWIF:
return sizeof(struct nmreq_vale_newif);
case NETMAP_REQ_VALE_DELIF:
case NETMAP_REQ_SYNC_KLOOP_STOP:
case NETMAP_REQ_CSB_ENABLE:
return 0;
case NETMAP_REQ_VALE_POLLING_ENABLE:
case NETMAP_REQ_VALE_POLLING_DISABLE:
return sizeof(struct nmreq_vale_polling);
case NETMAP_REQ_POOLS_INFO_GET:
return sizeof(struct nmreq_pools_info);
case NETMAP_REQ_SYNC_KLOOP_START:
return sizeof(struct nmreq_sync_kloop_start);
}
return 0;
}
static size_t
nmreq_opt_size_by_type(uint32_t nro_reqtype, uint64_t nro_size)
{
size_t rv = sizeof(struct nmreq_option);
#ifdef NETMAP_REQ_OPT_DEBUG
if (nro_reqtype & NETMAP_REQ_OPT_DEBUG)
return (nro_reqtype & ~NETMAP_REQ_OPT_DEBUG);
#endif /* NETMAP_REQ_OPT_DEBUG */
switch (nro_reqtype) {
#ifdef WITH_EXTMEM
case NETMAP_REQ_OPT_EXTMEM:
rv = sizeof(struct nmreq_opt_extmem);
break;
#endif /* WITH_EXTMEM */
case NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS:
if (nro_size >= rv)
rv = nro_size;
break;
case NETMAP_REQ_OPT_CSB:
rv = sizeof(struct nmreq_opt_csb);
break;
case NETMAP_REQ_OPT_SYNC_KLOOP_MODE:
rv = sizeof(struct nmreq_opt_sync_kloop_mode);
break;
case NETMAP_REQ_OPT_OFFSETS:
rv = sizeof(struct nmreq_opt_offsets);
break;
}
/* subtract the common header */
return rv - sizeof(struct nmreq_option);
}
/*
* nmreq_copyin: create an in-kernel version of the request.
*
* We build the following data structure:
*
* hdr -> +-------+ buf
* | | +---------------+
* +-------+ |usr body ptr |
* |options|-. +---------------+
* +-------+ | |usr options ptr|
* |body |--------->+---------------+
* +-------+ | | |
* | | copy of body |
* | | |
* | +---------------+
* | | NULL |
* | +---------------+
* | .---| |\
* | | +---------------+ |
* | .------| | |
* | | | +---------------+ \ option table
* | | | | ... | / indexed by option
* | | | +---------------+ | type
* | | | | | |
* | | | +---------------+/
* | | | |usr next ptr 1 |
* `-|----->+---------------+
* | | | copy of opt 1 |
* | | | |
* | | .-| nro_next |
* | | | +---------------+
* | | | |usr next ptr 2 |
* | `-`>+---------------+
* | | copy of opt 2 |
* | | |
* | .-| nro_next |
* | | +---------------+
* | | | |
* ~ ~ ~ ... ~
* | .-| |
* `----->+---------------+
* | |usr next ptr n |
* `>+---------------+
* | copy of opt n |
* | |
* | nro_next(NULL)|
* +---------------+
*
* The options and body fields of the hdr structure are overwritten
* with in-kernel valid pointers inside the buf. The original user
* pointers are saved in the buf and restored on copyout.
* The list of options is copied and the pointers adjusted. The
* original pointers are saved before the option they belonged.
*
* The option table has an entry for every available option. Entries
* for options that have not been passed contain NULL.
*
*/
int
nmreq_copyin(struct nmreq_header *hdr, int nr_body_is_user)
{
size_t rqsz, optsz, bufsz;
int error = 0;
char *ker = NULL, *p;
struct nmreq_option **next, *src, **opt_tab;
struct nmreq_option buf;
uint64_t *ptrs;
if (hdr->nr_reserved) {
if (netmap_verbose)
nm_prerr("nr_reserved must be zero");
return EINVAL;
}
if (!nr_body_is_user)
return 0;
hdr->nr_reserved = nr_body_is_user;
/* compute the total size of the buffer */
rqsz = nmreq_size_by_type(hdr->nr_reqtype);
if (rqsz > NETMAP_REQ_MAXSIZE) {
error = EMSGSIZE;
goto out_err;
}
if ((rqsz && hdr->nr_body == (uintptr_t)NULL) ||
(!rqsz && hdr->nr_body != (uintptr_t)NULL)) {
/* Request body expected, but not found; or
* request body found but unexpected. */
if (netmap_verbose)
nm_prerr("nr_body expected but not found, or vice versa");
error = EINVAL;
goto out_err;
}
bufsz = 2 * sizeof(void *) + rqsz +
NETMAP_REQ_OPT_MAX * sizeof(opt_tab);
/* compute the size of the buf below the option table.
* It must contain a copy of every received option structure.
* For every option we also need to store a copy of the user
* list pointer.
*/
optsz = 0;
for (src = (struct nmreq_option *)(uintptr_t)hdr->nr_options; src;
src = (struct nmreq_option *)(uintptr_t)buf.nro_next)
{
error = copyin(src, &buf, sizeof(*src));
if (error)
goto out_err;
optsz += sizeof(*src);
optsz += nmreq_opt_size_by_type(buf.nro_reqtype, buf.nro_size);
if (rqsz + optsz > NETMAP_REQ_MAXSIZE) {
error = EMSGSIZE;
goto out_err;
}
bufsz += sizeof(void *);
}
bufsz += optsz;
ker = nm_os_malloc(bufsz);
if (ker == NULL) {
error = ENOMEM;
goto out_err;
}
p = ker; /* write pointer into the buffer */
/* make a copy of the user pointers */
ptrs = (uint64_t*)p;
*ptrs++ = hdr->nr_body;
*ptrs++ = hdr->nr_options;
p = (char *)ptrs;
/* copy the body */
error = copyin((void *)(uintptr_t)hdr->nr_body, p, rqsz);
if (error)
goto out_restore;
/* overwrite the user pointer with the in-kernel one */
hdr->nr_body = (uintptr_t)p;
p += rqsz;
/* start of the options table */
opt_tab = (struct nmreq_option **)p;
p += sizeof(opt_tab) * NETMAP_REQ_OPT_MAX;
/* copy the options */
next = (struct nmreq_option **)&hdr->nr_options;
src = *next;
while (src) {
struct nmreq_option *opt;
/* copy the option header */
ptrs = (uint64_t *)p;
opt = (struct nmreq_option *)(ptrs + 1);
error = copyin(src, opt, sizeof(*src));
if (error)
goto out_restore;
/* make a copy of the user next pointer */
*ptrs = opt->nro_next;
/* overwrite the user pointer with the in-kernel one */
*next = opt;
/* initialize the option as not supported.
* Recognized options will update this field.
*/
opt->nro_status = EOPNOTSUPP;
/* check for invalid types */
if (opt->nro_reqtype < 1) {
if (netmap_verbose)
nm_prinf("invalid option type: %u", opt->nro_reqtype);
opt->nro_status = EINVAL;
error = EINVAL;
goto next;
}
if (opt->nro_reqtype >= NETMAP_REQ_OPT_MAX) {
/* opt->nro_status is already EOPNOTSUPP */
error = EOPNOTSUPP;
goto next;
}
/* if the type is valid, index the option in the table
* unless it is a duplicate.
*/
if (opt_tab[opt->nro_reqtype] != NULL) {
if (netmap_verbose)
nm_prinf("duplicate option: %u", opt->nro_reqtype);
opt->nro_status = EINVAL;
opt_tab[opt->nro_reqtype]->nro_status = EINVAL;
error = EINVAL;
goto next;
}
opt_tab[opt->nro_reqtype] = opt;
p = (char *)(opt + 1);
/* copy the option body */
optsz = nmreq_opt_size_by_type(opt->nro_reqtype,
opt->nro_size);
if (optsz) {
/* the option body follows the option header */
error = copyin(src + 1, p, optsz);
if (error)
goto out_restore;
p += optsz;
}
next:
/* move to next option */
next = (struct nmreq_option **)&opt->nro_next;
src = *next;
}
if (error)
nmreq_copyout(hdr, error);
return error;
out_restore:
ptrs = (uint64_t *)ker;
hdr->nr_body = *ptrs++;
hdr->nr_options = *ptrs++;
hdr->nr_reserved = 0;
nm_os_free(ker);
out_err:
return error;
}
static int
nmreq_copyout(struct nmreq_header *hdr, int rerror)
{
struct nmreq_option *src, *dst;
void *ker = (void *)(uintptr_t)hdr->nr_body, *bufstart;
uint64_t *ptrs;
size_t bodysz;
int error;
if (!hdr->nr_reserved)
return rerror;
/* restore the user pointers in the header */
ptrs = (uint64_t *)ker - 2;
bufstart = ptrs;
hdr->nr_body = *ptrs++;
src = (struct nmreq_option *)(uintptr_t)hdr->nr_options;
hdr->nr_options = *ptrs;
if (!rerror) {
/* copy the body */
bodysz = nmreq_size_by_type(hdr->nr_reqtype);
error = copyout(ker, (void *)(uintptr_t)hdr->nr_body, bodysz);
if (error) {
rerror = error;
goto out;
}
}
/* copy the options */
dst = (struct nmreq_option *)(uintptr_t)hdr->nr_options;
while (src) {
size_t optsz;
uint64_t next;
/* restore the user pointer */
next = src->nro_next;
ptrs = (uint64_t *)src - 1;
src->nro_next = *ptrs;
/* always copy the option header */
error = copyout(src, dst, sizeof(*src));
if (error) {
rerror = error;
goto out;
}
/* copy the option body only if there was no error */
if (!rerror && !src->nro_status) {
optsz = nmreq_opt_size_by_type(src->nro_reqtype,
src->nro_size);
if (optsz) {
error = copyout(src + 1, dst + 1, optsz);
if (error) {
rerror = error;
goto out;
}
}
}
src = (struct nmreq_option *)(uintptr_t)next;
dst = (struct nmreq_option *)(uintptr_t)*ptrs;
}
out:
hdr->nr_reserved = 0;
nm_os_free(bufstart);
return rerror;
}
struct nmreq_option *
nmreq_getoption(struct nmreq_header *hdr, uint16_t reqtype)
{
struct nmreq_option **opt_tab;
if (!hdr->nr_options)
return NULL;
opt_tab = (struct nmreq_option **)((uintptr_t)hdr->nr_options) -
(NETMAP_REQ_OPT_MAX + 1);
return opt_tab[reqtype];
}
static int
nmreq_checkoptions(struct nmreq_header *hdr)
{
struct nmreq_option *opt;
/* return error if there is still any option
* marked as not supported
*/
for (opt = (struct nmreq_option *)(uintptr_t)hdr->nr_options; opt;
opt = (struct nmreq_option *)(uintptr_t)opt->nro_next)
if (opt->nro_status == EOPNOTSUPP)
return EOPNOTSUPP;
return 0;
}
/*
* select(2) and poll(2) handlers for the "netmap" device.
*
* Can be called for one or more queues.
* Return true the event mask corresponding to ready events.
* If there are no ready events (and 'sr' is not NULL), do a
* selrecord on either individual selinfo or on the global one.
* Device-dependent parts (locking and sync of tx/rx rings)
* are done through callbacks.
*
* On linux, arguments are really pwait, the poll table, and 'td' is struct file *
* The first one is remapped to pwait as selrecord() uses the name as an
* hidden argument.
*/
int
netmap_poll(struct netmap_priv_d *priv, int events, NM_SELRECORD_T *sr)
{
struct netmap_adapter *na;
struct netmap_kring *kring;
struct netmap_ring *ring;
u_int i, want[NR_TXRX], revents = 0;
NM_SELINFO_T *si[NR_TXRX];
#define want_tx want[NR_TX]
#define want_rx want[NR_RX]
struct mbq q; /* packets from RX hw queues to host stack */
/*
* In order to avoid nested locks, we need to "double check"
* txsync and rxsync if we decide to do a selrecord().
* retry_tx (and retry_rx, later) prevent looping forever.
*/
int retry_tx = 1, retry_rx = 1;
/* Transparent mode: send_down is 1 if we have found some
* packets to forward (host RX ring --> NIC) during the rx
* scan and we have not sent them down to the NIC yet.
* Transparent mode requires to bind all rings to a single
* file descriptor.
*/
int send_down = 0;
int sync_flags = priv->np_sync_flags;
mbq_init(&q);
if (unlikely(priv->np_nifp == NULL)) {
return POLLERR;
}
mb(); /* make sure following reads are not from cache */
na = priv->np_na;
if (unlikely(!nm_netmap_on(na)))
return POLLERR;
if (unlikely(priv->np_csb_atok_base)) {
nm_prerr("Invalid poll in CSB mode");
return POLLERR;
}
if (netmap_debug & NM_DEBUG_ON)
nm_prinf("device %s events 0x%x", na->name, events);
want_tx = events & (POLLOUT | POLLWRNORM);
want_rx = events & (POLLIN | POLLRDNORM);
/*
* If the card has more than one queue AND the file descriptor is
* bound to all of them, we sleep on the "global" selinfo, otherwise
* we sleep on individual selinfo (FreeBSD only allows two selinfo's
* per file descriptor).
* The interrupt routine in the driver wake one or the other
* (or both) depending on which clients are active.
*
* rxsync() is only called if we run out of buffers on a POLLIN.
* txsync() is called if we run out of buffers on POLLOUT, or
* there are pending packets to send. The latter can be disabled
* passing NETMAP_NO_TX_POLL in the NIOCREG call.
*/
si[NR_RX] = priv->np_si[NR_RX];
si[NR_TX] = priv->np_si[NR_TX];
#ifdef __FreeBSD__
/*
* We start with a lock free round which is cheap if we have
* slots available. If this fails, then lock and call the sync
* routines. We can't do this on Linux, as the contract says
* that we must call nm_os_selrecord() unconditionally.
*/
if (want_tx) {
const enum txrx t = NR_TX;
for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
kring = NMR(na, t)[i];
if (kring->ring->cur != kring->ring->tail) {
/* Some unseen TX space is available, so what
* we don't need to run txsync. */
revents |= want[t];
want[t] = 0;
break;
}
}
}
if (want_rx) {
const enum txrx t = NR_RX;
int rxsync_needed = 0;
for (i = priv->np_qfirst[t]; i < priv->np_qlast[t]; i++) {
kring = NMR(na, t)[i];
if (kring->ring->cur == kring->ring->tail
|| kring->rhead != kring->ring->head) {
/* There are no unseen packets on this ring,
* or there are some buffers to be returned
* to the netmap port. We therefore go ahead
* and run rxsync. */
rxsync_needed = 1;
break;
}
}
if (!rxsync_needed) {
revents |= want_rx;
want_rx = 0;
}
}
#endif
#ifdef linux
/* The selrecord must be unconditional on linux. */
nm_os_selrecord(sr, si[NR_RX]);
nm_os_selrecord(sr, si[NR_TX]);
#endif /* linux */
/*
* If we want to push packets out (priv->np_txpoll) or
* want_tx is still set, we must issue txsync calls
* (on all rings, to avoid that the tx rings stall).
* Fortunately, normal tx mode has np_txpoll set.
*/
if (priv->np_txpoll || want_tx) {
/*
* The first round checks if anyone is ready, if not
* do a selrecord and another round to handle races.
* want_tx goes to 0 if any space is found, and is
* used to skip rings with no pending transmissions.
*/
flush_tx:
for (i = priv->np_qfirst[NR_TX]; i < priv->np_qlast[NR_TX]; i++) {
int found = 0;
kring = na->tx_rings[i];
ring = kring->ring;
/*
* Don't try to txsync this TX ring if we already found some
* space in some of the TX rings (want_tx == 0) and there are no
* TX slots in this ring that need to be flushed to the NIC
* (head == hwcur).
*/
if (!send_down && !want_tx && ring->head == kring->nr_hwcur)
continue;
if (nm_kr_tryget(kring, 1, &revents))
continue;
if (nm_txsync_prologue(kring, ring) >= kring->nkr_num_slots) {
netmap_ring_reinit(kring);
revents |= POLLERR;
} else {
if (kring->nm_sync(kring, sync_flags))
revents |= POLLERR;
else
nm_sync_finalize(kring);
}
/*
* If we found new slots, notify potential
* listeners on the same ring.
* Since we just did a txsync, look at the copies
* of cur,tail in the kring.
*/
found = kring->rcur != kring->rtail;
nm_kr_put(kring);
if (found) { /* notify other listeners */
revents |= want_tx;
want_tx = 0;
#ifndef linux
kring->nm_notify(kring, 0);
#endif /* linux */
}
}
/* if there were any packet to forward we must have handled them by now */
send_down = 0;
if (want_tx && retry_tx && sr) {
#ifndef linux
nm_os_selrecord(sr, si[NR_TX]);
#endif /* !linux */
retry_tx = 0;
goto flush_tx;
}
}
/*
* If want_rx is still set scan receive rings.
* Do it on all rings because otherwise we starve.
*/
if (want_rx) {
/* two rounds here for race avoidance */
do_retry_rx:
for (i = priv->np_qfirst[NR_RX]; i < priv->np_qlast[NR_RX]; i++) {
int found = 0;
kring = na->rx_rings[i];
ring = kring->ring;
if (unlikely(nm_kr_tryget(kring, 1, &revents)))
continue;
if (nm_rxsync_prologue(kring, ring) >= kring->nkr_num_slots) {
netmap_ring_reinit(kring);
revents |= POLLERR;
}
/* now we can use kring->rcur, rtail */
/*
* transparent mode support: collect packets from
* hw rxring(s) that have been released by the user
*/
if (nm_may_forward_up(kring)) {
netmap_grab_packets(kring, &q, netmap_fwd);
}
/* Clear the NR_FORWARD flag anyway, it may be set by
* the nm_sync() below only on for the host RX ring (see
* netmap_rxsync_from_host()). */
kring->nr_kflags &= ~NR_FORWARD;
if (kring->nm_sync(kring, sync_flags))
revents |= POLLERR;
else
nm_sync_finalize(kring);
send_down |= (kring->nr_kflags & NR_FORWARD);
ring_timestamp_set(ring);
found = kring->rcur != kring->rtail;
nm_kr_put(kring);
if (found) {
revents |= want_rx;
retry_rx = 0;
#ifndef linux
kring->nm_notify(kring, 0);
#endif /* linux */
}
}
#ifndef linux
if (retry_rx && sr) {
nm_os_selrecord(sr, si[NR_RX]);
}
#endif /* !linux */
if (send_down || retry_rx) {
retry_rx = 0;
if (send_down)
goto flush_tx; /* and retry_rx */
else
goto do_retry_rx;
}
}
/*
* Transparent mode: released bufs (i.e. between kring->nr_hwcur and
* ring->head) marked with NS_FORWARD on hw rx rings are passed up
* to the host stack.
*/
if (mbq_peek(&q)) {
netmap_send_up(na->ifp, &q);
}
return (revents);
#undef want_tx
#undef want_rx
}
int
nma_intr_enable(struct netmap_adapter *na, int onoff)
{
bool changed = false;
enum txrx t;
int i;
for_rx_tx(t) {
for (i = 0; i < nma_get_nrings(na, t); i++) {
struct netmap_kring *kring = NMR(na, t)[i];
int on = !(kring->nr_kflags & NKR_NOINTR);
if (!!onoff != !!on) {
changed = true;
}
if (onoff) {
kring->nr_kflags &= ~NKR_NOINTR;
} else {
kring->nr_kflags |= NKR_NOINTR;
}
}
}
if (!changed) {
return 0; /* nothing to do */
}
if (!na->nm_intr) {
nm_prerr("Cannot %s interrupts for %s", onoff ? "enable" : "disable",
na->name);
return -1;
}
na->nm_intr(na, onoff);
return 0;
}
/*-------------------- driver support routines -------------------*/
/* default notify callback */
static int
netmap_notify(struct netmap_kring *kring, int flags)
{
struct netmap_adapter *na = kring->notify_na;
enum txrx t = kring->tx;
nm_os_selwakeup(&kring->si);
/* optimization: avoid a wake up on the global
* queue if nobody has registered for more
* than one ring
*/
if (na->si_users[t] > 0)
nm_os_selwakeup(&na->si[t]);
return NM_IRQ_COMPLETED;
}
/* called by all routines that create netmap_adapters.
* provide some defaults and get a reference to the
* memory allocator
*/
int
netmap_attach_common(struct netmap_adapter *na)
{
if (!na->rx_buf_maxsize) {
/* Set a conservative default (larger is safer). */
na->rx_buf_maxsize = PAGE_SIZE;
}
#ifdef __FreeBSD__
if (na->na_flags & NAF_HOST_RINGS && na->ifp) {
na->if_input = na->ifp->if_input; /* for netmap_send_up */
}
na->pdev = na; /* make sure netmap_mem_map() is called */
#endif /* __FreeBSD__ */
if (na->na_flags & NAF_HOST_RINGS) {
if (na->num_host_rx_rings == 0)
na->num_host_rx_rings = 1;
if (na->num_host_tx_rings == 0)
na->num_host_tx_rings = 1;
}
if (na->nm_krings_create == NULL) {
/* we assume that we have been called by a driver,
* since other port types all provide their own
* nm_krings_create
*/
na->nm_krings_create = netmap_hw_krings_create;
na->nm_krings_delete = netmap_hw_krings_delete;
}
if (na->nm_notify == NULL)
na->nm_notify = netmap_notify;
na->active_fds = 0;
if (na->nm_mem == NULL) {
/* use iommu or global allocator */
na->nm_mem = netmap_mem_get_iommu(na);
}
if (na->nm_bdg_attach == NULL)
/* no special nm_bdg_attach callback. On VALE
* attach, we need to interpose a bwrap
*/
na->nm_bdg_attach = netmap_default_bdg_attach;
return 0;
}
/* Wrapper for the register callback provided netmap-enabled
* hardware drivers.
* nm_iszombie(na) means that the driver module has been
* unloaded, so we cannot call into it.
* nm_os_ifnet_lock() must guarantee mutual exclusion with
* module unloading.
*/
static int
netmap_hw_reg(struct netmap_adapter *na, int onoff)
{
struct netmap_hw_adapter *hwna =
(struct netmap_hw_adapter*)na;
int error = 0;
nm_os_ifnet_lock();
if (nm_iszombie(na)) {
if (onoff) {
error = ENXIO;
} else if (na != NULL) {
na->na_flags &= ~NAF_NETMAP_ON;
}
goto out;
}
error = hwna->nm_hw_register(na, onoff);
out:
nm_os_ifnet_unlock();
return error;
}
static void
netmap_hw_dtor(struct netmap_adapter *na)
{
if (na->ifp == NULL)
return;
NM_DETACH_NA(na->ifp);
}
/*
* Allocate a netmap_adapter object, and initialize it from the
* 'arg' passed by the driver on attach.
* We allocate a block of memory of 'size' bytes, which has room
* for struct netmap_adapter plus additional room private to
* the caller.
* Return 0 on success, ENOMEM otherwise.
*/
int
netmap_attach_ext(struct netmap_adapter *arg, size_t size, int override_reg)
{
struct netmap_hw_adapter *hwna = NULL;
struct ifnet *ifp = NULL;
if (size < sizeof(struct netmap_hw_adapter)) {
if (netmap_debug & NM_DEBUG_ON)
nm_prerr("Invalid netmap adapter size %d", (int)size);
return EINVAL;
}
if (arg == NULL || arg->ifp == NULL) {
if (netmap_debug & NM_DEBUG_ON)
nm_prerr("either arg or arg->ifp is NULL");
return EINVAL;
}
if (arg->num_tx_rings == 0 || arg->num_rx_rings == 0) {
if (netmap_debug & NM_DEBUG_ON)
nm_prerr("%s: invalid rings tx %d rx %d",
arg->name, arg->num_tx_rings, arg->num_rx_rings);
return EINVAL;
}
ifp = arg->ifp;
if (NM_NA_CLASH(ifp)) {
/* If NA(ifp) is not null but there is no valid netmap
* adapter it means that someone else is using the same
* pointer (e.g. ax25_ptr on linux). This happens for
* instance when also PF_RING is in use. */
nm_prerr("Error: netmap adapter hook is busy");
return EBUSY;
}
hwna = nm_os_malloc(size);
if (hwna == NULL)
goto fail;
hwna->up = *arg;
hwna->up.na_flags |= NAF_HOST_RINGS | NAF_NATIVE;
strlcpy(hwna->up.name, ifp->if_xname, sizeof(hwna->up.name));
if (override_reg) {
hwna->nm_hw_register = hwna->up.nm_register;
hwna->up.nm_register = netmap_hw_reg;
}
if (netmap_attach_common(&hwna->up)) {
nm_os_free(hwna);
goto fail;
}
netmap_adapter_get(&hwna->up);
NM_ATTACH_NA(ifp, &hwna->up);
nm_os_onattach(ifp);
if (arg->nm_dtor == NULL) {
hwna->up.nm_dtor = netmap_hw_dtor;
}
if_printf(ifp, "netmap queues/slots: TX %d/%d, RX %d/%d\n",
hwna->up.num_tx_rings, hwna->up.num_tx_desc,
hwna->up.num_rx_rings, hwna->up.num_rx_desc);
return 0;
fail:
nm_prerr("fail, arg %p ifp %p na %p", arg, ifp, hwna);
return (hwna ? EINVAL : ENOMEM);
}
int
netmap_attach(struct netmap_adapter *arg)
{
return netmap_attach_ext(arg, sizeof(struct netmap_hw_adapter),
1 /* override nm_reg */);
}
void
NM_DBG(netmap_adapter_get)(struct netmap_adapter *na)
{
if (!na) {
return;
}
refcount_acquire(&na->na_refcount);
}
/* returns 1 iff the netmap_adapter is destroyed */
int
NM_DBG(netmap_adapter_put)(struct netmap_adapter *na)
{
if (!na)
return 1;
if (!refcount_release(&na->na_refcount))
return 0;
if (na->nm_dtor)
na->nm_dtor(na);
if (na->tx_rings) { /* XXX should not happen */
if (netmap_debug & NM_DEBUG_ON)
nm_prerr("freeing leftover tx_rings");
na->nm_krings_delete(na);
}
netmap_pipe_dealloc(na);
if (na->nm_mem)
netmap_mem_put(na->nm_mem);
bzero(na, sizeof(*na));
nm_os_free(na);
return 1;
}
/* nm_krings_create callback for all hardware native adapters */
int
netmap_hw_krings_create(struct netmap_adapter *na)
{
int ret = netmap_krings_create(na, 0);
if (ret == 0) {
/* initialize the mbq for the sw rx ring */
u_int lim = netmap_real_rings(na, NR_RX), i;
for (i = na->num_rx_rings; i < lim; i++) {
mbq_safe_init(&NMR(na, NR_RX)[i]->rx_queue);
}
nm_prdis("initialized sw rx queue %d", na->num_rx_rings);
}
return ret;
}
/*
* Called on module unload by the netmap-enabled drivers
*/
void
netmap_detach(struct ifnet *ifp)
{
struct netmap_adapter *na = NA(ifp);
if (!na)
return;
NMG_LOCK();
netmap_set_all_rings(na, NM_KR_LOCKED);
/*
* if the netmap adapter is not native, somebody
* changed it, so we can not release it here.
* The NAF_ZOMBIE flag will notify the new owner that
* the driver is gone.
*/
if (!(na->na_flags & NAF_NATIVE) || !netmap_adapter_put(na)) {
na->na_flags |= NAF_ZOMBIE;
}
/* give active users a chance to notice that NAF_ZOMBIE has been
* turned on, so that they can stop and return an error to userspace.
* Note that this becomes a NOP if there are no active users and,
* therefore, the put() above has deleted the na, since now NA(ifp) is
* NULL.
*/
netmap_enable_all_rings(ifp);
NMG_UNLOCK();
}
/*
* Intercept packets from the network stack and pass them
* to netmap as incoming packets on the 'software' ring.
*
* We only store packets in a bounded mbq and then copy them
* in the relevant rxsync routine.
*
* We rely on the OS to make sure that the ifp and na do not go
* away (typically the caller checks for IFF_DRV_RUNNING or the like).
* In nm_register() or whenever there is a reinitialization,
* we make sure to make the mode change visible here.
*/
int
netmap_transmit(struct ifnet *ifp, struct mbuf *m)
{
struct netmap_adapter *na = NA(ifp);
struct netmap_kring *kring, *tx_kring;
u_int len = MBUF_LEN(m);
u_int error = ENOBUFS;
unsigned int txr;
struct mbq *q;
int busy;
u_int i;
i = MBUF_TXQ(m);
if (i >= na->num_host_rx_rings) {
i = i % na->num_host_rx_rings;
}
kring = NMR(na, NR_RX)[nma_get_nrings(na, NR_RX) + i];
// XXX [Linux] we do not need this lock
// if we follow the down/configure/up protocol -gl
// mtx_lock(&na->core_lock);
if (!nm_netmap_on(na)) {
nm_prerr("%s not in netmap mode anymore", na->name);
error = ENXIO;
goto done;
}
txr = MBUF_TXQ(m);
if (txr >= na->num_tx_rings) {
txr %= na->num_tx_rings;
}
tx_kring = NMR(na, NR_TX)[txr];
if (tx_kring->nr_mode == NKR_NETMAP_OFF) {
return MBUF_TRANSMIT(na, ifp, m);
}
q = &kring->rx_queue;
// XXX reconsider long packets if we handle fragments
if (len > NETMAP_BUF_SIZE(na)) { /* too long for us */
nm_prerr("%s from_host, drop packet size %d > %d", na->name,
len, NETMAP_BUF_SIZE(na));
goto done;
}
if (!netmap_generic_hwcsum) {
if (nm_os_mbuf_has_csum_offld(m)) {
nm_prlim(1, "%s drop mbuf that needs checksum offload", na->name);
goto done;
}
}
if (nm_os_mbuf_has_seg_offld(m)) {
nm_prlim(1, "%s drop mbuf that needs generic segmentation offload", na->name);
goto done;
}
#ifdef __FreeBSD__
ETHER_BPF_MTAP(ifp, m);
#endif /* __FreeBSD__ */
/* protect against netmap_rxsync_from_host(), netmap_sw_to_nic()
* and maybe other instances of netmap_transmit (the latter
* not possible on Linux).
* We enqueue the mbuf only if we are sure there is going to be
* enough room in the host RX ring, otherwise we drop it.
*/
mbq_lock(q);
busy = kring->nr_hwtail - kring->nr_hwcur;
if (busy < 0)
busy += kring->nkr_num_slots;
if (busy + mbq_len(q) >= kring->nkr_num_slots - 1) {
nm_prlim(2, "%s full hwcur %d hwtail %d qlen %d", na->name,
kring->nr_hwcur, kring->nr_hwtail, mbq_len(q));
} else {
mbq_enqueue(q, m);
nm_prdis(2, "%s %d bufs in queue", na->name, mbq_len(q));
/* notify outside the lock */
m = NULL;
error = 0;
}
mbq_unlock(q);
done:
if (m)
m_freem(m);
/* unconditionally wake up listeners */
kring->nm_notify(kring, 0);
/* this is normally netmap_notify(), but for nics
* connected to a bridge it is netmap_bwrap_intr_notify(),
* that possibly forwards the frames through the switch
*/
return (error);
}
/*
* Reset function to be called by the driver routines when reinitializing
* a hardware ring. The driver is in charge of locking to protect the kring
* while this operation is being performed. This is normally achieved by
* calling netmap_disable_all_rings() before triggering a reset.
* If the kring is not in netmap mode, return NULL to inform the caller
* that this is the case.
* If the kring is in netmap mode, set hwofs so that the netmap indices
* seen by userspace (head/cut/tail) do not change, although the internal
* NIC indices have been reset to 0.
* In any case, adjust kring->nr_mode.
*/
struct netmap_slot *
netmap_reset(struct netmap_adapter *na, enum txrx tx, u_int n,
u_int new_cur)
{
struct netmap_kring *kring;
u_int new_hwtail, new_hwofs;
if (!nm_native_on(na)) {
nm_prdis("interface not in native netmap mode");
return NULL; /* nothing to reinitialize */
}
if (tx == NR_TX) {
if (n >= na->num_tx_rings)
return NULL;
kring = na->tx_rings[n];
/*
* Set hwofs to rhead, so that slots[rhead] is mapped to
* the NIC internal slot 0, and thus the netmap buffer
* at rhead is the next to be transmitted. Transmissions
* that were pending before the reset are considered as
* sent, so that we can have hwcur = rhead. All the slots
* are now owned by the user, so we can also reinit hwtail.
*/
new_hwofs = kring->rhead;
new_hwtail = nm_prev(kring->rhead, kring->nkr_num_slots - 1);
} else {
if (n >= na->num_rx_rings)
return NULL;
kring = na->rx_rings[n];
/*
* Set hwofs to hwtail, so that slots[hwtail] is mapped to
* the NIC internal slot 0, and thus the netmap buffer
* at hwtail is the next to be given to the NIC.
* Unread slots (the ones in [rhead,hwtail[) are owned by
* the user, and thus the caller cannot give them
* to the NIC right now.
*/
new_hwofs = kring->nr_hwtail;
new_hwtail = kring->nr_hwtail;
}
if (kring->nr_pending_mode == NKR_NETMAP_OFF) {
kring->nr_mode = NKR_NETMAP_OFF;
return NULL;
}
if (netmap_verbose) {
nm_prinf("%s, hc %u->%u, ht %u->%u, ho %u->%u", kring->name,
kring->nr_hwcur, kring->rhead,
kring->nr_hwtail, new_hwtail,
kring->nkr_hwofs, new_hwofs);
}
kring->nr_hwcur = kring->rhead;
kring->nr_hwtail = new_hwtail;
kring->nkr_hwofs = new_hwofs;
/*
* Wakeup on the individual and global selwait
* We do the wakeup here, but the ring is not yet reconfigured.
* However, we are under lock so there are no races.
*/
kring->nr_mode = NKR_NETMAP_ON;
kring->nm_notify(kring, 0);
return kring->ring->slot;
}
/*
* Dispatch rx/tx interrupts to the netmap rings.
*
* "work_done" is non-null on the RX path, NULL for the TX path.
* We rely on the OS to make sure that there is only one active
* instance per queue, and that there is appropriate locking.
*
* The 'notify' routine depends on what the ring is attached to.
* - for a netmap file descriptor, do a selwakeup on the individual
* waitqueue, plus one on the global one if needed
* (see netmap_notify)
* - for a nic connected to a switch, call the proper forwarding routine
* (see netmap_bwrap_intr_notify)
*/
int
netmap_common_irq(struct netmap_adapter *na, u_int q, u_int *work_done)
{
struct netmap_kring *kring;
enum txrx t = (work_done ? NR_RX : NR_TX);
q &= NETMAP_RING_MASK;
if (netmap_debug & (NM_DEBUG_RXINTR|NM_DEBUG_TXINTR)) {
nm_prlim(5, "received %s queue %d", work_done ? "RX" : "TX" , q);
}
if (q >= nma_get_nrings(na, t))
return NM_IRQ_PASS; // not a physical queue
kring = NMR(na, t)[q];
if (kring->nr_mode == NKR_NETMAP_OFF) {
return NM_IRQ_PASS;
}
if (t == NR_RX) {
kring->nr_kflags |= NKR_PENDINTR; // XXX atomic ?
*work_done = 1; /* do not fire napi again */
}
return kring->nm_notify(kring, 0);
}
/*
* Default functions to handle rx/tx interrupts from a physical device.
* "work_done" is non-null on the RX path, NULL for the TX path.
*
* If the card is not in netmap mode, simply return NM_IRQ_PASS,
* so that the caller proceeds with regular processing.
* Otherwise call netmap_common_irq().
*
* If the card is connected to a netmap file descriptor,
* do a selwakeup on the individual queue, plus one on the global one
* if needed (multiqueue card _and_ there are multiqueue listeners),
* and return NR_IRQ_COMPLETED.
*
* Finally, if called on rx from an interface connected to a switch,
* calls the proper forwarding routine.
*/
int
netmap_rx_irq(struct ifnet *ifp, u_int q, u_int *work_done)
{
struct netmap_adapter *na = NA(ifp);
/*
* XXX emulated netmap mode sets NAF_SKIP_INTR so
* we still use the regular driver even though the previous
* check fails. It is unclear whether we should use
* nm_native_on() here.
*/
if (!nm_netmap_on(na))
return NM_IRQ_PASS;
if (na->na_flags & NAF_SKIP_INTR) {
nm_prdis("use regular interrupt");
return NM_IRQ_PASS;
}
return netmap_common_irq(na, q, work_done);
}
/* set/clear native flags and if_transmit/netdev_ops */
void
nm_set_native_flags(struct netmap_adapter *na)
{
struct ifnet *ifp = na->ifp;
/* We do the setup for intercepting packets only if we are the
* first user of this adapter. */
if (na->active_fds > 0) {
return;
}
na->na_flags |= NAF_NETMAP_ON;
nm_os_onenter(ifp);
+ nm_update_hostrings_mode(na);
}
void
nm_clear_native_flags(struct netmap_adapter *na)
{
struct ifnet *ifp = na->ifp;
/* We undo the setup for intercepting packets only if we are the
* last user of this adapter. */
if (na->active_fds > 0) {
return;
}
+ nm_update_hostrings_mode(na);
nm_os_onexit(ifp);
na->na_flags &= ~NAF_NETMAP_ON;
}
void
netmap_krings_mode_commit(struct netmap_adapter *na, int onoff)
{
enum txrx t;
for_rx_tx(t) {
int i;
for (i = 0; i < netmap_real_rings(na, t); i++) {
struct netmap_kring *kring = NMR(na, t)[i];
if (onoff && nm_kring_pending_on(kring))
kring->nr_mode = NKR_NETMAP_ON;
else if (!onoff && nm_kring_pending_off(kring))
kring->nr_mode = NKR_NETMAP_OFF;
}
}
}
/*
* Module loader and unloader
*
* netmap_init() creates the /dev/netmap device and initializes
* all global variables. Returns 0 on success, errno on failure
* (but there is no chance)
*
* netmap_fini() destroys everything.
*/
static struct cdev *netmap_dev; /* /dev/netmap character device. */
extern struct cdevsw netmap_cdevsw;
void
netmap_fini(void)
{
if (netmap_dev)
destroy_dev(netmap_dev);
/* we assume that there are no longer netmap users */
nm_os_ifnet_fini();
netmap_uninit_bridges();
netmap_mem_fini();
NMG_LOCK_DESTROY();
nm_prinf("netmap: unloaded module.");
}
int
netmap_init(void)
{
int error;
NMG_LOCK_INIT();
error = netmap_mem_init();
if (error != 0)
goto fail;
/*
* MAKEDEV_ETERNAL_KLD avoids an expensive check on syscalls
* when the module is compiled in.
* XXX could use make_dev_credv() to get error number
*/
netmap_dev = make_dev_credf(MAKEDEV_ETERNAL_KLD,
&netmap_cdevsw, 0, NULL, UID_ROOT, GID_WHEEL, 0600,
"netmap");
if (!netmap_dev)
goto fail;
error = netmap_init_bridges();
if (error)
goto fail;
#ifdef __FreeBSD__
nm_os_vi_init_index();
#endif
error = nm_os_ifnet_init();
if (error)
goto fail;
#if !defined(__FreeBSD__) || defined(KLD_MODULE)
nm_prinf("netmap: loaded module");
#endif
return (0);
fail:
netmap_fini();
return (EINVAL); /* may be incorrect */
}
diff --git a/sys/dev/netmap/netmap_kern.h b/sys/dev/netmap/netmap_kern.h
index 5d8957241c21..cc452657d8d5 100644
--- a/sys/dev/netmap/netmap_kern.h
+++ b/sys/dev/netmap/netmap_kern.h
@@ -1,2524 +1,2534 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (C) 2011-2014 Matteo Landi, Luigi Rizzo
* Copyright (C) 2013-2016 Universita` di Pisa
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/*
* $FreeBSD$
*
* The header contains the definitions of constants and function
* prototypes used only in kernelspace.
*/
#ifndef _NET_NETMAP_KERN_H_
#define _NET_NETMAP_KERN_H_
#if defined(linux)
#if defined(CONFIG_NETMAP_EXTMEM)
#define WITH_EXTMEM
#endif
#if defined(CONFIG_NETMAP_VALE)
#define WITH_VALE
#endif
#if defined(CONFIG_NETMAP_PIPE)
#define WITH_PIPES
#endif
#if defined(CONFIG_NETMAP_MONITOR)
#define WITH_MONITOR
#endif
#if defined(CONFIG_NETMAP_GENERIC)
#define WITH_GENERIC
#endif
#if defined(CONFIG_NETMAP_PTNETMAP)
#define WITH_PTNETMAP
#endif
#if defined(CONFIG_NETMAP_SINK)
#define WITH_SINK
#endif
#if defined(CONFIG_NETMAP_NULL)
#define WITH_NMNULL
#endif
#elif defined (_WIN32)
#define WITH_VALE // comment out to disable VALE support
#define WITH_PIPES
#define WITH_MONITOR
#define WITH_GENERIC
#define WITH_NMNULL
#else /* neither linux nor windows */
#define WITH_VALE // comment out to disable VALE support
#define WITH_PIPES
#define WITH_MONITOR
#define WITH_GENERIC
#define WITH_EXTMEM
#define WITH_NMNULL
#endif
#if defined(__FreeBSD__)
#include <sys/selinfo.h>
#define likely(x) __builtin_expect((long)!!(x), 1L)
#define unlikely(x) __builtin_expect((long)!!(x), 0L)
#define __user
#define NM_LOCK_T struct mtx /* low level spinlock, used to protect queues */
#define NM_MTX_T struct sx /* OS-specific mutex (sleepable) */
#define NM_MTX_INIT(m) sx_init(&(m), #m)
#define NM_MTX_DESTROY(m) sx_destroy(&(m))
#define NM_MTX_LOCK(m) sx_xlock(&(m))
#define NM_MTX_SPINLOCK(m) while (!sx_try_xlock(&(m))) ;
#define NM_MTX_UNLOCK(m) sx_xunlock(&(m))
#define NM_MTX_ASSERT(m) sx_assert(&(m), SA_XLOCKED)
#define NM_SELINFO_T struct nm_selinfo
#define NM_SELRECORD_T struct thread
#define MBUF_LEN(m) ((m)->m_pkthdr.len)
#define MBUF_TXQ(m) ((m)->m_pkthdr.flowid)
#define MBUF_TRANSMIT(na, ifp, m) ((na)->if_transmit(ifp, m))
#define GEN_TX_MBUF_IFP(m) ((m)->m_pkthdr.rcvif)
#define NM_ATOMIC_T volatile int /* required by atomic/bitops.h */
/* atomic operations */
#include <machine/atomic.h>
#define NM_ATOMIC_TEST_AND_SET(p) (!atomic_cmpset_acq_int((p), 0, 1))
#define NM_ATOMIC_CLEAR(p) atomic_store_rel_int((p), 0)
#if __FreeBSD_version >= 1100030
#define WNA(_ifp) (_ifp)->if_netmap
#else /* older FreeBSD */
#define WNA(_ifp) (_ifp)->if_pspare[0]
#endif /* older FreeBSD */
#if __FreeBSD_version >= 1100005
struct netmap_adapter *netmap_getna(if_t ifp);
#endif
#if __FreeBSD_version >= 1100027
#define MBUF_REFCNT(m) ((m)->m_ext.ext_count)
#define SET_MBUF_REFCNT(m, x) (m)->m_ext.ext_count = x
#else
#define MBUF_REFCNT(m) ((m)->m_ext.ref_cnt ? *((m)->m_ext.ref_cnt) : -1)
#define SET_MBUF_REFCNT(m, x) *((m)->m_ext.ref_cnt) = x
#endif
#define MBUF_QUEUED(m) 1
struct nm_selinfo {
/* Support for select(2) and poll(2). */
struct selinfo si;
/* Support for kqueue(9). See comments in netmap_freebsd.c */
struct taskqueue *ntfytq;
struct task ntfytask;
struct mtx m;
char mtxname[32];
int kqueue_users;
};
struct hrtimer {
/* Not used in FreeBSD. */
};
#define NM_BNS_GET(b)
#define NM_BNS_PUT(b)
#elif defined (linux)
#define NM_LOCK_T safe_spinlock_t // see bsd_glue.h
#define NM_SELINFO_T wait_queue_head_t
#define MBUF_LEN(m) ((m)->len)
#define MBUF_TRANSMIT(na, ifp, m) \
({ \
/* Avoid infinite recursion with generic. */ \
m->priority = NM_MAGIC_PRIORITY_TX; \
(((struct net_device_ops *)(na)->if_transmit)->ndo_start_xmit(m, ifp)); \
0; \
})
/* See explanation in nm_os_generic_xmit_frame. */
#define GEN_TX_MBUF_IFP(m) ((struct ifnet *)skb_shinfo(m)->destructor_arg)
#define NM_ATOMIC_T volatile long unsigned int
#define NM_MTX_T struct mutex /* OS-specific sleepable lock */
#define NM_MTX_INIT(m) mutex_init(&(m))
#define NM_MTX_DESTROY(m) do { (void)(m); } while (0)
#define NM_MTX_LOCK(m) mutex_lock(&(m))
#define NM_MTX_UNLOCK(m) mutex_unlock(&(m))
#define NM_MTX_ASSERT(m) mutex_is_locked(&(m))
#ifndef DEV_NETMAP
#define DEV_NETMAP
#endif /* DEV_NETMAP */
#elif defined (__APPLE__)
#warning apple support is incomplete.
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#define NM_LOCK_T IOLock *
#define NM_SELINFO_T struct selinfo
#define MBUF_LEN(m) ((m)->m_pkthdr.len)
#elif defined (_WIN32)
#include "../../../WINDOWS/win_glue.h"
#define NM_SELRECORD_T IO_STACK_LOCATION
#define NM_SELINFO_T win_SELINFO // see win_glue.h
#define NM_LOCK_T win_spinlock_t // see win_glue.h
#define NM_MTX_T KGUARDED_MUTEX /* OS-specific mutex (sleepable) */
#define NM_MTX_INIT(m) KeInitializeGuardedMutex(&m);
#define NM_MTX_DESTROY(m) do { (void)(m); } while (0)
#define NM_MTX_LOCK(m) KeAcquireGuardedMutex(&(m))
#define NM_MTX_UNLOCK(m) KeReleaseGuardedMutex(&(m))
#define NM_MTX_ASSERT(m) assert(&m.Count>0)
//These linknames are for the NDIS driver
#define NETMAP_NDIS_LINKNAME_STRING L"\\DosDevices\\NMAPNDIS"
#define NETMAP_NDIS_NTDEVICE_STRING L"\\Device\\NMAPNDIS"
//Definition of internal driver-to-driver ioctl codes
#define NETMAP_KERNEL_XCHANGE_POINTERS _IO('i', 180)
#define NETMAP_KERNEL_SEND_SHUTDOWN_SIGNAL _IO_direct('i', 195)
typedef struct hrtimer{
KTIMER timer;
BOOLEAN active;
KDPC deferred_proc;
};
/* MSVC does not have likely/unlikely support */
#ifdef _MSC_VER
#define likely(x) (x)
#define unlikely(x) (x)
#else
#define likely(x) __builtin_expect((long)!!(x), 1L)
#define unlikely(x) __builtin_expect((long)!!(x), 0L)
#endif //_MSC_VER
#else
#error unsupported platform
#endif /* end - platform-specific code */
#ifndef _WIN32 /* support for emulated sysctl */
#define SYSBEGIN(x)
#define SYSEND
#endif /* _WIN32 */
#define NM_ACCESS_ONCE(x) (*(volatile __typeof__(x) *)&(x))
#define NMG_LOCK_T NM_MTX_T
#define NMG_LOCK_INIT() NM_MTX_INIT(netmap_global_lock)
#define NMG_LOCK_DESTROY() NM_MTX_DESTROY(netmap_global_lock)
#define NMG_LOCK() NM_MTX_LOCK(netmap_global_lock)
#define NMG_UNLOCK() NM_MTX_UNLOCK(netmap_global_lock)
#define NMG_LOCK_ASSERT() NM_MTX_ASSERT(netmap_global_lock)
#if defined(__FreeBSD__)
#define nm_prerr_int printf
#define nm_prinf_int printf
#elif defined (_WIN32)
#define nm_prerr_int DbgPrint
#define nm_prinf_int DbgPrint
#elif defined(linux)
#define nm_prerr_int(fmt, arg...) printk(KERN_ERR fmt, ##arg)
#define nm_prinf_int(fmt, arg...) printk(KERN_INFO fmt, ##arg)
#endif
#define nm_prinf(format, ...) \
do { \
struct timeval __xxts; \
microtime(&__xxts); \
nm_prinf_int("%03d.%06d [%4d] %-25s " format "\n",\
(int)__xxts.tv_sec % 1000, (int)__xxts.tv_usec, \
__LINE__, __FUNCTION__, ##__VA_ARGS__); \
} while (0)
#define nm_prerr(format, ...) \
do { \
struct timeval __xxts; \
microtime(&__xxts); \
nm_prerr_int("%03d.%06d [%4d] %-25s " format "\n",\
(int)__xxts.tv_sec % 1000, (int)__xxts.tv_usec, \
__LINE__, __FUNCTION__, ##__VA_ARGS__); \
} while (0)
/* Disabled printf (used to be nm_prdis). */
#define nm_prdis(format, ...)
/* Rate limited, lps indicates how many per second. */
#define nm_prlim(lps, format, ...) \
do { \
static int t0, __cnt; \
if (t0 != time_second) { \
t0 = time_second; \
__cnt = 0; \
} \
if (__cnt++ < lps) \
nm_prinf(format, ##__VA_ARGS__); \
} while (0)
struct netmap_adapter;
struct nm_bdg_fwd;
struct nm_bridge;
struct netmap_priv_d;
struct nm_bdg_args;
/* os-specific NM_SELINFO_T initialization/destruction functions */
int nm_os_selinfo_init(NM_SELINFO_T *, const char *name);
void nm_os_selinfo_uninit(NM_SELINFO_T *);
const char *nm_dump_buf(char *p, int len, int lim, char *dst);
void nm_os_selwakeup(NM_SELINFO_T *si);
void nm_os_selrecord(NM_SELRECORD_T *sr, NM_SELINFO_T *si);
int nm_os_ifnet_init(void);
void nm_os_ifnet_fini(void);
void nm_os_ifnet_lock(void);
void nm_os_ifnet_unlock(void);
unsigned nm_os_ifnet_mtu(struct ifnet *ifp);
void nm_os_get_module(void);
void nm_os_put_module(void);
void netmap_make_zombie(struct ifnet *);
void netmap_undo_zombie(struct ifnet *);
/* os independent alloc/realloc/free */
void *nm_os_malloc(size_t);
void *nm_os_vmalloc(size_t);
void *nm_os_realloc(void *, size_t new_size, size_t old_size);
void nm_os_free(void *);
void nm_os_vfree(void *);
/* os specific attach/detach enter/exit-netmap-mode routines */
void nm_os_onattach(struct ifnet *);
void nm_os_ondetach(struct ifnet *);
void nm_os_onenter(struct ifnet *);
void nm_os_onexit(struct ifnet *);
/* passes a packet up to the host stack.
* If the packet is sent (or dropped) immediately it returns NULL,
* otherwise it links the packet to prev and returns m.
* In this case, a final call with m=NULL and prev != NULL will send up
* the entire chain to the host stack.
*/
void *nm_os_send_up(struct ifnet *, struct mbuf *m, struct mbuf *prev);
int nm_os_mbuf_has_seg_offld(struct mbuf *m);
int nm_os_mbuf_has_csum_offld(struct mbuf *m);
#include "netmap_mbq.h"
extern NMG_LOCK_T netmap_global_lock;
enum txrx { NR_RX = 0, NR_TX = 1, NR_TXRX };
static __inline const char*
nm_txrx2str(enum txrx t)
{
return (t== NR_RX ? "RX" : "TX");
}
static __inline enum txrx
nm_txrx_swap(enum txrx t)
{
return (t== NR_RX ? NR_TX : NR_RX);
}
#define for_rx_tx(t) for ((t) = 0; (t) < NR_TXRX; (t)++)
#ifdef WITH_MONITOR
struct netmap_zmon_list {
struct netmap_kring *next;
struct netmap_kring *prev;
};
#endif /* WITH_MONITOR */
/*
* private, kernel view of a ring. Keeps track of the status of
* a ring across system calls.
*
* nr_hwcur index of the next buffer to refill.
* It corresponds to ring->head
* at the time the system call returns.
*
* nr_hwtail index of the first buffer owned by the kernel.
* On RX, hwcur->hwtail are receive buffers
* not yet released. hwcur is advanced following
* ring->head, hwtail is advanced on incoming packets,
* and a wakeup is generated when hwtail passes ring->cur
* On TX, hwcur->rcur have been filled by the sender
* but not sent yet to the NIC; rcur->hwtail are available
* for new transmissions, and hwtail->hwcur-1 are pending
* transmissions not yet acknowledged.
*
* The indexes in the NIC and netmap rings are offset by nkr_hwofs slots.
* This is so that, on a reset, buffers owned by userspace are not
* modified by the kernel. In particular:
* RX rings: the next empty buffer (hwtail + hwofs) coincides with
* the next empty buffer as known by the hardware (next_to_check or so).
* TX rings: hwcur + hwofs coincides with next_to_send
*
* The following fields are used to implement lock-free copy of packets
* from input to output ports in VALE switch:
* nkr_hwlease buffer after the last one being copied.
* A writer in nm_bdg_flush reserves N buffers
* from nr_hwlease, advances it, then does the
* copy outside the lock.
* In RX rings (used for VALE ports),
* nkr_hwtail <= nkr_hwlease < nkr_hwcur+N-1
* In TX rings (used for NIC or host stack ports)
* nkr_hwcur <= nkr_hwlease < nkr_hwtail
* nkr_leases array of nkr_num_slots where writers can report
* completion of their block. NR_NOSLOT (~0) indicates
* that the writer has not finished yet
* nkr_lease_idx index of next free slot in nr_leases, to be assigned
*
* The kring is manipulated by txsync/rxsync and generic netmap function.
*
* Concurrent rxsync or txsync on the same ring are prevented through
* by nm_kr_(try)lock() which in turn uses nr_busy. This is all we need
* for NIC rings, and for TX rings attached to the host stack.
*
* RX rings attached to the host stack use an mbq (rx_queue) on both
* rxsync_from_host() and netmap_transmit(). The mbq is protected
* by its internal lock.
*
* RX rings attached to the VALE switch are accessed by both senders
* and receiver. They are protected through the q_lock on the RX ring.
*/
struct netmap_kring {
struct netmap_ring *ring;
uint32_t nr_hwcur; /* should be nr_hwhead */
uint32_t nr_hwtail;
/*
* Copies of values in user rings, so we do not need to look
* at the ring (which could be modified). These are set in the
* *sync_prologue()/finalize() routines.
*/
uint32_t rhead;
uint32_t rcur;
uint32_t rtail;
uint32_t nr_kflags; /* private driver flags */
#define NKR_PENDINTR 0x1 // Pending interrupt.
#define NKR_EXCLUSIVE 0x2 /* exclusive binding */
#define NKR_FORWARD 0x4 /* (host ring only) there are
packets to forward
*/
#define NKR_NEEDRING 0x8 /* ring needed even if users==0
* (used internally by pipes and
* by ptnetmap host ports)
*/
#define NKR_NOINTR 0x10 /* don't use interrupts on this ring */
#define NKR_FAKERING 0x20 /* don't allocate/free buffers */
uint32_t nr_mode;
uint32_t nr_pending_mode;
#define NKR_NETMAP_OFF 0x0
#define NKR_NETMAP_ON 0x1
uint32_t nkr_num_slots;
/*
* On a NIC reset, the NIC ring indexes may be reset but the
* indexes in the netmap rings remain the same. nkr_hwofs
* keeps track of the offset between the two.
*
* Moreover, during reset, we can restore only the subset of
* the NIC ring that corresponds to the kernel-owned part of
* the netmap ring. The rest of the slots must be restored
* by the *sync routines when the user releases more slots.
* The nkr_to_refill field keeps track of the number of slots
* that still need to be restored.
*/
int32_t nkr_hwofs;
int32_t nkr_to_refill;
/* last_reclaim is opaque marker to help reduce the frequency
* of operations such as reclaiming tx buffers. A possible use
* is set it to ticks and do the reclaim only once per tick.
*/
uint64_t last_reclaim;
NM_SELINFO_T si; /* poll/select wait queue */
NM_LOCK_T q_lock; /* protects kring and ring. */
NM_ATOMIC_T nr_busy; /* prevent concurrent syscalls */
/* the adapter the owns this kring */
struct netmap_adapter *na;
/* the adapter that wants to be notified when this kring has
* new slots available. This is usually the same as the above,
* but wrappers may let it point to themselves
*/
struct netmap_adapter *notify_na;
/* The following fields are for VALE switch support */
struct nm_bdg_fwd *nkr_ft;
uint32_t *nkr_leases;
#define NR_NOSLOT ((uint32_t)~0) /* used in nkr_*lease* */
uint32_t nkr_hwlease;
uint32_t nkr_lease_idx;
/* while nkr_stopped is set, no new [tr]xsync operations can
* be started on this kring.
* This is used by netmap_disable_all_rings()
* to find a synchronization point where critical data
* structures pointed to by the kring can be added or removed
*/
volatile int nkr_stopped;
/* Support for adapters without native netmap support.
* On tx rings we preallocate an array of tx buffers
* (same size as the netmap ring), on rx rings we
* store incoming mbufs in a queue that is drained by
* a rxsync.
*/
struct mbuf **tx_pool;
struct mbuf *tx_event; /* TX event used as a notification */
NM_LOCK_T tx_event_lock; /* protects the tx_event mbuf */
struct mbq rx_queue; /* intercepted rx mbufs. */
uint32_t users; /* existing bindings for this ring */
uint32_t ring_id; /* kring identifier */
enum txrx tx; /* kind of ring (tx or rx) */
char name[64]; /* diagnostic */
/* [tx]sync callback for this kring.
* The default nm_kring_create callback (netmap_krings_create)
* sets the nm_sync callback of each hardware tx(rx) kring to
* the corresponding nm_txsync(nm_rxsync) taken from the
* netmap_adapter; moreover, it sets the sync callback
* of the host tx(rx) ring to netmap_txsync_to_host
* (netmap_rxsync_from_host).
*
* Overrides: the above configuration is not changed by
* any of the nm_krings_create callbacks.
*/
int (*nm_sync)(struct netmap_kring *kring, int flags);
int (*nm_notify)(struct netmap_kring *kring, int flags);
#ifdef WITH_PIPES
struct netmap_kring *pipe; /* if this is a pipe ring,
* pointer to the other end
*/
uint32_t pipe_tail; /* hwtail updated by the other end */
#endif /* WITH_PIPES */
/* mask for the offset-related part of the ptr field in the slots */
uint64_t offset_mask;
/* maximum user-specified offset, as stipulated at bind time.
* Larger offset requests will be silently capped to offset_max.
*/
uint64_t offset_max;
/* minimum gap between two consecutive offsets into the same
* buffer, as stipulated at bind time. This is used to choose
* the hwbuf_len, but is not otherwise checked for compliance
* at runtime.
*/
uint64_t offset_gap;
/* size of hardware buffer. This may be less than the size of
* the netmap buffers because of non-zero offsets, or because
* the netmap buffer size exceeds the capability of the hardware.
*/
uint64_t hwbuf_len;
/* required alignment (in bytes) for the buffers used by this ring.
* Netmap buffers are aligned to cachelines, which should suffice
* for most NICs. If the user is passing offsets, though, we need
* to check that the resulting buf address complies with any
* alignment restriction.
*/
uint64_t buf_align;
/* hardware specific logic for the selection of the hwbuf_len */
int (*nm_bufcfg)(struct netmap_kring *kring, uint64_t target);
int (*save_notify)(struct netmap_kring *kring, int flags);
#ifdef WITH_MONITOR
/* array of krings that are monitoring this kring */
struct netmap_kring **monitors;
uint32_t max_monitors; /* current size of the monitors array */
uint32_t n_monitors; /* next unused entry in the monitor array */
uint32_t mon_pos[NR_TXRX]; /* index of this ring in the monitored ring array */
uint32_t mon_tail; /* last seen slot on rx */
/* circular list of zero-copy monitors */
struct netmap_zmon_list zmon_list[NR_TXRX];
/*
* Monitors work by intercepting the sync and notify callbacks of the
* monitored krings. This is implemented by replacing the pointers
* above and saving the previous ones in mon_* pointers below
*/
int (*mon_sync)(struct netmap_kring *kring, int flags);
int (*mon_notify)(struct netmap_kring *kring, int flags);
#endif
}
#ifdef _WIN32
__declspec(align(64));
#else
__attribute__((__aligned__(64)));
#endif
/* return 1 iff the kring needs to be turned on */
static inline int
nm_kring_pending_on(struct netmap_kring *kring)
{
return kring->nr_pending_mode == NKR_NETMAP_ON &&
kring->nr_mode == NKR_NETMAP_OFF;
}
/* return 1 iff the kring needs to be turned off */
static inline int
nm_kring_pending_off(struct netmap_kring *kring)
{
return kring->nr_pending_mode == NKR_NETMAP_OFF &&
kring->nr_mode == NKR_NETMAP_ON;
}
/* return the next index, with wraparound */
static inline uint32_t
nm_next(uint32_t i, uint32_t lim)
{
return unlikely (i == lim) ? 0 : i + 1;
}
/* return the previous index, with wraparound */
static inline uint32_t
nm_prev(uint32_t i, uint32_t lim)
{
return unlikely (i == 0) ? lim : i - 1;
}
/*
*
* Here is the layout for the Rx and Tx rings.
RxRING TxRING
+-----------------+ +-----------------+
| | | |
| free | | free |
+-----------------+ +-----------------+
head->| owned by user |<-hwcur | not sent to nic |<-hwcur
| | | yet |
+-----------------+ | |
cur->| available to | | |
| user, not read | +-----------------+
| yet | cur->| (being |
| | | prepared) |
| | | |
+-----------------+ + ------ +
tail->| |<-hwtail | |<-hwlease
| (being | ... | | ...
| prepared) | ... | | ...
+-----------------+ ... | | ...
| |<-hwlease +-----------------+
| | tail->| |<-hwtail
| | | |
| | | |
| | | |
+-----------------+ +-----------------+
* The cur/tail (user view) and hwcur/hwtail (kernel view)
* are used in the normal operation of the card.
*
* When a ring is the output of a switch port (Rx ring for
* a VALE port, Tx ring for the host stack or NIC), slots
* are reserved in blocks through 'hwlease' which points
* to the next unused slot.
* On an Rx ring, hwlease is always after hwtail,
* and completions cause hwtail to advance.
* On a Tx ring, hwlease is always between cur and hwtail,
* and completions cause cur to advance.
*
* nm_kr_space() returns the maximum number of slots that
* can be assigned.
* nm_kr_lease() reserves the required number of buffers,
* advances nkr_hwlease and also returns an entry in
* a circular array where completions should be reported.
*/
struct lut_entry;
#ifdef __FreeBSD__
#define plut_entry lut_entry
#endif
struct netmap_lut {
struct lut_entry *lut;
struct plut_entry *plut;
uint32_t objtotal; /* max buffer index */
uint32_t objsize; /* buffer size */
};
struct netmap_vp_adapter; // forward
struct nm_bridge;
/* Struct to be filled by nm_config callbacks. */
struct nm_config_info {
unsigned num_tx_rings;
unsigned num_rx_rings;
unsigned num_tx_descs;
unsigned num_rx_descs;
unsigned rx_buf_maxsize;
};
/*
* default type for the magic field.
* May be overridden in glue code.
*/
#ifndef NM_OS_MAGIC
#define NM_OS_MAGIC uint32_t
#endif /* !NM_OS_MAGIC */
/*
* The "struct netmap_adapter" extends the "struct adapter"
* (or equivalent) device descriptor.
* It contains all base fields needed to support netmap operation.
* There are in fact different types of netmap adapters
* (native, generic, VALE switch...) so a netmap_adapter is
* just the first field in the derived type.
*/
struct netmap_adapter {
/*
* On linux we do not have a good way to tell if an interface
* is netmap-capable. So we always use the following trick:
* NA(ifp) points here, and the first entry (which hopefully
* always exists and is at least 32 bits) contains a magic
* value which we can use to detect that the interface is good.
*/
NM_OS_MAGIC magic;
uint32_t na_flags; /* enabled, and other flags */
#define NAF_SKIP_INTR 1 /* use the regular interrupt handler.
* useful during initialization
*/
#define NAF_SW_ONLY 2 /* forward packets only to sw adapter */
#define NAF_BDG_MAYSLEEP 4 /* the bridge is allowed to sleep when
* forwarding packets coming from this
* interface
*/
#define NAF_MEM_OWNER 8 /* the adapter uses its own memory area
* that cannot be changed
*/
#define NAF_NATIVE 16 /* the adapter is native.
* Virtual ports (non persistent vale ports,
* pipes, monitors...) should never use
* this flag.
*/
#define NAF_NETMAP_ON 32 /* netmap is active (either native or
* emulated). Where possible (e.g. FreeBSD)
* IFCAP_NETMAP also mirrors this flag.
*/
#define NAF_HOST_RINGS 64 /* the adapter supports the host rings */
#define NAF_FORCE_NATIVE 128 /* the adapter is always NATIVE */
/* free */
#define NAF_MOREFRAG 512 /* the adapter supports NS_MOREFRAG */
#define NAF_OFFSETS 1024 /* the adapter supports the slot offsets */
#define NAF_HOST_ALL 2048 /* the adapter wants as many host rings as hw */
#define NAF_ZOMBIE (1U<<30) /* the nic driver has been unloaded */
#define NAF_BUSY (1U<<31) /* the adapter is used internally and
* cannot be registered from userspace
*/
int active_fds; /* number of user-space descriptors using this
interface, which is equal to the number of
struct netmap_if objs in the mapped region. */
u_int num_rx_rings; /* number of adapter receive rings */
u_int num_tx_rings; /* number of adapter transmit rings */
u_int num_host_rx_rings; /* number of host receive rings */
u_int num_host_tx_rings; /* number of host transmit rings */
u_int num_tx_desc; /* number of descriptor in each queue */
u_int num_rx_desc;
/* tx_rings and rx_rings are private but allocated as a
* contiguous chunk of memory. Each array has N+K entries,
* N for the hardware rings and K for the host rings.
*/
struct netmap_kring **tx_rings; /* array of TX rings. */
struct netmap_kring **rx_rings; /* array of RX rings. */
void *tailroom; /* space below the rings array */
/* (used for leases) */
NM_SELINFO_T si[NR_TXRX]; /* global wait queues */
/* count users of the global wait queues */
int si_users[NR_TXRX];
void *pdev; /* used to store pci device */
/* copy of if_qflush and if_transmit pointers, to intercept
* packets from the network stack when netmap is active.
*/
int (*if_transmit)(struct ifnet *, struct mbuf *);
/* copy of if_input for netmap_send_up() */
void (*if_input)(struct ifnet *, struct mbuf *);
/* Back reference to the parent ifnet struct. Used for
* hardware ports (emulated netmap included). */
struct ifnet *ifp; /* adapter is ifp->if_softc */
/*---- callbacks for this netmap adapter -----*/
/*
* nm_dtor() is the cleanup routine called when destroying
* the adapter.
* Called with NMG_LOCK held.
*
* nm_register() is called on NIOCREGIF and close() to enter
* or exit netmap mode on the NIC
* Called with NNG_LOCK held.
*
* nm_txsync() pushes packets to the underlying hw/switch
*
* nm_rxsync() collects packets from the underlying hw/switch
*
* nm_config() returns configuration information from the OS
* Called with NMG_LOCK held.
*
* nm_bufcfg()
* the purpose of this callback is to fill the kring->hwbuf_len
* (l) and kring->buf_align fields. The l value is most important
* for RX rings, where we want to disallow writes outside of the
* netmap buffer. The l value must be computed taking into account
* the stipulated max_offset (o), possibly increased if there are
* alignment constraints, the maxframe (m), if known, and the
* current NETMAP_BUF_SIZE (b) of the memory region used by the
* adapter. We want the largest supported l such that o + l <= b.
* If m is known to be <= b - o, the callback may also choose the
* largest l <= m, ignoring the offset. The buf_align field is
* most important for TX rings when there are offsets. The user
* will see this value in the ring->buf_align field. Misaligned
* offsets will cause the corresponding packets to be silently
* dropped.
*
* nm_krings_create() create and init the tx_rings and
* rx_rings arrays of kring structures. In particular,
* set the nm_sync callbacks for each ring.
* There is no need to also allocate the corresponding
* netmap_rings, since netmap_mem_rings_create() will always
* be called to provide the missing ones.
* Called with NNG_LOCK held.
*
* nm_krings_delete() cleanup and delete the tx_rings and rx_rings
* arrays
* Called with NMG_LOCK held.
*
* nm_notify() is used to act after data have become available
* (or the stopped state of the ring has changed)
* For hw devices this is typically a selwakeup(),
* but for NIC/host ports attached to a switch (or vice-versa)
* we also need to invoke the 'txsync' code downstream.
* This callback pointer is actually used only to initialize
* kring->nm_notify.
* Return values are the same as for netmap_rx_irq().
*/
void (*nm_dtor)(struct netmap_adapter *);
int (*nm_register)(struct netmap_adapter *, int onoff);
void (*nm_intr)(struct netmap_adapter *, int onoff);
int (*nm_txsync)(struct netmap_kring *kring, int flags);
int (*nm_rxsync)(struct netmap_kring *kring, int flags);
int (*nm_notify)(struct netmap_kring *kring, int flags);
int (*nm_bufcfg)(struct netmap_kring *kring, uint64_t target);
#define NAF_FORCE_READ 1
#define NAF_FORCE_RECLAIM 2
#define NAF_CAN_FORWARD_DOWN 4
/* return configuration information */
int (*nm_config)(struct netmap_adapter *, struct nm_config_info *info);
int (*nm_krings_create)(struct netmap_adapter *);
void (*nm_krings_delete)(struct netmap_adapter *);
/*
* nm_bdg_attach() initializes the na_vp field to point
* to an adapter that can be attached to a VALE switch. If the
* current adapter is already a VALE port, na_vp is simply a cast;
* otherwise, na_vp points to a netmap_bwrap_adapter.
* If applicable, this callback also initializes na_hostvp,
* that can be used to connect the adapter host rings to the
* switch.
* Called with NMG_LOCK held.
*
* nm_bdg_ctl() is called on the actual attach/detach to/from
* to/from the switch, to perform adapter-specific
* initializations
* Called with NMG_LOCK held.
*/
int (*nm_bdg_attach)(const char *bdg_name, struct netmap_adapter *,
struct nm_bridge *);
int (*nm_bdg_ctl)(struct nmreq_header *, struct netmap_adapter *);
/* adapter used to attach this adapter to a VALE switch (if any) */
struct netmap_vp_adapter *na_vp;
/* adapter used to attach the host rings of this adapter
* to a VALE switch (if any) */
struct netmap_vp_adapter *na_hostvp;
/* standard refcount to control the lifetime of the adapter
* (it should be equal to the lifetime of the corresponding ifp)
*/
int na_refcount;
/* memory allocator (opaque)
* We also cache a pointer to the lut_entry for translating
* buffer addresses, the total number of buffers and the buffer size.
*/
struct netmap_mem_d *nm_mem;
struct netmap_mem_d *nm_mem_prev;
struct netmap_lut na_lut;
/* additional information attached to this adapter
* by other netmap subsystems. Currently used by
* bwrap, LINUX/v1000 and ptnetmap
*/
void *na_private;
/* array of pipes that have this adapter as a parent */
struct netmap_pipe_adapter **na_pipes;
int na_next_pipe; /* next free slot in the array */
int na_max_pipes; /* size of the array */
/* Offset of ethernet header for each packet. */
u_int virt_hdr_len;
/* Max number of bytes that the NIC can store in the buffer
* referenced by each RX descriptor. This translates to the maximum
* bytes that a single netmap slot can reference. Larger packets
* require NS_MOREFRAG support. */
unsigned rx_buf_maxsize;
char name[NETMAP_REQ_IFNAMSIZ]; /* used at least by pipes */
#ifdef WITH_MONITOR
unsigned long monitor_id; /* debugging */
#endif
};
static __inline u_int
nma_get_ndesc(struct netmap_adapter *na, enum txrx t)
{
return (t == NR_TX ? na->num_tx_desc : na->num_rx_desc);
}
static __inline void
nma_set_ndesc(struct netmap_adapter *na, enum txrx t, u_int v)
{
if (t == NR_TX)
na->num_tx_desc = v;
else
na->num_rx_desc = v;
}
static __inline u_int
nma_get_nrings(struct netmap_adapter *na, enum txrx t)
{
return (t == NR_TX ? na->num_tx_rings : na->num_rx_rings);
}
static __inline u_int
nma_get_host_nrings(struct netmap_adapter *na, enum txrx t)
{
return (t == NR_TX ? na->num_host_tx_rings : na->num_host_rx_rings);
}
static __inline void
nma_set_nrings(struct netmap_adapter *na, enum txrx t, u_int v)
{
if (t == NR_TX)
na->num_tx_rings = v;
else
na->num_rx_rings = v;
}
static __inline void
nma_set_host_nrings(struct netmap_adapter *na, enum txrx t, u_int v)
{
if (t == NR_TX)
na->num_host_tx_rings = v;
else
na->num_host_rx_rings = v;
}
static __inline struct netmap_kring**
NMR(struct netmap_adapter *na, enum txrx t)
{
return (t == NR_TX ? na->tx_rings : na->rx_rings);
}
int nma_intr_enable(struct netmap_adapter *na, int onoff);
/*
* If the NIC is owned by the kernel
* (i.e., bridge), neither another bridge nor user can use it;
* if the NIC is owned by a user, only users can share it.
* Evaluation must be done under NMG_LOCK().
*/
#define NETMAP_OWNED_BY_KERN(na) ((na)->na_flags & NAF_BUSY)
#define NETMAP_OWNED_BY_ANY(na) \
(NETMAP_OWNED_BY_KERN(na) || ((na)->active_fds > 0))
/*
* derived netmap adapters for various types of ports
*/
struct netmap_vp_adapter { /* VALE software port */
struct netmap_adapter up;
/*
* Bridge support:
*
* bdg_port is the port number used in the bridge;
* na_bdg points to the bridge this NA is attached to.
*/
int bdg_port;
struct nm_bridge *na_bdg;
int retry;
int autodelete; /* remove the ifp on last reference */
/* Maximum Frame Size, used in bdg_mismatch_datapath() */
u_int mfs;
/* Last source MAC on this port */
uint64_t last_smac;
};
struct netmap_hw_adapter { /* physical device */
struct netmap_adapter up;
#ifdef linux
struct net_device_ops nm_ndo;
struct ethtool_ops nm_eto;
#endif
const struct ethtool_ops* save_ethtool;
int (*nm_hw_register)(struct netmap_adapter *, int onoff);
};
#ifdef WITH_GENERIC
/* Mitigation support. */
struct nm_generic_mit {
struct hrtimer mit_timer;
int mit_pending;
int mit_ring_idx; /* index of the ring being mitigated */
struct netmap_adapter *mit_na; /* backpointer */
};
struct netmap_generic_adapter { /* emulated device */
struct netmap_hw_adapter up;
/* Pointer to a previously used netmap adapter. */
struct netmap_adapter *prev;
/* Emulated netmap adapters support:
* - save_if_input saves the if_input hook (FreeBSD);
* - mit implements rx interrupt mitigation;
*/
void (*save_if_input)(struct ifnet *, struct mbuf *);
struct nm_generic_mit *mit;
#ifdef linux
netdev_tx_t (*save_start_xmit)(struct mbuf *, struct ifnet *);
#endif
/* Is the adapter able to use multiple RX slots to scatter
* each packet pushed up by the driver? */
int rxsg;
/* Is the transmission path controlled by a netmap-aware
* device queue (i.e. qdisc on linux)? */
int txqdisc;
};
#endif /* WITH_GENERIC */
static __inline u_int
netmap_real_rings(struct netmap_adapter *na, enum txrx t)
{
return nma_get_nrings(na, t) +
!!(na->na_flags & NAF_HOST_RINGS) * nma_get_host_nrings(na, t);
}
/* account for fake rings */
static __inline u_int
netmap_all_rings(struct netmap_adapter *na, enum txrx t)
{
return max(nma_get_nrings(na, t) + 1, netmap_real_rings(na, t));
}
int netmap_default_bdg_attach(const char *name, struct netmap_adapter *na,
struct nm_bridge *);
struct nm_bdg_polling_state;
/*
* Bridge wrapper for non VALE ports attached to a VALE switch.
*
* The real device must already have its own netmap adapter (hwna).
* The bridge wrapper and the hwna adapter share the same set of
* netmap rings and buffers, but they have two separate sets of
* krings descriptors, with tx/rx meanings swapped:
*
* netmap
* bwrap krings rings krings hwna
* +------+ +------+ +-----+ +------+ +------+
* |tx_rings->| |\ /| |----| |<-tx_rings|
* | | +------+ \ / +-----+ +------+ | |
* | | X | |
* | | / \ | |
* | | +------+/ \+-----+ +------+ | |
* |rx_rings->| | | |----| |<-rx_rings|
* | | +------+ +-----+ +------+ | |
* +------+ +------+
*
* - packets coming from the bridge go to the brwap rx rings,
* which are also the hwna tx rings. The bwrap notify callback
* will then complete the hwna tx (see netmap_bwrap_notify).
*
* - packets coming from the outside go to the hwna rx rings,
* which are also the bwrap tx rings. The (overwritten) hwna
* notify method will then complete the bridge tx
* (see netmap_bwrap_intr_notify).
*
* The bridge wrapper may optionally connect the hwna 'host' rings
* to the bridge. This is done by using a second port in the
* bridge and connecting it to the 'host' netmap_vp_adapter
* contained in the netmap_bwrap_adapter. The brwap host adapter
* cross-links the hwna host rings in the same way as shown above.
*
* - packets coming from the bridge and directed to the host stack
* are handled by the bwrap host notify callback
* (see netmap_bwrap_host_notify)
*
* - packets coming from the host stack are still handled by the
* overwritten hwna notify callback (netmap_bwrap_intr_notify),
* but are diverted to the host adapter depending on the ring number.
*
*/
struct netmap_bwrap_adapter {
struct netmap_vp_adapter up;
struct netmap_vp_adapter host; /* for host rings */
struct netmap_adapter *hwna; /* the underlying device */
/*
* When we attach a physical interface to the bridge, we
* allow the controlling process to terminate, so we need
* a place to store the n_detmap_priv_d data structure.
* This is only done when physical interfaces
* are attached to a bridge.
*/
struct netmap_priv_d *na_kpriv;
struct nm_bdg_polling_state *na_polling_state;
/* we overwrite the hwna->na_vp pointer, so we save
* here its original value, to be restored at detach
*/
struct netmap_vp_adapter *saved_na_vp;
int (*nm_intr_notify)(struct netmap_kring *kring, int flags);
};
int nm_bdg_polling(struct nmreq_header *hdr);
int netmap_bdg_attach(struct nmreq_header *hdr, void *auth_token);
int netmap_bdg_detach(struct nmreq_header *hdr, void *auth_token);
#ifdef WITH_VALE
int netmap_vale_list(struct nmreq_header *hdr);
int netmap_vi_create(struct nmreq_header *hdr, int);
int nm_vi_create(struct nmreq_header *);
int nm_vi_destroy(const char *name);
#else /* !WITH_VALE */
#define netmap_vi_create(hdr, a) (EOPNOTSUPP)
#endif /* WITH_VALE */
#ifdef WITH_PIPES
#define NM_MAXPIPES 64 /* max number of pipes per adapter */
struct netmap_pipe_adapter {
/* pipe identifier is up.name */
struct netmap_adapter up;
#define NM_PIPE_ROLE_MASTER 0x1
#define NM_PIPE_ROLE_SLAVE 0x2
int role; /* either NM_PIPE_ROLE_MASTER or NM_PIPE_ROLE_SLAVE */
struct netmap_adapter *parent; /* adapter that owns the memory */
struct netmap_pipe_adapter *peer; /* the other end of the pipe */
int peer_ref; /* 1 iff we are holding a ref to the peer */
struct ifnet *parent_ifp; /* maybe null */
u_int parent_slot; /* index in the parent pipe array */
};
#endif /* WITH_PIPES */
#ifdef WITH_NMNULL
struct netmap_null_adapter {
struct netmap_adapter up;
};
#endif /* WITH_NMNULL */
/* return slots reserved to rx clients; used in drivers */
static inline uint32_t
nm_kr_rxspace(struct netmap_kring *k)
{
int space = k->nr_hwtail - k->nr_hwcur;
if (space < 0)
space += k->nkr_num_slots;
nm_prdis("preserving %d rx slots %d -> %d", space, k->nr_hwcur, k->nr_hwtail);
return space;
}
/* return slots reserved to tx clients */
#define nm_kr_txspace(_k) nm_kr_rxspace(_k)
/* True if no space in the tx ring, only valid after txsync_prologue */
static inline int
nm_kr_txempty(struct netmap_kring *kring)
{
return kring->rhead == kring->nr_hwtail;
}
/* True if no more completed slots in the rx ring, only valid after
* rxsync_prologue */
#define nm_kr_rxempty(_k) nm_kr_txempty(_k)
/* True if the application needs to wait for more space on the ring
* (more received packets or more free tx slots).
* Only valid after *xsync_prologue. */
static inline int
nm_kr_wouldblock(struct netmap_kring *kring)
{
return kring->rcur == kring->nr_hwtail;
}
/*
* protect against multiple threads using the same ring.
* also check that the ring has not been stopped or locked
*/
#define NM_KR_BUSY 1 /* some other thread is syncing the ring */
#define NM_KR_STOPPED 2 /* unbounded stop (ifconfig down or driver unload) */
#define NM_KR_LOCKED 3 /* bounded, brief stop for mutual exclusion */
/* release the previously acquired right to use the *sync() methods of the ring */
static __inline void nm_kr_put(struct netmap_kring *kr)
{
NM_ATOMIC_CLEAR(&kr->nr_busy);
}
/* true if the ifp that backed the adapter has disappeared (e.g., the
* driver has been unloaded)
*/
static inline int nm_iszombie(struct netmap_adapter *na);
/* try to obtain exclusive right to issue the *sync() operations on the ring.
* The right is obtained and must be later relinquished via nm_kr_put() if and
* only if nm_kr_tryget() returns 0.
* If can_sleep is 1 there are only two other possible outcomes:
* - the function returns NM_KR_BUSY
* - the function returns NM_KR_STOPPED and sets the POLLERR bit in *perr
* (if non-null)
* In both cases the caller will typically skip the ring, possibly collecting
* errors along the way.
* If the calling context does not allow sleeping, the caller must pass 0 in can_sleep.
* In the latter case, the function may also return NM_KR_LOCKED and leave *perr
* untouched: ideally, the caller should try again at a later time.
*/
static __inline int nm_kr_tryget(struct netmap_kring *kr, int can_sleep, int *perr)
{
int busy = 1, stopped;
/* check a first time without taking the lock
* to avoid starvation for nm_kr_get()
*/
retry:
stopped = kr->nkr_stopped;
if (unlikely(stopped)) {
goto stop;
}
busy = NM_ATOMIC_TEST_AND_SET(&kr->nr_busy);
/* we should not return NM_KR_BUSY if the ring was
* actually stopped, so check another time after
* the barrier provided by the atomic operation
*/
stopped = kr->nkr_stopped;
if (unlikely(stopped)) {
goto stop;
}
if (unlikely(nm_iszombie(kr->na))) {
stopped = NM_KR_STOPPED;
goto stop;
}
return unlikely(busy) ? NM_KR_BUSY : 0;
stop:
if (!busy)
nm_kr_put(kr);
if (stopped == NM_KR_STOPPED) {
/* if POLLERR is defined we want to use it to simplify netmap_poll().
* Otherwise, any non-zero value will do.
*/
#ifdef POLLERR
#define NM_POLLERR POLLERR
#else
#define NM_POLLERR 1
#endif /* POLLERR */
if (perr)
*perr |= NM_POLLERR;
#undef NM_POLLERR
} else if (can_sleep) {
tsleep(kr, 0, "NM_KR_TRYGET", 4);
goto retry;
}
return stopped;
}
/* put the ring in the 'stopped' state and wait for the current user (if any) to
* notice. stopped must be either NM_KR_STOPPED or NM_KR_LOCKED
*/
static __inline void nm_kr_stop(struct netmap_kring *kr, int stopped)
{
kr->nkr_stopped = stopped;
while (NM_ATOMIC_TEST_AND_SET(&kr->nr_busy))
tsleep(kr, 0, "NM_KR_GET", 4);
}
/* restart a ring after a stop */
static __inline void nm_kr_start(struct netmap_kring *kr)
{
kr->nkr_stopped = 0;
nm_kr_put(kr);
}
/*
* The following functions are used by individual drivers to
* support netmap operation.
*
* netmap_attach() initializes a struct netmap_adapter, allocating the
* struct netmap_ring's and the struct selinfo.
*
* netmap_detach() frees the memory allocated by netmap_attach().
*
* netmap_transmit() replaces the if_transmit routine of the interface,
* and is used to intercept packets coming from the stack.
*
* netmap_load_map/netmap_reload_map are helper routines to set/reset
* the dmamap for a packet buffer
*
* netmap_reset() is a helper routine to be called in the hw driver
* when reinitializing a ring. It should not be called by
* virtual ports (vale, pipes, monitor)
*/
int netmap_attach(struct netmap_adapter *);
int netmap_attach_ext(struct netmap_adapter *, size_t size, int override_reg);
void netmap_detach(struct ifnet *);
int netmap_transmit(struct ifnet *, struct mbuf *);
struct netmap_slot *netmap_reset(struct netmap_adapter *na,
enum txrx tx, u_int n, u_int new_cur);
int netmap_ring_reinit(struct netmap_kring *);
int netmap_rings_config_get(struct netmap_adapter *, struct nm_config_info *);
/* Return codes for netmap_*x_irq. */
enum {
/* Driver should do normal interrupt processing, e.g. because
* the interface is not in netmap mode. */
NM_IRQ_PASS = 0,
/* Port is in netmap mode, and the interrupt work has been
* completed. The driver does not have to notify netmap
* again before the next interrupt. */
NM_IRQ_COMPLETED = -1,
/* Port is in netmap mode, but the interrupt work has not been
* completed. The driver has to make sure netmap will be
* notified again soon, even if no more interrupts come (e.g.
* on Linux the driver should not call napi_complete()). */
NM_IRQ_RESCHED = -2,
};
/* default functions to handle rx/tx interrupts */
int netmap_rx_irq(struct ifnet *, u_int, u_int *);
#define netmap_tx_irq(_n, _q) netmap_rx_irq(_n, _q, NULL)
int netmap_common_irq(struct netmap_adapter *, u_int, u_int *work_done);
#ifdef WITH_VALE
/* functions used by external modules to interface with VALE */
#define netmap_vp_to_ifp(_vp) ((_vp)->up.ifp)
#define netmap_ifp_to_vp(_ifp) (NA(_ifp)->na_vp)
#define netmap_ifp_to_host_vp(_ifp) (NA(_ifp)->na_hostvp)
#define netmap_bdg_idx(_vp) ((_vp)->bdg_port)
const char *netmap_bdg_name(struct netmap_vp_adapter *);
#else /* !WITH_VALE */
#define netmap_vp_to_ifp(_vp) NULL
#define netmap_ifp_to_vp(_ifp) NULL
#define netmap_ifp_to_host_vp(_ifp) NULL
#define netmap_bdg_idx(_vp) -1
#endif /* WITH_VALE */
static inline int
nm_netmap_on(struct netmap_adapter *na)
{
return na && na->na_flags & NAF_NETMAP_ON;
}
static inline int
nm_native_on(struct netmap_adapter *na)
{
return nm_netmap_on(na) && (na->na_flags & NAF_NATIVE);
}
static inline struct netmap_kring *
netmap_kring_on(struct netmap_adapter *na, u_int q, enum txrx t)
{
struct netmap_kring *kring = NULL;
if (!nm_native_on(na))
return NULL;
if (t == NR_RX && q < na->num_rx_rings)
kring = na->rx_rings[q];
else if (t == NR_TX && q < na->num_tx_rings)
kring = na->tx_rings[q];
else
return NULL;
return (kring->nr_mode == NKR_NETMAP_ON) ? kring : NULL;
}
static inline int
nm_iszombie(struct netmap_adapter *na)
{
return na == NULL || (na->na_flags & NAF_ZOMBIE);
}
+static inline void
+nm_update_hostrings_mode(struct netmap_adapter *na)
+{
+ /* Process nr_mode and nr_pending_mode for host rings. */
+ na->tx_rings[na->num_tx_rings]->nr_mode =
+ na->tx_rings[na->num_tx_rings]->nr_pending_mode;
+ na->rx_rings[na->num_rx_rings]->nr_mode =
+ na->rx_rings[na->num_rx_rings]->nr_pending_mode;
+}
+
void nm_set_native_flags(struct netmap_adapter *);
void nm_clear_native_flags(struct netmap_adapter *);
void netmap_krings_mode_commit(struct netmap_adapter *na, int onoff);
/*
* nm_*sync_prologue() functions are used in ioctl/poll and ptnetmap
* kthreads.
* We need netmap_ring* parameter, because in ptnetmap it is decoupled
* from host kring.
* The user-space ring pointers (head/cur/tail) are shared through
* CSB between host and guest.
*/
/*
* validates parameters in the ring/kring, returns a value for head
* If any error, returns ring_size to force a reinit.
*/
uint32_t nm_txsync_prologue(struct netmap_kring *, struct netmap_ring *);
/*
* validates parameters in the ring/kring, returns a value for head
* If any error, returns ring_size lim to force a reinit.
*/
uint32_t nm_rxsync_prologue(struct netmap_kring *, struct netmap_ring *);
/* check/fix address and len in tx rings */
#if 1 /* debug version */
#define NM_CHECK_ADDR_LEN(_na, _a, _l) do { \
if (_a == NETMAP_BUF_BASE(_na) || _l > NETMAP_BUF_SIZE(_na)) { \
nm_prlim(5, "bad addr/len ring %d slot %d idx %d len %d", \
kring->ring_id, nm_i, slot->buf_idx, len); \
if (_l > NETMAP_BUF_SIZE(_na)) \
_l = NETMAP_BUF_SIZE(_na); \
} } while (0)
#else /* no debug version */
#define NM_CHECK_ADDR_LEN(_na, _a, _l) do { \
if (_l > NETMAP_BUF_SIZE(_na)) \
_l = NETMAP_BUF_SIZE(_na); \
} while (0)
#endif
#define NM_CHECK_ADDR_LEN_OFF(na_, l_, o_) do { \
if ((l_) + (o_) < (l_) || \
(l_) + (o_) > NETMAP_BUF_SIZE(na_)) { \
(l_) = NETMAP_BUF_SIZE(na_) - (o_); \
} } while (0)
/*---------------------------------------------------------------*/
/*
* Support routines used by netmap subsystems
* (native drivers, VALE, generic, pipes, monitors, ...)
*/
/* common routine for all functions that create a netmap adapter. It performs
* two main tasks:
* - if the na points to an ifp, mark the ifp as netmap capable
* using na as its native adapter;
* - provide defaults for the setup callbacks and the memory allocator
*/
int netmap_attach_common(struct netmap_adapter *);
/* fill priv->np_[tr]xq{first,last} using the ringid and flags information
* coming from a struct nmreq_register
*/
int netmap_interp_ringid(struct netmap_priv_d *priv, struct nmreq_header *hdr);
/* update the ring parameters (number and size of tx and rx rings).
* It calls the nm_config callback, if available.
*/
int netmap_update_config(struct netmap_adapter *na);
/* create and initialize the common fields of the krings array.
* using the information that must be already available in the na.
* tailroom can be used to request the allocation of additional
* tailroom bytes after the krings array. This is used by
* netmap_vp_adapter's (i.e., VALE ports) to make room for
* leasing-related data structures
*/
int netmap_krings_create(struct netmap_adapter *na, u_int tailroom);
/* deletes the kring array of the adapter. The array must have
* been created using netmap_krings_create
*/
void netmap_krings_delete(struct netmap_adapter *na);
int netmap_hw_krings_create(struct netmap_adapter *na);
void netmap_hw_krings_delete(struct netmap_adapter *na);
/* set the stopped/enabled status of ring
* When stopping, they also wait for all current activity on the ring to
* terminate. The status change is then notified using the na nm_notify
* callback.
*/
void netmap_set_ring(struct netmap_adapter *, u_int ring_id, enum txrx, int stopped);
/* set the stopped/enabled status of all rings of the adapter. */
void netmap_set_all_rings(struct netmap_adapter *, int stopped);
/* convenience wrappers for netmap_set_all_rings */
void netmap_disable_all_rings(struct ifnet *);
void netmap_enable_all_rings(struct ifnet *);
int netmap_buf_size_validate(const struct netmap_adapter *na, unsigned mtu);
int netmap_do_regif(struct netmap_priv_d *priv, struct netmap_adapter *na,
struct nmreq_header *);
void netmap_do_unregif(struct netmap_priv_d *priv);
u_int nm_bound_var(u_int *v, u_int dflt, u_int lo, u_int hi, const char *msg);
int netmap_get_na(struct nmreq_header *hdr, struct netmap_adapter **na,
struct ifnet **ifp, struct netmap_mem_d *nmd, int create);
void netmap_unget_na(struct netmap_adapter *na, struct ifnet *ifp);
int netmap_get_hw_na(struct ifnet *ifp,
struct netmap_mem_d *nmd, struct netmap_adapter **na);
void netmap_mem_restore(struct netmap_adapter *na);
#ifdef WITH_VALE
uint32_t netmap_vale_learning(struct nm_bdg_fwd *ft, uint8_t *dst_ring,
struct netmap_vp_adapter *, void *private_data);
/* these are redefined in case of no VALE support */
int netmap_get_vale_na(struct nmreq_header *hdr, struct netmap_adapter **na,
struct netmap_mem_d *nmd, int create);
void *netmap_vale_create(const char *bdg_name, int *return_status);
int netmap_vale_destroy(const char *bdg_name, void *auth_token);
#else /* !WITH_VALE */
#define netmap_bdg_learning(_1, _2, _3, _4) 0
#define netmap_get_vale_na(_1, _2, _3, _4) 0
#define netmap_bdg_create(_1, _2) NULL
#define netmap_bdg_destroy(_1, _2) 0
#endif /* !WITH_VALE */
#ifdef WITH_PIPES
/* max number of pipes per device */
#define NM_MAXPIPES 64 /* XXX this should probably be a sysctl */
void netmap_pipe_dealloc(struct netmap_adapter *);
int netmap_get_pipe_na(struct nmreq_header *hdr, struct netmap_adapter **na,
struct netmap_mem_d *nmd, int create);
#else /* !WITH_PIPES */
#define NM_MAXPIPES 0
#define netmap_pipe_alloc(_1, _2) 0
#define netmap_pipe_dealloc(_1)
#define netmap_get_pipe_na(hdr, _2, _3, _4) \
((strchr(hdr->nr_name, '{') != NULL || strchr(hdr->nr_name, '}') != NULL) ? EOPNOTSUPP : 0)
#endif
#ifdef WITH_MONITOR
int netmap_get_monitor_na(struct nmreq_header *hdr, struct netmap_adapter **na,
struct netmap_mem_d *nmd, int create);
void netmap_monitor_stop(struct netmap_adapter *na);
#else
#define netmap_get_monitor_na(hdr, _2, _3, _4) \
(((struct nmreq_register *)(uintptr_t)hdr->nr_body)->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX) ? EOPNOTSUPP : 0)
#endif
#ifdef WITH_NMNULL
int netmap_get_null_na(struct nmreq_header *hdr, struct netmap_adapter **na,
struct netmap_mem_d *nmd, int create);
#else /* !WITH_NMNULL */
#define netmap_get_null_na(hdr, _2, _3, _4) \
(((struct nmreq_register *)(uintptr_t)hdr->nr_body)->nr_flags & (NR_MONITOR_TX | NR_MONITOR_RX) ? EOPNOTSUPP : 0)
#endif /* WITH_NMNULL */
#ifdef CONFIG_NET_NS
struct net *netmap_bns_get(void);
void netmap_bns_put(struct net *);
void netmap_bns_getbridges(struct nm_bridge **, u_int *);
#else
extern struct nm_bridge *nm_bridges;
#define netmap_bns_get()
#define netmap_bns_put(_1)
#define netmap_bns_getbridges(b, n) \
do { *b = nm_bridges; *n = NM_BRIDGES; } while (0)
#endif
/* Various prototypes */
int netmap_poll(struct netmap_priv_d *, int events, NM_SELRECORD_T *td);
int netmap_init(void);
void netmap_fini(void);
int netmap_get_memory(struct netmap_priv_d* p);
void netmap_dtor(void *data);
int netmap_ioctl(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
struct thread *, int nr_body_is_user);
int netmap_ioctl_legacy(struct netmap_priv_d *priv, u_long cmd, caddr_t data,
struct thread *td);
size_t nmreq_size_by_type(uint16_t nr_reqtype);
/* netmap_adapter creation/destruction */
// #define NM_DEBUG_PUTGET 1
#ifdef NM_DEBUG_PUTGET
#define NM_DBG(f) __##f
void __netmap_adapter_get(struct netmap_adapter *na);
#define netmap_adapter_get(na) \
do { \
struct netmap_adapter *__na = na; \
nm_prinf("getting %p:%s (%d)", __na, (__na)->name, (__na)->na_refcount); \
__netmap_adapter_get(__na); \
} while (0)
int __netmap_adapter_put(struct netmap_adapter *na);
#define netmap_adapter_put(na) \
({ \
struct netmap_adapter *__na = na; \
nm_prinf("putting %p:%s (%d)", __na, (__na)->name, (__na)->na_refcount); \
__netmap_adapter_put(__na); \
})
#else /* !NM_DEBUG_PUTGET */
#define NM_DBG(f) f
void netmap_adapter_get(struct netmap_adapter *na);
int netmap_adapter_put(struct netmap_adapter *na);
#endif /* !NM_DEBUG_PUTGET */
/*
* module variables
*/
#define NETMAP_BUF_BASE(_na) ((_na)->na_lut.lut[0].vaddr)
#define NETMAP_BUF_SIZE(_na) ((_na)->na_lut.objsize)
extern int netmap_no_pendintr;
extern int netmap_verbose;
#ifdef CONFIG_NETMAP_DEBUG
extern int netmap_debug; /* for debugging */
#else /* !CONFIG_NETMAP_DEBUG */
#define netmap_debug (0)
#endif /* !CONFIG_NETMAP_DEBUG */
enum { /* debug flags */
NM_DEBUG_ON = 1, /* generic debug messages */
NM_DEBUG_HOST = 0x2, /* debug host stack */
NM_DEBUG_RXSYNC = 0x10, /* debug on rxsync/txsync */
NM_DEBUG_TXSYNC = 0x20,
NM_DEBUG_RXINTR = 0x100, /* debug on rx/tx intr (driver) */
NM_DEBUG_TXINTR = 0x200,
NM_DEBUG_NIC_RXSYNC = 0x1000, /* debug on rx/tx intr (driver) */
NM_DEBUG_NIC_TXSYNC = 0x2000,
NM_DEBUG_MEM = 0x4000, /* verbose memory allocations/deallocations */
NM_DEBUG_VALE = 0x8000, /* debug messages from memory allocators */
NM_DEBUG_BDG = NM_DEBUG_VALE,
};
extern int netmap_txsync_retry;
extern int netmap_generic_hwcsum;
extern int netmap_generic_mit;
extern int netmap_generic_ringsize;
extern int netmap_generic_rings;
#ifdef linux
extern int netmap_generic_txqdisc;
#endif
/*
* NA returns a pointer to the struct netmap adapter from the ifp.
* WNA is os-specific and must be defined in glue code.
*/
#define NA(_ifp) ((struct netmap_adapter *)WNA(_ifp))
/*
* we provide a default implementation of NM_ATTACH_NA/NM_DETACH_NA
* based on the WNA field.
* Glue code may override this by defining its own NM_ATTACH_NA
*/
#ifndef NM_ATTACH_NA
/*
* On old versions of FreeBSD, NA(ifp) is a pspare. On linux we
* overload another pointer in the netdev.
*
* We check if NA(ifp) is set and its first element has a related
* magic value. The capenable is within the struct netmap_adapter.
*/
#define NETMAP_MAGIC 0x52697a7a
#define NM_NA_VALID(ifp) (NA(ifp) && \
((uint32_t)(uintptr_t)NA(ifp) ^ NA(ifp)->magic) == NETMAP_MAGIC )
#define NM_ATTACH_NA(ifp, na) do { \
WNA(ifp) = na; \
if (NA(ifp)) \
NA(ifp)->magic = \
((uint32_t)(uintptr_t)NA(ifp)) ^ NETMAP_MAGIC; \
} while(0)
#define NM_RESTORE_NA(ifp, na) WNA(ifp) = na;
#define NM_DETACH_NA(ifp) do { WNA(ifp) = NULL; } while (0)
#define NM_NA_CLASH(ifp) (NA(ifp) && !NM_NA_VALID(ifp))
#endif /* !NM_ATTACH_NA */
#define NM_IS_NATIVE(ifp) (NM_NA_VALID(ifp) && NA(ifp)->nm_dtor == netmap_hw_dtor)
#if defined(__FreeBSD__)
/* Assigns the device IOMMU domain to an allocator.
* Returns -ENOMEM in case the domain is different */
#define nm_iommu_group_id(dev) (-1)
/* Callback invoked by the dma machinery after a successful dmamap_load */
static void netmap_dmamap_cb(__unused void *arg,
__unused bus_dma_segment_t * segs, __unused int nseg, __unused int error)
{
}
/* bus_dmamap_load wrapper: call aforementioned function if map != NULL.
* XXX can we do it without a callback ?
*/
static inline int
netmap_load_map(struct netmap_adapter *na,
bus_dma_tag_t tag, bus_dmamap_t map, void *buf)
{
if (map)
bus_dmamap_load(tag, map, buf, NETMAP_BUF_SIZE(na),
netmap_dmamap_cb, NULL, BUS_DMA_NOWAIT);
return 0;
}
static inline void
netmap_unload_map(struct netmap_adapter *na,
bus_dma_tag_t tag, bus_dmamap_t map)
{
if (map)
bus_dmamap_unload(tag, map);
}
#define netmap_sync_map(na, tag, map, sz, t)
/* update the map when a buffer changes. */
static inline void
netmap_reload_map(struct netmap_adapter *na,
bus_dma_tag_t tag, bus_dmamap_t map, void *buf)
{
if (map) {
bus_dmamap_unload(tag, map);
bus_dmamap_load(tag, map, buf, NETMAP_BUF_SIZE(na),
netmap_dmamap_cb, NULL, BUS_DMA_NOWAIT);
}
}
#elif defined(_WIN32)
#else /* linux */
int nm_iommu_group_id(bus_dma_tag_t dev);
#include <linux/dma-mapping.h>
/*
* on linux we need
* dma_map_single(&pdev->dev, virt_addr, len, direction)
* dma_unmap_single(&adapter->pdev->dev, phys_addr, len, direction)
*/
#if 0
struct e1000_buffer *buffer_info = &tx_ring->buffer_info[l];
/* set time_stamp *before* dma to help avoid a possible race */
buffer_info->time_stamp = jiffies;
buffer_info->mapped_as_page = false;
buffer_info->length = len;
//buffer_info->next_to_watch = l;
/* reload dma map */
dma_unmap_single(&adapter->pdev->dev, buffer_info->dma,
NETMAP_BUF_SIZE, DMA_TO_DEVICE);
buffer_info->dma = dma_map_single(&adapter->pdev->dev,
addr, NETMAP_BUF_SIZE, DMA_TO_DEVICE);
if (dma_mapping_error(&adapter->pdev->dev, buffer_info->dma)) {
nm_prerr("dma mapping error");
/* goto dma_error; See e1000_put_txbuf() */
/* XXX reset */
}
tx_desc->buffer_addr = htole64(buffer_info->dma); //XXX
#endif
static inline int
netmap_load_map(struct netmap_adapter *na,
bus_dma_tag_t tag, bus_dmamap_t map, void *buf, u_int size)
{
if (map) {
*map = dma_map_single(na->pdev, buf, size,
DMA_BIDIRECTIONAL);
if (dma_mapping_error(na->pdev, *map)) {
*map = 0;
return ENOMEM;
}
}
return 0;
}
static inline void
netmap_unload_map(struct netmap_adapter *na,
bus_dma_tag_t tag, bus_dmamap_t map, u_int sz)
{
if (*map) {
dma_unmap_single(na->pdev, *map, sz,
DMA_BIDIRECTIONAL);
}
}
#ifdef NETMAP_LINUX_HAVE_DMASYNC
static inline void
netmap_sync_map_cpu(struct netmap_adapter *na,
bus_dma_tag_t tag, bus_dmamap_t map, u_int sz, enum txrx t)
{
if (*map) {
dma_sync_single_for_cpu(na->pdev, *map, sz,
(t == NR_TX ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
}
}
static inline void
netmap_sync_map_dev(struct netmap_adapter *na,
bus_dma_tag_t tag, bus_dmamap_t map, u_int sz, enum txrx t)
{
if (*map) {
dma_sync_single_for_device(na->pdev, *map, sz,
(t == NR_TX ? DMA_TO_DEVICE : DMA_FROM_DEVICE));
}
}
static inline void
netmap_reload_map(struct netmap_adapter *na,
bus_dma_tag_t tag, bus_dmamap_t map, void *buf)
{
u_int sz = NETMAP_BUF_SIZE(na);
if (*map) {
dma_unmap_single(na->pdev, *map, sz,
DMA_BIDIRECTIONAL);
}
*map = dma_map_single(na->pdev, buf, sz,
DMA_BIDIRECTIONAL);
}
#else /* !NETMAP_LINUX_HAVE_DMASYNC */
#define netmap_sync_map_cpu(na, tag, map, sz, t)
#define netmap_sync_map_dev(na, tag, map, sz, t)
#endif /* NETMAP_LINUX_HAVE_DMASYNC */
#endif /* linux */
/*
* functions to map NIC to KRING indexes (n2k) and vice versa (k2n)
*/
static inline int
netmap_idx_n2k(struct netmap_kring *kr, int idx)
{
int n = kr->nkr_num_slots;
if (likely(kr->nkr_hwofs == 0)) {
return idx;
}
idx += kr->nkr_hwofs;
if (idx < 0)
return idx + n;
else if (idx < n)
return idx;
else
return idx - n;
}
static inline int
netmap_idx_k2n(struct netmap_kring *kr, int idx)
{
int n = kr->nkr_num_slots;
if (likely(kr->nkr_hwofs == 0)) {
return idx;
}
idx -= kr->nkr_hwofs;
if (idx < 0)
return idx + n;
else if (idx < n)
return idx;
else
return idx - n;
}
/* Entries of the look-up table. */
#ifdef __FreeBSD__
struct lut_entry {
void *vaddr; /* virtual address. */
vm_paddr_t paddr; /* physical address. */
};
#else /* linux & _WIN32 */
/* dma-mapping in linux can assign a buffer a different address
* depending on the device, so we need to have a separate
* physical-address look-up table for each na.
* We can still share the vaddrs, though, therefore we split
* the lut_entry structure.
*/
struct lut_entry {
void *vaddr; /* virtual address. */
};
struct plut_entry {
vm_paddr_t paddr; /* physical address. */
};
#endif /* linux & _WIN32 */
struct netmap_obj_pool;
/* alignment for netmap buffers */
#define NM_BUF_ALIGN 64
/*
* NMB return the virtual address of a buffer (buffer 0 on bad index)
* PNMB also fills the physical address
*/
static inline void *
NMB(struct netmap_adapter *na, struct netmap_slot *slot)
{
struct lut_entry *lut = na->na_lut.lut;
uint32_t i = slot->buf_idx;
return (unlikely(i >= na->na_lut.objtotal)) ?
lut[0].vaddr : lut[i].vaddr;
}
static inline void *
PNMB(struct netmap_adapter *na, struct netmap_slot *slot, uint64_t *pp)
{
uint32_t i = slot->buf_idx;
struct lut_entry *lut = na->na_lut.lut;
struct plut_entry *plut = na->na_lut.plut;
void *ret = (i >= na->na_lut.objtotal) ? lut[0].vaddr : lut[i].vaddr;
#ifdef _WIN32
*pp = (i >= na->na_lut.objtotal) ? (uint64_t)plut[0].paddr.QuadPart : (uint64_t)plut[i].paddr.QuadPart;
#else
*pp = (i >= na->na_lut.objtotal) ? plut[0].paddr : plut[i].paddr;
#endif
return ret;
}
static inline void
nm_write_offset(struct netmap_kring *kring,
struct netmap_slot *slot, uint64_t offset)
{
slot->ptr = (slot->ptr & ~kring->offset_mask) |
(offset & kring->offset_mask);
}
static inline uint64_t
nm_get_offset(struct netmap_kring *kring, struct netmap_slot *slot)
{
uint64_t offset = (slot->ptr & kring->offset_mask);
if (unlikely(offset > kring->offset_max))
offset = kring->offset_max;
return offset;
}
static inline void *
NMB_O(struct netmap_kring *kring, struct netmap_slot *slot)
{
void *addr = NMB(kring->na, slot);
return (char *)addr + nm_get_offset(kring, slot);
}
static inline void *
PNMB_O(struct netmap_kring *kring, struct netmap_slot *slot, uint64_t *pp)
{
void *addr = PNMB(kring->na, slot, pp);
uint64_t offset = nm_get_offset(kring, slot);
addr = (char *)addr + offset;
*pp += offset;
return addr;
}
/*
* Structure associated to each netmap file descriptor.
* It is created on open and left unbound (np_nifp == NULL).
* A successful NIOCREGIF will set np_nifp and the first few fields;
* this is protected by a global lock (NMG_LOCK) due to low contention.
*
* np_refs counts the number of references to the structure: one for the fd,
* plus (on FreeBSD) one for each active mmap which we track ourselves
* (linux automatically tracks them, but FreeBSD does not).
* np_refs is protected by NMG_LOCK.
*
* Read access to the structure is lock free, because ni_nifp once set
* can only go to 0 when nobody is using the entry anymore. Readers
* must check that np_nifp != NULL before using the other fields.
*/
struct netmap_priv_d {
struct netmap_if * volatile np_nifp; /* netmap if descriptor. */
struct netmap_adapter *np_na;
struct ifnet *np_ifp;
uint32_t np_flags; /* from the ioctl */
u_int np_qfirst[NR_TXRX],
np_qlast[NR_TXRX]; /* range of tx/rx rings to scan */
uint16_t np_txpoll;
uint16_t np_kloop_state; /* use with NMG_LOCK held */
#define NM_SYNC_KLOOP_RUNNING (1 << 0)
#define NM_SYNC_KLOOP_STOPPING (1 << 1)
int np_sync_flags; /* to be passed to nm_sync */
int np_refs; /* use with NMG_LOCK held */
/* pointers to the selinfo to be used for selrecord.
* Either the local or the global one depending on the
* number of rings.
*/
NM_SELINFO_T *np_si[NR_TXRX];
/* In the optional CSB mode, the user must specify the start address
* of two arrays of Communication Status Block (CSB) entries, for the
* two directions (kernel read application write, and kernel write
* application read).
* The number of entries must agree with the number of rings bound to
* the netmap file descriptor. The entries corresponding to the TX
* rings are laid out before the ones corresponding to the RX rings.
*
* Array of CSB entries for application --> kernel communication
* (N entries). */
struct nm_csb_atok *np_csb_atok_base;
/* Array of CSB entries for kernel --> application communication
* (N entries). */
struct nm_csb_ktoa *np_csb_ktoa_base;
#ifdef linux
struct file *np_filp; /* used by sync kloop */
#endif /* linux */
};
struct netmap_priv_d *netmap_priv_new(void);
void netmap_priv_delete(struct netmap_priv_d *);
static inline int nm_kring_pending(struct netmap_priv_d *np)
{
struct netmap_adapter *na = np->np_na;
enum txrx t;
int i;
for_rx_tx(t) {
for (i = np->np_qfirst[t]; i < np->np_qlast[t]; i++) {
struct netmap_kring *kring = NMR(na, t)[i];
if (kring->nr_mode != kring->nr_pending_mode) {
return 1;
}
}
}
return 0;
}
/* call with NMG_LOCK held */
static __inline int
nm_si_user(struct netmap_priv_d *priv, enum txrx t)
{
return (priv->np_na != NULL &&
(priv->np_qlast[t] - priv->np_qfirst[t] > 1));
}
#ifdef WITH_PIPES
int netmap_pipe_txsync(struct netmap_kring *txkring, int flags);
int netmap_pipe_rxsync(struct netmap_kring *rxkring, int flags);
int netmap_pipe_krings_create_both(struct netmap_adapter *na,
struct netmap_adapter *ona);
void netmap_pipe_krings_delete_both(struct netmap_adapter *na,
struct netmap_adapter *ona);
int netmap_pipe_reg_both(struct netmap_adapter *na,
struct netmap_adapter *ona);
#endif /* WITH_PIPES */
#ifdef WITH_MONITOR
struct netmap_monitor_adapter {
struct netmap_adapter up;
struct netmap_priv_d priv;
uint32_t flags;
};
#endif /* WITH_MONITOR */
#ifdef WITH_GENERIC
/*
* generic netmap emulation for devices that do not have
* native netmap support.
*/
int generic_netmap_attach(struct ifnet *ifp);
int generic_rx_handler(struct ifnet *ifp, struct mbuf *m);;
int nm_os_catch_rx(struct netmap_generic_adapter *gna, int intercept);
int nm_os_catch_tx(struct netmap_generic_adapter *gna, int intercept);
int na_is_generic(struct netmap_adapter *na);
/*
* the generic transmit routine is passed a structure to optionally
* build a queue of descriptors, in an OS-specific way.
* The payload is at addr, if non-null, and the routine should send or queue
* the packet, returning 0 if successful, 1 on failure.
*
* At the end, if head is non-null, there will be an additional call
* to the function with addr = NULL; this should tell the OS-specific
* routine to send the queue and free any resources. Failure is ignored.
*/
struct nm_os_gen_arg {
struct ifnet *ifp;
void *m; /* os-specific mbuf-like object */
void *head, *tail; /* tailq, if the OS-specific routine needs to build one */
void *addr; /* payload of current packet */
u_int len; /* packet length */
u_int ring_nr; /* packet length */
u_int qevent; /* in txqdisc mode, place an event on this mbuf */
};
int nm_os_generic_xmit_frame(struct nm_os_gen_arg *);
int nm_os_generic_find_num_desc(struct ifnet *ifp, u_int *tx, u_int *rx);
void nm_os_generic_find_num_queues(struct ifnet *ifp, u_int *txq, u_int *rxq);
void nm_os_generic_set_features(struct netmap_generic_adapter *gna);
static inline struct ifnet*
netmap_generic_getifp(struct netmap_generic_adapter *gna)
{
if (gna->prev)
return gna->prev->ifp;
return gna->up.up.ifp;
}
void netmap_generic_irq(struct netmap_adapter *na, u_int q, u_int *work_done);
//#define RATE_GENERIC /* Enables communication statistics for generic. */
#ifdef RATE_GENERIC
void generic_rate(int txp, int txs, int txi, int rxp, int rxs, int rxi);
#else
#define generic_rate(txp, txs, txi, rxp, rxs, rxi)
#endif
/*
* netmap_mitigation API. This is used by the generic adapter
* to reduce the number of interrupt requests/selwakeup
* to clients on incoming packets.
*/
void nm_os_mitigation_init(struct nm_generic_mit *mit, int idx,
struct netmap_adapter *na);
void nm_os_mitigation_start(struct nm_generic_mit *mit);
void nm_os_mitigation_restart(struct nm_generic_mit *mit);
int nm_os_mitigation_active(struct nm_generic_mit *mit);
void nm_os_mitigation_cleanup(struct nm_generic_mit *mit);
#else /* !WITH_GENERIC */
#define generic_netmap_attach(ifp) (EOPNOTSUPP)
#define na_is_generic(na) (0)
#endif /* WITH_GENERIC */
/* Shared declarations for the VALE switch. */
/*
* Each transmit queue accumulates a batch of packets into
* a structure before forwarding. Packets to the same
* destination are put in a list using ft_next as a link field.
* ft_frags and ft_next are valid only on the first fragment.
*/
struct nm_bdg_fwd { /* forwarding entry for a bridge */
void *ft_buf; /* netmap or indirect buffer */
uint8_t ft_frags; /* how many fragments (only on 1st frag) */
uint16_t ft_offset; /* dst port (unused) */
uint16_t ft_flags; /* flags, e.g. indirect */
uint16_t ft_len; /* src fragment len */
uint16_t ft_next; /* next packet to same destination */
};
/* struct 'virtio_net_hdr' from linux. */
struct nm_vnet_hdr {
#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 /* Use csum_start, csum_offset */
#define VIRTIO_NET_HDR_F_DATA_VALID 2 /* Csum is valid */
uint8_t flags;
#define VIRTIO_NET_HDR_GSO_NONE 0 /* Not a GSO frame */
#define VIRTIO_NET_HDR_GSO_TCPV4 1 /* GSO frame, IPv4 TCP (TSO) */
#define VIRTIO_NET_HDR_GSO_UDP 3 /* GSO frame, IPv4 UDP (UFO) */
#define VIRTIO_NET_HDR_GSO_TCPV6 4 /* GSO frame, IPv6 TCP */
#define VIRTIO_NET_HDR_GSO_ECN 0x80 /* TCP has ECN set */
uint8_t gso_type;
uint16_t hdr_len;
uint16_t gso_size;
uint16_t csum_start;
uint16_t csum_offset;
};
#define WORST_CASE_GSO_HEADER (14+40+60) /* IPv6 + TCP */
/* Private definitions for IPv4, IPv6, UDP and TCP headers. */
struct nm_iphdr {
uint8_t version_ihl;
uint8_t tos;
uint16_t tot_len;
uint16_t id;
uint16_t frag_off;
uint8_t ttl;
uint8_t protocol;
uint16_t check;
uint32_t saddr;
uint32_t daddr;
/*The options start here. */
};
struct nm_tcphdr {
uint16_t source;
uint16_t dest;
uint32_t seq;
uint32_t ack_seq;
uint8_t doff; /* Data offset + Reserved */
uint8_t flags;
uint16_t window;
uint16_t check;
uint16_t urg_ptr;
};
struct nm_udphdr {
uint16_t source;
uint16_t dest;
uint16_t len;
uint16_t check;
};
struct nm_ipv6hdr {
uint8_t priority_version;
uint8_t flow_lbl[3];
uint16_t payload_len;
uint8_t nexthdr;
uint8_t hop_limit;
uint8_t saddr[16];
uint8_t daddr[16];
};
/* Type used to store a checksum (in host byte order) that hasn't been
* folded yet.
*/
#define rawsum_t uint32_t
rawsum_t nm_os_csum_raw(uint8_t *data, size_t len, rawsum_t cur_sum);
uint16_t nm_os_csum_ipv4(struct nm_iphdr *iph);
void nm_os_csum_tcpudp_ipv4(struct nm_iphdr *iph, void *data,
size_t datalen, uint16_t *check);
void nm_os_csum_tcpudp_ipv6(struct nm_ipv6hdr *ip6h, void *data,
size_t datalen, uint16_t *check);
uint16_t nm_os_csum_fold(rawsum_t cur_sum);
void bdg_mismatch_datapath(struct netmap_vp_adapter *na,
struct netmap_vp_adapter *dst_na,
const struct nm_bdg_fwd *ft_p,
struct netmap_ring *dst_ring,
u_int *j, u_int lim, u_int *howmany);
/* persistent virtual port routines */
int nm_os_vi_persist(const char *, struct ifnet **);
void nm_os_vi_detach(struct ifnet *);
void nm_os_vi_init_index(void);
/*
* kernel thread routines
*/
struct nm_kctx; /* OS-specific kernel context - opaque */
typedef void (*nm_kctx_worker_fn_t)(void *data);
/* kthread configuration */
struct nm_kctx_cfg {
long type; /* kthread type/identifier */
nm_kctx_worker_fn_t worker_fn; /* worker function */
void *worker_private;/* worker parameter */
int attach_user; /* attach kthread to user process */
};
/* kthread configuration */
struct nm_kctx *nm_os_kctx_create(struct nm_kctx_cfg *cfg,
void *opaque);
int nm_os_kctx_worker_start(struct nm_kctx *);
void nm_os_kctx_worker_stop(struct nm_kctx *);
void nm_os_kctx_destroy(struct nm_kctx *);
void nm_os_kctx_worker_setaff(struct nm_kctx *, int);
u_int nm_os_ncpus(void);
int netmap_sync_kloop(struct netmap_priv_d *priv,
struct nmreq_header *hdr);
int netmap_sync_kloop_stop(struct netmap_priv_d *priv);
#ifdef WITH_PTNETMAP
/* ptnetmap guest routines */
/*
* ptnetmap_memdev routines used to talk with ptnetmap_memdev device driver
*/
struct ptnetmap_memdev;
int nm_os_pt_memdev_iomap(struct ptnetmap_memdev *, vm_paddr_t *, void **,
uint64_t *);
void nm_os_pt_memdev_iounmap(struct ptnetmap_memdev *);
uint32_t nm_os_pt_memdev_ioread(struct ptnetmap_memdev *, unsigned int);
/*
* netmap adapter for guest ptnetmap ports
*/
struct netmap_pt_guest_adapter {
/* The netmap adapter to be used by netmap applications.
* This field must be the first, to allow upcast. */
struct netmap_hw_adapter hwup;
/* The netmap adapter to be used by the driver. */
struct netmap_hw_adapter dr;
/* Reference counter to track users of backend netmap port: the
* network stack and netmap clients.
* Used to decide when we need (de)allocate krings/rings and
* start (stop) ptnetmap kthreads. */
int backend_users;
};
int netmap_pt_guest_attach(struct netmap_adapter *na,
unsigned int nifp_offset,
unsigned int memid);
bool netmap_pt_guest_txsync(struct nm_csb_atok *atok,
struct nm_csb_ktoa *ktoa,
struct netmap_kring *kring, int flags);
bool netmap_pt_guest_rxsync(struct nm_csb_atok *atok,
struct nm_csb_ktoa *ktoa,
struct netmap_kring *kring, int flags);
int ptnet_nm_krings_create(struct netmap_adapter *na);
void ptnet_nm_krings_delete(struct netmap_adapter *na);
void ptnet_nm_dtor(struct netmap_adapter *na);
/* Helper function wrapping nm_sync_kloop_appl_read(). */
static inline void
ptnet_sync_tail(struct nm_csb_ktoa *ktoa, struct netmap_kring *kring)
{
struct netmap_ring *ring = kring->ring;
/* Update hwcur and hwtail as known by the host. */
nm_sync_kloop_appl_read(ktoa, &kring->nr_hwtail, &kring->nr_hwcur);
/* nm_sync_finalize */
ring->tail = kring->rtail = kring->nr_hwtail;
}
#endif /* WITH_PTNETMAP */
#ifdef __FreeBSD__
/*
* FreeBSD mbuf allocator/deallocator in emulation mode:
*/
#if __FreeBSD_version < 1100000
/*
* For older versions of FreeBSD:
*
* We allocate EXT_PACKET mbuf+clusters, but need to set M_NOFREE
* so that the destructor, if invoked, will not free the packet.
* In principle we should set the destructor only on demand,
* but since there might be a race we better do it on allocation.
* As a consequence, we also need to set the destructor or we
* would leak buffers.
*/
/* mbuf destructor, also need to change the type to EXT_EXTREF,
* add an M_NOFREE flag, and then clear the flag and
* chain into uma_zfree(zone_pack, mf)
* (or reinstall the buffer ?)
*/
#define SET_MBUF_DESTRUCTOR(m, fn) do { \
(m)->m_ext.ext_free = (void *)fn; \
(m)->m_ext.ext_type = EXT_EXTREF; \
} while (0)
static int
void_mbuf_dtor(struct mbuf *m, void *arg1, void *arg2)
{
/* restore original mbuf */
m->m_ext.ext_buf = m->m_data = m->m_ext.ext_arg1;
m->m_ext.ext_arg1 = NULL;
m->m_ext.ext_type = EXT_PACKET;
m->m_ext.ext_free = NULL;
if (MBUF_REFCNT(m) == 0)
SET_MBUF_REFCNT(m, 1);
uma_zfree(zone_pack, m);
return 0;
}
static inline struct mbuf *
nm_os_get_mbuf(struct ifnet *ifp, int len)
{
struct mbuf *m;
(void)ifp;
m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
if (m) {
/* m_getcl() (mb_ctor_mbuf) has an assert that checks that
* M_NOFREE flag is not specified as third argument,
* so we have to set M_NOFREE after m_getcl(). */
m->m_flags |= M_NOFREE;
m->m_ext.ext_arg1 = m->m_ext.ext_buf; // XXX save
m->m_ext.ext_free = (void *)void_mbuf_dtor;
m->m_ext.ext_type = EXT_EXTREF;
nm_prdis(5, "create m %p refcnt %d", m, MBUF_REFCNT(m));
}
return m;
}
#else /* __FreeBSD_version >= 1100000 */
/*
* Newer versions of FreeBSD, using a straightforward scheme.
*
* We allocate mbufs with m_gethdr(), since the mbuf header is needed
* by the driver. We also attach a customly-provided external storage,
* which in this case is a netmap buffer. When calling m_extadd(), however
* we pass a NULL address, since the real address (and length) will be
* filled in by nm_os_generic_xmit_frame() right before calling
* if_transmit().
*
* The dtor function does nothing, however we need it since mb_free_ext()
* has a KASSERT(), checking that the mbuf dtor function is not NULL.
*/
#if __FreeBSD_version <= 1200050
static void void_mbuf_dtor(struct mbuf *m, void *arg1, void *arg2) { }
#else /* __FreeBSD_version >= 1200051 */
/* The arg1 and arg2 pointers argument were removed by r324446, which
* in included since version 1200051. */
static void void_mbuf_dtor(struct mbuf *m) { }
#endif /* __FreeBSD_version >= 1200051 */
#define SET_MBUF_DESTRUCTOR(m, fn) do { \
(m)->m_ext.ext_free = (fn != NULL) ? \
(void *)fn : (void *)void_mbuf_dtor; \
} while (0)
static inline struct mbuf *
nm_os_get_mbuf(struct ifnet *ifp, int len)
{
struct mbuf *m;
(void)ifp;
(void)len;
m = m_gethdr(M_NOWAIT, MT_DATA);
if (m == NULL) {
return m;
}
m_extadd(m, NULL /* buf */, 0 /* size */, void_mbuf_dtor,
NULL, NULL, 0, EXT_NET_DRV);
return m;
}
#endif /* __FreeBSD_version >= 1100000 */
#endif /* __FreeBSD__ */
struct nmreq_option * nmreq_getoption(struct nmreq_header *, uint16_t);
int netmap_init_bridges(void);
void netmap_uninit_bridges(void);
/* Functions to read and write CSB fields from the kernel. */
#if defined (linux)
#define CSB_READ(csb, field, r) (get_user(r, &csb->field))
#define CSB_WRITE(csb, field, v) (put_user(v, &csb->field))
#else /* ! linux */
#define CSB_READ(csb, field, r) (r = fuword32(&csb->field))
#define CSB_WRITE(csb, field, v) (suword32(&csb->field, v))
#endif /* ! linux */
/* some macros that may not be defined */
#ifndef ETH_HLEN
#define ETH_HLEN 6
#endif
#ifndef ETH_FCS_LEN
#define ETH_FCS_LEN 4
#endif
#ifndef VLAN_HLEN
#define VLAN_HLEN 4
#endif
#endif /* _NET_NETMAP_KERN_H_ */

File Metadata

Mime Type
application/octet-stream
Expires
Mon, Apr 22, 12:20 AM (2 d)
Storage Engine
chunks
Storage Format
Chunks
Storage Handle
OA.1Fw3mAcuD
Default Alt Text
(7 MB)

Event Timeline